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

目录
相关文章
|
7天前
|
SQL 缓存 监控
大厂面试高频:4 大性能优化策略(数据库、SQL、JVM等)
本文详细解析了数据库、缓存、异步处理和Web性能优化四大策略,系统性能优化必知必备,大厂面试高频。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:4 大性能优化策略(数据库、SQL、JVM等)
|
15天前
|
SQL 存储 缓存
如何优化SQL查询性能?
【10月更文挑战第28天】如何优化SQL查询性能?
60 10
|
14天前
|
SQL 存储 缓存
SQL Server 数据太多如何优化
11种优化方案供你参考,优化 SQL Server 数据库性能得从多个方面着手,包括硬件配置、数据库结构、查询优化、索引管理、分区分表、并行处理等。通过合理的索引、查询优化、数据分区等技术,可以在数据量增大时保持较好的性能。同时,定期进行数据库维护和清理,保证数据库高效运行。
|
28天前
|
SQL 资源调度 分布式计算
如何让SQL跑快一点?(优化指南)
这篇文章主要探讨了如何在阿里云MaxCompute(原ODPS)平台上对SQL任务进行优化,特别是针对大数据处理和分析场景下的性能优化。
|
1月前
|
SQL Oracle 关系型数据库
SQL优化-使用联合索引和函数索引
在一次例行巡检中,发现一条使用 `to_char` 函数将日期转换为字符串的 SQL 语句 CPU 利用率很高。为了优化该语句,首先分析了 where 条件中各列的选择性,并创建了不同类型的索引,包括普通索引、函数索引和虚拟列索引。通过对比不同索引的执行计划,最终确定了使用复合索引(包含函数表达式)能够显著降低查询成本,提高执行效率。
|
1月前
|
SQL 关系型数据库 MySQL
如何确认SQL用了索引:详细技巧与方法
在数据库管理中,索引是提高SQL查询性能的重要手段
|
1月前
|
SQL 监控 数据库
慢SQL对数据库写入性能的影响及优化技巧
在数据库管理系统中,慢SQL(即执行缓慢的SQL语句)不仅会影响查询性能,还可能对数据库的写入性能产生显著的不利影响
|
1月前
|
SQL 关系型数据库 PostgreSQL
遇到SQL 子查询性能很差?其实可以这样优化
遇到SQL 子查询性能很差?其实可以这样优化
87 2
|
1月前
|
SQL 存储 关系型数据库
SQL默认索引是什么:深入解析与技巧
在SQL数据库中,索引是一种用于提高查询性能的重要数据结构
|
1月前
|
SQL 存储 关系型数据库
SQL默认索引是什么
在SQL数据库中,索引是一种用于提高查询性能的数据结构