mysql 索引优化

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: mysql 索引优化

背景

    首先是因为一个慢sql带来的思考与建议。

    随着2017双11即将来临,大家也都进入了备战状态。稳定性保障是我们双11核心目标的第一大目标,是其他一切目标的前提。大家必须带着危机感和使命感,进入备战状态了。

   今年业务量预计是去年的好几倍,仓配+村淘订单预计1000万单,我们如何应对这次战役呢,首先我们需要提效能,今年的核心保障人数比去年更少,业务降级比去年也少,平均每笔订单的机器成本还需要下降33.3%,单机QPS需要大幅度提升,意味着我们的压力是往年双11的好几倍,我们能否经得起考验呢!

 

    最近共配多级网络项目刚刚上线,发现线上有很多因为慢sql问题导致的性能问题。DB是整个系统的数据核心也是稳定性最底层的支持,如果这一层出现问题,将会是很严重的。说到DB问题,一个是数据可靠性,另一个是性能,前者在阿里的技术体系上保障,后者最容易出现问题的恐怕就是索引了。

通过这次的教训,呼吁各owner在双11前对重要表索引进行Review。

今天就通过具体的案例来和大家一起探讨下如何建索引,优化一个慢sql。

 

慢sql优化

线上案例

履行系统多级网络项目刚刚上线,为了支持多级作业,将运单和作业单做了分离,将原先的ct_order,拆分出nc_tms_order作业单。刚上线很多业务场景还没有加索引,刚上线几天随着业务不断深入,终于在今天爆发了。因为一个慢sql,导致DB服务器load上升,DB链接池耗尽,应用接口超时,甚至期间业务无法正常使用。

其中一个sql

SELECT COUNT(1)
FROM`nc_tms_order_0340``nc_tms_order`
WHERE`nc_tms_order`.`wh_id`= 108884
AND`nc_tms_order`.`order_type`= 1
AND`nc_tms_order`.`distribution_order_id`='12059380340'
AND`nc_tms_order`.`is_deleted`= 0

 

 

从IDB上看到,平均响应时间5s,扫描303531行,流量一旦上来,基本就打满整个线程池,db性能急剧下降。

这个时候一并收到ctn报警,因为ctn用户地址转换也依赖这个数据库实例,系统间的耦合产生的连锁反应立即出现,所以有必要将两个重要应用做隔离,后续再讨论。

 

紧接着开发同学想到的是加索引,下午17.10分通过加上索引后,DB的各项性能数据恢复。

具体故障排查过程单独分析,这里主要说说,索引应该如何加。以下是DB性能指标图反映了从链接数耗尽、cpu接近100%,到恢复的过程。经历了有惊很险的过程。

 

 

 

 

 

如下图,新增

唯一索引:idx_whid_distributionorderid(wh_id,distribution_order_id)

通过这个索引解决线上故障,接下来分析下优化过程。

 

至少我第一眼看上去觉得索引有点多乱。

 

 

 

 

wh_id,总条目数是30万+,wh_id唯一值条目为2,按照公式count(distinct col)/count(*)

区分度基本等于0。

 

sql解析的额外信息,当出现using index时,表示sql使用覆盖索引,性能较好,而当出现using filesort、using temporary、using where时,查询需要优化。

 

我这里先总结几条建索引的几个原则,后面有机会再和大家一起深入下Mysql索引知识。

 

建索引的几大原则

对于索引我总结三大原则:

  1、最左前缀原则;2、不冗余原则;3、最大选择性原则。

基本掌握这三条,对于索引的优化理论上是没有问题了。

最左前缀原则

一般在where条件中两个及以上字段时,我们会建联合索引。

高效使用索引的首要条件是知道什么样的查询会使用到索引,这个问题和B+Tree中的“最左前缀原理”有关,下面通过例子说明最左前缀原理。

MySQL中的索引可以以一定顺序引用多个列,这种索引叫做联合索引,一般的,一个联合索引是一个有序元组,其中各个元素均为数据表的一列,实际上要严格定义索引需要用到关系代数。另外,单列索引可以看成联合索引元素数为1的特例

 

  • 最左前缀是一个很重要的原则。

