sql优化案例一—数据分布散导致索引失效走全表扫描

简介: 数据分布太散导致索引失效

问题背景:下午收到慢查询导致超时告警,用pt-query-digest 获取到相应慢sql,如下。
select count(1) from (

        select  mdos.sn_code,count(1) from mps_device_order_static mdos
        inner join business b on b.sid=mdos.sid
        inner join qdsp_device qd on qd.sn_code=mdos.sn_code and b.guid=qd.business_guid
        where 1=1 and mdos.sn_code<>'' and mdos.sn_code<>'null' and  1=1  AND mdos.static_date>='2020-05-01'  AND mdos.static_date<='2020-05-30'
        group by mdos.sn_code) tab;
AI 代码解读

分析过程:

查看执行计划
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY NULL ALL NULL NULL NULL NULL 58943 100.00 NULL
2 DERIVED mdos NULL ALL idx_static_date,idx_sn_code,idx_sid,idx_static_date_sn_code_total_money_total_count NULL NULL NULL 2064111 28.56 Using where; Using temporary; Using filesort
2 DERIVED qd NULL eq_ref ux_sn_code,ix_business_guid ux_sn_code 202 management.mdos.sn_code 1 100.00 NULL
2 DERIVED b NULL eq_ref PRIMARY,ix_sid_int PRIMARY 144 management.qd.business_guid 1 10.00 Using where

4 rows in set, 1 warning (0.00 sec)

发现驱动表mdos 即mps_device_order_static 走全表扫描

各表索引情况如下:
mps_device_order_static 表索引
PRIMARY KEY (id),
KEY idx_static_date (static_date),
KEY idx_agent_id (agent_id),
KEY idx_sn_code (sn_code),
KEY idx_sid (sid),
KEY idx_static_date_sn_code_total_money_total_count (static_date,sn_code,total_money,total_count)
)

business 表索引

PRIMARY KEY (guid),
UNIQUE KEY ix_account (account),
UNIQUE KEY ix_sid_int (sid),
KEY ix_agent_guid (agent_guid),
KEY ix_open_id (open_id),
KEY ix_vip_guid (vip_guid),
KEY ix_vip_account (vip_account),
KEY ix_vpi_cloud_open_id (vip_cloud_open_id),
KEY ix_sub_channel_number (sub_channel_number),
KEY ix_register_time (register_time) USING BTREE,
KEY ix_wechat_appid (wechat_appid),
KEY idx_tong_cheng_hui_guid (tong_cheng_hui_guid)
)

qdsp_device 表索引
PRIMARY KEY (id),
UNIQUE KEY ux_sn_code (sn_code),
KEY ix_agent_guid (agent_guid),
KEY ix_business_guid (business_guid) USING BTREE,
KEY idx_chain_store_guid_sn_code (chain_store_guid,sn_code),
KEY idx_terminal_number (terminal_number),
KEY idx_storehouse_guid (storehouse_guid),
KEY idx_device_status (device_status)
)

查看了sql各连接条件,和where语句中条件,确认所有条件字段都是有索引的。所以应该是什么原因导致了索引失效

尝试将语句进行拆解,一步一步分析
首先试试驱动表mdos单表执行计划
explain select mdos.sn_code,count(*) from mps_device_order_static mdos

        #inner join business b on b.sid=mdos.sid
        #inner join qdsp_device qd on qd.sn_code=mdos.sn_code and b.guid=qd.business_guid
        where 1=1 and mdos.sn_code<>'' and mdos.sn_code<>'null' AND mdos.static_date>='2020-05-01'  AND mdos.static_date<='2020-05-30'
        group by mdos.sn_code;
AI 代码解读

+----+-------------+-------+------------+-------+-----------------------------------------------------------------------------+-------------------------------------------------+---------+------+---------+----------+-----------------------------------------------------------+

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE mdos NULL range idx_static_date,idx_sn_code,idx_static_date_sn_code_total_money_total_count idx_static_date_sn_code_total_money_total_count 3 NULL 1032055 50.00 Using where; Using index; Using temporary; Using filesort

1 row in set, 1 warning (0.00 sec)
发现正常走索引

接着试
explain select mdos.sn_code,count(*) from mps_device_order_static mdos

        #inner join business b on b.sid=mdos.sid
        inner join qdsp_device qd on qd.sn_code=mdos.sn_code #and b.guid=qd.business_guid
        where 1=1 and mdos.sn_code<>'' and mdos.sn_code<>'null' AND mdos.static_date>='2020-05-01'  AND mdos.static_date<='2020-05-30'
        group by mdos.sn_code;
