开发环境:MySQL5.7.31
在MySQL中,索引(Index)是帮助高效获取数据的数据结构。
我们可以将数据库理解为一本书,数据库中的各个数据列(column)就是目录中的章节标题,行(row)就是章节的内容。而索引好比目录,将这些标题管理起来,这样我们找一篇文章的时候,可以根据标题在目录中寻找,这样比起一页一页翻书找标题快的多。
MySQL中索引最常用的数据结构是B+Tree,可以做到减少IO,加速查询,本篇在下文会详细描写B+树。另外还有一种数据结构是hash。
注:所有的MySql列类型(字段类型)都可以被索引,也就是可以给任意字段设置索引
索引的优点
索引的缺点
通过对优缺点的学习不难发现索引并不是越多越好,只有合理使用索引才能发挥索引的作用,提高开发效率,在这里提及几个建立索引常用原则:
where sex = "男"
,这得查出多少数据呀。这些使用原则并不全面,了解对索引的使用还可以从哪些字段适合建立索引、索引的优化等方面入手。
适合建立索引
不适合建立索引
MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。
索引列中的值必须是唯一的,但是允许为空值
是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引
也叫联合索引,指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀原则
也叫最左匹配原则,即最左优先,在检索数据时从组合索引的最左边开始匹配。
就是说一条sql语句用到了组合索引中最左边的索引,那么这条sql语句就可以利用这个组合索引去匹配。但是,如果遇到范围查询符号,如>, <, between, like
的时候就会停止匹配。
下面举一个简单的例子来说明
比如我们建立一个组合索引(a,b)
,这个组合索引包含了字段a和字段b,在where条件语句中,如下两个语句都能匹配:
a=1
a=1 and b=2
另外
b=2 and a=1
也会成功匹配,因为MySQL中的优化器会调整a, b的顺序,让它们与组合索引的顺序一致。
但是,单纯只有
b = 2
这种情况就匹配不到了
如果我们建立了索引为(a, b, c, d)
,那么下面这种情况:
a=1 and b=2 and c>3 and d=4
a,b,c三个字段能用到索引,但是d无法匹配到索引,因为之前的字段出现了范围查询
例1:
如下语句,该如何建立组合索引?
SELECT * FROM table WHERE a = 1 and b = 2 and c = 3;
不要只回到a,b,c。其实a,b,c、b,c,a、c,b,a等都可以,因为上面说过了mysql会自动调整顺序,但是顺序并不是随便放都可以,主要要将区分度高的字段放在前面,区分度低的字段放在后面。
例2
如下语句,该如何建立组合索引?
SELECT * FROM table WHERE a > 1 and b = 2;
不要回答a,b,这里应该建立b,a才对。如果是a,b,会因为a是范围查询,导致b匹配不到所以。因为mysql会自动调节顺序,b,a能让两个字段都匹配上。
例3
如下语句,该如何建立组合索引?
SELECT * FROM `table` WHERE a > 1 and b = 2 and c > 3;
(b,a)或者(b,c)都可以
例4
如下语句,该如何建立组合索引?
SELECT * FROM `table` WHERE a = 1 ORDER BY b;
a,b。因为当a = 1的时候,b相对有序,可以避免再次排序!
那么
SELECT * FROM `table` WHERE a > 1 ORDER BY b;
如何建立索引?
对(a)建立索引,因为a的值是一个范围,这个范围内b值是无序的,没有必要对(a,b)建立索引。
例5
如下语句,该如何建立组合索引?
SELECT * FROM `table` WHERE a IN (1,2,3) and b > 1;
还是对(a,b)建立索引,因为IN在这里可以视为等值引用,不会中止索引匹配,所以还是(a,b)!
全文索引,只有在MyISAM引擎上才能使用,只能在CHAR,VARCHAR,TEXT类型字段上使用全文索引,就是在一堆文字中,通过其中的某个关键字等,就能找到该字段所属的记录行。
比如有文本“好好学习,天天向上…”,通过“学习”就能找到这条记录
聚集索引,也叫聚簇索引。数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。
聚集索引就好比字典按字母排序,一个汉语字典,我们希望查找“张”,我们可以直接翻到字典的最后,找到zh开头,然后找到张。因为字典内容本身是按照拼音排版的,所以字典内容本身就是一个聚集索引。
补充:
InnoDB存储引擎表是索引组织表,即表中数据按照主键顺序存放。而聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页。聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。
由于实际的数据页只能按照一棵B+树进行排序,因此每张表只能拥有一个聚集索引。由于定义了数据的逻辑顺序,聚集索引能够特别快地访问针对范围值的查询。查询优化器能够快速发现某一段范围的数据页需要扫描。
聚集索引的存储并不是物理上连续的,而是逻辑上连续的。这其中有两点:一是前面说过的页通过双向链表链接,页按照主键的顺序排序;另一点是每个页中的记录也是通过双向链表进行维护的,物理存储上可以同样不按照主键存储。
也称为二级索引或辅助索引
就是我们在查询的时候,where后面需要写id之外的其他字段名称来进行查询,比如说是where name=xx,没法用到主键索引的效率,怎么办,就需要我们添加辅助索引了,给name添加一个辅助索引。同样以字典为例,在查找一个不认识的字的时候,我们可以先通过字典的偏旁部首目录,找到字在哪一页,然后通过页码找到“张”。因为字典不是根据偏旁部首排版的,所以需要通过两步才能真正找到。
补充:
对于辅助索引(Secondary Index),叶子节点并不包含行记录的全部数据。叶子节点除了包含键值以外,每个叶子节点中的索引行中还包含了一个书签(bookmark)。该书签用来告诉InnoDB存储引擎哪里可以找到与索引相对应的行数据。由于InnoDB存储引擎表是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。
当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后再通过主键索引来找到一个完整的行记录。
聚集索引和非聚集索引
CREATE TABLE 表名(
字段名 数据类型 [完整性约束条件],
……,
[UNIQUE | FULLTEXT | SPATIAL] INDEX | KEY
[索引名](字段名1 [(长度)] [ASC | DESC]) [USING 索引方法]
);
说明:
单列索引(示例):
CREATE TABLE projectfile (
id INT AUTO_INCREMENT COMMENT '附件id',
fileuploadercode VARCHAR(128) COMMENT '附件上传者code',
projectid INT COMMENT '项目id;此列受project表中的id列约束',
filename VARCHAR (512) COMMENT '附件名',
fileurl VARCHAR (512) COMMENT '附件下载地址',
filesize BIGINT COMMENT '附件大小,单位Byte',
-- 主键本身也是一种索引(注:也可以在上面的创建字段时使该字段主键自增)
PRIMARY KEY (id),
-- 主外键约束(注:project表中的id字段约束了此表中的projectid字段)
FOREIGN KEY (projectid) REFERENCES project (id),
-- 给projectid字段创建了唯一索引(注:也可以在上面的创建字段时使用unique来创建唯一索引)
UNIQUE INDEX (projectid),
-- 给fileuploadercode字段创建普通索引
INDEX (fileuploadercode)
-- 指定使用INNODB存储引擎(该引擎支持事务)、utf8字符编码
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT '项目附件表'
组合索引(示例):
CREATE TABLE projectfile (
id INT AUTO_INCREMENT COMMENT '附件id',
fileuploadercode VARCHAR(128) COMMENT '附件上传者code',
projectid INT COMMENT '项目id;此列受project表中的id列约束',
filename VARCHAR (512) COMMENT '附件名',
fileurl VARCHAR (512) COMMENT '附件下载地址',
filesize BIGINT COMMENT '附件大小,单位Byte',
-- 主键本身也是一种索引(注:也可以在上面的创建字段时使该字段主键自增)
PRIMARY KEY (id),
-- 创建组合索引
INDEX (fileuploadercode,projectid)
-- 指定使用INNODB存储引擎(该引擎支持事务)、utf8字符编码
ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL] INDEX | KEY [索引名] (字段名1 [(长度)] [ASC | DESC]) [USING 索引方法];
或
CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX 索引名 ON 表名(字段名) [USING 索引方法];
alter table user add primary key(id)
alter table user add unqiue(id)
alter table user add index index_name(id)
alter table user add index(username(4))
alter table user add fulltext(username)
alter table user add index index_name(id, age, gender)
(了解语法是show index即可)
查看索引的语法格式如下:
SHOW INDEX FROM <表名> [ FROM <数据库名>]
语法说明如下:
示例:
mysql> SHOW INDEX FROM tb_stu_info2\G
*************************** 1. row ***************************
Table: tb_stu_info2
Non_unique: 0
Key_name: height
Seq_in_index: 1
Column_name: height
Collation: A
Cardinality: 0
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
Index_comment:
1 row in set (0.03 sec)
其中各主要参数说明如下:
基本语法:
当不再需要索引时,可以使用 DROP INDEX
语句或 ALTER TABLE
语句来对索引进行删除。
语法格式:
DROP INDEX <索引名> ON <表名>
语法说明如下:
根据 ALTER TABLE 语句的语法可知,该语句也可以用于删除索引。具体使用方法是将 ALTER TABLE 语句的语法中部分指定为以下子句中的某一项。
注意:如果删除的列是索引的组成部分,那么在删除该列时,也会将该列从索引中删除;如果组成索引的所有列都被删除,那么整个索引将被删除。
【实例 1】删除表 tb_stu_info 中的索引,输入的 SQL 语句和执行结果如下所示。
mysql> DROP INDEX height
-> ON tb_stu_info;
Query OK, 0 rows affected (0.27 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE tb_stu_info\G
*************************** 1. row ***************************
Table: tb_stu_info
Create Table: CREATE TABLE `tb_stu_info` (
`id` int(11) NOT NULL,
`name` char(45) DEFAULT NULL,
`dept_id` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`height` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gb2312
1 row in set (0.00 sec)
【实例 2】删除表 tb_stu_info2 中名称为 id 的索引,输入的 SQL 语句和执行结果如下所示。
mysql> ALTER TABLE tb_stu_info2
-> DROP INDEX height;
Query OK, 0 rows affected (0.13 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE tb_stu_info2\G
*************************** 1. row ***************************
Table: tb_stu_info2
Create Table: CREATE TABLE `tb_stu_info2` (
`id` int(11) NOT NULL,
`name` char(45) DEFAULT NULL,
`dept_id` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`height` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gb2312
1 row in set (0.00 sec)
二叉树是一种二分查找树,有很好的查找性能,相当于二分查找。
但是当N比较大的时候,树的深度比较高。数据查询的时间主要依赖于磁盘IO的次数,二叉树深度越大,查找的次数越多,性能越差。
最坏的情况是退化成了链表,如下图
为了让二叉树不至于退化成链表,人们发明了AVL树(平衡二叉搜索树):
平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。
而后还有多叉树
多叉树就是节点可以是M个,能有效地减少高度,高度变小后,节点变少I/O自然少,性能比二叉树好了
B树简单地说就是多叉树,每个叶子会存储数据,和指向下一个节点的指针。
例如要查找9,步骤如下
B+树是B树的改进,简单地说是:只有叶子节点才存数据,非叶子节点是存储的指针;所有叶子节点构成一个有序链表
B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了
例如要查找关键字16,步骤如下
可以从这几个维度去看这个问题,查询是否够快,效率是否稳定,存储数据多少,以及查找磁盘次数等等。
为什么不使用哈希结构?我们知道哈希结构,类似k-v结构,也就是,key和value是一对一关系。它用于等值查询还可以,但是范围查询它是无能为力的哦。
为什么不使用二叉树?如果二叉树特殊化为一个链表,相当于全表扫描。那么还要索引干嘛。
为什么不使用平衡二叉树?它也是一颗二叉查找树,任何节点的两个子树高度最大差为1。所以就不会出现特殊化一个链表的情况啦。但是,平衡二叉树插入或者更新是,需要左旋右旋维持平衡,维护代价大如果数量多的话,树的高度会很高。因为数据是存在磁盘的,以它作为索引结构,每次从磁盘读取一个节点,操作IO的次数就多啦。
为什么不使用B树?B+树是B树的升级版,详见两者的区别。
这里我放上一篇很好的索引面试场景题博客:https://www.cnblogs.com/jay-huaxiao/p/14352349.html
分别从以下几点开展:
详见我另一篇博客:https://www.cnblogs.com/kylinxxx/p/14403135.html
在使用InnoDB存储引擎时,如果没有特别的需要,尽量使用一个与业务无关的递增字段作为主键,主键字段不宜过长。如常用的雪花算法生成主键
尽量在索引列上完成分组、排序,遵循索引最左前缀法则,如果order by的条件不在索引列上,就会产生Using filesort,降低查询性能。
当查询语句的where条件或group by、order by含多列时,可根据实际情况优先考虑联合索引(multiple-column index),这样可以减少单列索引(single-column index)的个数,有助于高效查询。
建立联合索引时要特别注意column的次序,应结合上面提到的最左前缀法则以及实际的过滤、分组、排序需求。区分度最高的建议放最左边。
再强调一次最左前缀原则中提及的例子,在order by中是经常使用到的
索引列上做任何操作(表达式、函数计算、类型转换等)时无法使用索引会导致全表扫描
MySQL分页查询大多数写法可能如下:
select * from table limit offset,N;
MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下。
可以对超过特定阈值的页数进行SQL改写如下:
先快速定位需要获取的id段,然后再关联
select a.* from table a, (select id from table where 条件 limit 100000,20 ) b where a.id = b.id;
或者
select a.* from table a inner join (select id from table where 条件 limit 100000,20) b on a.id = b.id;
记住前文说的索引使用原则,总结成较短的话
索引文件具有B+Tree最左前缀匹配特性,如果左边的值未确定,那么无法使用索引,所以应尽量避免左模糊(即%xxx)或者全模糊(即%xxx%)。简单的说就是百分号后的索引会失效。
原则:小表驱动大表,即小的数据集驱动大的数据集
(1)当A表的数据集大于B表的数据集时,in优于exists
select * from A where id in (select id from B)
(2)当A表的数据集小于B表的数据集时,exists优于in
select * from A where exists (select 1 from B where B.id = A.id)
如果 where 条件的列和 select 的列都在一个索引中,通过这个索引就可以完成查询,这就叫就叫覆盖索引;当然,覆盖索引基本针对的是组合索引
InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录。使用覆盖索引的一个好处是辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作。
利用覆盖索引(covering index)来进行查询操作,避免回表,从而增加磁盘I/O。换句话说就是,尽可能避免select *语句,只选择必要的列,去除无用的列。
详情可以看这篇专利:https://zhuanlan.zhihu.com/p/107125866
阿里巴巴Java开发手册中有这样的规约:
不要使用count(列名)或count(常量)来替代count(),count()是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关【说明:count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行】。
count(distinct col)计算该列除NULL之外的不重复行数,注意count(distinct col1, col2)如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0
另外,InnoDB对count(*)、count(1)的处理完全一致。
(1) 需要join的字段,数据类型必须绝对一致;
(2) 多表join时,保证被关联的字段有索引
索引条件下推(Index Condition Pushdown),简称ICP。MySQL5.6新添加,用于优化数据的查询。索引下推需要明白什么是覆盖索引和回表,详见上一节的覆盖索引。
博主青石路的索引下推写的很好,同样他的博文也很优秀,我就不在这里做更多的记录了,直接放上他的索引下推博客地址:https://www.cnblogs.com/youzhibing/p/12318565.html
放一篇敖丙的文章:https://www.cnblogs.com/aobing/p/13623703.html
如果被问到这个问题,可以从b+树展开,并说到减少磁盘IO的次数,毕竟索引快就是通过索引的结构最大化的减少数据库的IO次数
很可能是没用到索引,摘抄一个网上看到的例子:
某单表写入了近2亿条数据,过程中发现配的报表有几个数据查询时间太长,所以重点看了几个慢查询SQL。避免敏感信息,这里对其提取简化做个记录。
mysql> select count(*) from tb_alert;
+-----------+
| count(*) |
+-----------+
| 198101877 |
+-----------+
表join后,取前10条数据就花了15秒,看了下SQL执行计划,如下:
mysql> select * from tb_alert left join tb_situation_alert on tb_alert.alert_id = tb_situation_alert.alert_id limit 10;
10 rows in set (15.46 sec)
mysql> explain select * from tb_alert left join tb_situation_alert on tb_alert.alert_id = tb_situation_alert.alert_id limit 10;
+----+-------------+--------------------+------------+------+---------------+------+---------+------+-----------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------+------------+------+---------------+------+---------+------+-----------+----------+----------------------------------------------------+
| 1 | SIMPLE | tb_alert | NULL | ALL | NULL | NULL | NULL | NULL | 190097118 | 100.00 | NULL |
| 1 | SIMPLE | tb_situation_alert | NULL | ALL | NULL | NULL | NULL | NULL | 8026988 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+--------------------+------------+------+---------------+------+---------+------+-----------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
可以看出join的时候没有用上索引,tb_situation_alert表上联合主键是这样的PRIMARY KEY (situation_id, alert_id),参与表join字段是alert_id,原来是不符合联合索引的最左前缀法则,仅从这条sql看,解决方案有两种,一种是对tb_situation_alert表上的alert_id单独建立索引,另外一种是调换联合主键的列的次序,改为PRIMARY KEY (alert_id, situation_id)。当然不能因为多配一张报表,就改其他产线的表的主键索引,这并不合理。在这里,应该对alert_id列单独建立索引。
mysql> create index idx_alert_id on tb_situation_alert (alert_id);
mysql> select * from tb_alert left join tb_situation_alert on tb_alert.alert_id = tb_situation_alert.alert_id limit 100;
100 rows in set (0.01 sec)
mysql> explain select * from tb_alert left join tb_situation_alert on tb_alert.alert_id = tb_situation_alert.alert_id limit 100;
+----+-------------+--------------------+------------+------+---------------+--------------+---------+---------------------------------+-----------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------+------------+------+---------------+--------------+---------+---------------------------------+-----------+----------+-------+
| 1 | SIMPLE | tb_alert | NULL | ALL | NULL | NULL | NULL | NULL | 190097118 | 100.00 | NULL |
| 1 | SIMPLE | tb_situation_alert | NULL | ref | idx_alert_id | idx_alert_id | 8 | tb_alert.alert_id | 2 | 100.00 | NULL |
+----+-------------+--------------------+------------+------+---------------+--------------+---------+---------------------------------+-----------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
优化后,执行计划可以看出join的时候走了索引,查询前100条0.01秒,和之前的取前10条数据就花了15秒天壤之别。
从第10000000条数据往后翻页时,25秒才能出结果,这里就能使用上面的分页查询优化技巧了。上面讲优化建议时,没看执行计划,这里正好看一下。
mysql> select * from tb_alert limit 10000000, 10;
10 rows in set (25.23 sec)
mysql> explain select * from tb_alert limit 10000000, 10;
+----+-------------+----------+------------+------+---------------+------+---------+------+-----------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+-----------+----------+-------+
| 1 | SIMPLE | tb_alert | NULL | ALL | NULL | NULL | NULL | NULL | 190097118 | 100.00 | NULL |
+----+-------------+----------+------------+------+---------------+------+---------+------+-----------+----------+-------+
1 row in set, 1 warning (0.00 sec)
再看下使用上分页查询优化技巧的sql的执行计划
mysql> select * from tb_alert a inner join (select alert_id from tb_alert limit 10000000, 10) b on a.alert_id = b.alert_id;
10 rows in set (2.29 sec)
mysql> explain select * from tb_alert a inner join (select alert_id from tb_alert a2 limit 10000000, 10) b on a.alert_id = b.alert_id;
+----+-------------+------------+------------+--------+---------------+---------------+---------+-----------+-----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+---------------+---------+-----------+-----------+----------+-------------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 10000010 | 100.00 | NULL |
| 1 | PRIMARY | a | NULL | eq_ref | PRIMARY | PRIMARY | 8 | b.alert_id | 1 | 100.00 | NULL |
| 2 | DERIVED | a2 | NULL | index | NULL | idx_processed | 5 | NULL | 190097118 | 100.00 | Using index |
+----+-------------+------------+------------+--------+---------------+---------------+---------+-----------+-----------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
分析SQL后,发现根本上并非分组聚合慢,而是扫描联合索引后,回表导致性能低下,去除不必要的字段,使用覆盖索引。
这里避免敏感信息,只演示分组聚合前的简化SQL,主要问题也是在这。
表上有联合索引KEY idx_alert_start_host_template_id ( alert_start, alert_host, template_id)
,优化前的sql为
mysql> select alert_start, alert_host, template_id, alert_service from tb_alert where alert_start > {ts '2019-06-05 00:00:10.0'} limit 10000;
10000 rows in set (1 min 5.22 sec)
使用覆盖索引,去掉template_id列,就能避免回表,查询时间从1min多变为0.03秒,如下:
mysql> select alert_start, alert_host, template_id from tb_alert where alert_start > {ts '2019-06-05 00:00:10.0'} limit 10000;
10000 rows in set (0.03 sec)
mysql> explain select alert_start, alert_host, template_id from tb_alert where alert_start > {ts '2019-06-05 00:00:10.0'} limit 10000;
+----+-------------+----------+------------+-------+------------------------------------+------------------------------------+---------+------+----------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+------------------------------------+------------------------------------+---------+------+----------+----------+--------------------------+
| 1 | SIMPLE | tb_alert | NULL | range | idx_alert_start_host_template_id | idx_alert_start_host_template_id | 9 | NULL | 95048559 | 100.00 | Using where; Using index |
+----+-------------+----------+------------+-------+------------------------------------+------------------------------------+---------+------+----------+----------+--------------------------+
1 row in set, 1 warning (0.01 sec)
手机扫一扫
移动阅读更方便
你可能感兴趣的文章