mysql会从左至右匹配,直到遇到范围查找(> < like between)就停止。

如:

select * from table1 where a=1 and b=2 and c<3 and d=9 ;

建立的联合索引为:(a,b,c,d)  实际使用的索引为(a,b,c)。因为遇到了c<3就停止了,d列就没有用上。

前面讲过联合索引是有序元组,

则mysql实际建的索引为:(a) (a,b)  (a,b,c) (a,b,c,d)。

举个例子:where b=2 and c=3 and d=9 ;按照最左匹配原则,这个条件就没法走索引了,首先必须有a。

 

  • =,in可以乱序,查询优化器会帮你优化成索引可以识别的形式。也就是说,where b=2 and a=1 and c<3

使用的索引任然为(a,b,c)组合。

 

  • 回到线上案例

索引:idx_whid_distributionorderid(wh_id,distribution_order_id)

索引组合 (wh_id) ,(wh_id,distribution_order_id)

相当于建了一个wh_id 的单列索引,也就是说当你要根据wh_id查询时,是不需要再新建索引了。

 

不冗余原则

  • 尽量扩展索引、不要新建索引

mysql目前主要索引有:FULLTEXT,HASH,BTREE

好的索引可以提高我们的查询效率,不好的索引不但不会起作用,反而给DB带来负担,基于BTREE结构,插入、修改都会重新调整索引结构,存储成本增加,写效率降低,同时DB系统也要消耗资源去维护。

基于刚才的最左匹配原则,尽量在原有基础上扩展索引,不要新增索引。

 

  • 能用单索引,不用联合索引;能用窄索引,不用宽索引;能复用索引,不新建索引。

回到线上案例:

nc_tms_order、ct_order看看分别有哪些索引

 

 

看到这里我开始凌乱,好像什么字段都可以加索引。

为此专门针对ct_order表两个具有比较性的索引做了性能测试,ct_order_code,lc_order_code区分度都是非常高的字段,前者是好于后者(联合station_id并没有起到太多优化作用)。

idx_ct_order_code(ct_order_code),

idx_ct_order_lc_order_code(station_id,lc_order_code)

 

那么接下来我们说说那些字段适合建索引。

最大选择性原则

  • 选择区分度高列做索引

   什么是区分度高的字段呢?

一般两种情况不建议建索引:

1、一两千条甚至几百条,没必要建索引,让查询做全表扫描就好了。

因为不是你建了就一定会走索引,执行计划会选择一个最优的方式,msql辅助索引的叶子节点并不直接存储实际数据,只是主建ID,再通过主键索引二次查找。这么一来全表可能很有可能效率更高。

2、索引选择性较低的情况。

