网站首页 > 技术文章 正文
目录
- 事故现场
- 解决方案
- “回表查询”介绍
- InnoDB的索引
- 什么是回表查询
- 怎么优化回表查询
事故现场
数据库使用的MySQL,有一个日志表,需要进行分页查询,于是很容易就想到了limit [offset偏移量] [count数量]这个查询方式,当我们偏移量比较小时,似乎是没什么问题
SELECT * FROM t_log WHERE type = 1 LIMIT 5, 50
查询时间:0.45s
12
但是随着offset的增加,就出现了查询时间越来越长,但是每次查出的数据都只有50条,这就让我特别不理解
SELECT * FROM t_log WHERE type = 1 LIMIT 500000, 50
查询时间:57.252s
SELECT * FROM t_log WHERE type = 1 LIMIT 1000000, 50
查询时间:89.15s
1234
解决方案
查阅资料发现“limit”的工作方式是:
第一步.先查询offset+count条数据;
第二步.再抛弃前offset条数据
但是全字段查询肯定会有回表查询操作,这就导致了进行百万次的回表查询,速度肯定会很慢,于是我的解决思路是,在“第一步”时不进行回表查询,这样会不会效率提高很多,于是把sql改成下面的等效查询。
SELECT *
FROM t_log t
RIGHT JOIN (
SELECT uid
FROM t_log
WHERE type = 1
LIMIT 1000000,50
) tmp ON tmp.uid = t.uid
查询时间:0.64
123456789
这下时间缩短了一百倍多,查出来的结果也是正确的,达到了我们要的效果,到此sql已经优化好了。
提到的“回表查询”
上面说到“第一步”中进行了回表查询,什么是回表查询呢?
InnoDB的索引
这里我们不得不先解释一下InnoDB的索引,要从InnoDB的索引实现说起。InnoDB有两大类索引,一类是聚集索引(Clustered Index),一类是普通索引(Secondary Index)
InnoDB聚集索引和普通索引有什么差异?有什么区别呢?
InnoDB的聚集索引
每行数据是存在InnoDB聚集索引的叶子节点上的,因此InnoDB必须要有且只有一个聚集索引,下面聚集索引的生成规则:
- 如果表定义了PK(Primary Key,主键),那么PK就是聚集索引。
- 如果表没有定义PK,则第一个NOT NULL UNIQUE的列就是聚集索引。
- 否则InnoDB会另外创建一个隐藏的ROWID作为聚集索引。
这种机制使得基于PK的查询速度非常快,因为直接定位的行记录。
InnoDB普通索引
InnoDB普通索引的叶子节点存储主键值。想拿到行数据,还得去聚集索引中扫描索引树。
注意,不是存储行记录头指针,MyISAM的索引叶子节点存储记录指针。
什么是回表查询
下面举个例子解释:
假设有个表user(id PK,name,code),id是聚集索引,code是普通索引。表中有几条数据。
idnamecode-----1小明AQ-----4小陈DR-----7小红CY-----9小刘FP-----
那么两种索引的B+树索引就是如下图这样:
从普通索引无法直接定位行记录,那普通索引的查询过程是怎么样的呢?
例:select * from user where code = 'CY';
其查询过程在通常情况下是需要扫描两遍索引树的,这里的执行过程是这样的:
如带色的路径:
- 第一遍先通过普通索引定位到主键值
- 然后第二遍再通过聚集索引定位到具体行记录。
这就是所谓的**回表查询**,即先定位主键值,再根据主键值定位行记录,性能相对于只扫描一遍聚集索引树的性能要低一些。
怎么优化回表查询
那怎么样解决这个性能低的问题呢?这就涉及到一个概念---------覆盖索引
覆盖索引
覆盖索引就是是一种避免回表查询的优化策略。就是把所有需要查询的字段都放到普通索引中,这样普通索引查到的叶子结点(即上图中的黑色方框)中已经能够得到所需的所有字段,就不会再去聚集索引中再查询。
实现覆盖索引的方式
可分为两种:
- 第一种减少查询字段只查询缩影有的字段,例如我们上面提到的使用limit查询,我们只查了id字段,这样几百万条数据就不会回表查询,外层查询时只有50条数据去聚集索引里进行了查询。又如上面的user表 优化sql为不查询name字段。
例:select id,code from user where code = 'CY';
- 第二种方式就是修改表创建的索引,增加需要查询的字段,如上面user表,把name也加到索引中,设置(name,code)两个字段的联合索引 。
覆盖索引的定义与注意事项
如果一个索引覆盖(包含)了所有需要查询的字段的值,这个索引就是覆盖索引。因为索引中已经包含了要查询的字段的值,因此查询的时候直接返回索引中的字段值就可以了,不需要再到表中查询,避免了对主键索引的二次查询,也就提高了查询的效率。
要注意的是,不是所有类型的索引都可以成为覆盖索引的。因为覆盖索引必须要存储索引的列值,而哈希索引、空间索引和全文索引等都不存储索引列值,索引MySQL只能使用B-Tree索引做覆盖索引。
另外,当发起一个被索引覆盖的查询(索引覆盖查询)时,在explain(执行计划)的Extra列可以看到【Using Index】的信息。
覆盖索引的优点
1.索引条目通常远小于数据行的大小,因为覆盖索引只需要读取索引,极大地减少了数据的访问量。
2.索引是按照列值顺序存储的,对于IO密集的范围查找会比随机从磁盘读取每一行数据的IO小很多。
3.一些存储引擎比如MyISAM在内存中只缓存索引,数据则依赖操作系统来缓存,因此要访问数据的话需要一次系统调用,使用覆盖索引则避免了这一点。
4.由于InnoDB的聚簇索引,覆盖索引对InnoDB引擎下的数据库表特别有用。因为InnoDB的二级索引在叶子节点中保存了行的主键值,如果二级索引能够覆盖查询,就避免了对主键索引的二次查询。
关注我,每天进步一点点!
猜你喜欢
- 2024-10-27 MySQL左查询最全介绍(sql 左查询)
- 2024-10-27 mysql查询指定父级下所有子级(mysql根据子节点查父)
- 2024-10-27 MySQL数据库入门(七)多表查询及大量练习
- 2024-10-27 mysql进行数据查询(单表查询)(mysql 查询数据表)
- 2024-10-27 【执行计划】MySQL如何执行查询?(mysql执行查询语句)
- 2024-10-27 MySQL查询列必须和group by字段一致吗?
- 2024-10-27 查看MySQL所有数据库列表(mysql怎么查看数据库列表)
- 2024-10-27 「mysql」mysql调优时必须掌握的慢查询语句排查命令
- 2024-10-27 有一张1000W 数据记录的表,主键id自增,如何随机抽取10条记录?
- 2024-10-27 MySQL进阶实战7,查询的执行过程(mysql查询机制)
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)