一文让你搞懂 MySQL

一、数据库驱动

二、sql 语句执行过程

三、InnoDB 储存引擎的架构设计

redo(重做日志)日志刷盘策略的选择建议 :

  • 建议的设置为1,也就是说,提交事务的时候,redo 日志必须是刷入磁盘文件的,这样可以严格保证提交事务之后,数据是绝对不会丢失的,因为有 redo 日志在磁盘文件里可以恢复你做的所有修改
  • 如果要是选择 0 的话,可能你提交事务之后,MySQL 宕机,那么此时 redo 日志没有刷盘,导致内存里的 redo 日志丢失,你提交的事务更新的数据就丢失了;
  • 如果要是选择 2的话,如果机器宕机,虽然之前提交事务的时候,redo 日志进入 os cache 了,但是还没进入磁盘文件,此时机器宕机还是会导致 os cach e里的 redo日志 丢失。

增删改是针对缓存池进行的

四、bin log

redo log (重做日志),是一种偏向物理性质的重做日志,里面记录类似“对哪个数据页中的什么记录,做了什么修改”,它本身就是 InnoDB 存储引擎特有的一个东西

binlog (归档日志):它里面记录的是偏向于逻辑性的日志,类似于“对 users 表中的 id = 10 的一行数据做了更新操作,更新以后的值是什么”,是属于 MySQL Server 自己的日志文件

在提交事务的时候,先把 redo log 写入磁盘文件,然后再把 binlog 写入磁盘文件,然后把 binlog文件名称 和这次更新的 binlog 日志在文件里的位置都写入到 redo log 日志文件里去,同时在 redo log 日志文件里写入一个 commit 标记,标志事务提交完成。commit 标记可以保证 redolog 日志文件和 binlog 日志文件的一致性

执行器的作用:它负责跟 InnoDB 进行交互,包括从磁盘里加载数据到 Buffer Pool 中进行缓存,包括写入 undo 日志,包括更新日志 Buffer Pool 里的数据,以及写入 redo log buffer, redo log 刷入磁盘,写 binlog。配合完成一个 SQL 语句在磁盘与内存层面的全部数据更新操作

binlog 刷盘策略:有一个 sync_binlog 参数可以控制 binlog 的刷屏策略,默认值为 0,此时你把 binlog 写入磁盘的时候,其实不是直接进入磁盘文件,而是进入 os cache 内存缓存,设置为 1 的话,binlog 直接写入到磁盘文件中去,所以宕机 binlog 也不会丢失

五、生产经验

Java 应用的部署:

  • 一般Java应用系统部署在 4 核 8G 的机器上,每秒钟抗下 500 左右的并发访问量,差不多是比较合适的
  • 4 核 8G 的机器部署普通的Java应用系统,每秒大致就是抗下几百的并发访问,从每秒一两百请求到每秒七八百请求,都是有可能的,关键是看你每个请求处理需要耗费多长时间

数据库部署:

  • 一般 8核16G 的机器部署的 MySQL 数据库,每秒抗个一两千并发请求是没问题的,但是如果你的并发量再高一些,假设每秒有几千并发请求,那么可能数据库就会有点危险了,因为数据库的 CPU、磁盘、IO、内存的负载都会很高,弄不数据库压力过大就会宕机。
  • 对于 16 核 32G 的机器部署的 MySQL 数据库而言,每秒抗个两三千,甚至三四千的并发请求也都是可以的,但是如果你达到每秒上万请求,那么数据库的 CPU、磁盘、IO、内存的负载瞬间都会飙升到很高,数据库也是可能会扛不住宕机的
  • 最好是采用 SSD 固态硬盘而不是普通的机械硬盘,因为数据库最大的复杂就在于大量的磁盘 IO,他需要大量的读写磁盘文件,所以如果能使用 SSD 固态硬盘,那么你的数据库每秒能抗的并发请求量就会更高一些

