首页 / Prisma ORM 入门教程 / 索引设计与命名映射

Prisma ORM 入门教程

索引设计与命名映射

本教程共 54 篇 · 第 15 篇 · 更新于 2026-08-11 · 约 4 分钟阅读

Prisma索引@@index左前缀@map命名映射性能优化

本节目标:掌握 @@index 的配置参数与左前缀规则,学会用 @map/@@map 解耦命名。

数据一多,查询就慢。索引(index)是数据库为加速查询准备的数据结构,相当于书的目录。代价是写入变慢、占用空间。加索引,是在读写之间找平衡。

基础索引:单列与复合

Prisma 用 @@index 声明索引:

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  author    String
  createdAt DateTime @default(now())

  @@index([author])
  @@index([author, createdAt(sort: Desc)])
}

单列索引服务等值查询。复合索引要遵守左前缀规则:查询条件必须命中最左边的字段,索引才生效。上面的第二个索引能加速 WHERE author = ?,也能加速 WHERE author = ? AND createdAt > ?,但单独查 createdAt 用不上它。

所以复合索引的字段顺序要按查询模式来排:最常过滤的放最前。

@unique@@unique 会顺带创建唯一索引,既保证不重复,又加速查询:

model User {
  id    Int    @id @default(autoincrement())
  email String @unique
}

高级参数:type、sort、length、where

PostgreSQL 的 @@index 支持自定义访问类型:

model Session {
  id    Int    @id @default(autoincrement())
  token String
  tags  String[]

  @@index([token], type: Hash)  // 只服务等值查询
  @@index([tags], type: Gin)    // 服务数组包含查询
}
  • Hash:等值查询快、省空间,但不支持范围查询
  • Gin:适合 JSONB、数组、全文检索
  • Gist、SpGist、Brin:分别服务网络地址、文本模式、时间序列类数据

sort 控制索引内列的排序方向,复合索引里常见 createdAt(sort: Desc);length 是 MySQL 专属,给超长字符串做前缀索引;Gin/Gist 索引还能用 ops 指定操作符类。

部分索引(partial index)只索引满足条件的行,体积小、写入快:

generator client {
  provider        = "prisma-client"
  output          = "./generated"
  previewFeatures = ["partialIndexes"]
}

model User {
  id        Int       @id @default(autoincrement())
  email     String
  status    String
  deletedAt DateTime?

  @@unique([email], where: raw("status = 'active'"))
  @@index([email], where: { deletedAt: null })
}

where 有两种写法:raw(“SQL 片段”) 最灵活,对象字面量有类型安全。部分索引支持 PostgreSQL、SQLite、SQL Server、CockroachDB,MySQL 不支持。

Tip

软删除场景是部分索引的经典用法:WHERE "deletedAt" IS NULL 让唯一索引只约束未删除的行,允许重复的已删除数据存在。

索引与查询匹配

判断索引有没有用,看查询的 where、orderBy 是否命中。常用手段:

  • 覆盖查询:让索引包含 where 和 orderBy 用到的全部列
  • 用 EXPLAIN 看执行计划,确认索引真被采用
  • 别过度索引:每多一个索引,写入就多一分开销
Note

不要「为每个字段都建索引」。先跑真实查询,用 EXPLAIN 找出慢的,再针对性地加。

命名映射:@map 与 @@map

数据库里叫 comments 的表,内省后会变成模型 Comment,并自动补上 @@map("comments")。模型名与表名就这样解耦了:

model Comment {
  content String @map("comment_text")
  email   String @map("commenter_email")

  @@map("comments")
}

好处是:数据库沿用团队的 snake_case 规范,Prisma Client API 里却用漂亮的 camelCase。

索引和约束的名字也能自定义,用 map 参数:

model Post {
  id    Int    @id
  title String

  @@index([title], map: "my_custom_index_name")
}

不写 map 时,Prisma 按约定自动取名:{表名}_pkey{表名}_{列名}_key{表名}_{列名}_idx{表名}_{列名}_fkey。注意命名以数据库里的实际表名、列名为准,不是 Schema 里的模型名。

Note

内省时,只有不符合 Prisma 默认约定的索引名才会渲染出 map 参数,符合约定的省略不写,保持 Schema 干净。

参考来源

  • Prisma 官方文档:Indexes
  • Prisma 官方文档:Database mapping
  • Mapagam:Implementing Database Indexes、Working with Model Attributes