05 | 深入浅出索引(下)

select * from T where k between 3 and 5,需执行几次树搜索,扫描多少行?2、2

mysql> create  table T (

ID int primary  key,

k int NOT NULL  DEFAULT 0,

s varchar(16) NOT  NULL DEFAULT '',

index k(k))

engine=InnoDB;

insert into T  values(100,1,  'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');

InnoDB 的索引组织结构

执行流程:

1. k 索引树找 k=3 ,取得 ID = 300;

2. 再到 ID 索引树查到 ID=300 对应 R3;

3. 在 k 索引树取下一个值 k=5,取得 ID=500;

4. 再回到 ID 索引树查到 ID=500 对应的 R4;

5.  k 索引树取下一个值 k=6,不满足条件,循环结束。

回到主键索引树搜索的过程,为回表(步骤 2 和 4)。读了 k 索引树的 3 条记录( 1、3 和 5)

查询结果需数据只主键索引上有,不得不回表。如何优化、避免回表过程?

一、覆盖索引

select ID from T where k between 3 and 5,查 ID 的值(已经在 k 索引树上),不需回表。也就是说,在这个查询里面,索引 k “覆盖”查询需求,为覆盖索引

减少树搜索次数,也是优化手段。

索引 k 上读了三个记录,R3~R5,但对于 MySQL 的 Server 层来说,找引擎拿两条记录,扫描行数是 2

假设这个市民表的定义是这样的:

CREATE TABLE  `tuser` (

  `id` int(11) NOT NULL,

  `id_card` varchar(32) DEFAULT NULL,

  `name` varchar(32) DEFAULT NULL,

  `age` int(11) DEFAULT NULL,

  `ismale` tinyint(1) DEFAULT NULL,

  PRIMARY KEY (`id`),

  KEY `id_card` (`id_card`),

  KEY `name_age` (`name`,`age`)

) ENGINE=InnoDB

高频请求,根据身份证号查询姓名,联合索引就有意义。覆盖索引,不需回表查整行记录

索引维护有代价。建立冗余索引支持覆盖索引时就权衡考虑

二、最左前缀原则

按身份证号去查地址?怎么做?每种查询都设计一个索引,索引是不是太多

B+ 树这种索引结构,用索引最左前缀定位记录。(name,age)这个联合索引来分析。

图 2 (name,age)索引示意图

name= “张三”时,快速定位 ID4+向后遍历 get所有结果。

name like ‘张 %’"时,也能够用这个索引,查找 ID3,向后遍历,直到不满足

满足最左前缀,可利用索引加速检索。可以是联合索引最左 N 个字段,也可字符串索引最左 M 个字符。

在建立联合索引时,如何安排索引内字段顺序

索引复用能力。有了 (a,b) 联合索引后,不需要单独在 a 上建索引。第一原则是,调整顺序,可少维护

开头问题,并用索引支持“根据身份证号查询地址”,需同时维护 (a,b)、(b) 这两个索引。

考虑空间,name 比 age 字段大 ,创建(name,age) 联合索引和(age) 单字段索引。

三、索引下推

不符合最左前缀部分,会怎样?

联合索引(name, age)“名字第一个字是张,年龄10 岁男孩”

mysql> select * from tuser where name like '张%' and age=10 and ismale=1;

搜索索引树时, “张”找到 ID3。比全表扫描好。然后判断其他条件

索引下推优化(index condition pushdown), 索引遍历过程中,对索引包含字段先判断,直接过滤掉不满足条件的记录,减少回表次数。

图 3 无索引下推执行流程
图 4 索引下推执行流程

虚线箭头表示回表一次。

图 3 :在 (name,age) 索引里面我特意去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,按顺序把“name 第一个字是’张’”的记录一条条取出来回表。回表 4 次

图 4 跟图 3 的区别InnoDB 在 (name,age) 索引内部判断 age 是否等于 10不等跳过。回表 2 次

小结

覆盖索引、前缀索引、索引下推。满足语句需求情况下,少访问资源。问题:

主键索引也可用多个字段。

CREATE TABLE  `geek` (

  `a` int(11) NOT NULL,

  `b` int(11) NOT NULL,

  `c` int(11) NOT NULL,

  `d` int(11) NOT NULL,

  PRIMARY KEY (`a`,`b`),

  KEY `c` (`c`),

  KEY `ca` (`c`,`a`),

  KEY `cb` (`c`,`b`)

) ENGINE=InnoDB;

历史原因,需 a、b 做联合主键。

既然主键包含 a、b 这两个字段, c 上创建索引,已经包含了三个字段,为什么要创建“ca”“cb”这两个索引?同事说因为业务里面这样语句:

select * from  geek where c=N order by a limit 1;

select * from  geek where c=N order by b limit 1;

问题:解释对吗,为了这两个查询模式,这两个索引是否都是必须的?为什么呢?

答:最左匹配原则

–a-|–b--|–c--|–d--

1 2 3 d

1 3 2 d

1 4 3 d

2 1 3 d

2 2 2 d

2 3 4 d

主键 a,b: 聚簇索引组织顺序相当于 order by a,b ,先按 a ,再 b 排序,c 无序。


索引 ca  先 c ,再 a 排序,记录主键

–c--|–a--|–主键部分b-- (注意,这里不是 ab,而是只有 b)

2 1 3

2 2 2

3 1 2

3 1 4

3 2 1

4 2 3

跟索引 c 的数据一样


索引 cb 的组织是先按 c 排序,在按 b 排序,同时记录主键

–c--|–b--|–主键部分a-- (同上)

2 2 2

2 3 1

3 1 2

3 2 1

3 4 1

4 3 2

ca 去,cb 保留

评论1

线上表, 记录日志, 定期删除数据. 表实际10G, 索引30G. 阿里云上占了40G. 真金白银啊.

后来了解到是 InnoDB 引擎删除表记录,索引还在, 未释放。重新建表才能重建索引.

评论2

不影响排序结果的情况下,取出主键后,回表前,对获取到主键排序

不论是否使用 Multi-Range Read (MRR) 策略,SQL语句写法不变

你可能感兴趣的:(05 | 深入浅出索引(下))