# mysql 中的索引类型

在 mysql 中，有两类索引：聚簇索引(clustered) 和 非聚簇索引(nonclustered)。正常情况下，InnoDB 的主键使用 聚簇索引，MyISAM 使用的是 非聚簇索引。这个差异也给两个引擎的最优使用场景带来了不同，使用时注意测试、考察。

先说下 聚簇索引 和 非聚簇索引 的差异：

* 聚簇索引
  * 主键和表数据一起存储：主键索引的叶结点存储主键值的同时也包含了行数据。二级索引的叶结点存储行的主键值
* 非聚簇索引
  * 索引和表数据分开存储：所有的节点都是索引，叶子节点存储的是 索引 和 索引对应的数据记录地址。主键索引和二级索引在存储上没有任何区别。

## B+Tree

这里，我们需要说一下，常用的索引结构 B+ tree, ![BTree](https://github.com/joyous-x/mbook/tree/57d70d25d77cb23236d1894dae5ba11fd191061c/rsc/db/B-Tree.png) ![B+Tree](https://github.com/joyous-x/mbook/tree/57d70d25d77cb23236d1894dae5ba11fd191061c/rsc/db/B+Tree.png)

从图中也可以看到，B+树与B树的不同在于： 1. 所有数据都存储在叶子节点，非叶子节点不存储真正的data 2. 为所有叶子节点增加了一个链指针

## 聚簇索引

根据上述信息，我们可以想象，对于聚簇索引来说: 1. 插入速度严重依赖于插入顺序，按照主键的顺序插入是最快的方式，否则将会出现页分裂，严重影响性能。

同时, 由于主键发生变更的话, 索引结构需要再平衡、以及相关数据的移动, 这个代价相对较大, 所以一般设置主键为不可更新的.

因此，对于InnoDB表，我们一般都会定义一个自增的ID列为主键。

页分裂是指，当行的主键值要求必须将这一行插入到某个已满的页中时，存储引擎会将该页分裂成两个页面来容纳该行，这就是一次分裂操作。页分裂会导致表占用更多的磁盘空间。

1. 二级索引访问需要两次索引查找，第一次找到主键值，第二次根据主键值找到行数据。而非聚簇索引，主键索引和非主键索引，都可以直接定位数据的具体位置，然后进行访问即可。
2. 当通过聚簇索引查找目标数据时理论上比非聚簇索引要快，因为非聚簇索引定位到对应主键时还要多一次目标记录寻址,即多一次I/O。
3. 当需要取出一定范围内的数据时，用聚簇索引要比用非聚簇索引好

对于聚簇索引的存储引擎，因为数据是根据主键顺序连续存储的，数据的物理存放顺序与索引顺序是一致的，所以只要索引是相邻的，那么对应的数据一定也是相邻地存放在磁盘上的，索引结构相对紧凑，磁盘碎片少，效率也高。可以很方便的取出一定范围内的数据。

对于聚簇索引也有一些不足之处，如： 1. 聚簇索引可能导致全表扫描变慢，尤其是行比较稀疏，或者由于页分裂导致数据存储不连续的时候。

1. 顺序主键在高并发工作负载下，在InnoDB中按主键顺序插入可能会造成明显的争用。

主键的上界会成为“热点”。因为所有的插入都发生在这里，所以并发插入可能导致间隙锁竞争。

另一个热点可能是auto\_increment锁机制；如果遇到这个问题，则可能需要考虑重新设计表或者应用，比如应用层面生成单调递增的主键ID，插表不使用auto\_increment机制，或者更改innodb\_autonc\_lock\_mode配置。

## 非聚簇索

对于非聚簇索引而言， 1. 二级索引可能比想象的要更大，因为在二给索引的叶子节点包含了引用行的主键列。

1. 由于插入的数据，是顺序写入的，与索引顺序无关，所以可以不必用单调递增ID。
2. 但是 MyISAM 的锁是表级：写入数据，会使用全局(独占)表锁，读取数据也会使用全局(非独占)表锁。而 Innodb 使用行级锁，同时有 MVCC 加持读性能。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://joyous-x.gitbook.io/mbook/part-iii-database/mysql_index.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