QPS(Query Per Second ):数据库每秒可以处理多少个请求,你大致可以理解为,一次请求就是一条SQL 语句,也就是说这个数据库每秒可以处理多少个 SQL 语句

TPS(Transaction Per Second):数据库每秒可处理的事务量,这个 TPS 往往是用在数据库中较多一些,其实从字面意思就能看的出来,他就是说数据库每秒会处理多少次事务提交或者回滚

IOPS:这个指的是机器的随机IO并发处理的能力,比如机器可以达到 200 IOPS,意思就是说每秒可以执行 200 个随机 IO 读写请求,如果说 IOPS 指标太低了,那么会导致你内存里的脏数据刷回磁盘的效率就会不高。

吞吐量:这个指的是机器的磁盘存储每秒可以读写多少字节的数据量

latency:这个指标说的是往磁盘里写入一条数据的延迟,一般来说,当然是你的磁盘读写延迟越低,那么你的数据库性能就越高,你执行每个 SQL 语句和事务的时候速度就会越快。

其他方面的因素:

  • CPU 负载:CPU 负载是一个很重要的性能指标,因为假设你数据库压测到了每秒处理 3000 请求了,可能其他的性能指标
    都还正常,但是此时 CPU 负载特别高,那么也说明你的数据库不能继续往下压测更高的 QPS 了,否则 CPU 是吃不消的。
  • 网络负载:这个主要是要看看你的机器带宽情况下,在压测到一定的 QPS 和 TPS 的时候,每秒钟机器的网卡会输入多少 MB 数据,会输出多少 MB 数据,因为有可能你的网络带宽最多每秒传输 100MB 的数据,那么可能你的 QPS 到 1000 的时候,网卡就打满了,已经每秒传输 100MB 的数据了,此时即使其他指标都还算正常,但是你也不能继续压测下去了
  • 内存负载:这个就是看看在压测到一定情况下的时候,你的机器内存耗费了多少,如果说机器内存耗费过高了,说明也不能继续压测下去了

六、Buffer Pool

七、flush 链表和 free 链表

八、LRU算法

触发 MySQL 的预读机制 :

  • 有一个参数是 innodb_read_ahead_threshold,他的默认值是 56,意思就是如果顺序的访问了一个区里的多个数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去
  • 如果 Buffer Pool 里缓存了一个区里的 13个 连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会直接触发预读机制,把这个区里的其他的数据页都加载到缓存里去 。这个机制是通过参数 innodb_random_read_ahead 来控制的,他默认是 OFF,也就是这个规则是关闭的

如果你使用简单的 LRU 链表的机制,其实是漏洞百出的,因为很可能预读机制,或者全表扫描的机制,都会一下子把大量未来可能不怎么访问的数据页加载到缓存页里去,然后 LRU 链表的前面全部是这些未来可能不怎么会被访问的缓存页!而真正之前一直频繁被访问的缓存页可能此时都在 LRU 链表的尾部了!

  • 冷热数据的比例是由innodb_old_blocks_pct参数控制的,他默认是 37,也就是说冷数据占比 37%
  • 第一次把一个数据页加载到缓存页之后,这个缓存页实际上是被放在冷数据区域的链表头部位置
  • 当我们把数据页加载到缓存页之后,1s 内如果访问这个缓存页,此时是不会把这个缓存页加载到热数据区域链表头部的,假如在 1s 之后访问,就会把这个缓存页加载到热数据区域的链表头部。这个 1s 是 MySQL 默认的,可以用一个 innodb_old_blocks_time 参数改变其大小
  • 预读机制和全表扫描加载进来的一大堆缓存页,此时都在冷数据区域里,跟热数据区域里的频繁访问的缓存页,是没关系的!
  • 此时缓存页不够了怎么办:直接就是可以找到LRU链表中的冷数据区域的尾部的缓存页,他们肯定是之前被加载进来的,而且加
    载进来1s过后都没人访问过,说明这个缓存页压根儿就没人愿意去访问他!他就是冷数据
  • MySQL LRU的一点优化:就是当热数据区域链表的前 1/4 被访问时是不会移动到热数据区域的链表头部,只有热数据链表的后 3 / 4 才会被加载到热数据区域链表的头部,因为缓存页的移动是有开销的,这样可以提升性能

