RDS for MySQL 表和索引优化实战—田杰

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: RDS for MySQL 表和索引优化实战 --田杰

主要内容:

一、 主键索引

二、 二级索引

三、 数学分析

四、 索引的作用

 

一、主键索引:

作为数据库的使用者,每天都需要跟数据库打交道,避免不了接触两个概念,一个是表,一个是索引。日常思维中,表是用来存储表中的数据,索引是用来加速查询访问。下面来看一下RDS for MySQLInnoDB引擎下面,数据的物理组织是如何组织?

image.png

如上图所示这张表,它的主键是1id4个字节、id作为主键,后面跟着一个单字符的C1,然后还有一个int类型的c2,然后单字符的c3,同时在 c1字段上有索引,这是很简单的一张表。这个数据是如何组织?

InnoDB引擎下,数据是存储在主键中,就是指数据是通过主建进行物理组织,跟Oracle本身默认的堆表不一样,Oracle本身默认创建的表如果不指定的话是一个堆表,真的有一个对象、物理结构、数据结构,一堆的数据结构来存储数据,同时主建是另外一个数据结构,是两份数据。对于MySQLInnoDB引擎下面,本身数据是存储在主键的叶子节点中,如下图所示,“c1c2c33列数据都存储在主键的叶子节点。

整个组建的数据结构是B-TreeB指的是Balance Tree多路平衡树,而不是Binary tree二叉树。多路平衡树和二叉树之间区别在于:

·二叉树只有左分支和右分支,而且不限定左分支的深度和右分支的深度,也可以指树的高度,不限定左分支右分支的高度否是一致。

·多路平衡树指,从最上面的根节点到任何一个叶子节点是一致的,树高从任何一个维度来看,从任何一个叶子节点到根节点,从跟到任何一个业务节点,必须是一致的。多路指是每一个节点,不论分支节点、跟节点,下面可以挂多个此子节点。

同时在整个的结构里面,在Oracle的体系中,对于每个存储数据的基础单元叫块“block”,再MySQL中做称为页。在MySQL里面,如果不特意指定,默认都是16KB作为一页,从磁盘上访问,不管是读取一行数据,还是读取一个字节,都要读16KB的数据到内存中。

数据组织结构有几个关键点:

第一个关键点:数据是存储在主键中,必须显示定义主键,如果不显示定义主键会出现两种情况:

·第一种情况是在数据传输时,无法判断数据是不是重复,是不是唯一的。主键的定义是非空、唯一。

·第二种情况是把RDS for MySQL的备份还原到线下的数据时,没有主键的表,读取时字段对不上。因为阿里的SQL,为了避免出现没有定义主键,导致的问题,默认隐式的增加一个字段,把备份还原到物理备份或还原到本地时,这张表会多出一个字段,会导致恢复时这张表不可访问。

第二个关键点:表的每个数据块大小都是16KB,分支节点也是16KB,分支节点上的节点数越多,树越扁,树高越小,查询的越快。因为根和分支节点都需要内存运行,树高越高,需要读的16KB的块数就越多,导致查询会更慢。

第三个关键点:块的尺寸一定的情况下,里面带的条目数越多,树高越小,访问数据的代价越低。条目数取决于主键的数据类型,如下图所示,int类型4个字节,big int8个字节,如果使用的是字符串,在uti8字符集的情况下,是char类型,至少要3个字节以上。所以关键点是主键尽量使int或者是big int这种整形,本身它很小,通常情况下一个16KB的块能放几百个int类型的条目,树高很容易控制在很小。

第四个关键点:本身它是一棵平衡树,平衡树的要求就在于,从根到任何一个叶子节点高度都要一样,树高是固定值,这棵树不断的被增、删、改的情况下,为了保证根与分支节点与叶子节点的一致性, 建议用auto_increment正向递增或者使用sequence,如果在有drds或者sequence引擎的情况下,可以用savings或者outer equipment这种单向递增,保证每次的写入的操作,尤其是插入操作,性能一致。

 

二、二级索引

