88
99<!-- TOC depthFrom:2 depthTo:3 -->
1010
11- - [ 一、索引简介] ( #一索引简介 )
12- - [ 索引的优缺点] ( #索引的优缺点 )
13- - [ 何时使用索引] ( #何时使用索引 )
14- - [ 二、索引的数据结构] ( #二索引的数据结构 )
15- - [ 哈希索引] ( #哈希索引 )
16- - [ B 树索引] ( #b-树索引 )
17- - [ 全文索引] ( #全文索引 )
18- - [ 空间数据索引] ( #空间数据索引 )
19- - [ 三、索引的类型] ( #三索引的类型 )
20- - [ 四、索引的策略] ( #四索引的策略 )
21- - [ 索引基本原则] ( #索引基本原则 )
22- - [ 独立的列] ( #独立的列 )
23- - [ 覆盖索引] ( #覆盖索引 )
24- - [ 前缀索引] ( #前缀索引 )
25- - [ 最左前缀匹配原则] ( #最左前缀匹配原则 )
26- - [ = 和 in 可以乱序] ( #-和-in-可以乱序 )
27- - [ 五、索引最佳实践] ( #五索引最佳实践 )
28- - [ 参考资料] ( #参考资料 )
11+ - [ 1. 索引简介] ( #1-索引简介 )
12+ - [ 1.1. 索引的优缺点] ( #11-索引的优缺点 )
13+ - [ 1.2. 何时使用索引] ( #12-何时使用索引 )
14+ - [ 2. 索引的数据结构] ( #2-索引的数据结构 )
15+ - [ 2.1. 哈希索引] ( #21-哈希索引 )
16+ - [ 2.2. B 树索引] ( #22-b-树索引 )
17+ - [ 2.3. 全文索引] ( #23-全文索引 )
18+ - [ 2.4. 空间数据索引] ( #24-空间数据索引 )
19+ - [ 3. 索引的类型] ( #3-索引的类型 )
20+ - [ 3.1. 主键索引(` PRIMARY ` )] ( #31-主键索引primary )
21+ - [ 3.2. 唯一索引(` UNIQUE ` )] ( #32-唯一索引unique )
22+ - [ 3.3. 普通索引(` INDEX ` )] ( #33-普通索引index )
23+ - [ 3.4. 全文索引(` FULLTEXT ` )] ( #34-全文索引fulltext )
24+ - [ 3.5. 联合索引] ( #35-联合索引 )
25+ - [ 4. 索引的策略] ( #4-索引的策略 )
26+ - [ 4.1. 索引基本原则] ( #41-索引基本原则 )
27+ - [ 4.2. 独立的列] ( #42-独立的列 )
28+ - [ 4.3. 覆盖索引] ( #43-覆盖索引 )
29+ - [ 4.4. 使用索引来排序] ( #44-使用索引来排序 )
30+ - [ 4.5. 前缀索引] ( #45-前缀索引 )
31+ - [ 4.6. 最左前缀匹配原则] ( #46-最左前缀匹配原则 )
32+ - [ 4.7. = 和 in 可以乱序] ( #47--和-in-可以乱序 )
33+ - [ 5. 索引最佳实践] ( #5-索引最佳实践 )
34+ - [ 6. 参考资料] ( #6-参考资料 )
2935
3036<!-- /TOC -->
3137
32- ## 一、 索引简介
38+ ## 1. 索引简介
3339
3440** _ 索引优化应该是查询性能优化的最有效手段_ ** 。
3541
36- ### 索引的优缺点
42+ ### 1.1. 索引的优缺点
3743
3844B+ 树索引,按照顺序存储数据,所以 Mysql 可以用来做 ORDER BY 和 GROUP BY 操作。因为数据是有序的,所以 B+ 树也就会将相关的列值都存储在一起。最后,因为索引中存储了实际的列值,所以某些查询只使用索引就能够完成全部查询。
3945
@@ -51,7 +57,7 @@ B+ 树索引,按照顺序存储数据,所以 Mysql 可以用来做 ORDER BY
5157- ** 索引需要占用额外的物理空间** ,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立组合索引那么需要的空间就会更大。
5258- 写操作(` INSERT ` /` UPDATE ` /` DELETE ` )时很可能需要更新索引,导致数据库的写操作性能降低。
5359
54- ### 何时使用索引
60+ ### 1.2. 何时使用索引
5561
5662> 索引能够轻易将查询性能提升几个数量级。
5763
@@ -68,9 +74,9 @@ B+ 树索引,按照顺序存储数据,所以 Mysql 可以用来做 ORDER BY
6874- 列名不经常出现在 ` WHERE ` 或连接(` JOIN ` )条件中 - 索引就会经常不命中,没有意义,还增加空间开销。
6975- 对于特大型表,建立和使用索引的代价将随之增长。可以考虑使用分区技术或 Nosql。
7076
71- ## 二、 索引的数据结构
77+ ## 2. 索引的数据结构
7278
73- ### 哈希索引
79+ ### 2.1. 哈希索引
7480
7581> Hash 索引只有精确匹配索引所有列的查询才有效。
7682
@@ -89,7 +95,7 @@ B+ 树索引,按照顺序存储数据,所以 Mysql 可以用来做 ORDER BY
8995- 哈希索引** 只支持等值比较查询** ,不支持任何范围查询,如 ` WHERE price > 100 ` 。
9096- 哈希索引有** 可能出现哈希冲突** ,出现哈希冲突时,必须遍历链表中所有的行指针,逐行比较,直到找到符合条件的行。
9197
92- ### B 树索引
98+ ### 2.2. B 树索引
9399
94100通常我们所说的索引是指` B-Tree ` 索引,它是目前关系型数据库中查找数据最为常用和有效的索引,大多数存储引擎都支持这种索引。使用` B-Tree ` 这个术语,是因为 MySQL 在` CREATE TABLE ` 或其它语句中使用了这个关键字,但实际上不同的存储引擎可能使用不同的数据结构,比如 InnoDB 就是使用的` B+Tree ` 。
95101
@@ -141,25 +147,25 @@ B+ 树索引适用于**全键值查找**、**键值范围查找**和**键前缀
141147
142148这时候我们就要优先考虑上一段提到的“尽量使用主键查询”原则,直接将这个索引设置为主键,可以避免每次查询需要搜索两棵树。
143149
144- ### 全文索引
150+ ### 2.3. 全文索引
145151
146152MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
147153
148154全文索引一般使用倒排索引实现,它记录着关键词到其所在文档的映射。
149155
150156InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
151157
152- ### 空间数据索引
158+ ### 2.4. 空间数据索引
153159
154160MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。
155161
156162必须使用 GIS 相关的函数来维护数据。
157163
158- ## 三、 索引的类型
164+ ## 3. 索引的类型
159165
160166主流的关系型数据库一般都支持以下索引类型:
161167
162- ### 主键索引(` PRIMARY ` )
168+ ### 3.1. 主键索引(` PRIMARY ` )
163169
164170主键索引:一种特殊的唯一索引,不允许有空值。一个表只能有一个主键(在 InnoDB 中本质上即聚簇索引),一般是在建表的时候同时创建主键索引。
165171
@@ -171,7 +177,7 @@ CREATE TABLE `table` (
171177)
172178```
173179
174- ### 唯一索引(` UNIQUE ` )
180+ ### 3.2. 唯一索引(` UNIQUE ` )
175181
176182唯一索引:** 索引列的值必须唯一,但允许有空值** 。如果是组合索引,则列值的组合必须唯一。
177183
@@ -182,7 +188,7 @@ CREATE TABLE `table` (
182188)
183189```
184190
185- ### 普通索引(` INDEX ` )
191+ ### 3.3. 普通索引(` INDEX ` )
186192
187193普通索引:最基本的索引,没有任何限制。
188194
@@ -193,7 +199,7 @@ CREATE TABLE `table` (
193199)
194200```
195201
196- ### 全文索引(` FULLTEXT ` )
202+ ### 3.4. 全文索引(` FULLTEXT ` )
197203
198204全文索引:主要用来查找文本中的关键字,而不是直接与索引中的值相比较。
199205
@@ -207,7 +213,7 @@ CREATE TABLE `table` (
207213)
208214```
209215
210- ### 联合索引
216+ ### 3.5. 联合索引
211217
212218组合索引:多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
213219
@@ -218,7 +224,7 @@ CREATE TABLE `table` (
218224)
219225```
220226
221- ## 四、 索引的策略
227+ ## 4. 索引的策略
222228
223229假设有以下表:
224230
@@ -234,15 +240,15 @@ CREATE TABLE `t` (
234240) ENGINE= InnoDB;
235241```
236242
237- ### 索引基本原则
243+ ### 4.1. 索引基本原则
238244
239245- ** 索引不是越多越好,不要为所有列都创建索引** 。要考虑到索引的维护代价、空间占用和查询时回表的代价。索引一定是按需创建的,并且要尽可能确保足够轻量。一旦创建了多字段的联合索引,我们要考虑尽可能利用索引本身完成数据查询,减少回表的成本。
240246- 要** 尽量避免冗余和重复索引** 。
241247- 要** 考虑删除未使用的索引** 。
242248- ** 尽量的扩展索引,不要新建索引** 。
243249- ** 频繁作为 ` WHERE ` 过滤条件的列应该考虑添加索引** 。
244250
245- ### 独立的列
251+ ### 4.2. 独立的列
246252
247253** “独立的列” 是指索引列不能是表达式的一部分,也不能是函数的参数** 。
248254
@@ -257,9 +263,9 @@ SELECT actor_id FROM actor WHERE actor_id + 1 = 5;
257263SELECT ... WHERE TO_DAYS(current_date ) - TO_DAYS(date_col) <= 10 ;
258264```
259265
260- ### 覆盖索引
266+ ### 4.3. 覆盖索引
261267
262- ** 覆盖索引是指,索引上的信息足够满足查询请求,不需要再回到主键索引上去取数据 。**
268+ ** 覆盖索引是指,索引上的信息足够满足查询请求,不需要回表查询数据 。**
263269
264270【示例】范围查询
265271
@@ -290,7 +296,7 @@ select * from T where k between 3 and 5
290296
291297** 由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。**
292298
293- ### 使用索引来排序
299+ ### 4.4. 使用索引来排序
294300
295301Mysql 有两种方式可以生成排序结果:通过排序操作;或者按索引顺序扫描。
296302
@@ -302,7 +308,7 @@ Mysql 有两种方式可以生成排序结果:通过排序操作;或者按
3023082 . 从索引 (city,name,age) 取下一个记录,同样取出这三个字段的值,作为结果集的一部分直接返回;
3033093 . 重复执行步骤 2,直到查到第 1000 条记录,或者是不满足 city='杭州’条件时循环结束。
304310
305- ### 前缀索引
311+ ### 4.5. 前缀索引
306312
307313有时候需要索引很长的字符列,这会让索引变得大且慢。
308314
@@ -331,9 +337,11 @@ select
331337from SUser;
332338```
333339
334- 当然,使用前缀索引很可能会损失区分度,所以你需要预先设定一个可以接受的损失比例,比如 5%。然后,在返回的 L4~ L7 中,找出不小于 L \* 95% 的值,假设这里 L6、L7 都满足,你就可以选择前缀长度为 6。
340+ 当然,** 使用前缀索引很可能会损失区分度** ,所以你需要预先设定一个可以接受的损失比例,比如 5%。然后,在返回的 L4~ L7 中,找出不小于 L \* 95% 的值,假设这里 L6、L7 都满足,你就可以选择前缀长度为 6。
335341
336- ### 最左前缀匹配原则
342+ 此外,** ` order by ` 无法使用前缀索引,无法把前缀索引用作覆盖索引** 。
343+
344+ ### 4.6. 最左前缀匹配原则
337345
338346不只是索引的全部定义,只要满足最左前缀,就可以利用索引来加速检索。这个最左前缀可以是联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符。
339347
@@ -366,19 +374,19 @@ customer_id_selectivity: 0.0373
366374 COUNT(*): 16049
367375```
368376
369- ### = 和 in 可以乱序
377+ ### 4.7. = 和 in 可以乱序
370378
371379** 不需要考虑 ` = ` 、` IN ` 等的顺序** ,Mysql 会自动优化这些条件的顺序,以匹配尽可能多的索引列。
372380
373381【示例】如有索引 (a, b, c, d),查询条件 ` c > 3 and b = 2 and a = 1 and d < 4 ` 与 ` a = 1 and c > 3 and b = 2 and d < 4 ` 等顺序都是可以的,MySQL 会自动优化为 a = 1 and b = 2 and c > 3 and d < 4,依次命中 a、b、c、d。
374382
375- ## 五、 索引最佳实践
383+ ## 5. 索引最佳实践
376384
377385创建了索引,并非一定有效。比如不满足前缀索引、最左前缀匹配原则、查询条件涉及函数计算等情况都无法使用索引。此外,即使 SQL 本身符合索引的使用条件,MySQL 也会通过评估各种查询方式的代价,来决定是否走索引,以及走哪个索引。
378386
379387因此,在尝试通过索引进行 SQL 性能优化的时候,务必通过执行计划(` EXPLAIN ` )或实际的效果来确认索引是否能有效改善性能问题,否则增加了索引不但没解决性能问题,还增加了数据库增删改的负担。如果对 EXPLAIN 给出的执行计划有疑问的话,你还可以利用 ` optimizer_trace ` 查看详细的执行计划做进一步分析。
380388
381- ## 参考资料
389+ ## 6. 参考资料
382390
383391- [ 《高性能 MySQL》] ( https://book.douban.com/subject/23008813/ )
384392- [ 数据库两大神器【索引和锁】] ( https://juejin.im/post/5b55b842f265da0f9e589e79 )
0 commit comments