AI 代码解读

+----+-------------+-------+------------+--------+-----------------------------------------------------------------------------+-------------------------------------------------+---------+-------------------------+---------+----------+-----------------------------------------------------------+

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE mdos NULL range idx_static_date,idx_sn_code,idx_static_date_sn_code_total_money_total_count idx_static_date_sn_code_total_money_total_count 3 NULL 1032055 50.00 Using where; Using index; Using temporary; Using filesort
1 SIMPLE qd NULL eq_ref ux_sn_code ux_sn_code 202 management.mdos.sn_code 1 100.00 Using index

也能正常走索引

接着试
explain select mdos.sn_code,count(*) from mps_device_order_static mdos

        inner join business b on b.sid=mdos.sid
        #inner join qdsp_device qd on qd.sn_code=mdos.sn_code and b.guid=qd.business_guid
        where 1=1 and mdos.sn_code<>'' and mdos.sn_code<>'null' AND mdos.static_date>='2020-05-01'  AND mdos.static_date<='2020-05-30'
        group by mdos.sn_code;
AI 代码解读
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE mdos NULL ALL idx_static_date,idx_sn_code,idx_sid,idx_static_date_sn_code_total_money_total_count NULL NULL NULL 2064111 14.28 Using where; Using temporary; Using filesort
1 SIMPLE b NULL eq_ref ix_sid_int ix_sid_int 4 management.mdos.sid 1 100.00 Using where; Using index

2 rows in set, 1 warning (0.01 sec)
发现走全表索引失效,走全表扫描。这时感觉应该是和business 表关联这里有问题,但还是没找到头绪

改写下sql试试
explain select count(distinct mdos.sn_code) from mps_device_order_static mdos
join business b on b.sid=mdos.sid
join qdsp_device qd on qd.sn_code=mdos.sn_code and b.guid=qd.business_guid
where 1=1 and mdos.sn_code<>'' AND mdos.static_date>='2020-05-01' AND mdos.static_date<='2020-05-30';

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE mdos NULL ALL idx_static_date,idx_sn_code,idx_sid,idx_static_date_sn_code_total_money_total_count NULL NULL NULL 2064111 14.28 Using where
1 SIMPLE qd NULL eq_ref ux_sn_code ux_sn_code 202 management.mdos.sn_code 1 100.00 Using index
1 SIMPLE b NULL eq_ref ix_sid_int ix_sid_int 4 management.mdos.sid 1 100.00 Using where; Using index

3 rows in set, 1 warning (0.00 sec)
还是不行

接着试试走强制索引
explain select count(distinct mdos.sn_code) from mps_device_order_static mdos force index(idx_static_date)
join business b on b.sid=mdos.sid
join qdsp_device qd on qd.sn_code=mdos.sn_code and b.guid=qd.business_guid
where 1=1 and mdos.sn_code<>'' AND mdos.static_date>='2020-05-01' AND mdos.static_date<='2020-05-30';

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE mdos NULL range idx_static_date idx_static_date 3 NULL 589440 90.00 Using index condition; Using where
1 SIMPLE qd NULL eq_ref ux_sn_code,ix_business_guid ux_sn_code 202 management.mdos.sn_code 1 100.00 NULL
1 SIMPLE b NULL eq_ref PRIMARY,ix_sid_int PRIMARY 144 management.qd.business_guid 1 10.00 Using where

3 rows in set, 1 warning (0.00 sec)
这时发现能走索引,速度快了很多。但是没找到原因

后来咨询了一位大佬,根据他的猜测可能是表数据量比较大,同时数据分布比较分散,所以导致索引失效走全表扫描。根据他的猜测,缩短了过滤时间,将mdos.static_date>='2020-05-01' AND mdos.static_date<='2020-05-30' 缩短到一周试试。
explain select count(distinct mdos.sn_code) from mps_device_order_static mdos force index(idx_static_date)
join business b on b.sid=mdos.sid
join qdsp_device qd on qd.sn_code=mdos.sn_code and b.guid=qd.business_guid
where 1=1 and mdos.sn_code<>'' AND mdos.static_date>='2020-05-01' AND mdos.static_date<='2020-05-07';

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE mdos NULL range idx_static_date idx_static_date 3 NULL 133714 90.00 Using index condition; Using where
1 SIMPLE qd NULL eq_ref ux_sn_code,ix_business_guid ux_sn_code 202 management.mdos.sn_code 1 100.00 NULL
1 SIMPLE b NULL eq_ref PRIMARY,ix_sid_int PRIMARY 144 management.qd.business_guid 1 10.00 Using where