MySQL 不仅会把冷数据区域链表尾部的缓冲页刷入磁盘,还会可能把 flush 链表中的所有缓存页刷入磁盘

九、生产经验

可以用多个 buffer pool 来提高并发访问量

可以只用参数 innodb_buffer_pool_size = 8589934592 来设置 Buffer Pool 的大小(是所有 Buffer Pool 的总大小)
使用参数 innodb_buffer_pool_instances = 4 来设置 Buffer Pool 的个数

每个 buffer pool 里已经有了多个 chunk,每个 chunk 就是一系列的描述数据块和缓存页 。buffer pool 按照 chunk 为单位,拆分为了一系列的小数据块,但是每个 buffer pool 是共用一套 free、flush、lru 的链表的。

十、MySQL 一行数据在物理磁盘怎样存储的?

1、一行数据的存储格式

  • MySQL 在物理磁盘上的存储格式是以 数据页的形式存在的,我们从物理磁盘一次性读取一页或者多页数据到缓存池中,或者从缓存池中刷新一页或多页数据到物理磁盘上

  • 一行数据的存储格式 :变长字段的长度列表,null 值列表,数据头,column01 的值,column02 的值,column0n 的值…

  • 表里的很多行数据落地到磁盘的时候,都是一大坨数据放在一个磁盘文件里都挨着存储的

  • 变长字段:根据变长字段的长度列表来确定变长字段的长度,一十六进制来表示,比如:VARCHAR(5),那么变长字段的长度列表为 0x5,要是有多个变长字段在一起,则他们的变长字段的长度列表是逆序排列的,比如:VARCHAR(5),VARCHAR(10),此时存储格式为:0xA,0x5,null值列表 头字段 …

  • null 值列表:NULL 值是以二进制 bit 位来存储的,而不是存储的 “NULL” 字符串。只要是允许你为 NULL 的字段,都有一个二进制 bit 位的值,并且和变长字段长度列表一样,它是按逆序排列的,并且实际 NULL 值列表存放的时候,它一般起码是 8 个 bit 位的倍数

  • 数据头:

  • 由 40个bit 位组成,第一个和第二个 bit 位时预留位,没有任何意义

  • 下一个 bit 位是 delete_mask,标识这行数据是否被删除了,其实未必是立马就把它从磁盘删除了

  • 下一个 bit 位时 min_rec_mask,标记 B+ 树里每一层的非叶子节点里的最小值

  • 接下来的 4 个 bit 位是 n_owned,它记录了一个记录数

  • 接着 13 位是 heap_no,代表当前这行数据在堆里的位置

  • 然后 3 个 bit 位是 record_type,说明这行数据的类型,0 代表普通类型,1代表的是 B+ 树非叶子节点,2 代表最小值数据,3 代表的是最大值数据

  • 最后是16 个 bit 的 next_record,这个是指向他下一条数据的指针

  • 真实数据 :这一行的数据是根据数据库指定的字符集编码,经过编码之后再存储的:

    0x09 0x04 00000101 0000000000000000000010000000000000011001 616161 636320 6262626262
    

    其中会加入一些隐藏字段:

    • DB_ROW_ID 字段 :一个行的唯一标识
    • DB_TRX_ID 字段 :说这是哪个事务更新的数据
    • DB_ROLL_PTR 字段 :这是回滚指针,是用来进行事务回滚的
    • 最终存储样子为:
    0x09 0x04 00000101 0000000000000000000010000000000000011001 00000000094C(DB_ROW_ID)00000000032D(DB_TRX_ID) EA000010078E(DB_ROL_PTR) 616161 636320 6262626262
    

2、行溢出:

