## 什么是索引

所有数据库都支持索引，索引文件通过额外占用磁盘空间，提供了一个快速查询记录的方案。查询时先查询索引文件，根据索引文件的指示再去查询真实的数据，在数据量较大时有明显的性能优势。

索引有两个用途：

1. 在集合中为查询条件的**字段**建立索引，是保证数据库性能、提升用户体验的重要手段。
2. 索引可以控制字段中各记录的唯一性。比如某字段被设为唯一索引，则这个字段在整个数据记录集合中的值不会有重复。如果是普通唯一索引，则意味着该字段不能为null。如果是稀疏索引，则代表可以为null，但不为null的值不能重复。

如果您的查询操作包含了过滤条件（包含等值测试和范围过滤）或者是排序功能，或者需要唯一性，则要考虑给集合的相关字段添加索引。通常来说需要为以下方法/属性内用到的**字段**添加索引`where、match、orderBy、sort`，还包括clientDB内`getTree`和`getTreePath`的`startWith属性`。

如果相关字段没有设为索引，当数据表的记录数量变大后，查询会变慢甚至超时报错。这点尤其需要注意。已经有一些开发者遭遇线上故障。开发时没有配索引，因为数据量小而没有性能问题。上线后数据量越来越大，查询越来越慢，直到超时，引发线上事故。

## 添加索引

### uniCloud web控制台添加

1. 进入 [uniCloud 控制台](https://console.cloud.tencent.com/tcb)。
2. 切换到【云数据库】标签页，并选择需要添加索引的集合，进入索引管理 tab 页，如下图。

  ![web控制台添加索引](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/15d24770-5faf-11eb-8d54-21c4ca4ce5d7.jpg)  
  
3. 添加索引。
  
  ![添加索引](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/fca53140-1d91-11eb-880a-0db19f4f74bb.jpg)

注意：
- 索引是支持多字段组合的，所以不是简单的设某个字段为索引。而是需要先给索引起一个name，然后在该索引下配置1个或多个字段。
- 索引字段的排序，指查询语句中的orderby的顺序。如果实际查询是需要倒叙，那么索引就设为倒叙，这样查询速度才能变快。

### 在db_init.json内配置集合索引

在db_init.json内用如下写法可以给特定集合设置索引（推荐在服务空间初始化时使用）

```json
{
  "opendb-news-article": {
    "data": [],
    "index":[{
      "IndexName": "user_article_", // 索引名称
      "MgoKeySchema": { // 索引规则
          "MgoIndexKeys": [{
              "Name": "user_id", // 索引字段
              "Direction": "1" // 索引方向，1：ASC-升序，-1：DESC-降序，2dsphere：地理位置
          },{
              "Name": "article_id", // 索引字段
              "Direction": "1" // 索引方向，1：ASC-升序，-1：DESC-降序，2dsphere：地理位置
          }],
          "MgoIsUnique": false // 索引是否唯一
      }
    }]
  }
}
```

## 单字段索引

您可以为查询条件对应的字段创建单字段索引，如果该字段是嵌套字段，可以使用"点表示法" 。例如对如下格式的记录中的`color`字段进行索引时，可以用`style.color`表示。

```json
{
  "_id": "",
  "style": {
    "color": ""
  }
}
```

在设置单字段索引时，可任意指定索引的排序为升序或降序，数据库总能在对索引字段的排序查询中，进行正确的排序。

## 组合索引

组合索引即一个索引包含多个字段。当查询条件使用的字段包含在索引定义的所有字段或前缀字段里时，会命中索引，优化查询性能。

> 索引前缀即组合索引的字段中定义的前 1 到多个字段，例如对集合 **students** 中 **name**, **age**, **score** 三个字段按顺序定义了组合索引，那么该索引的前缀包含
> 
> - **name**
> - **name, age**
> 
> 能命中索引的查询字段组合包含
> 
> - **name**
> - **name, age**
> - **name, age, score**
> 

```json
{
  "_id": "1",
  "name": "luke",
  "age": 26,
  "score": 80
}
```

组合索引**字段顺序决定组合索引效果**

例如定义组合索引分别为 **name, age** 与 **age, name** 是不同的。当组合索引为 **name, age** 时，其索引前缀为 **name**, 对字段 **name** 的查询可以命中 **name, age** 索引, 而对字段 **age** 的查询无法命中该索引，因为 **age** 不属于 **name, age** 的前缀（反之字段 **age** 能命中 **age, name** 索引）。


### 地理位置索引

云数据库目前支持建立平面几何的地理位置索引，使用地理位置查询功能时，必须为地理位置数据的字段建立地理位置索引。

例如对含地理位置字段 **point** 的集合建立地理位置索引：

```json
{
  "_id": "",
  "point": new db.Geo.Point(50, 50)
}
```

![地理位置索引](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/21b31780-5fb0-11eb-bdc1-8bd33eb6adaa.jpg)

### TTL索引@ttl

ttl索引用于设置数据过期时间，并在数据过期后进行删除。**仅阿里云支持**

配置方式如下：

![](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-f184e7c3-1912-41b2-b81f-435d1b37c7b4/25187fa4-dc40-48a2-ba00-e6ad8c604c39.jpg)

**注意**

- 数据删除并非实时，mongoDB会在后台定时进行数据清理
- 启用ttl索引后仅可添加一个索引字段
- 仅支持对存储了日期类型（并非时间戳）的字段进行设置ttl索引

## 索引使用注意事项

### 唯一性限制

创建索引时，索引属性选择**唯一**，即可添加唯一性限制。此限制会要求集合中**索引字段对应的值不能重复**。

例如，某个集合内建立了索引字段 `foo`，且属性为“唯一”，那么在这个集合内，要求不能存在 `foo` 字段相同的文档。

**注意**

假如**记录中不存在某个字段，则对索引字段来说其值默认为 null**。如果索引有唯一性限制，则不允许存在两个或以上的该字段为空 / 不存在该字段的记录。

针对上述问题，阿里云支持将索引设置为[稀疏索引](uniCloud/db-index.md?id=sparse)，腾讯云暂不支持稀疏索引。

### 稀疏索引@sparse

> 仅阿里云支持

稀疏索引适用于需要某个字段唯一，但是这个字段又可能为空的场景。以用户表为例，用户可能是通过邮箱注册、也可能是通过手机号注册，所以需要保证邮箱、手机号唯一且允许为空，这时候就可以分别将邮箱、手机号的索引设置为稀疏索引来处理这种场景。**注意uni-id内支持多应用用户隔离（同一个手机号、邮箱等可以在不同应用注册账号），手机号、邮箱等都不可设置唯一索引**

**配置索引为稀疏索引**

![](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/daf77fd0-5fb3-11eb-b680-7980c8a877b8.jpg)

### 字段大小限制

- 索引字段的值大小限制**不能超过 1024 字节**。
- 添加索引时，如果集合中已有文档索引字段内容超过 1024 字节，添加索引时将报错。
- 已设置索引的字段，如果插入一个文档，文档中该字段内容超过 1024 字节将会报错。

**即不要对大段的文字（例如新闻的内容）设置索引**

> 每个英文字母(不分大小写)占一字节的空间，每个中文汉字占两字节的空间。

### 正则表达式

正则查询无法使用索引提升性能。