3 rows in set, 1 warning (0.01 sec)
可以正常走索引了。

至此,得出两种解决办法
1、利用force index 强制走索引
select count(distinct mdos.sn_code) from mps_device_order_static mdos force index(idx_static_date)
join business b on b.sid=mdos.sid
join qdsp_device qd on qd.sn_code=mdos.sn_code and b.guid=qd.business_guid
where 1=1 and mdos.sn_code<>'' AND mdos.static_date>='2020-05-01' AND mdos.static_date<='2020-05-30';

2、缩短过滤时间,查询多次
select count(distinct mdos.sn_code) from mps_device_order_static mdos force index(idx_static_date)
join business b on b.sid=mdos.sid
join qdsp_device qd on qd.sn_code=mdos.sn_code and b.guid=qd.business_guid
where 1=1 and mdos.sn_code<>'' AND mdos.static_date>='2020-05-01' AND mdos.static_date<='2020-05-07';

第2种方法更好,更快

相关文章
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
SQL优化策略与实践:组合索引与最左前缀原则详解
本文介绍了SQL优化的多种方式,包括优化查询语句(避免使用SELECT *、减少数据处理量)、使用索引(创建合适索引类型)、查询缓存、优化表结构、使用存储过程和触发器、批量处理以及分析和监控数据库性能。同时,文章详细讲解了组合索引的概念及其最左前缀原则,即MySQL从索引的最左列开始匹配条件,若跳过最左列,则索引失效。通过示例代码,展示了如何在实际场景中应用这些优化策略,以提高数据库查询效率和系统响应速度。
132 10
数据库数据恢复——sql server数据库被加密的数据恢复案例
SQL server数据库数据故障: SQL server数据库被加密,无法使用。 数据库MDF、LDF、log日志文件名字被篡改。 数据库备份被加密,文件名字被篡改。
MySQL进阶突击系列(07) 她气鼓鼓递来一条SQL | 怎么看执行计划、SQL怎么优化?
在日常研发工作当中,系统性能优化,从大的方面来看主要涉及基础平台优化、业务系统性能优化、数据库优化。面对数据库优化,除了DBA在集群性能、服务器调优需要投入精力,我们研发需要负责业务SQL执行优化。当业务数据量达到一定规模后,SQL执行效率可能就会出现瓶颈,影响系统业务响应。掌握如何判断SQL执行慢、以及如何分析SQL执行计划、优化SQL的技能,在工作中解决SQL性能问题显得非常关键。
【YashanDB知识库】字段加上索引后,SQL查询不到结果
【YashanDB知识库】字段加上索引后,SQL查询不到结果
大数据新视界--大数据大厂之MySQL 数据库课程设计:MySQL 数据库 SQL 语句调优的进阶策略与实际案例(2-2)
本文延续前篇,深入探讨 MySQL 数据库 SQL 语句调优进阶策略。包括优化索引使用,介绍多种索引类型及避免索引失效等;调整数据库参数,如缓冲池、连接数和日志参数;还有分区表、垂直拆分等其他优化方法。通过实际案例分析展示调优效果。回顾与数据库课程设计相关文章,强调全面认识 MySQL 数据库重要性。为读者提供综合调优指导,确保数据库高效运行。
如何优化SQL查询以提高数据库性能?
这篇文章以生动的比喻介绍了优化SQL查询的重要性及方法。它首先将未优化的SQL查询比作在自助餐厅贪多嚼不烂的行为,强调了只获取必要数据的必要性。接着,文章详细讲解了四种优化策略:**精简选择**(避免使用`SELECT *`)、**专业筛选**(利用`WHERE`缩小范围)、**高效联接**(索引和限制数据量)以及**使用索引**(加速搜索)。此外,还探讨了如何避免N+1查询问题、使用分页限制结果、理解执行计划以及定期维护数据库健康。通过这些技巧,可以显著提升数据库性能,让查询更高效流畅。
基于SQL Server / MySQL进行百万条数据过滤优化方案
对百万级别数据进行高效过滤查询,需要综合使用索引、查询优化、表分区、统计信息和视图等技术手段。通过合理的数据库设计和查询优化,可以显著提升查询性能,确保系统的高效稳定运行。
187 9
数据库数据恢复—SQL Server报错“错误 823”的数据恢复案例
SQL Server数据库附加数据库过程中比较常见的报错是“错误 823”,附加数据库失败。 如果数据库有备份则只需还原备份即可。但是如果没有备份,备份时间太久,或者其他原因导致备份不可用,那么就需要通过专业手段对数据库进行数据恢复。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问