所谓选择性(Selectivity),是指不重复的索引值(也叫基数,Cardinality)与表记录数(#T)的比值。

Index Selectivity = Cardinality / #T

显然选择性的取值范围为(0, 1],选择性越高的索引价值越大,这是由B+Tree的性质决定的。

 

  • 回到线上案例 wh_id 最好不用做索引字段,这个和性别男、女作为索引字段没区别:

SELECT count(DISTINCT(wh_id))/count(*) AS Selectivity

FROM nc_tms_order_0340nc_tms_order;

 

0

 

选择性不足0.0001(精确值为0.00000666),按Selectivity值越大价值越大原则,实在没有什么必要为其单独建索引。

再看下distribution_order_id 单列索引。

SELECT count(DISTINCT(distribution_order_id))/count(*) AS Selectivity FROM nc_tms_order_0340 nc_tms_order;

0.0030

Selectivity = 0.0030 ,比之前有所优化,但其实不不是特别理想。

 

联合索引

SELECT count(DISTINCT(concat(wh_id,distribution_order_id)))/count(*) AS Selectivity FROM nc_tms_order_0340 nc_tms_order;

0.0030

Selectivity = 0.0030

 

从值来看,这里建联合索引的价值并不是特别大。一个distrubution_id 搞定。

 

那么我们在建一个索引或联合索引的时候拿不准的时候可以先计算下选择性值以及通过explain测试。

一般情况,status、is_deleted列不建议建索引。

 

  • 创建复合索引,需要注意把区分度最大的放到最前面。也就是值越大的放前面,当然需根据时间场景和sql通过执行计划进行优化。

 

  • 前缀索引

有一种与索引选择性有关的索引优化策略叫做前缀索引,就是用列的前缀代替整个列作为索引key,当前缀长度合适时,可以做到既使得前缀索引的选择性接近全列索引,同时因为索引key变短而减少了索引文件的大小和维护开销。

其他

  • 索引列不能参与计算

   比如from_unixtime(create_time) = ’2017-11-11’就不能使用到索引,语句应该写成create_time = unix_timestamp(’2017-11-11’);

  • 主键最好使用自增型,保证数据连续性(mysql innodb 主键默认采用b+tree,索引和数据放在同一个btree中),不要使用uuid、hash、md5等

  • 不要使用前匹配的like查询,会导致索引失效。可以使用后匹配like,如"xxx%"。
  • 在字符串列上创建索引,尽量使用前缀索引。前缀基数根据具体业务,在匹配度和存储量(索引的存储量)之前做一个平衡。
  • 不要使用 not inlike,会导致索引失效。not in可以用not exists替换。in和or所在列最好有索引

 

说了这么多留action吧,大家回去看看ct_order,nc_tms_order看看如何优化吧。

 

其实数据库索引调优,光靠理论是不行的,需要结合实际情况。MySQL机制复杂,如查询优化策略和各种引擎的实现差异等都会使情况变复杂。我们在了解这些原则和基础之上,要不断的实践和总结,从而真正达到高效使用MySQL索引的目的。

 

执行计划explain命令

explain 是sql优化神奇。

公司IDB的执行计划被包装过了,除了被动查慢sql调优上看到,一直没找主动做执行计划的地方,有知道的可以告诉我下。

 

以下均为工具介绍,可留做备查。

explain用法

EXPLAIN tbl_name或:EXPLAIN [EXTENDED] SELECT select_options

举例

mysql> explain select * from event;  
+—-+————-+——-+——+—————+——+———+——+——+——-+  
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |  
+—-+————-+——-+——+—————+——+———+——+——+——-+  
| 1 | SIMPLE | event | ALL | NULL | NULL | NULL | NULL | 13 | |  
+—-+————-+——-+——+—————+——+———+——+——+——-+  
1 row in set (0.00 sec)

各个属性的含义

  • id:select查询的序列号
  • select_type:select查询的类型,主要是区别普通查询和联合查询、子查询之类的复杂查询。
  • table:输出的行所引用的表。
  • type:联合查询所使用的类型。

type显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,得保证查询至少达到range级别,最好能达到ref。

就type进行详细的介绍:all : 即全表扫描

index : 按索引次序扫描,就type进行详细的介绍:

System,const,eq_ref,ref,range,index,all

all : 即全表扫描

 

index : 按索引次序扫描,先读索引,再读实际的行,结果还是全表扫描,主要优点是避免了排序。因为索引是排好的。

range:以范围的形式扫描。

explain select * from a where a_id > 1\G

ref:非唯一索引访问(只有普通索引)

create table a(a_id int not null, key(a_id));

insert into a values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);

mysql> explain select * from a where a_id=1\G

eq_ref:使用唯一索引查找(主键或唯一索引)

const:常量查询

当出现using index时,表示sql使用覆盖索引,性能较好,而当出现using filesort、using temporary、using where时,查询需要优化。

先读索引,再读实际的行,结果还是全表扫描,主要优点是避免了排序。因为索引是排好的。

range:以范围的形式扫描。

explain select * from a where a_id > 1\G

ref:非唯一索引访问(只有普通索引)

create table a(a_id int not null, key(a_id));

insert into a values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);

mysql> explain select * from a where a_id=1\G

eq_ref:使用唯一索引查找(主键或唯一索引)

const:常量查询

当出现using index时,表示sql使用覆盖索引,性能较好,而当出现using filesort、using temporary、using where时,查询需要优化。