除了主键以外的索引,其他都称之为二级索引。二级索引跟的主键索引一样,也是B-Tree

image.png

二级索引在MySQL里与Oracle在设计上不一样,如下图所示,对“c1”字段做了一个索引,但实际上数据存储在主键中,所以数据寻址时,没有必要放数据的真正物理地址,在Oracle里,是数据的物理地址。在MySQL里面直接放主键的值,因为知道主键的值,就能定位到这行记录。虽然索引放的是“c1”,但是真正存储时,节点中是把主键的值都要存起来,这种数据类型导致主键的数据长度不能太长,否则会有问题。

补充一下,因为主键除了单项递增数据类型要小以外,如uuidMd5不建议用来做主键,因为长度太长。如果长度太长,磁盘块里头放的会很小,树结构会变得很臃肿,相同存储相同的数据量,索引会占很大的空间。对于IO产生很大的影响,相同的硬件条件下,访问数据速度要慢,开销大、成本高。

 

三、数学分析

假设一张表里有一亿行记录,每一个磁盘页存放16KB的块,存放的条目数叫做平衡因子/分支因子(Balance factor)。

每个16KB分支节点可以存储的索引字段个数,取两个比较极限的情况,“b=2”每一个16KB的块里只能放两条记录,类似于二叉树,b=100”每一个记录里能放100个条记录,就每一个16KB的块里能放100条记录。

树高(h),树高影响物理IO,从磁盘上获取数据到内存,需要花多少个物业取IO访问到数据,数据库的增、删、改、查(selectinterrupteddeletereplace),所有操作都是发生在内存。

索引B树高度:从叶子节点到根节点的节点个数。树高(h)固定的计算公式:

image.png

是跟 “b”和“n”相关的取两个的对数,“b”是平衡因子,在“b=2”的情况下,树高是27,在“b=100”的情况下,从定位1行记录的开销需要读2716KB的块到内存,b定义成100,只需读416KB的块到内存,开销差异很明显。

存储空间的差异更明显,只说主键“b=2”情况下,需要花费781,250 MB的存储容量;“b=100”的情况下,花费15,625 MB的存储容量。

一般RDS内存正常情况下,最大的能到470GB内存,“b=100”时,770%~75%左右是分配给InnoDB用来缓存磁盘块, 770%~75%是可以保证数据完全缓存在内存中,表的核心数据都保存在内存里,也不会超出内存最大值。“b=2”时,只考虑了主健,不考虑任何二级索引,核心表数据也需要781GB,会需要出现物理IO换入换出数据,因为超出了内存空间。同样的存储,两种情况的代价是完全不一样,性能表现截然不同。

 

四、索引的作用

通过图书馆的模型介绍索引的作用,去图书馆内找一本书,正常的情况下,需要一个目录的,通过查目录,查询到所需的书在哪个书架上面的哪个位置上面,这样可以快速找到,这个目录就相当于索引,它是一个从空间换时间,通过提前做准备好其他空间,来缓解访问时间。需要注意的是这里只是寻找一本书,访问一个数据,如果访问的数据量,需要获取的数据量占总数据量的一定比例之后,就会引起质变。

优化是提高访问效率。

两个概念:TRTS

TR是随机访问一个16KB的块,TS是顺序的去读一个16KB的块需要的时间。

通过user用户表,(id int primary key, age int, fname char(1));

通过Select fname from users where age = 10进行一个简单的查询。

image.png

这里我们忽略树高,假设数据已经都在内存,因为不知道树读取的位置是在哪,那么下一次对根来说就是一个随机读,读到根在忽略树高且树高合理的情况下,需要画n减一个顺序图,把表编辑,因为没有索引。

位次条件下建索引,首先考虑位次条件, where后的条件是什么,根据什么条件去访问数据,根据规则去优化访问,如上图所示,age没有索引,就全面扫描,但在不同的情况下

TR = 10 ms

TS = 0.01 ms

RT = TR * 1 + TS * (n - 1)

 = 10 ms * 1 + 0.01 ms * (900K -1)

