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;

分析过程:

查看执行计划
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;

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

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;

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

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;
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种方法更好,更快

目录
相关文章
|
27天前
|
SQL 数据库 数据库管理
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(一)模式、表、索引与视图
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(一)模式、表、索引与视图
60 11
|
1天前
|
SQL 存储 小程序
数据库数据恢复—Sql Server数据库文件丢失的数据恢复案例
数据库数据恢复环境: 5块硬盘组建一组RAID5阵列,划分LUN供windows系统服务器使用。windows系统服务器内运行了Sql Server数据库,存储空间在操作系统层面划分了三个逻辑分区。 数据库故障: 数据库文件丢失,主要涉及3个数据库,数千张表。数据库文件丢失原因未知,不能确定丢失的数据库文件的存放位置。数据库文件丢失后,服务器仍处于开机状态,所幸未写入大量数据。
数据库数据恢复—Sql Server数据库文件丢失的数据恢复案例
|
3天前
|
SQL 自然语言处理 搜索推荐
SQL Server 索引和视图
SQL Server 索引和视图
|
8天前
|
SQL 存储 关系型数据库
【MySQL系列笔记】SQL优化
SQL优化是通过调整数据库查询、索引、表结构和配置参数等方式,提高SQL查询性能和效率的过程。它旨在减少查询执行时间、减少系统资源消耗,从而提升数据库系统整体性能。优化方法包括索引优化、查询重写、表分区、适当选择和调整数据库引擎等。
184 3
|
9天前
|
存储 SQL 缓存
30个业务场景的SQL优化
这些优化策略和示例可以帮助改善 `SQL` 查询的性能和效率。在实践中,需要综合考虑数据库设计、`SQL` 编写、服务器配置等多方面因素,选择合适的优化方法,并进行充分的测试和验证。以上 30 个经验是 V 哥在实际经验中总结的内容,当然,业务场景不同,具体的优化策略也会不同,按实际情况处理,这不就是程序员要做的事情么。
|
10天前
|
SQL 存储 算法
clickhouse SQL优化
clickhouse 是 OLAP 数据库,但其具有独特的索引设计,所以如果拿 MySQL 或者其他 RDB 的优化经验来优化 clickhouse 可能得不到很好的效果,所以特此单独整理一篇文档,用于有 SQL 优化需求的同学,本人接触 clickhouse 时间也不长,难免有不足的地方,如果大家发现错误,还请不吝指正。
|
12天前
|
SQL 关系型数据库 MySQL
【MySQL】SQL优化
【MySQL】SQL优化
|
14天前
|
SQL 存储 关系型数据库
MySQL SQL优化
MySQL SQL优化
16 0
|
17天前
|
SQL 分布式计算 资源调度
一文解析 ODPS SQL 任务优化方法原理
本文重点尝试从ODPS SQL的逻辑执行计划和Logview中的执行计划出发,分析日常数据研发过程中各种优化方法背后的原理,覆盖了部分调优方法的分析,从知道怎么优化,到为什么这样优化,以及还能怎样优化。
103481 1
|
20天前
|
SQL 关系型数据库 MySQL
explain是什么?explain优缺点及如何使用explain优化SQL
explain是什么?explain优缺点及如何使用explain优化SQL
41 1