MYSQL 的10大经典优化案例场景实战


Posted in MySQL onSeptember 14, 2021

一、SQL优化一般步骤

通过慢查日志等定位那些执行效率较低的SQL语句

1、explain 分析SQL的执行计划

需要重点关注typerowsfilteredextra

type由上至下,效率越来越高

  • ALL 全表扫描
  • index 索引全扫描
  • range 索引范围扫描,常用语<,<=,>=,between,in等操作
  • ref 使用非唯一索引扫描或唯一索引前缀扫描,返回单条记录,常出现在关联查询中
  • eq_ref 类似ref,区别在于使用的是唯一索引,使用主键的关联查询
  • const/system 单条记录,系统会把匹配行中的其他列作为常数处理,如主键或唯一索引查询
  • null MySQL不访问任何表或索引,直接返回结果

虽然上至下,效率越来越高,但是根据cost模型,假设有两个索引idx1(a, b, c),idx2(a, c),SQL为"select * from t where a = 1 and b in (1, 2) order by c";如果走idx1,那么是type为range,如果走idx2,那么type是ref;当需要扫描的行数,使用idx2大约是idx1的5倍以上时,会用idx1,否则会用idx2

Extra

  • Using filesort:MySQL需要额外的一次传递,以找出如何按排序顺序检索行。通过根据联接类型浏览所有行并为所有匹配WHERE子句的行保存排序关键字和行的指针来完成排序。然后关键字被排序,并按排序顺序检索行。
  • Using temporary:使用了临时表保存中间结果,性能特别差,需要重点优化
  • Using index:表示相应的 select 操作中使用了覆盖索引(Coveing Index),避免访问了表的数据行,效率不错!如果同时出现 using where,意味着无法直接通过索引查找来查询到符合条件的数据。
  • Using index condition:MySQL5.6之后新增的ICPusing index condtion就是使用了ICP(索引下推),在存储引擎层进行数据过滤,而不是在服务层过滤,利用索引现有的数据减少回表的数据。

2、show profile 分析

了解SQL执行的线程的状态及消耗的时间。
默认是关闭的,开启语句“set profiling = 1;”

SHOW PROFILES ;
SHOW PROFILE FOR QUERY  #{id};

3、trace

trace分析优化器如何选择执行计划,通过trace文件能够进一步了解为什么优惠券选择A执行计划而不选择B执行计划。

set optimizer_trace="enabled=on";
set optimizer_trace_max_mem_size=1000000;
select * from information_schema.optimizer_trace;

4、确定问题并采用相应的措施

  • 优化索引
  • 优化SQL语句:修改SQL、IN 查询分段、时间查询分段、基于上一次数据过滤
  • 改用其他实现方式:ES、数仓等
  • 数据碎片处理

二、场景分析(案例)

1、 最左匹配

索引

KEY `idx_shopid_orderno` (`shop_id`,`order_no`)

SQL语句

select * from _t where orderno=''

查询匹配从左往右匹配,要使用order_no走索引,必须查询条件携带shop_id或者索引(shop_id,order_no)调换前后顺序

2、隐式转换

索引

KEY `idx_mobile` (`mobile`)

SQL语句

select * from _user where mobile=12345678901

隐式转换相当于在索引上做运算,会让索引失效。mobile是字符类型,使用了数字,应该使用字符串匹配,否则MySQL会用到隐式替换,导致索引失效。

3、大分页

索引

KEY `idx_a_b_c` (`a`, `b`, `c`)

SQL语句

select * from _t where a = 1 and b = 2 order by c desc limit 10000, 10;

对于大分页的场景,可以优先让产品优化需求,如果没有优化的,有如下两种优化方式,

一种是把上一次的最后一条数据,也即上面的c传过来,然后做“c < xxx”处理,但是这种一般需要改接口协议,并不一定可行。

另一种是采用延迟关联的方式进行处理,减少SQL回表,但是要记得索引需要完全覆盖才有效果,SQL改动如下

select t1.* from _t t1, (select id from _t where a = 1 and b = 2 order by c desc limit 10000, 10) t2 where t1.id = t2.id;

4、in + order by

索引

KEY `idx_shopid_status_created` (`shop_id`, `order_status`, `created_at`)

SQL语句

select * from _order where shop_id = 1 and order_status in (1, 2, 3) order by created_at desc limit 10

in查询在MySQL底层是通过n*m的方式去搜索,类似union,但是效率比union高。
in查询在进行cost代价计算时(代价 = 元组数 * IO平均值),是通过将in包含的数值,一条条去查询获取元组数的,因此这个计算过程会比较的慢,所以MySQL设置了个临界值(eq_range_index_dive_limit),5.6之后超过这个临界值后该列的cost就不参与计算了。因此会导致执行计划选择不准确。默认是200,即in条件超过了200个数据,会导致in的代价计算存在问题,可能会导致Mysql选择的索引不准确。

处理方式:可以(order_status, created_at)互换前后顺序,并且调整SQL为延迟关联。

5、范围查询阻断,后续字段不能走索引

索引

KEY `idx_shopid_created_status` (`shop_id`, `created_at`, `order_status`)

SQL语句

select * from _order where shop_id = 1 and created_at > '2021-01-01 00:00:00' and order_status = 10

范围查询还有“IN、between

6、不等于、不包含不能用到索引的快速搜索

可以用到ICP

select * from _order where shop_id=1 and order_status not in (1,2)
select * from _order where shop_id=1 and order_status != 1

在索引上,避免使用NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE

