4. 子查询优化
MySQL
从
4.1
版本开始支持子查询,使用子查询可以进行
SELECT
语句的嵌套查询,即一个
SELECT
查询的结果作为另一个SELECT
语句的条件。
子查询可以一次性完成很多逻辑上需要多个步骤才能完成的
SQL
操作
。
子查询是
MySQL
的一项重要的功能,可以帮助我们通过一个
SQL
语句实现比较复杂的查询。但是,子
查询的执行效率不高。
原因:
① 执行子查询时,
MySQL
需要为内层查询语句的查询结果
建立一个临时表
,然后外层查询语句从临时表中查询记录。查询完毕后,再
撤销这些临时表
。这样会消耗过多的
CPU
和
IO
资源,产生大量的慢查询。
② 子查询的结果集存储的临时表,不论是内存临时表还是磁盘临时表都
不会存在索引
,所以查询性能会受到一定的影响。
③ 对于返回结果集比较大的子查询,其对查询性能的影响也就越大。
在
MySQL
中,可以使用连接(
JOIN
)查询来替代子查询。
连接查询
不需要建立临时表
,其
速度比子查询要快
,如果查询中使用索引的话,性能就会更好。
结论:尽量不要使用NOT IN 或者 NOT EXISTS,用LEFT JOIN xxx ON xx WHERE xx IS NULL替代
8.优先考虑覆盖索引
8.1 什么是覆盖索引?
理解方式一
:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。
一个索引包含了满足查询结果的数据就叫做覆盖索引。
理解方式二
:非聚簇复合索引的一种形式,它包括在查询里的
SELECT
、
JOIN
和
WHERE
子句用到的所有列 (即建索引的字段正好是覆盖查询条件中所涉及的字段)。
简单说就是,
索引列
+
主键
包含
SELECT
到
FROM
之间查询的列
。
8.2 覆盖索引的利弊
好处:
1.
避免
Innodb
表进行索引的二次查询(回表)
2.
可以把随机
IO
变成顺序
IO
加快查询效率
弊端:
索引字段的维护
总是有代价的。因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑了。这是业务 DBA,或者称为业务数据架构师的工作。
10. 使用索引下推
Index Condition Pushdown(ICP)
是
MySQL 5.6
中新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式。ICP
可以减少存储引擎访问基表的次数以及
MySQL
服务器访问存储引擎的次数。
10.1 使用前后的扫描过程对比
在不使用
ICP
索引扫描的过程:
storage
层:只将满足
index key
条件的索引记录对应的整行记录取出,返回给
server
层
server
层:对返回的数据,使用后面的
where
条件过滤,直至返回最后一行。
server层在生成执行计划后, 按如下步骤执行查询:
1. server层首先调用存储引擎的接口进行读数据(read data)==>定位到满足条件的第一条二级索引记录(Read index)
2. 根据B+树索引快速定位到这条二级索引记录后, 根据二级索引记录的主键值进行回表操作(get records),将完整的用户记录返回给存储引擎(Load data), 将完整记录返回给server层
3. server层再根据using where 判断其他搜索条件是否成立, 如果成立则将其发送给其他客户端; 否则向存储引擎要下一条数据
4. 由于每条记录都有next_record 属性, 根据该属性定位下一条符合条件的二级索引记录
下图即Using prefix index to get records过程
使用
ICP
扫描的过程:
storage
层:
首先将
index key
条件满足的索引记录区间确定,然后在索引上使用
index filter对每条记录
进行过滤。将满足的
index filter条件的索引记录才去回表取出整行记录返回
server
层。不满足
index filter
条件的索引记录丢弃,不回表、也不会返回server
层, 之后判断下一条数据。
server
层:
对返回的数据,使用
table filter
条件做最后的过滤。
server层在生成执行计划后, 按如下步骤执行查询:
1. server层首先调用存储引擎的接口进行读数据(read data)==>定位到满足条件的第一条二级索引记录(Read index)
2. 根据B+树索引快速定位到这条二级索引记录后, 先不进行回表操作(get records),而是
先判断一下所有关于索引中包含的列的条件(
就是假如使用的是联合索引, 而其他条件所在列恰好也在联合索引内)是否成立(Using index condition), 如果不成立, 直接跳过不再回表, 如果成立, 正常回表并将完整的用户记录返回给存储引擎(Load data), 将完整记录返回给server层
3. server层再判断其他搜索条件(
不在联合索引内,如果有这样的条件还要加上using where)是否成立, 如果成立则将其发送给其他客户端; 否则向存储引擎要下一条数据
4. 由于每条记录都有next_record 属性, 根据该属性定位下一条符合条件的二级索引记录, 并继续上述操作
下图即 Using index condition 操作
使用前后的成本差别
使用前,存储层多返回了需要被
index filter
过滤掉的整行记录
使用
ICP
后,直接就去掉了不满足
index filter
条件的记录,省去了他们回表和传递到
server
层的成本。
ICP
的
加速效果
取决于在存储引擎内通过
ICP
筛选
掉的数据的比例。
10.2 ICP的使用条件
ICP
的使用条件:
① 只能用于二级索引
(secondary index)
毕竟一级索引也不需要回表
②
explain
显示的执行计划中
type
值(
join
类型)为
range
、
ref
、
eq_ref
或者
ref_or_null
。
③ 并非全部
where
条件都可以用
ICP
筛选,如果
where
条件的字段不在索引列中,还是要读取整表的记录到server
端做
where
过滤。
④
ICP
可以用于
MyISAM
和
InnnoDB
存储引擎
⑤
MySQL 5.6
版本的不支持分区表的
ICP
功能,
5.7
版本的开始支持。
⑥ 当
SQL
使用覆盖索引时,不支持
ICP
优化方法。
10.3 ICP使用案例
12. 其它查询优化策略
12.1 EXISTS 和 IN 的区分
问题:
不太理解哪种情况下应该使用 EXISTS,哪种情况应该用 IN。选择的标准是看能否使用表的索引吗?
这两条语句有一些区别 : 上面的是不相关子查询, 需要从内部查出数据给外边用
: 下面的是相关子查询, 需要从外部传入数据给内部用
12.2 COUNT(*)与COUNT(具体字段)效率
面试经常问
问:在
MySQL
中统计数据表的行数,可以使用三种方式:
SELECT COUNT(*)
、
SELECT COUNT(1)
和
SELECT COUNT(具体字段
)
,使用这三者之间的查询效率是怎样的?
12.3 关于SELECT(*)
在表查询中,建议明确字段,不要使用
*
作为查询的字段列表,推荐使用
SELECT <
字段列表
>
查询。原因:
①
MySQL
在解析的过程中,会通过
查询数据字典
将
"*"
按序转换成所有列名,这会大大的耗费资源和时
间。
② 无法使用
覆盖索引
12.5 多使用COMMIT
只要有可能,在程序中尽量多使用
COMMIT
,这样程序的性能得到提高,需求也会因为
COMMIT
所释放的资源而减少。
COMMIT
所释放的资源:
回滚段上用于恢复数据的信息
被程序语句获得的锁
redo / undo log buffer 中的空间
管理上述 3
种资源中的内部花费
3 join语句原理
我碰见题时候再来补充
4. 排序优化
同上