一行数据存储的内容太多了,一个数据页都放不下了,此时只能溢出这个数据页,把数据溢出存放到其他数据页里去,那些数据页就叫做溢出页, 当它被读到 Buffer Pool 中时,也会以几个缓存页来存储

这个数据页同时包含一个 20 个字节的指针,指向了其他的一些数据页,那些数据页用链表串联起来

3、数据页结构:

数据页里面没有数据时,不存在数据行,当数据行满了之后,也就没有空闲区域了

4、表空间:

我们平时创建的那些表都是有对应的表空间的,每个表空间就是对应了磁盘上的数据文件,在表空间里有很多组数据区,一组数据区是 256 个数据区,每个数据区包含了 64 个数据页,是 1mb

每一个组数据区的第一个数据区的前 3 个数据页,都是存放一些特殊的信息的

  • FSP_HDR 这个数据页,他里面就存放了表空间和这一组数据区的一些属性
  • IBUF_BITMAP 数据页,里面存放的是这一组数据页的所有 insert buffer 的一些信息
  • INODE 数据页,这里也是存放了一些特殊的信息
  • XDES 数据页就是用来存放这一组数据区的一些相关属性的

5、磁盘随机读写的原理

磁盘随机读的性能是比较差的,所以不可能每次更新数据都进行磁盘随机读,必须是读取一个数据页之后放到 BufferPool 的缓存里去,下次要更新的时候直接更新 Buffer Pool 里的缓存页

对于磁盘随机读来说 ,性能指标是 IOPS 和响应延迟

磁盘顺序写的性能其实是很高的,某种程度上来说,几乎可以跟内存随机读写的性能差不多,尤其是在数据库里其实也用了 os cache 机制,就是 redo log 顺序写入磁盘之前,先是进入 os cache,就是操作系统管理的内存缓存里

十一、生产经验

对于 RAID 锂电池充放电问题导致的存储性能抖动,一般有三种解决方案:

  • 给 RAID 卡把锂电池换成电容,电容是不用频繁充放电的,不会导致充放电的性能抖动,还有就是电容可以支持透明充放电,就是自动检查电量,自动进行充电,不会说在充放电的时候让写 IO 直接走磁盘,但是更换电容很麻烦,而且电容比较容易老化,这个其实一般不常用
  • 手动充放电,这个比较常用,包括一些大家知道的顶尖互联网大厂的数据库服务器的 RAID 就是用了这个方案避免性能抖动,就是关闭 RAID 自动充放电,然后写一个脚本,脚本每隔一段时间自动在晚上凌晨的业务低峰时期,脚本手动触发充放电,这样可以避免业务高峰期的时候 RAID 自动充放电引起性能抖动
  • 充放电的时候不要关闭 write back,就是设置一下,锂电池充放电的时候不要把缓存级别从 write back 修改为 writethrough,这个也是可以做到的,可以和第二个策略配合起来使用

可以利用 Linux 的 ulimit -HSn 65535 设置linux的文件句柄数为 65535

十二、redo log

把缓存页刷入磁盘是随机写磁盘

redo log 写日志,是顺序写入磁盘文件,每次都是追加到磁盘文件末尾去

redo log 文件格式:

  • 日志类型(就是类似MLOG_1BYTE之类的),表空间 ID,数据页号,数据页中的偏移量,具体修改的数据
  • 日志类型(就是类似MLOG_WRITE_STRING 之类的),表空间 ID,数据页号,数据页中的偏移量,修改数据长度,具体修改的数据 (MLOG_WRITE_STRING 这种类型的日志不知道具体修改了多少字节的数据)

redo log block:

如果写入redo log buffer 的日志已经占据了 redo log buffer 总容量的一半了,也就是超过了 8MB 的 redo log 在缓冲里了,此时就会把他们刷入到磁盘文件里去

一个事务提交的时候,必须把他的那些 redo log 所在的 redo log block 都刷入到磁盘文件里去,只有这样,当事务提交之后,他修改的数据绝对不会丢失,因为 redo log 里有重做日志,随时可以恢复事务做的修改

