|搞懂这些SQL优化技巧,面试横着走( 三 )


这个结论不仅对 order by 有效 , 对其他需要排序的操作也有效 。 比如 group by 、union 、distinct 等 。
?正确使用 hint 优化语句
MySQL 中可以使用 hint 指定优化器在执行时选择或忽略特定的索引 。
一般而言 , 处于版本变更带来的表结构索引变化 , 更建议避免使用 hint , 而是通过 Analyze table 多收集统计信息 。
但在特定场合下 , 指定 hint 可以排除其他索引干扰而指定更优的执行计划:

  • USE INDEX 在你查询语句中表名的后面 , 添加 USE INDEX 来提供希望 MySQL 去参考的索引列表 , 就可以让 MySQL 不再考虑其他可用的索引 。
例子: SELECT col1 FROM table USE INDEX (mod_time, name)...
  • IGNORE INDEX 如果只是单纯的想让 MySQL 忽略一个或者多个索引 , 可以使用 IGNORE INDEX 作为 Hint 。
例子: SELECT col1 FROM table IGNORE INDEX (priority) ...
  • FORCE INDEX 为强制 MySQL 使用一个特定的索引 , 可在查询中使用FORCE INDEX 作为 Hint 。
例子: SELECT col1 FROM table FORCE INDEX (mod_time) ...
在查询的时候 , 数据库系统会自动分析查询语句 , 并选择一个最合适的索引 。 但是很多时候 , 数据库系统的查询优化器并不一定总是能使用最优索引 。
如果我们知道如何选择索引 , 可以使用 FORCE INDEX 强制查询使用指定的索引 。
例如:
SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC; SELECT 语句其他优化
①避免出现 select *
首先 , select * 操作在任何类型数据库中都不是一个好的 SQL 编写习惯 。
使用 select * 取出全部列 , 会让优化器无法完成索引覆盖扫描这类优化 , 会影响优化器对执行计划的选择 , 也会增加网络带宽消耗 , 更会带来额外的 I/O , 内存和 CPU 消耗 。
建议提出业务实际需要的列数 , 将指定列名以取代 select * 。 具体详情见《为什么大家都说SELECT * 效率低》
②避免出现不确定结果的函数
特定针对主从复制这类业务场景 。 由于原理上从库复制的是主库执行的语句 , 使用如 now()、rand()、sysdate()、current_user() 等不确定结果的函数很容易导致主库与从库相应的数据不一致 。
另外不确定值的函数 , 产生的 SQL 语句无法利用 query cache 。
③多表关联查询时 , 小表在前 , 大表在后
在 MySQL 中 , 执行 from 后的表关联查询是从左往右执行的(Oracle 相反) , 第一张表会涉及到全表扫描 。
所以将小表放在前面 , 先扫小表 , 扫描快效率较高 , 在扫描后面的大表 , 或许只扫描大表的前 100 行就符合返回条件并 return 了 。
例如:表 1 有 50 条数据 , 表 2 有 30 亿条数据;如果全表扫描表 2 , 你品 , 那就先去吃个饭再说吧是吧 。
④使用表的别名
当在 SQL 语句中连接多个表时 , 请使用表的别名并把别名前缀于每个列名上 。 这样就可以减少解析的时间并减少哪些友列名歧义引起的语法错误 。
⑤用 where 字句替换 HAVING 字句
避免使用 HAVING 字句 , 因为 HAVING 只会在检索出所有记录之后才对结果集进行过滤 , 而 where 则是在聚合前刷选记录 , 如果能通过 where 字句限制记录的数目 , 那就能减少这方面的开销 。
HAVING 中的条件一般用于聚合函数的过滤 , 除此之外 , 应该将条件写在 where 字句中 。
where 和 having 的区别:where 后面不能使用组函数 。
⑥调整 Where 字句中的连接顺序
MySQL 采用从左往右 , 自上而下的顺序解析 where 子句 。 根据这个原理 , 应将过滤数据多的条件往前放 , 最快速度缩小结果集 。
增删改 DML 语句优化


推荐阅读