7、优化器选择不使用索引的情况

如果要求访问的数据量很小,则优化器还是会选择辅助索引,但是当访问的数据占整个表中数据的蛮大一部分时(一般是20%左右),优化器会选择通过聚集索引来查找数据。

select * from _order where  order_status = 1

查询出所有未支付的订单,一般这种订单是很少的,即使建了索引,也没法使用索引。

8、复杂查询

select sum(amt) from _t where a = 1 and b in (1, 2, 3) and c > '2020-01-01';
select * from _t where a = 1 and b in (1, 2, 3) and c > '2020-01-01' limit 10;

如果是统计某些数据,可能改用数仓进行解决;

如果是业务上就有那么复杂的查询,可能就不建议继续走SQL了,而是采用其他的方式进行解决,比如使用ES等进行解决。

9、asc和desc混用

select * from _t where a=1 order by b desc, c asc

desc 和asc混用时会导致索引失效

10、大数据

对于推送业务的数据存储,可能数据量会很大,如果在方案的选择上,最终选择存储在MySQL上,并且做7天等有效期的保存。

那么需要注意,频繁的清理数据,会照成数据碎片,需要联系DBA进行数据碎片处理

到此这篇关于10大经典 MYSQL 优化案例场景实战的文章就介绍到这了,更多相关MYSQL 优化案例场景实战内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
mysql批量新增和存储的方法实例
Apr 07 MySQL
MySQL主从复制断开的常用修复方法
Apr 07 MySQL
MySQL慢查询的坑
Apr 28 MySQL
MySQL 8.0 之不可见列的基本操作
May 20 MySQL
linux下导入、导出mysql数据库命令的实现方法
May 26 MySQL
SQL之各种join小结详细讲解
Aug 04 MySQL
MySQL如何解决幻读问题
Aug 07 MySQL
一文搞清楚MySQL count(*)、count(1)、count(col)区别
Mar 03 MySQL
MySQL之MyISAM存储引擎的非聚簇索引详解
Mar 03 MySQL
分享MySQL常用 内核 Debug 几种常见方法
Mar 17 MySQL
MySQL创建管理KEY分区
Apr 13 MySQL
MySQL串行化隔离级别(间隙锁实现)
Jun 16 MySQL
MySQL中连接查询和子查询的问题
mysql配置SSL证书登录的实现
MySQL约束超详解
Sep 04 #MySQL
MySQL中的隐藏列的具体查看
Sep 04 #MySQL
Mysql实现简易版搜索引擎的示例代码
Aug 30 #MySQL
详细聊聊MySQL中慢SQL优化的方向
Aug 30 #MySQL
MySQL8.0的WITH查询详情
Aug 30 #MySQL
You might like
用Php编写注册后Email激活验证的实例代码
2013/03/11 PHP
强制PHP命令行脚本单进程运行的方法
2014/04/15 PHP
php针对cookie操作的队列操作类实例
2014/12/10 PHP
PHP递归创建多级目录
2015/11/05 PHP
php删除一个路径下的所有文件夹和文件的方法
2018/02/07 PHP
Laravel 集成微信用户登录和绑定的实现
2019/12/27 PHP
Javascript实例教程(19) 使用HoTMetal(7)
2006/12/23 Javascript
javascript 关闭IE6、IE7
2009/06/01 Javascript
关于jQuery $.isNumeric vs. $.isNaN vs. isNaN
2013/04/15 Javascript
使用JS CSS去除IE链接虚线框的三种方法
2013/11/14 Javascript
JavaScript实现选择框按比例拖拉缩放的方法
2015/08/04 Javascript
jquery实现带渐变淡入淡出并向右依次展开的多级菜单效果实例
2015/08/22 Javascript
JavaScript重定向URL参数的两种方法小结
2016/10/19 Javascript
自己封装的一个简单的倒计时功能实例
2016/11/23 Javascript
JavaScript使用ZeroClipboard操作剪切板
2017/05/10 Javascript
JS实现页面内跳转的简单代码
2017/09/03 Javascript
详解vue-cli 接口代理配置
2017/12/13 Javascript
js实现表格单列按字母排序
2020/08/12 Javascript
javascript实现数字时钟效果
2021/02/06 Javascript
[01:17]Ti4 循环赛第一日回顾
2014/07/11 DOTA
[01:05]DOTA2完美大师赛趣味视频之选手教你打职业
2017/11/23 DOTA
Python爬虫框架Scrapy实例代码
2018/03/04 Python
使用python获取csv文本的某行或某列数据的实例
2018/04/03 Python
TensorFlow实现模型评估
2018/09/07 Python
浅谈Python批处理文件夹中的txt文件
2019/03/11 Python
Python实现将蓝底照片转化为白底照片功能完整实例
2019/12/13 Python
Tensorflow的常用矩阵生成方式
2020/01/04 Python
Python使用PyQt5/PySide2编写一个极简的音乐播放器功能
2020/02/07 Python
GLAMGLOW格莱魅美国官网:美国知名的面膜品牌
2016/12/31 全球购物
阿根廷票务网站:StubHub阿根廷
2018/04/13 全球购物
现代家居用品及礼品:LBC Modern
2018/06/24 全球购物
全球最受追捧的运动服品牌领先数字目的地:Stylerunner
2020/11/25 全球购物
一道SQL存储过程面试题
2016/10/07 面试题
大学生实习证明范本
2014/01/15 职场文书
军人离婚协议书样本
2014/10/21 职场文书
2016大学生暑期社会实践心得体会
2016/01/14 职场文书