后台线程定时刷新,有一个后台线程每隔 1 秒就会把 redo log buffer 里的r edo log block 刷到磁盘文件里去

MySQL 关闭的时候,redo log block 都会刷入到磁盘里去

提交事务的时候,事务对应的 redo log 必须是刷入磁盘文件,接着才算是事务提交成功,否则事务提交就是失败

十三、undo log

十四、事务

1、事务的问题

无论是脏写还是脏读,都是因为一个事务去更新或者查询了另外一个还没提交的事务更新过的数据

幻读指的就是你一个事务用一样的 SQL 多次查询,结果每次查询都会发现查到了一些之前没看到过的数据

2、事务隔离级别:

  • read uncommitted(读未提交):不允许发生脏写
  • read committed(读已提交):不允许发生脏读、脏写
  • repeatable read(可重复度):不会发生脏写、脏读和不可重复读
  • serializable(串行化):不允许你多个事务并发执行,只能串行起来执行
  • SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level (设置MySQL默认隔离级别)

3、undo log 日志链条

4、ReadView

事务 A 执行是会开启一个 ReadView,它有四个参数:

  • 一个是 m_ids,这个就是说此时有哪些事务在 MySQL 里执行还没提交的;
  • 一个是 min_trx_id,就是 m_ids 里最小的值;
  • 一个是 max_trx_id,这是说 MySQL 下一个要生成的事务 id,就是最大事务 id;
  • 一个是 creator_trx_id,就是你这个事务的 id

事务 A 只能读取自己修改的值或者是小于 min_ids 的事务id的事务修改的值,因为此时小于 min_ids 的事务已经提交了,没有提交的话它会出现在 事务A 的 ReadView 里的,即它会在事务 A 的 m_ids 里。它是根据 undo log 日志链条来寻找最近的满足条件的一个版本

事务A不能读取在事务 id 在 m_ids 里的事务或者是大于 max_trx_id 事务修改的值(除开自己修改的值)

5、Mysql 实现 Read Commited隔离级别

注意:在此隔离级别下,事务A每次查询时都会产生一个新的 ReadView

6、Mysql 实现RR隔离级别

注意:ReadView 在事务A开始之后就会一直不变

7、MySQL解决幻读

十五、锁

1、共享锁和独占锁

独占锁:即X锁,当有一个事务加了独占锁之后,此时其他事务再要更新这行数据,都是要加独占锁的,但是只能生成独占锁在后面等待

select * from table for update

共享锁:即S锁,多个事务可以同时加共享锁

select * from table lock in share mode
锁类型独占锁共享锁
独占锁互斥互斥
共享锁互斥不互斥

当有人在更新数据的时候,其他的事务可以读取这行数据吗?默认情况下需要加锁吗?

不用,因为默认情况下,有人在更新数据的时候,然后你要求读取这行数据,直接是默认开启 mvcc机制的,也就是根据 undolog 版本链条寻址你能读取到的版本,完全不用管别人的更新是否提交

2、意向独占锁和意向共享锁

意向独占锁是在更新表中的数据的时候自动加的,意向共享锁是在读取表中的数据的时候自动加的,它们与独占锁和共享锁直接的互斥关系为:

锁类型独占锁意向独占锁共享锁意向共享锁
独占锁互斥互斥互斥互斥
意向独占锁互斥不互斥互斥不互斥
共享锁互斥互斥不互斥不互斥
意向共享锁互斥不互斥不互斥不互斥

独占锁和所有的锁都互斥,意向独占锁和共享锁互斥,其他都不互斥

3、数据库为啥会抖动?

可能一次读操作会读取大量的数据,然后此时内存可能不够,就需要将大量的缓冲页刷入磁盘,此时就会出现数据库性能抖动的问题