= 10 ms + 9000 ms

= 9.01 Sec

RT = TR * 1 + TS * (n - 1)

= 10 ms * 1 + 0.01 ms * (9000 -1)

 = 10 ms + 90 ms

= 100 ms

RT = TR * 1 + TS * (n - 1)

= 10 ms * 1 + 0.01 ms * (9 -1)

 = 10 ms + 0.08 ms

= 10.08 ms

 

全表扫描不一定不是好事,取决于访问、目的、还有数据量的多少。

如果表里没太大的数据量,没有必要去访问索引,根据公式推理

结论就是访问的位次上面没有索引,全表扫描来获取数据,全面扫描不一定不好,不好的代价是在于表里的数据量,随着数据量的上升,TS的访问的代价会快速的增加,会导致时间快速增加。

随着数据量增加,上线时间加长,表的数据量越来越多,查询越来越慢,可以看在a制字段上面加一个索引,如下图所示:

image.png

由此推出,查询慢,在位次条件对应的情况下建索引,前提是获取的数据量是占总表的数据量很小的一部分,索引才是生效的。

实际上不管是oracle,还是MySQL的优化器也好,根据查询,先估访问的数据量大概会占总表的数据量的多少,包括根据版本不一样,会有略微变化。如果查询获取的数据量超过了总表数据量的一定比例之后,就不能再走这种索引访问形式,索引访问形式会带来大量的回表访问,会引起大量的随机读。

总结次位条件上面尽量建索引来加速访问,节省CPU,从时间来衡量,就是减少访问时间,减少查询的处理时间。

建索引前提下是访问的数据要占总表数据量很小的一部分。

如何进行优化,如下表所示,公式内头最大的开销是在4个随机读上面,来源于回表的3个随机读,产生三个随机读的原因是在于索引里没有fname,没有需要的数据,把fname放到我的索引里,Users (id int primary key, age int, fname char(1), key idx_age_fname (age,fname));这个时候访问就会变成了一个随机读,访问的根,三个顺序读,找到三个数据,通过扫索引的三行数据,就满足了查询,公式就会变成1个随机读加3个顺序读,出来是10个毫秒,这个索引叫做覆盖性索引,能够完全满足查询,不用回表,是在对于查询来说是最高效的一个访问形式。

image.png

如上图所示,从9秒钟优化到10 个毫秒,900倍的性能提升,最核心的表最频繁的查询建覆盖性索引。

建组合索引的时候,区分度越高的字段放在最前面,因为区分度越高,中间结果的索引片会越小越薄。目的就是只有在选择很小量的数据的时候,索引才是高效的。

反推就是中间生成的结果集或者中间的节点索引片越薄,包含的数据越小,索引对查询的加速是越高的。

最左原则,要求能匹配到的位次条件不能出现在第二个,位次条件一定要匹配到第一个字段才可以,就是在匹配索引跟查询的位次的时候,按照从左到右来匹配。如果已经有查询在建组合索引的时候,一定要把几个条件里区分度最高的放在最左边,其次,跟区分度类似的字段,尽量的往前放,经常被更新的字段尽量往后放,

最后,如果相同区分度都差不多,尽量把age看它的位次条件,如果是等值的,不管是等号大于等于小于等于都可以,只要带等号的尽量往前放,因为如果带着等号,条件会被用来生成中间的临时索引片或者中间的结果集。后面字段的条件依然能被用来生成中间的结果集。

fname是否能被用来生成结果集,需要看位次条件,第一个的位次条件,如果是等值带等号,第二个字段肯定能被用来,只要它后边位次加减中有,肯定能用来生成中间的结果集,生成重量级结构机,第三个能不能被用,要看第二个是不是带等值条件,就是说如果带等值条件,尽量往前放,归结起来就是:

1、    区分度越高的位次条件尽量往前放,如不是经常被更新的;

2、    带等值条件的尽量往前放;

