8
8
9
9
<!-- TOC depthFrom:2 depthTo:3 -->
10
10
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-参考资料 )
29
35
30
36
<!-- /TOC -->
31
37
32
- ## 一、 索引简介
38
+ ## 1. 索引简介
33
39
34
40
** _ 索引优化应该是查询性能优化的最有效手段_ ** 。
35
41
36
- ### 索引的优缺点
42
+ ### 1.1. 索引的优缺点
37
43
38
44
B+ 树索引,按照顺序存储数据,所以 Mysql 可以用来做 ORDER BY 和 GROUP BY 操作。因为数据是有序的,所以 B+ 树也就会将相关的列值都存储在一起。最后,因为索引中存储了实际的列值,所以某些查询只使用索引就能够完成全部查询。
39
45
@@ -51,7 +57,7 @@ B+ 树索引,按照顺序存储数据,所以 Mysql 可以用来做 ORDER BY
51
57
- ** 索引需要占用额外的物理空间** ,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立组合索引那么需要的空间就会更大。
52
58
- 写操作(` INSERT ` /` UPDATE ` /` DELETE ` )时很可能需要更新索引,导致数据库的写操作性能降低。
53
59
54
- ### 何时使用索引
60
+ ### 1.2. 何时使用索引
55
61
56
62
> 索引能够轻易将查询性能提升几个数量级。
57
63
@@ -68,9 +74,9 @@ B+ 树索引,按照顺序存储数据,所以 Mysql 可以用来做 ORDER BY
68
74
- 列名不经常出现在 ` WHERE ` 或连接(` JOIN ` )条件中 - 索引就会经常不命中,没有意义,还增加空间开销。
69
75
- 对于特大型表,建立和使用索引的代价将随之增长。可以考虑使用分区技术或 Nosql。
70
76
71
- ## 二、 索引的数据结构
77
+ ## 2. 索引的数据结构
72
78
73
- ### 哈希索引
79
+ ### 2.1. 哈希索引
74
80
75
81
> Hash 索引只有精确匹配索引所有列的查询才有效。
76
82
@@ -89,7 +95,7 @@ B+ 树索引,按照顺序存储数据,所以 Mysql 可以用来做 ORDER BY
89
95
- 哈希索引** 只支持等值比较查询** ,不支持任何范围查询,如 ` WHERE price > 100 ` 。
90
96
- 哈希索引有** 可能出现哈希冲突** ,出现哈希冲突时,必须遍历链表中所有的行指针,逐行比较,直到找到符合条件的行。
91
97
92
- ### B 树索引
98
+ ### 2.2. B 树索引
93
99
94
100
通常我们所说的索引是指` B-Tree ` 索引,它是目前关系型数据库中查找数据最为常用和有效的索引,大多数存储引擎都支持这种索引。使用` B-Tree ` 这个术语,是因为 MySQL 在` CREATE TABLE ` 或其它语句中使用了这个关键字,但实际上不同的存储引擎可能使用不同的数据结构,比如 InnoDB 就是使用的` B+Tree ` 。
95
101
@@ -141,25 +147,25 @@ B+ 树索引适用于**全键值查找**、**键值范围查找**和**键前缀
141
147
142
148
这时候我们就要优先考虑上一段提到的“尽量使用主键查询”原则,直接将这个索引设置为主键,可以避免每次查询需要搜索两棵树。
143
149
144
- ### 全文索引
150
+ ### 2.3. 全文索引
145
151
146
152
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
147
153
148
154
全文索引一般使用倒排索引实现,它记录着关键词到其所在文档的映射。
149
155
150
156
InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
151
157
152
- ### 空间数据索引
158
+ ### 2.4. 空间数据索引
153
159
154
160
MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。
155
161
156
162
必须使用 GIS 相关的函数来维护数据。
157
163
158
- ## 三、 索引的类型
164
+ ## 3. 索引的类型
159
165
160
166
主流的关系型数据库一般都支持以下索引类型:
161
167
162
- ### 主键索引(` PRIMARY ` )
168
+ ### 3.1. 主键索引(` PRIMARY ` )
163
169
164
170
主键索引:一种特殊的唯一索引,不允许有空值。一个表只能有一个主键(在 InnoDB 中本质上即聚簇索引),一般是在建表的时候同时创建主键索引。
165
171
@@ -171,7 +177,7 @@ CREATE TABLE `table` (
171
177
)
172
178
```
173
179
174
- ### 唯一索引(` UNIQUE ` )
180
+ ### 3.2. 唯一索引(` UNIQUE ` )
175
181
176
182
唯一索引:** 索引列的值必须唯一,但允许有空值** 。如果是组合索引,则列值的组合必须唯一。
177
183
@@ -182,7 +188,7 @@ CREATE TABLE `table` (
182
188
)
183
189
```
184
190
185
- ### 普通索引(` INDEX ` )
191
+ ### 3.3. 普通索引(` INDEX ` )
186
192
187
193
普通索引:最基本的索引,没有任何限制。
188
194
@@ -193,7 +199,7 @@ CREATE TABLE `table` (
193
199
)
194
200
```
195
201
196
- ### 全文索引(` FULLTEXT ` )
202
+ ### 3.4. 全文索引(` FULLTEXT ` )
197
203
198
204
全文索引:主要用来查找文本中的关键字,而不是直接与索引中的值相比较。
199
205
@@ -207,7 +213,7 @@ CREATE TABLE `table` (
207
213
)
208
214
```
209
215
210
- ### 联合索引
216
+ ### 3.5. 联合索引
211
217
212
218
组合索引:多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
213
219
@@ -218,7 +224,7 @@ CREATE TABLE `table` (
218
224
)
219
225
```
220
226
221
- ## 四、 索引的策略
227
+ ## 4. 索引的策略
222
228
223
229
假设有以下表:
224
230
@@ -234,15 +240,15 @@ CREATE TABLE `t` (
234
240
) ENGINE= InnoDB;
235
241
```
236
242
237
- ### 索引基本原则
243
+ ### 4.1. 索引基本原则
238
244
239
245
- ** 索引不是越多越好,不要为所有列都创建索引** 。要考虑到索引的维护代价、空间占用和查询时回表的代价。索引一定是按需创建的,并且要尽可能确保足够轻量。一旦创建了多字段的联合索引,我们要考虑尽可能利用索引本身完成数据查询,减少回表的成本。
240
246
- 要** 尽量避免冗余和重复索引** 。
241
247
- 要** 考虑删除未使用的索引** 。
242
248
- ** 尽量的扩展索引,不要新建索引** 。
243
249
- ** 频繁作为 ` WHERE ` 过滤条件的列应该考虑添加索引** 。
244
250
245
- ### 独立的列
251
+ ### 4.2. 独立的列
246
252
247
253
** “独立的列” 是指索引列不能是表达式的一部分,也不能是函数的参数** 。
248
254
@@ -257,9 +263,9 @@ SELECT actor_id FROM actor WHERE actor_id + 1 = 5;
257
263
SELECT ... WHERE TO_DAYS(current_date ) - TO_DAYS(date_col) <= 10 ;
258
264
```
259
265
260
- ### 覆盖索引
266
+ ### 4.3. 覆盖索引
261
267
262
- ** 覆盖索引是指,索引上的信息足够满足查询请求,不需要再回到主键索引上去取数据 。**
268
+ ** 覆盖索引是指,索引上的信息足够满足查询请求,不需要回表查询数据 。**
263
269
264
270
【示例】范围查询
265
271
@@ -290,7 +296,7 @@ select * from T where k between 3 and 5
290
296
291
297
** 由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。**
292
298
293
- ### 使用索引来排序
299
+ ### 4.4. 使用索引来排序
294
300
295
301
Mysql 有两种方式可以生成排序结果:通过排序操作;或者按索引顺序扫描。
296
302
@@ -302,7 +308,7 @@ Mysql 有两种方式可以生成排序结果:通过排序操作;或者按
302
308
2 . 从索引 (city,name,age) 取下一个记录,同样取出这三个字段的值,作为结果集的一部分直接返回;
303
309
3 . 重复执行步骤 2,直到查到第 1000 条记录,或者是不满足 city='杭州’条件时循环结束。
304
310
305
- ### 前缀索引
311
+ ### 4.5. 前缀索引
306
312
307
313
有时候需要索引很长的字符列,这会让索引变得大且慢。
308
314
@@ -331,9 +337,11 @@ select
331
337
from SUser;
332
338
```
333
339
334
- 当然,使用前缀索引很可能会损失区分度,所以你需要预先设定一个可以接受的损失比例,比如 5%。然后,在返回的 L4~ L7 中,找出不小于 L \* 95% 的值,假设这里 L6、L7 都满足,你就可以选择前缀长度为 6。
340
+ 当然,** 使用前缀索引很可能会损失区分度** ,所以你需要预先设定一个可以接受的损失比例,比如 5%。然后,在返回的 L4~ L7 中,找出不小于 L \* 95% 的值,假设这里 L6、L7 都满足,你就可以选择前缀长度为 6。
335
341
336
- ### 最左前缀匹配原则
342
+ 此外,** ` order by ` 无法使用前缀索引,无法把前缀索引用作覆盖索引** 。
343
+
344
+ ### 4.6. 最左前缀匹配原则
337
345
338
346
不只是索引的全部定义,只要满足最左前缀,就可以利用索引来加速检索。这个最左前缀可以是联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符。
339
347
@@ -366,19 +374,19 @@ customer_id_selectivity: 0.0373
366
374
COUNT(*): 16049
367
375
```
368
376
369
- ### = 和 in 可以乱序
377
+ ### 4.7. = 和 in 可以乱序
370
378
371
379
** 不需要考虑 ` = ` 、` IN ` 等的顺序** ,Mysql 会自动优化这些条件的顺序,以匹配尽可能多的索引列。
372
380
373
381
【示例】如有索引 (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。
374
382
375
- ## 五、 索引最佳实践
383
+ ## 5. 索引最佳实践
376
384
377
385
创建了索引,并非一定有效。比如不满足前缀索引、最左前缀匹配原则、查询条件涉及函数计算等情况都无法使用索引。此外,即使 SQL 本身符合索引的使用条件,MySQL 也会通过评估各种查询方式的代价,来决定是否走索引,以及走哪个索引。
378
386
379
387
因此,在尝试通过索引进行 SQL 性能优化的时候,务必通过执行计划(` EXPLAIN ` )或实际的效果来确认索引是否能有效改善性能问题,否则增加了索引不但没解决性能问题,还增加了数据库增删改的负担。如果对 EXPLAIN 给出的执行计划有疑问的话,你还可以利用 ` optimizer_trace ` 查看详细的执行计划做进一步分析。
380
388
381
- ## 参考资料
389
+ ## 6. 参考资料
382
390
383
391
- [ 《高性能 MySQL》] ( https://book.douban.com/subject/23008813/ )
384
392
- [ 数据库两大神器【索引和锁】] ( https://juejin.im/post/5b55b842f265da0f9e589e79 )
0 commit comments