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. 排序优化
同上