举例,上图如果写的是不等于d那么age可以用来生成中间结果索引片,lname也能用来生成中间索引片,如果这里fname的位次条件,就不能用来生成索引片,只能用来做引擎下推的过滤,或者是server层的过滤,通过已经存储好的索引去生成中间结果集是基本上对CPU消耗是非常低的,但如果靠CPU去做过滤,生成结果集是非常耗CPU的,这是两种完全不同的访问方式。

还有如果前面的等值全是等值条件,排序之前所有的索引前置的字段都是等值的情况下,就可以直接通过索引来避免它的排序。

覆盖性索引并排序例子:

image.png

RDS for MySQL数据库的日常运维开发的使用的规范和建议

image.png

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
24天前
|
存储 SQL 关系型数据库
【MySQL】4. 表的操作
【MySQL】4. 表的操作
21 0
|
28天前
|
SQL 关系型数据库 MySQL
【MySQL技术专题】「问题实战系列」深入探索和分析MySQL数据库的数据备份和恢复实战开发指南(8.0版本升级篇)
【MySQL技术专题】「问题实战系列」深入探索和分析MySQL数据库的数据备份和恢复实战开发指南(8.0版本升级篇)
96 0
|
23天前
|
存储 关系型数据库 MySQL
MySQL数据库性能大揭秘:表设计优化的高效策略(优化数据类型、增加冗余字段、拆分表以及使用非空约束)
MySQL数据库性能大揭秘:表设计优化的高效策略(优化数据类型、增加冗余字段、拆分表以及使用非空约束)
|
23天前
|
缓存 关系型数据库 MySQL
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
|
1天前
|
SQL 关系型数据库 MySQL
不允许你不知道的 MySQL 优化实战(一)
不允许你不知道的 MySQL 优化实战(一)
|
4天前
|
关系型数据库 MySQL 中间件
【MySQL实战笔记】07 | 行锁功过:怎么减少行锁对性能的影响?-02 死锁和死锁检测
【4月更文挑战第19天】在高并发环境下,死锁发生在多个线程间循环等待资源时,导致无限期等待。MySQL中,死锁可通过`innodb_lock_wait_timeout`参数设置超时或`innodb_deadlock_detect`开启死锁检测来解决。默认的50s超时可能不适用于在线服务,而频繁检测会消耗大量CPU。应对热点行更新引发的性能问题,可以暂时关闭死锁检测(风险是产生大量超时),控制并发度,或通过分散记录减少锁冲突,例如将数据分拆到多行以降低死锁概率。
19 1
|
7天前
|
SQL 关系型数据库 MySQL
Python与MySQL数据库交互:面试实战
【4月更文挑战第16天】本文介绍了Python与MySQL交互的面试重点,包括使用`mysql-connector-python`或`pymysql`连接数据库、执行SQL查询、异常处理、防止SQL注入、事务管理和ORM框架。易错点包括忘记关闭连接、忽视异常处理、硬编码SQL、忽略事务及过度依赖低效查询。通过理解这些问题和提供策略,可提升面试表现。
27 6
|
9天前
|
SQL 关系型数据库 MySQL
MySQL环境搭建——“MySQL数据库”
MySQL环境搭建——“MySQL数据库”
|
9天前
|
SQL NoSQL 关系型数据库
初识MySQL数据库——“MySQL数据库”
初识MySQL数据库——“MySQL数据库”
|
14天前
|
存储 关系型数据库 MySQL
【MySQL实战笔记】 04 | 深入浅出索引(上)-02
【4月更文挑战第9天】InnoDB数据库使用B+树作为索引模型,其中主键索引的叶子节点存储完整行数据,非主键索引则存储主键值。主键查询只需搜索一棵树,而非主键查询需两次搜索,因此推荐使用主键查询以提高效率。在插入新值时,B+树需要维护有序性,可能导致数据页分裂影响性能。自增主键在插入时可避免数据挪动和页分裂,且占用存储空间小,通常更为理想。然而,如果场景仅需唯一索引,可直接设为主键以减少查询步骤。
15 1
【MySQL实战笔记】 04 | 深入浅出索引(上)-02

相关产品

  • 云数据库 RDS MySQL 版