possible_keys:指出MySQL能使用哪个索引在该表中找到行。如果是空的,没有相关的索引。这时要提高性能,可通过检验WHERE子句,看是否引用某些字段,或者检查字段不是适合索引。

  • key:显示MySQL实际决定使用的键。如果没有索引被选择,键是NULL。
  • key_len:显示MySQL决定使用的键长度。如果键是NULL,长度就是NULL。文档提示特别注意这个值可以得出一个多重主键里mysql实际使用了哪一部分。
  • ref:显示哪个字段或常数与key一起被使用。
  • rows:这个数表示mysql要遍历多少数据才能找到,在innodb上是不准确的。
  • Extra:如果是Only index,这意味着信息只用索引树中的信息检索出的,这比扫描整个表要快。

如果是where used,就是使用上了where限制。

如果是impossible where 表示用不着where,一般就是没查出来啥。

如果此信息显示Using filesort或者Using temporary的话会很吃力,WHERE和ORDER BY的索引经常无法兼顾,如果按照WHERE来确定索引,那么在ORDER BY时,就必然会引起Using filesort,这就要看是先过滤再排序划算,还是先排序再过滤划算。

 

 

 

 

 

MySQL索引原理

未完待续 。。。

可以参考:王小东《高性能MySQL》

索引价值

索引基本原理

索引数据结构

b+tree介绍

磁盘IO原理

 

 

索引优化实践

    sql优化实践:https://lark.alipay.com/nongcun/fc/glqvng

 

 

相关书籍介绍

[1] Baron Scbwartz等 著,王小东等 译;高性能MySQL(High Performance MySQL);电子工业出版社,2010

[2] Michael Kofler 著,杨晓云等 译;MySQL5权威指南(The Definitive Guide to MySQL5);人民邮电出版社,2006

[3] 姜承尧 著;MySQL技术内幕-InnoDB存储引擎;机械工业出版社,2011

[4] D Comer, Ubiquitous B-tree; ACM Computing Surveys (CSUR), 1979

[5] Codd, E. F. (1970). “A relational model of data for large shared data banks”. Communications of the ACM, , Vol. 13, No. 6, pp. 377-387


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4天前
|
SQL 关系型数据库 MySQL
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
MySQL慢查询优化、索引优化,是必知必备,大厂面试高频,本文深入详解,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验分享。
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
|
26天前
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
8天前
|
SQL 关系型数据库 MySQL
MySQL慢查询优化、索引优化、以及表等优化详解
本文详细介绍了MySQL优化方案,包括索引优化、SQL慢查询优化和数据库表优化,帮助提升数据库性能。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
MySQL慢查询优化、索引优化、以及表等优化详解
|
13天前
|
缓存 监控 关系型数据库
如何优化MySQL查询速度?
如何优化MySQL查询速度?【10月更文挑战第31天】
40 3
|
15天前
|
缓存 关系型数据库 MySQL
如何优化 MySQL 数据库的性能?
【10月更文挑战第28天】
38 1
|
23天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:百万级数据统计优化实践
【10月更文挑战第21天】 在处理大规模数据集时,传统的单体数据库解决方案往往力不从心。MySQL和Redis的组合提供了一种高效的解决方案,通过将数据库操作与高速缓存相结合,可以显著提升数据处理的性能。本文将分享一次实际的优化案例,探讨如何利用MySQL和Redis共同实现百万级数据统计的优化。
59 9
|
17天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
84 1
|
23天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:优化百万数据查询的实战经验
【10月更文挑战第13天】 在处理大规模数据集时,传统的关系型数据库如MySQL可能会遇到性能瓶颈。为了提升数据处理的效率,我们可以结合使用MySQL和Redis,利用两者的优势来优化数据查询。本文将分享一次实战经验,探讨如何通过MySQL与Redis的协同工作来优化百万级数据统计。
52 5
|
27天前
|
存储 关系型数据库 MySQL
如何在MySQL中进行索引的创建和管理?
【10月更文挑战第16天】如何在MySQL中进行索引的创建和管理?
57 1
|
27天前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
49 1