再就是每次我们更新数据库的时候,都会写 redo log,然后把 redo log 写入磁盘,假如 redo log 日志文件都写满了,此时就需要从头开始覆盖前面的第一个日志文件开始写,但是可能第一个 redo log 日志文件的对应的数据还在缓存中,此时就需要将这些数据刷入磁盘,也会出现数据库性能抖动的问题

数据库抖动优化:

  • 问题一:尽量减少缓存页 flush 到磁盘的频率
    • 解决:数据库采用大内存机器,给 buffer pool分配的内存空间大一些,那么他缓存页填满的速率低一些,flush 磁盘的频率也会比较低
  • 问题二:尽量提升缓存页 flush 到磁盘的速度
    • 解决:1. 采用 SSD 固态硬盘,而不是机械硬盘,因为固态硬盘的随机读写性能比机械硬盘要高 2. 就是把 innodb_io_capacity 设置为 SSD 固态硬盘的IOPS,让他刷缓存页尽量快,同时设置innodb_flush_neighbors 为 0,让他每次别刷临近缓存页,减少要刷缓存页的数量,这样就可以把刷缓存页的性能提升到最高

十六、索引

1、磁盘数据页的储存结构

数据行是根据主键从小到大排序的

2、没有索引查询

先遍历数据页,逐个将数据页加载进入 Buffer Pool,如果此时有主键,就跟根据页目录中的主键进行二分查找,找到对应的槽位,然后遍历槽位里的数据行,就能快速找到那个主键对应的数据了。如果此时不是根据主键查找,就根据数据行之间的单向链表依次遍历,找到对应的数据行。上述过程即为全表扫描

3、页分裂

索引运作的一个核心基础就是要求你后一个数据页的主键值都大于前面一个数据页的主键值,如果主键是自增的话,可以保证这一点,但是如果不是自增的话,可能就会导致你后一个数据页里的主键就会小于前一个数据页里的主键,因此我们需要页分裂来保证后一个数据页的主键都是大于前一个数据页的主键

4、索引的设计

每个数据页的页号,还有数据页里最小的主键值放在一起,组成一个索引的目录

根据主键目录,就可以直接确定数据页

假设你有很多的数据页,在主键目录里就会有很多的数据页和最小主键值,此时你完全可以根据二分查找的方式来找你要找的id到底在哪个数据页里

5、B+树

索引也是分页的,叫做索引页,当数据非常多的时候,我们就需要设置多个层级的索引页,索引树 MySQL的索引是用 B+ 树组成的,其实就是由多个索引页组成的,根据索引页一层一层往下找

6、聚簇索引

如果一颗大的 B+ 树索引数据结构里,叶子节点就是数据页自己本身,那么此时我们就可以称这颗 B+ 树索引为聚簇索引!

增删改数据的时候,一方面会更新数据页,一方面其实会给你自动维护 B+ 树结构的聚簇索引,给新增和更新索引页,这个聚簇索引是默认就会给你建立的

7、二级索引

8、索引的好处和坏处

空间上而言,你要是给很多字段创建很多的索引,那你必须会有很多棵索引 B+ 树,每一棵 B+ 树都要占用很多的磁盘空间啊!所以你要是搞的索引太多了,是很耗费磁盘空间的

在进行增删改查的时候,每次都需要维护各个索引的数据有序性,因为每个索引 B+ 树都要求页内是按照值大小排序的,页之间也是有序的,下一个页的所有值必须大于上一个页的所有值

9、联合索引

索引页和数据页之间是指针,数据页之间是双向链表,索引页之间是双向链表,索引页内部是单链表,数据页内部是单链表

全值匹配规则:假设你的 SQL 语句的 where 条件里用的几个字段的名称和顺序,都跟你的索引里的字段一样,同时你还是用等号在做等值匹配 ,此时就会根据联合索引来查询

10、索引使用规则

最左侧列匹配:我们根据最左侧的字段查找就可以走索引

最左前缀匹配原则:用在模糊查询的时候

范围查找规则

等值匹配+范围匹配的规则

