@@ -123,9 +123,9 @@ MySQL 8.x 中实现的索引新特性:
123
123
124
124
![ ] ( https://oss.javaguide.cn/github/javaguide/open-source-project/cluster-index.png )
125
125
126
- ## 二级索引(辅助索引)
126
+ ## 二级索引
127
127
128
- ** 二级索引又称为辅助索引 ,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。**
128
+ ** 二级索引(Secondary Index)又称为辅助索引 ,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。**
129
129
130
130
唯一索引,普通索引,前缀索引等索引属于二级索引。
131
131
@@ -147,7 +147,7 @@ PS: 不懂的同学可以暂存疑,慢慢往下看,后面会有答案的,
147
147
148
148
#### 聚簇索引介绍
149
149
150
- ** 聚簇索引即索引结构和数据一起存放的索引 ,并不是一种单独的索引类型。InnoDB 中的主键索引就属于聚簇索引。**
150
+ ** 聚簇索引(Clustered Index)即索引结构和数据一起存放的索引 ,并不是一种单独的索引类型。InnoDB 中的主键索引就属于聚簇索引。**
151
151
152
152
在 MySQL 中,InnoDB 引擎的表的 ` .ibd ` 文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。
153
153
@@ -167,7 +167,7 @@ PS: 不懂的同学可以暂存疑,慢慢往下看,后面会有答案的,
167
167
168
168
#### 非聚簇索引介绍
169
169
170
- ** 非聚簇索引即索引结构和数据分开存放的索引 ,并不是一种单独的索引类型。二级索引(辅助索引)就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。**
170
+ ** 非聚簇索引(Non-Clustered Index)即索引结构和数据分开存放的索引 ,并不是一种单独的索引类型。二级索引(辅助索引)就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。**
171
171
172
172
非聚簇索引的叶子节点并不一定存放数据的指针,因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
173
173
@@ -214,21 +214,90 @@ SELECT id FROM table WHERE id=1;
214
214
215
215
### 覆盖索引
216
216
217
- 如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“ 覆盖索引” 。我们知道在 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
217
+ 如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为 ** 覆盖索引(Covering Index) ** 。我们知道在 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
218
218
219
219
** 覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。**
220
220
221
- > 如主键索引,如果一条 SQL 需要查询主键,那么正好根据主键索引就可以查到主键。
222
- >
223
- > 再如普通索引,如果一条 SQL 需要查询 name,name 字段正好有索引,
221
+ > 如主键索引,如果一条 SQL 需要查询主键,那么正好根据主键索引就可以查到主键。再如普通索引,如果一条 SQL 需要查询 name,name 字段正好有索引,
224
222
> 那么直接根据这个索引就可以查到数据,也无需回表。
225
223
226
224
![ 覆盖索引] ( https://oss.javaguide.cn/github/javaguide/database/mysql20210420165341868.png )
227
225
226
+ 我们这里简单演示一下覆盖索引的效果。
227
+
228
+ 1、创建一个名为 ` cus_order ` 的表,来实际测试一下这种排序方式。为了测试方便, ` cus_order ` 这张表只有 ` id ` 、` score ` 、` name ` 这 3 个字段。
229
+
230
+ ``` sql
231
+ CREATE TABLE `cus_order ` (
232
+ ` id` int (11 ) unsigned NOT NULL AUTO_INCREMENT,
233
+ ` score` int (11 ) NOT NULL ,
234
+ ` name` varchar (11 ) NOT NULL DEFAULT ' ' ,
235
+ PRIMARY KEY (` id` )
236
+ ) ENGINE= InnoDB AUTO_INCREMENT= 100000 DEFAULT CHARSET= utf8mb4;
237
+ ```
238
+
239
+ 2、定义一个简单的存储过程(PROCEDURE)来插入 100w 测试数据。
240
+
241
+ ``` sql
242
+ DELIMITER ;;
243
+ CREATE DEFINER= ` root` @` %` PROCEDURE ` BatchinsertDataToCusOder` (IN start_num INT ,IN max_num INT )
244
+ BEGIN
245
+ DECLARE i INT default start_num;
246
+ WHILE i < max_num DO
247
+ insert into ` cus_order` (` id` , ` score` , ` name` )
248
+ values (i,RAND() * 1000000 ,CONCAT(' user' , i));
249
+ SET i = i + 1 ;
250
+ END WHILE;
251
+ END;;
252
+ DELIMITER ;
253
+ ```
254
+
255
+ 存储过程定义完成之后,我们执行存储过程即可!
256
+
257
+ ``` sql
258
+ CALL BatchinsertDataToCusOder(1 , 1000000 ); # 插入100w+的随机数据
259
+ ```
260
+
261
+ 等待一会,100w 的测试数据就插入完成了!
262
+
263
+ 3、创建覆盖索引并使用 ` EXPLAIN ` 命令分析。
264
+
265
+ 为了能够对这 100w 数据按照 ` score ` 进行排序,我们需要执行下面的 SQL 语句。
266
+
267
+ ``` sql
268
+ SELECT ` score` ,` name` FROM ` cus_order` ORDER BY ` score` DESC ;# 降序排序
269
+ ```
270
+
271
+ 使用 ` EXPLAIN ` 命令分析这条 SQL 语句,通过 ` Extra ` 这一列的 ` Using filesort ` ,我们发现是没有用到覆盖索引的。
272
+
273
+ ![ ] ( https://oss.javaguide.cn/github/javaguide/mysql/not-using-covering-index-demo.png )
274
+
275
+ 不过这也是理所应当,毕竟我们现在还没有创建索引呢!
276
+
277
+ 我们这里以 ` score ` 和 ` name ` 两个字段建立联合索引:
278
+
279
+ ``` sql
280
+ ALTER TABLE ` cus_order` ADD INDEX id_score_name(score, name);
281
+ ```
282
+
283
+ 创建完成之后,再用 ` EXPLAIN ` 命令分析再次分析这条 SQL 语句。
284
+
285
+ ![ ] ( https://oss.javaguide.cn/github/javaguide/mysql/using-covering-index-demo.png )
286
+
287
+ 通过 ` Extra ` 这一列的 ` Using index ` ,说明这条 SQL 语句成功使用了覆盖索引。
288
+
289
+ 关于 ` EXPLAIN ` 命令的详细介绍请看:[ MySQL 执行计划分析] ( https://javaguide.cn/database/mysql/mysql-query-execution-plan.html ) 这篇文章。
290
+
228
291
### 联合索引
229
292
230
293
使用表中的多个字段创建索引,就是 ** 联合索引** ,也叫 ** 组合索引** 或 ** 复合索引** 。
231
294
295
+ 以 ` score ` 和 ` name ` 两个字段建立联合索引:
296
+
297
+ ``` sql
298
+ ALTER TABLE ` cus_order` ADD INDEX id_score_name(score, name);
299
+ ```
300
+
232
301
### 最左前缀匹配原则
233
302
234
303
最左前缀匹配原则指的是,在使用联合索引时,** MySQL** 会根据联合索引中的字段顺序,从左到右依次到查询条件中去匹配,如果查询条件中存在与联合索引中最左侧字段相匹配的字段,则就会使用该字段过滤一批数据,直至联合索引中全部字段匹配完成,或者在执行过程中遇到范围查询(如 ** ` > ` ** 、** ` < ` ** )才会停止匹配。对于 ** ` >= ` ** 、** ` <= ` ** 、** ` BETWEEN ` ** 、** ` like ` ** 前缀匹配的范围查询,并不会停止匹配。所以,我们在使用联合索引时,可以将区分度高的字段放在最左边,这也可以过滤更多数据。
@@ -326,4 +395,4 @@ mysql> EXPLAIN SELECT `score`,`name` FROM `cus_order` ORDER BY `score` DESC;
326
395
| filtered | 按表条件过滤后,留存的记录数的百分比 |
327
396
| Extra | 附加信息 |
328
397
329
- 篇幅问题,我这里只是简单介绍了一下 MySQL 执行计划,详细介绍请看:[ SQL 的执行计划 ] ( https://javaguide.cn/database/mysql/mysql-query-execution-plan.html ) 这篇文章。
398
+ 篇幅问题,我这里只是简单介绍了一下 MySQL 执行计划,详细介绍请看:[ MySQL 执行计划分析 ] ( https://javaguide.cn/database/mysql/mysql-query-execution-plan.html ) 这篇文章。
0 commit comments