覆盖索引:就是你要查询的字段就在联合索引里,此时就不需要进行回表,效率就会高很多。联合索引的字段是由主键值和创建索引时的字段组成

11、进行排序时使用索引

在order by里要不然就是每个字段后面什么都不加,直接就是 order by xx1,xx2,xx3,要不然就都加 DESC降序排列,就是 order by xx1DESC,xx2 DESC,xx3 DESC。

如果都是升序排列,直接就从索引树里最小的开始读取一定条数就可以了,要是都是降序排列,就是从索引树里最大的数据开始读取一定的条数就可以了,但是你不能 order by 语句里有的字段升序有的字段降序,那是不能用索引的

要是你 order by 语句里有的字段不在联合索引里,或者是你对 order by 语句里的字段用了复杂的函数,这些也不能使用索引去进行排序了

12、索引设计原则

  • 可以设计一个或者两三个联合索引,每一个联合索引都尽量去包含上你的 where、order by、group by里的字段,接着你就要仔细审查每个 SQL 语句,是不是每个 where、order by、group by 后面跟的字段顺序,都是某个联合索引的最左侧字段开始的部分字段? 如果是的话,那么你的每个 SQL 语句都会用上你的索引了
  • 一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出 B+ 树快速二分查找的优势来
  • 尽量是对那些字段的类型比较小的列来设计索引,比如说什么 tinyint 之类的,因为他的字段类型比较小,说明这个字段自己本身的值占用磁盘空间小,此时你在搜索的时候性能也会比较好一点
  • 对于那种比较长的字符串类型的列,可以设计前缀索引,仅仅包含部分字符到索引树里去,where 查询还是可以用的,但是 order by和group by 就用不上了
  • 尽量不要让你的查询语句里的字段搞什么函数,或者是搞个计算
  • 插入数据有主键,有主键就要更新聚簇索引树,插入一条数据肯定会包含索引里各个字段的值,那联合索引的 B+ 树也要更新
  • 插入的数据值可能根本不是按照顺序来的,很可能会导致索引树里的某个页就会自动分裂,这个页分裂的过程就很耗费时间,因此一般让大家设计索引别太多,建议两三个联合索引就应该覆盖掉你这个表的全部查询了
  • 建议大家主键一定是自增的,别用 UUID 之类的

13、索引设计实战

  • 一旦你的一个字段做范围查询用到了索引,那么这个字段接下来的条件句不能用索引了
  • 实际设计索引的时候,必须经常用作查询的字段放在联合索引的最后一个,才能保证你 SQL 里的每个字段都能基于索引去查询
  • 范围查询的时候,也就只有第一个范围查询是可以用上索引的,第一个范围查询之后的其他范围查询是用不上索引的
  • 核心重点就是,尽量利用一两个复杂的多字段联合索引,抗下你 80% 以上的 查询,然后用一两个辅助索引抗下剩余 20% 的非典型查询,保证你99%以上的查询都能充分利用索引,就能保证你的查询速度和性能!

十七、执行计划

  • 进行等值匹配:
    • const:是直接通过索引定位到数据,速度极快 (主键或者唯一索引的访问 )
    • ref:查询速度也是很快的 (普通索引,主键或者唯一索引用了一个 IS NULL/IS NOT NULL)
    • ref_or_null:对一个二级索引同时比较了一个值还有限定了 IS NULL
    • index:针对这种只要遍历二级索引就可以拿到你想要的数据,而不需要回源到聚簇索引的访问方式,就叫做 index 访问方式
  • 性能比较:const、ref 和 range,本质都是基于索引树的二分查找和多层跳转来查询,所以性能一般都是很高的,index 是走遍历二级索引树的叶子节点的方式来执行的,那肯定比基于索引树的二分查找要慢多了,但是还是比全表扫描好一些的

十八、主从复制

巨人的肩膀:

https://apppukyptrl1086.pc.xiaoe-tech.com/detail/p_5e0c2a35dbbc9_MNDGDYba/6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值