宿迁网站建设宿迁/百度竞价优缺点

宿迁网站建设宿迁,百度竞价优缺点,wordpress服务器操作系统,问答网站开发大纲 一.InnoDB的内存结构和更新机制 二.InnoDB的存储模型 三.并发事务原理 四.索引原理和索引优化 一.InnoDB的内存模型 1.SQL的执行流程 2.InnoDB的内存模型 3.Buffer Pool中的空闲缓存页与free链表 4.Buffer Pool中的脏页和flush链表 5.Buffer Pool通过LRU链表来淘…

大纲

一.InnoDB的内存结构和更新机制

二.InnoDB的存储模型

三.并发事务原理

四.索引原理和索引优化

一.InnoDB的内存模型

1.SQL的执行流程

2.InnoDB的内存模型

3.Buffer Pool中的空闲缓存页与free链表

4.Buffer Pool中的脏页和flush链表

5.Buffer Pool通过LRU链表来淘汰缓存页

6.Buffer Pool的缓存页以及几个链表总结

7.LRU链表的冷数据区域的缓存页何时刷盘

8.增大Buffer Pool来提升MySQL的并发能力

1.SQL的执行流程

MySQL驱动 -> 数据库连接池 -> 网络IO线程 -> SQL接口 -> 查询解析器 -> 查询优化器 -> 执行器 -> 调用存储引擎接口

SQL的执行流程:

一.由MySQL工作线程去监听网络请求和读取网络连接的SQL数据

二.MySQL工作线程读取出SQL语句后会将SQL语句转交给SQL接口去执行

三.通过查询解析器Parser解析SQL语句让MySQL能看得懂SQL逻辑

四.通过查询优化器Optimizer选择最优的查询路径

五.由执行器按照查询优化器选择的执行计划不停调用存储引擎接口

六.存储引擎接口会查询内存缓存数据、查询磁盘数据、更新磁盘数据等

2.InnoDB的内存模型

Buffer Pool就是数据库的一个内存组件,里面缓存了磁盘上的真实数据。当执行更新时,会写undo日志、修改Buffer Pool数据、写redo日志。当提交事务时,会将redo日志刷磁、binlog刷盘、添加commit标记。最后后台IO线程会随机把Buffer Pool里的脏数据刷入到磁盘数据文件中。

(1)缓冲池Buffer Pool的默认大小是128MB

缓冲池Buffer Pool的大小根据服务器的配置来调整。比如服务器的配置是16核32GB,可以给缓冲池Buffer Pool分配20GB内存。

(2)数据页是MySQL抽象出来的数据单位

磁盘文件中有很多数据页,每一页中放了很多行数据。如果数据库要更新某一行数据,首先会找到这行数据所在的数据页,然后把这行数据页加载到缓冲池Buffer Pool中。缓冲池Buffer Pool中存放的一个一个的数据页,也被称为缓存页。数据页默认大小为16KB,数据页和缓存页的大小是一样的。

(3)Buffer Pool中每个缓存页都有对应的描述数据

描述数据包括:缓存页所属的表空间、数据页的编号、缓存页在Buffer Pool中的地址等。每个缓存页的描述数据放Buffer Pool最前面,各个缓存页放在后面。Buffer Pool里的一个描述数据大小相当于一个缓存页的5%,约800字节。

3.Buffer Pool中的空闲缓存页与free链表

数据库的Buffer Pool里会包含很多个缓存页,同时每个缓存页还有对应的描述数据。

数据库启动时,会按照设置的Buffer Pool大小,去操作系统申请一块内存区域,作为Buffer Pool的内存区域。申请完毕后,数据库会按照默认的缓存页大小及对应的描述数据大小,在Buffer Pool中划分一个个缓存页和对应的描述数据。

然后当数据库把Buffer Pool划分完毕后,里面的缓存页都是空的。需要等数据库运行起来后执行增删改查操作时:才会把对应的数据页从磁盘里读取出来,放入Buffer Pool中的缓存页里。

(1)数据库启动时会按照设置的Buffer Pool大小向OS申请内存

当数据库向OS申请到设置的Buffer Pool大小的内存后,就会在缓冲池中划分出一个个空闲缓存页和相应的描述数据块。

(2)Buffer Pool有一个叫free链表的双向链表

free链表的每个节点是一个空闲缓存页的描述数据块的地址,通过free链表可知哪些缓存页是空闲的。

(3)根据free链表的节点可得到一个空闲缓存页

从free链表中获取一个节点后,根据该节点就能找到对应的空闲缓存页。接着就可以将磁盘中的数据页读取到该空闲缓存页里。同时把该数据页的描述数据写到该空闲缓存页对应的描述数据块里。以及把表空间号 + 数据页号作为key,缓存页地址作为value,写到哈希表。这样下次读取该数据页时可通过key查哈希表,直接从缓冲池里进行读取。

(4)增删改查一条数据时InnoDB引擎会怎么处理

首先InnoDB会获取到对应数据的"表空间号 + 数据页号"。然后根据"表空间号 + 数据页号"作为key,去哈希表中进行查询。如果能查到缓存页地址,则去Buffer Pool中读取对应的缓存页数据。如果哈希表查不到,则说明要将磁盘的数据页读取到缓冲区的缓存页里。

于是会先从free链表里获取一个节点,然后找到其描述数据块的地址。通过该地址可得到一个空闲缓存页,就能把数据页读取到该空闲缓存页里。同时会把描述数据也写到该缓存页的描述数据块里,以及把表空间号 + 数据页号作为key,缓存页地址作为value,写到哈希表。

4.Buffer Pool中的脏页和flush链表

MySQL在执行增删改语句时,如果在哈希表中发现数据页没有缓存,则会基于free链表找到一个空闲的缓存页,然后将数据页读取到缓存页里。如果在哈希表中发现数据页已缓存,那么会直接使用缓存页。

因此,无论如何,要更新的数据页都会在Buffer Pool的缓存页里。MySQL是基于Buffer Pool内存来执行具体的增删改查操作的。

所以,当MySQL去更新Buffer Pool的缓存页中的数据时,一旦更新完,则缓存页里的数据和磁盘上数据页的数据就不一致了。这时就说该缓存页是脏数据,或者脏页。

在Buffer Pool里,有些缓存页经过修改是脏页,有些则只有查而不是脏页。所以为了方便数据库从缓存页中区分出脏页,数据库引入了一个跟free链表类似的flush链表。

凡是被修改过的缓存页,都会把它的描述数据块加入到flush链表中。flush的意思就是这些都是脏页,后续都是要flush刷新到磁盘上去的。

(1)通过free链表来管理所有空闲的数据页

加载磁盘的数据页时,先通过free链表拿到空闲的缓存页地址,然后再把磁盘的数据页写到这个Buffer Pool中的缓存页里。

(2)通过哈希表来管理在Buffer Pool缓存的数据页

根据哈希表可快速从Buffer Pool查出缓存的数据页。

(3)通过flush链表来管理更新后等待被刷盘的缓存页

free链表和flush链表都通过使用地址指针来大大减少内存的占用。free链表和flush链表的节点都是由缓存页的描述数据块来实现的。free链表和flush链表都通过两个指针来构成双向链表。

5.Buffer Pool通过LRU链表来淘汰缓存页

要知道哪些缓存页经常被访问、哪些缓存页很少被访问,可借助LRU链表。LRU就是Least Recently Used,最近最少使用的意思。

(1)简单LRU链表的工作原理

假设InnoDB从磁盘加载一个数据页到缓存页时,就把这个缓存页的描述数据块放到LRU链表头部去。

那么只要一个缓存页有数据,那么该缓存页就会在LRU里。并且最新加载数据的缓存页,会被放到LRU链表的头部。

假设某个缓存页的描述数据块本来在LRU链表的尾部,后面只要查询或者修改了这个缓存页的数据,也会把其描述数据块挪动到LRU链表头部。

总之,就是保证最近被访问过的缓存页,一定在LRU链表的头部。这样当缓冲区没有空闲的缓存页时,可以在LRU链表尾部找一个缓存页。而这个缓存页就是最近最少被访问的那个缓存页。然后把LRU链表尾部的那个缓存页刷入磁盘从而腾出一个空闲的缓存页,最后把需要的磁盘数据页加载到这个空闲的缓存页中即可。

这个LRU链表需要一定长度,不能只有2个节点。否则如果先是节点1被访问100次,接着到节点2被访问。这样虽然链表尾部是节点1,但实际上节点1是最近最少被访问的。

(2)简单LRU链表可能存在的问题

问题一:预读机制导致相邻数据页也一块被加载到缓冲池。此时在LRU链表中排前面的,可能都是通过预读机制加载进来的。

问题二:全表扫描可能会一下子把一个表的所有数据页都加载到缓冲池,此时在LRU链表中排前面的,可能都是通过全表扫描加载进来的。

触发预读机制的情况:

情况一:参数innodb_read_ahead_threshold的默认值是56。如果顺序访问一个区里多个数据页,访问的数据页的数量超过此阈值。那么就会触发预读机制,将下一个相邻区中所有数据页加载到缓冲池。

情况二:如果缓冲池中缓存了一个区里的13个连续的被频繁访问的数据页,那么就会触发预读机制,将这个区里其他数据页也加载到缓冲池。这种情况由参数innodb_random_read_ahead控制,默认关闭。

(3)基于冷热数据分离思想设计LRU链表

这套冷热数据分离的机制包含三个方案:

方案一:缓存页分冷热数据加载

方案二:冷数据转化为热数据进行时间限制

方案三:淘汰缓存页时优先淘汰冷数据区域

为解决简单LRU链表带来的预读和全表扫描问题,InnoDB设计LRU链表时用了冷热数据分离的思想。InnoDB的LRU链表,会被拆分为两个部分。一部分是热数据,一部分是冷数据。冷热数据的比例由innodb_old_blocks_pct参数控制,默认是37。

当数据页第一次被加载到内存时,缓存页对应的描述数据块节点会被放在LRU链表的冷数据区域的头部。被加载到内存的数据页,如果在默认1s后继续被访问,则该缓存页对应的描述数据块节点会被挪动到热数据区域的链表头部。对应的innodb_old_blocks_time参数默认就是设置为1s。

如果数据加载到缓存页之后过了1s+的时间,该缓存页被访问,则对应的描述数据块会被放入热数据区域的链表头部。如果数据加载到缓存页之后在1s内,该缓存页被访问,则对应的描述数据块不会被放入热数据区域。

如果访问了热数据区域中的一个缓存页,是否应该马上把它移动到热数据区域的链表头部?由于热数据区域里的缓存页可能是被经常访问的,所以不建议频繁移动,否则影响性能。

因此LRU链表的热数据区域的访问规则是:只有在热数据区域的后3/4部分的缓存页被访问了,才会移动到链表头部。如果是热数据区域的前面1/4部分的缓存页被访问了,那么不需要移动。这样尽可能减少链表中的节点频繁移动。

6.Buffer Pool的缓存页以及几个链表总结

Buffer Pool在被使用时,会频繁从磁盘上加载数据页到缓存页里。然后free链表、flush链表、LRU链表都会被同时使用,这三个链表都是双向链表。

(1)当加载一个数据页到一个缓存页时

InnoDB就会从free链表里移除这个缓存页。然后会把这个缓存页放入到LRU链表的冷数据区域头部。

(2)当修改一个缓存页时

InnoDB就会在flush链表中记录这个脏页。而且可能会把该缓存页从LRU链表的冷数据区域移动到热数据区域头部。

(3)当查询一个缓存页时

InnoDB可能会把该缓存页从LRU链表冷数据区域移动到热数据区域头部,或者从LRU链表的热数据区域其他位置移动到热数据区域头部。

总之,MySQL在执行增删改查时:首先会大量操作缓存页以及对应的几个链表。然后当缓存页满时,会基于LRU链表淘汰缓存页。也就是先把要淘汰的缓存页刷入磁盘,然后清空该缓存页。接着再把需要的数据页加载到空闲的缓存页中。

7.LRU链表的冷数据区域的缓存页何时刷盘

时机一:定时把LRU尾部的部分缓存页刷入磁盘

第一个时机并不是在缓存页满的时候,才会将缓存页刷入磁盘。而是有一个后台定时任务线程,该线程会定时把LRU链表的冷数据区域尾部的一些缓存页刷入磁盘。然后清空几个缓存页,并将这些缓存页加回free链表。

时机二:把flush链表中的一些缓存页定时刷入磁盘

如果仅仅是把LRU链表中冷数据区域的缓存页刷入磁盘,还是不够的。因为在LRU链表的热数据区域里很多缓存页可能也会被频繁的修改,这些缓存页不可能永远都不刷入磁盘中。

所以这个后台线程同时也会在MySQL不怎么繁忙时,找个时间把flush链表中的缓存页都刷入磁盘中。

只要flush链表中的缓存页被刷入磁盘,则这些缓存页也会从flush链表和LRU链表中移除,然后加入到free链表中。

时机三:实在没有空闲缓存页时

假设所有的free链表都被使用,同时flush链表中有很多被修改过的缓存页,以及LRU链表中也有很多缓存页进行冷热数据分离。此时如果要从磁盘加载数据页到一个空闲缓存页中,就会从LRU链表的冷数据区域尾部找到一个缓存页,刷入磁盘和清空。

8.增大Buffer Pool来提升MySQL的并发能力

当Buffer Pool用完时,此时需要先把一个缓存页刷入磁盘腾出空闲缓存页,再从磁盘读取数据页。这种情况要执行两次磁盘IO,性能低下。一次是缓存页刷入磁盘,一次是从磁盘读取数据页加载到缓存页。

由于InnoDB在使用缓存页的过程中,会有一个后台线程定时地把LRU链表冷数据区域的一些缓存页刷入磁盘。所以缓存页是一边被使用,一边被后台线程定时地释放。如果缓存页被使用得很快,而后台线程释放缓存页的速度很慢,那么必然频繁出现缓存页被使用完的情况。

从InnoDB角度看,它无法控制缓存页被使用的速度。因为缓存页被使用的速度依赖于外部服务调用的并发程度。另外InnoDB的后台线程会定时释放一批缓存页,这个过程也很难优化。因为如果频繁释放也会造成磁盘IO频繁,从而影响性能。

所以最后可以依靠InnoDB的Buffer Pool的大小来避免。如果MySQL要抗高并发的访问,那么机器必然要配置很大的内存空间,起码是32G+、64GB、128GB。此时就可以设置Buffer Pool很大的内存空间,如20GB、48GB,80GB。

这样在高并发场景下:虽然Buffer Pool的缓存页被频繁使用,但后台线程也在定时释放缓存页。由于Buffer Pool内存很大,所以可能需要较长时间才会导致缓存页用完。需要的时间越长,那么就越可以撑到数据库访问高峰期已过去。只要高峰一过,后台线程又不停地基于flush链表和LRU链表释放缓存页,那么空闲的缓存页数量又会慢慢多起来。

MySQL的生产经验,就是给MySQL设置多个Buffer Pool来优化并发能力。如果Buffer Pool的内存小于1GB,MySQL默认只会给一个Buffer Pool。如果Buffer Pool的内存较大如8G,那么可给MySQL设置多个Buffer Pool。每个Buffer Pool负责管理一部分缓存页和描述数据块,每个Buffer Pool拥有独立的free、flush、LRU链表。这时即便多个线程并发来访问也可以把压力分开,比如有的线程访问这个Buffer Pool,有的线程访问另外的Buffer Pool。

通过多个Buffer Pool,MySQL多线程并发访问的性能就会得到提升,多个线程可以在不同的Buffer Pool中加锁和执行自己的操作。

二.InnoDB的存储模型

1.InnoDB的存储模型以及对应的读写机制

2.提交事务时会将redo日志写入磁盘中

3.MySQL的redo log和binlog对比

4.提交事务时同时也会写入binlog

5.在redo日志中写入commit标记的意义

6.后台IO线程随机将内存更新后的脏数据刷回磁盘

7.InnoDB的执行流程总结

8.redo日志和redo log机制的作用

9.redo日志会写入日志文件里的Redo Log Blcok

10.Redo Log Buffer和Redo Log文件

11.redo日志从Redo Log Buffer中刷盘时机

12.undo log回滚日志原理

13.系统和数据库能抗多少QPS

14.性能压测指标和命令

15.简单总结增删改SQL语句的实现原理

1.InnoDB的存储模型以及对应的读写机制

在逻辑层面上,InnoDB的数据是插入一个一个的表中。在物理层面上,InnoDB的数据是插入一个一个的表空间。

表空间对应着磁盘文件,磁盘文件里存放的就是数据。磁盘文件存放数据时,会被拆分为一个一个的数据区组。每个数据区组包含256个数据区,每个数据区包含64个数据页。每个数据页包含一行行的数据。

数据页又包含了:文件头、数据页头、最小记录和最大记录、多个数据行、空闲空间、数据页目录、文件尾部。

每个数据行又附加了真实数据外的很多信息:变长字段的长度列表、null值列表、数据头、真实数据和隐藏字段。

通过数据页、数据区、数据行附加的特殊信息,可以让InnoDB在磁盘文件里实现B+索引、事务等复杂的机制。

当数据库执行增删改查时:必须把磁盘文件里的一个数据页加载到内存Buffer Pool中的缓存页里,然后增删改查都针对缓存页里的数据进行。

所以要读写数据时:会根据表找到一个表空间,通过表空间就可以找到对应的磁盘文件。通过磁盘文件就可以从里面找一个数据区组中的一个数据区。然后从该数据区中找一个数据页出来。最后就可以把这个数据页从磁盘加载到Buffer Pool缓存页里。

当执行更新时,会写undo日志、修改Buffer Pool数据、写redo日志。

当提交事务时,会将redo日志刷磁、binlog刷盘、添加commit标记。最后后台IO线程会随机把Buffer Pool里的脏数据刷入到磁盘数据文件中。

2.提交事务时会将redo日志写入磁盘中

如果InnoDB想要提交一个事务,就会根据一定的策略把redo日志从Redo Log Buffer中刷入到磁盘文件里,这个策略是通过如下这个参数来配置的:innodb_flush_log_at_trx_commit。

(1)当innodb_flush_log_at_trx_commit = 0时

那么进行事务提交时,不会把Redo Log Buffer的数据刷入到磁盘文件里。这时即便提交了事务,但如果MySQL宕机了,内存里的数据也会全部丢失而且redo日志里没有数据。

(2)当innodb_flush_log_at_trx_commit = 1时

那么进行事务提交时,会把内存中的redo log刷入到磁盘文件里。只要事务提交成功,那么redo log就必然在磁盘里。哪怕此时Buffer Pool中更新过的数据还没刷新到磁盘,系统崩溃重启后,也可以根据磁盘中的redo log恢复。

(3)当innodb_flush_log_at_trx_commit = 2时

那么进行事务提交时,会把内存中的redo log写入到OS Cache缓存里。OS Cache缓存里的数据可能在1秒后才会被写入到磁盘文件中。

在这种模式下,当InnoDB存储引擎提交事务后,redo log可能还停留在OS Cache缓存里,还没实际进入到磁盘文件。而此时MySQL所在机器宕机了,那么OS Cache里的redo log也会丢失。从而出现即便提交了事务,但是数据还是丢失了的情况。

3.MySQL的redo log和binlog对比

MySQL的redo log,是一种偏向物理性的重做日志。因为其记录的是:对哪个数据页中的哪条记录做了什么修改。而且redo log是属于InnoDB存储引擎特有的日志文件。

MySQL的binlog,是一种偏向于逻辑性的日志,也叫归档日志。类似"对users表中id=1的一行记录做了更新操作,更新后的值是什么"。binlog不是InnoDB存储引擎特有的日志文件,binlog是属于MySQL数据库层面的日志文件。

4.提交事务时同时也会写入binlog

提交事务时,除了会把redo日志写入到磁盘文件中,还会把这次SQL更新对应的binlog日志写入到磁盘文件中。执行器这个组件,它会负责和InnoDB存储引擎进行如下交互:

步骤1:从磁盘加载数据到Buffer Pool缓存

步骤2:写入undo日志

步骤3:更新Buffer Pool里的数据

步骤4:写入redo日志到Redo Log Buffer

步骤5:redo日志刷入磁盘

步骤6:写入binlog日志

步骤7:在redo日志中写入commit标记

其中步骤1、2、3、4是执行更新语句的阶段,而步骤5、6、7是属于提交事务的阶段。

当MySQL把binlog写入磁盘后,接着就会完成最终的事务提交。此时会把本次更新对应的binlog文件名称和位置,都写入到redo日志里,同时在redo日志文件里写入一个commit标记。在完成这个事情后,才算是最终完成事务的提交。

5.在redo日志中写入commit标记的意义

写入commit标记是用来保持redo日志与binlog日志一致。也就是说,在提交事务的时候,上述的步骤5、6、7必须都执行完毕,才算是提交了事务。

(1)如果刚完成步骤5时,redo日志刚刷入到磁盘文件,MySQL宕机了

这时因为在redo日志没有最终的事务commit标记,所以此次事务不成功。因为不允许出现这样的情况:redo日志文件里有更新日志,但是binlog日志文件里没有对应的更新日志。否则就会导致数据不一致。

(2)如果在完成步骤6时,binlog日志已写入磁盘,MySQL宕机了

这时因为在redo日志没有最终的事务commit标记,所以此次事务也失败。所以必须要在redo日志写入最终的事务commit标记,才算事务提交成功。

这样redo日志有本次更新的日志,binlog日志也有本次更新的日志,从而实现redo日志和binlog日志完全一致。

6.后台IO线程随机将内存更新后的脏数据刷回磁盘

当完成事务提交后,MySQL已把内存中的Buffer Pool缓存数据更新了,同时磁盘里也有redo日志和binlog日志,但磁盘上的数据文件还是旧值。

这时MySQL会有一个后台IO线程,在事务提交后的某个时间,随机把内存Buffer Pool中修改后的脏数据刷回到磁盘上的数据文件里。

当IO线程把Buffer Pool里修改后的脏数据刷回磁盘后,磁盘上的数据才会跟内存里的数据一样,都是修改后的值。

当IO线程把脏数据刷回磁盘之前,即便MySQL宕机也没关系。因为重启后会根据redo日志恢复提交事务时所做的修改到内存里。之后IO线程还是会把修改后的数据刷到磁盘的数据文件里。

7.InnoDB的执行流程总结

InnoDB存储引擎会使用Buffer Pool、Redo Log Buffer来缓存数据。InnoDB存储引擎有属于自己的undo日志文件、redo日志文件,MySQL也有属于自己的binlog日志文件。

执行更新时:会修改Buffer Pool里的数据、写undo日志、写Redo Log Buffer。

提交事务时:会把binlog刷入磁盘、在redo日志中写入事务标记,把redo日志刷入磁盘。最后InnoDB后台的IO线程会随机把Buffer Pool的脏数据刷入到磁盘文件。

(1)MySQL宕机重启如何确定是否需要从redo日志恢复数据

MySQL宕机重启,如何确定脏数据在宕机前是否已全部刷写回磁盘文件。

MySQL宕机重启,InnoDB会首先去查看数据页中LSN的数值。LSN就是InnoDB使用的一个版本标记的计数。如果数据页中的LSN异于redo日志的commit标记,那么就去查看redo日志的LSN大小。如果数据页的LSN值大,则说明数据页领先redo日志,不需要恢复,反之则需要从redo日志中恢复。

(2)从redo日志恢复数据时是全量恢复还是指定位置后恢复

redo日志是划归于一个redo日志组的。默认一个redo日志组有两个redo日志文件。写redo日志时是循环写入,写满一个redo日志文件再写另外一个。

在写满切换redo日志文件时,会触发数据库的检查点checkpoint。checkpoint所做的事就是把脏页刷新回磁盘。

当DB重启恢复时只需要恢复checkpoint之后的数据即可。所以redo日志文件大小不宜过大,不然导致恢复时需要更长的时间。redo日志文件大小也不宜过小,不然导致频繁切换触发检测点降低性能。

(3)既然有redo日志来保证崩溃恢复,为什么还要有binlog日志

binlog日志其实就是归档日志,主要用来做数据恢复的。MySQL最开始设计时只有MyISAM引擎只有binlog,不支持InnoDB。此外数据库备份以及hadoop系统数据分析都是binlog来实现的,所以还需要binlog。

(4)redo日志和binlog日志的数据结构是怎样的

redo日志是循环写,会把redo日志分为0,1,2,3四个区间,有两个指针。writepos指针是一边写一边向后移动,checkpoint指针是一边擦除一边向后移动。所以redo日志是不能保存很多记录的,必须持久化到磁盘中。binlog日志是追加写,不会覆盖之前的日志。

(5)binlog日志和redo日志是怎么保持一致性的

binlog日志和redo日志是通过两阶段提交来保持一致性的。否则如果数据库系统发生crash,则通过redo日志恢复的数据库和通过binlog日志恢复出来的临时库不一致。

8.redo日志和redo log机制的作用

(1)redo log保证事务提交后修改的数据不丢失

更新完缓存页后,必须要写一条redo log,这样才能记录对数据库的修改。

redo log可以保证事务提交后:如果事务中由增删改SQL更新的缓存页还没刷新到磁盘时MySQL宕机,那么MySQL重启后,就可以把redo log重做一遍,恢复事务在当时更新的缓存页,然后再把缓存页刷新到磁盘。所以redo log的本质是保证事务提交后,修改的数据绝对不会丢失。

(2)redo log出现的步骤分析

步骤一:MySQL在执行增删改SQL语句时,都是针对一个表中的某些数据执行的。此时首先会找到这个表对应的表空间,然后找到表空间对应的磁盘文件。接着从磁盘文件里把需要更新的数据所在的数据页从磁盘读取出来,也就是将磁盘上的数据页放到Buffer Pool中的缓存页里。

步骤二:读取磁盘文件的数据页到Buffer Pool的缓存页后,MySQL就会根据增删改SQL语句对缓存页执行更新操作。

步骤三:在MySQL更新缓存页时,会更新free链表、会更新flush链表、会更新LRU链表。然后后台有专门的IO线程,不定时根据flush链表、LRU链表,把更新过的缓存页(脏页)刷新回磁盘文件的数据页里。

但这种机制有个漏洞:万一事务里有增删改SQL语句更新了缓存页,事务提交了但还没来得及让IO线程把缓存页刷新到磁盘而MySQL宕机。这时Buffer Pool内存里的数据就会丢失,刚做完的事务更新数据也丢失。但也不可能每次提交一个事务,就把事务更新的缓存页刷新回磁盘文件。因为将缓存页刷新到磁盘文件里,是对磁盘随机写的,性能很差。这会导致数据库的性能和并发能力都很弱,因此才引入了这个redo log机制。

步骤四:提交事务时把MySQL对缓存页的修改以日志形式写入redo log日志文件。这种redo log日志的格式大致为:对表空间XX中的数据页XX中的偏移量为XXXX的地方更新了数据XXX。

只要事务提交时将所做修改以日志形式写入redo log,则宕机也没关系。因为重启后可以根据redo log,在Buffer Pool里恢复宕机前的事务更新。

(3)修改过的缓存页刷盘和redo log刷盘的差别

事务提交时把修改过的缓存页刷入磁盘,和事务提交时把所做修改的redo log写入日志文件的差别:

一.如果把修改过的缓存页都刷入磁盘

由于一个缓存页是16K,数据还是比较大的,将其刷入磁盘会比较耗时,且修改的缓存页可能仅有几字节的改动,把完整缓存页刷入磁盘不划算。

二.将缓存页刷入磁盘时是对磁盘进行随机写

这时由于一个缓存页对应的位置可能在磁盘文件的一个随机位置,比如偏移量为45336的地方,所以只能进行性能很差的磁盘随机写。

三.如果是将redo log写入日志文件

由于一行redo log只占几十字节,所以写入磁盘日志文件的速度会很快。其中redo log只包含表空间号、数据页号、磁盘文件偏移量、更新值。此外将redo log写入日志时是对磁盘进行顺序写,速度也很快。其中每次进行顺序写时都是直接追加到磁盘文件尾部的。所以提交事务时,使用数据量少 + 顺序写的redo log记录所做的修改,性能会远超直接刷新缓存页到磁盘,这可以让数据库的并发能力更强。

9.redo日志会写入日志文件里的Redo Log Blcok

redo log日志本质上记录的是:对某个表空间的某个数据页的某个偏移量的地方修改了几个字节的值。

所以redo日志需要记录的就是:表空间号 + 数据页号 + 偏移量 + 修改几个字节的值 + 具体的值。

MySQL内有一个数据结构,叫做Redo Log Blcok。redo日志是用一个Redo Log Blcok来存放多个单行日志的。

一个Redo Log Block是512字节,分为3个部分:一是12字节的Header,二是496字节的Body,三是4字节的Trailer。

往一个文件里写数据,可认为是从第一行开始从左往右写,会有很多行。假设现在要写第一条redo日志:首先会把该日志数据放到内存中的一个叫Redo Log Block的数据结构里,然后不断往这个Redo Log Block的数据结构添加一条条redo日志,直到内存里的这个Redo Log Block满了,已经达到512字节。当一个Redo Log Block满时,再一次性把它写入到磁盘文件。

10.Redo Log Buffer和Redo Log文件

已知MySQL执行完增删改的SQL语句后:会先让redo日志进入Redo Log Block,然后再写入磁盘的redo日志文件。

Redo Log Buffer是MySQL启动时向操作系统申请的一块连续内存空间。Buffer Pool也是MySQL启动时向操作系统申请的一块连续内存空间。Buffer Pool会在申请内存后划分很多空的缓存页和一些链表结构。Redo Log Buffer也会在申请内存后,划分很多空的Redo Log Block。

innodb_log_buffer_size可配置Redo Log Buffer的大小,默认是16MB。其实16MB已经够大了,毕竟一个Redo Log Block才512字节,每条redo日志也就几个到几十个字节而已。

从Redo Log Buffer结构可知:当要写一条redo日志时,就会从第一个Redo Log Block开始写入。写满了一个Redo Log Block,就会继续写下一个Redo Log Block。以此类推,直到所有Redo Log Block写满。当Redo Log Buffer里的所有Redo Log Block都被写满后,就会强制把Redo Log Block刷入到磁盘中。

当一个Redo Log Block满512字节后,也会被追加到redo日志文件里。然后在磁盘文件里不停地追加一个又一个的Redo Log Block。

此外,MySQL平时执行一个事务的过程中,每个事务都会有多个增删改操作,这样就会有多条redo日志。这多条redo日志就是一组Redo Log Group,每次一组Redo Log Group都先在别的地方暂存,执行完后再把一组redo日志写到Redo Log Block里。

如果一组Redo Log Group中的redo日志太多,那么就可能会将其存放在两个Redo Log Block中。如果一组Redo Log Group比较小,那么也可能多个Redo Log Group是在一个Redo Log Block里。

11.redo日志从Redo Log Buffer中刷盘时机

(1)Redo Log Block什么时候刷盘

时机一:Redo Log Buffer已使用过半时

如果Redo Log Buffer的日志已占据Redo Log Buffer总容量16M的一半,即超过了8MB的redo日志在缓冲里,此时就会把它们刷入磁盘文件中。

时机二:事务被提交时

一个事务提交时,要把其redo日志所在的Redo Log Block刷入磁盘文件。这样它修改的数据才不会丢失,随时可通过redo日志恢复事务所做修改。

(innodb_flush_log_at_trx_commit的值为1)

时机三:后台线程定时刷新

有个后台线程会每秒把Redo Log Buffer的Redo Log Block刷到磁盘文件。

时机四:关闭MySQL时

当关闭MySQL时,Redo Log Buffer的Redo Log Block都会刷入到磁盘里。

(2)redo log日志刷盘的场景

场景一:MySQL瞬间执行了大量高并发SQL,1秒就产生了超过8MB的redo日志。此时这些redo日志占据了Redo Log Buffer的一半空间,于是就会刷盘。

这种redo日志刷盘,在MySQL承受高并发请求时是比较常见的。比如每秒执行上万个增删改SQL,每个SQL的redo日志有几百个字节。此时是可能1s生成超8MB的redo日志的,从而触发刷新redo日志到磁盘。但是这种高并发请求的情况一般不常见。

场景二:正常情况执行一个事务,一般会在几十毫秒到几百毫秒间执行完毕。通常MySQL单事务性能一般不会超过1秒,否则就太慢了。所以执行完一个事务,也会马上把这个事务的redo日志刷入磁盘。这种情况则比较常见,当一个短事务提交时往往会发生redo日志刷盘。

场景三:后台线程每秒自动刷新redo日志到磁盘去。

总而言之:一个事务执行时,事务对应的redo日志都进入到Redo Log Buffer。一个事务提交时,事务对应的redo日志刷入磁盘文件才算事务提交成功。这样才能确保事务提交后,数据不会丢,只要有redo日志在磁盘里就行。

(3)磁盘上到底有几个redo日志文件

大量的redo日志是否都放在一个文件里,磁盘空间是否会越占越多?默认情况下,redo log都会写入一个目录中的文件里。这个目录可通过show variables like 'datadir'来查看,可通过innodb_log_group_home_dir参数来进行设置。

redo日志文件是有多个的,写满了一个就会写下一个redo日志文件。可以限制redo日志文件的数量。通过innodb_log_file_size可指定每个redo日文件的大小,默认48MB。通过innodb_log_files_in_group可指定redo日志文件的数量,默认2个。

所以,默认情况下,目录里就两个日志文件,分别为ib_logfile0和ib_logfile1,每个48MB。先写满第一个再进行写满第二个,一个写满了交替覆盖式去写另外一个。

因此,MySQL最多只保留最近的96MB的redo日志而已。事实上这已足够多了,毕竟一条redo log通常就几个字节到几十个字节,96MB已足够存储上百万条redo log了。

12.undo log回滚日志原理

(1)redo log应对的场景—事务提交数据丢失

已知对Buffer Pool里的缓存页执行增删改操作时,必须要写对应的redo log日志记录下要做的哪些修改。redo log日志都会先进入Redo Log Buffer中的一个Redo Log Blcok,然后事务提交时会将Redo Log Block刷入到磁盘的redo日志文件里。

万一事务已提交,而事务修改的缓存页还没刷入磁盘上的数据页文件。此时MySQL宕机,那么Buffer Pool里被事务修改过的数据就全部丢失。

但只要有redo log,MySQL重启后又可以把那些还没刷入磁盘的缓存页它们所对应的redo log都加载出来,在Buffer Pool的缓存页里重做一遍,这样就可以保证事务提交之后,修改的数据绝对不会丢。

(2)undo log应对的场景—进行事务回滚

假设现在在一个事务里要执行一些增删改操作:那么需要先把对应的数据页从磁盘加载出来放到Buffer Pool的缓存页,然后在缓存页进行增删改,同时记录redo log。

万一这个事务里的增删改操作执行了一半时,需要进行事务回滚。比如一个事务里有4个增删改操作,结果已经执行了2个增删改SQL。已经更新了一些Buffer Pool的数据,但还有2个增删改SQL还没执行。此时如果要回滚事务,就必须对已经在Buffer Pool缓存页里执行过的增删改SQL操作进行回滚。所以才必须引入另外一种日志,就是undo log回滚日志。

(3)undo log回滚日志的作用

执行事务时,很多insert、update和delete语句都在更新缓存页的数据。如果事务回滚,需要根据每条SQL对应的undo log回滚日志恢复数据。

13.系统和数据库能抗多少QPS

4核8G的机器部署普通的Java系统,每秒能抗下几百的请求,从每秒一两百请求到每秒七八百请求都有可能。

8核16G的机器部署的MySQL数据库,每秒能抗一两千请求。16核32G的机器部署的MySQL数据库,每秒能抗两到四千的请求。

14.性能压测指标和命令

IO相关的压测性能指标—IOPS、吞吐量、latency

压测时要关注的其他性能指标—CPU、网络、内存

(1)IOPS

IOPS指机器的随机IO并发处理能力。比如机器可以达200的IOPS,表示每秒可以执行200个随机IO读写请求。

InnoDB更新内存中的脏数据时,最后会由后台IO线程在不确定的时间刷回到磁盘,这就涉及随机IO。如果IOPS指标太低,那么会导致内存里的脏数据刷回磁盘的效率不高。

(2)吞吐量

吞吐量指的是机器的磁盘存储每秒可以读写多少字节的数据量。MySQL在执行各种SQL语句、提交事务时,会大量写redo、binlog日志,这些日志都会写到磁盘文件。所以一台机器的磁盘存储每秒可以读写多少字节的数据量,就决定了它每秒可以把多少日志写入到磁盘。

一般写redo日志,都会对磁盘文件进行顺序写入,也就是一行接着一行的写,而不会进行随机读写。普通磁盘的顺序写入的吞吐量每秒都可以达到200MB左右。通常而言,机器的磁盘吞吐量都是足够承载高并发请求的。

(3)latency

latency指的是往磁盘里写入一条数据的延迟。MySQL在执行一条SQL语句和提交事务时,都需要顺序写一条redo日志和一条binlog日志到磁盘文件。所以写一条日志到磁盘文件里,到底是延迟1ms还是延迟100us,很影响SQL语句的执行性能。

一般来说,磁盘读写延迟越低,数据库的性能就越高,执行每个SQL语句和事务的时候速度就会越快。

(4)相关命令

CPU负载:top

内存负载:top下的Mem

磁盘IO吞吐量:dstat -d (每秒上百MB)

磁盘IOPS:dstat -r (每秒两三百)

网卡流量:dstat -n (千兆网卡每秒100MB左右)

15.简单总结增删改SQL语句的实现原理

其中会涉及MySQL的Buffer Pool机制,redo log机制和undo log机制。MySQL执行增删改时,首先从磁盘加载数据页到Buffer Pool的缓存页。然后更新缓存页,同时会记录undo log回滚日志和redo log重做日志。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/70466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常用网络工具分析(ping,tcpdump等)

写在前面 本文看下常用网络工具。 1:ping 1.1:用途 用于检验网络的连通性。 1.2:实战 在Linux环境中执行:ping www.sina.com.cn: [rootlocalhost ~]# ping www.sina.com.cn PING spool.grid.sinaedge.com (111.…

基于Flask的第七次人口普查数据分析系统的设计与实现

【Flask】基于Flask的第七次人口普查数据分析系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 基于Flask的人口普查可视化分析系统 二、项目界面展示 登录/注册 首页/详情 …

11.Docker 之分布式仓库 Harbor

Docker 之分布式仓库 Harbor Docker 之分布式仓库 Harbor1. Harbor 组成2. 安装 Harbor Docker 之分布式仓库 Harbor Harbor 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器,由 VMware 开源,其通过添加一些企业必需的功能特性,例…

Zookeeper应用案例-分布式锁-实现思路

以下是具体实现代码 第一步:注册锁节点 第二步:获取锁节点,如果自己是最小的节点,就获取权限 第三步:拿到锁就开始自己的业务逻辑 第四步:业务逻辑好了就要释放这把锁 第五步:重新注册监听&…

Elasticsearch7.1.1 配置密码和SSL证书

生成SSL证书 ./elasticsearch-certutil ca -out config/certs/elastic-certificates.p12 -pass 我这里没有设置ssl证书密码,如果需要设置密码,需要再配置给elasticsearch 在之前的步骤中,如果我们对elastic-certificates.p12 文件配置了密码…

Linux(ubuntu) GPU CUDA 构建Docker镜像

一、创建Dockerfile FROM ubuntu:20.04#非交互式,以快速运行自动化任务或脚本,无需图形界面 ENV DEBIAN_FRONTENDnoninteractive# 安装基础工具 RUN apt-get update && apt-get install -y \curl \wget \git \build-essential \software-proper…

蓝桥杯——lcd显示

一:复制文件 从官方参考文件中复制相关文件,Src中的lcd.c,Inc中的lcd.h,fonts.h复制到自己创建的文件中 二:lcd初始化 在lcd.h中找到四个初始化函数,将其写到main文件中 三:写lcd显示函数 在…

中兴G7615AV5

参考文献: G7615AV5 光猫新版固件通过修改备份配置文件固化Telnet 中兴7615AV5光猫配置指南 前言:(不如咸鱼30远程全权搞定,花小钱办大事)截至2025年2月22号,这个设备开启Telnet只能去咸鱼找别人远程开&…

python: SQLAlchemy (ORM) Simple example using SQLite

领域层(Domain Laye):定义了 School 实体类和 SchoolRepository 抽象基类,明确了业务实体和数据访问的契约。 基础设施层(Infrastructure Laye):通过 SQLAlchemy 实现了 SchoolRepository 类&am…

fastadmin实现海报批量生成、邮件批量发送

记录一个海报批量生成、邮件批量发送功能开发,业务场景如下: 国外客户做观展预登记,工作人员通过后台,批量给这些观众生成入场证件并发送到观众登记的邮箱,以方便观众入场时快速进场。证件信息包含入场二维码、姓名&a…

solidity之Foundry安装配置(一)

一门面向合约的高级编程语言,主要用来编写以太坊只能合约。 Solidity受C语言,Python和js影响,但为编译成为以太坊虚拟机字节码在EVM上执行,很多特性和限制都和EVM相关。 Solidity 是静态类型语言,支持继承、库、自定义…

功能全面的手机壁纸应用,种类齐全、众多高清壁纸

软件介绍 应用亮点:今天给大家分享一款超神奇的手机应用 —— 奇幻壁纸。它作为手机动态壁纸软件,功能超全面,操作还便捷,极具创意,能瞬间将你的手机屏幕变成奇幻世界,带来全新视觉感受。 使用便捷性&…

使用docker配置PostgreSQL

配置docker阿里云镜像仓库 国内使用docker hub拉取镜像比较慢,所以首先配置个人的镜像仓库。 阿里云的个人镜像仓库是免费的,对个人来说足够用。 具体操作参考阿里云官方链接 。 关于个人镜像仓库的使用参考链接。 配置完个人镜像仓库后将公网配置到doc…

【跟我学YOLO】(1)YOLO12:以注意力为中心的物体检测

欢迎关注『跟我学 YOLO』系列 【跟我学YOLO】(1)YOLO12:以注意力为中心的物体检测] 0. YOLOv12 简介0.1 YOLO12 论文下载0.2 YOLO12 的主要改进0.3 YOLO12 支持的任务和性能0.4 论文摘要 1. 背景介绍2. 相关的工作3. 方法3.1 效率分析3.2 区域…

VSCode - VSCode 切换自动换行

VSCode 自动换行 1、基本介绍 在 VSCode 中,启用自动换行可以让长行代码自动折行显示,避免水平滚动条频繁使用,提升代码阅读体验 如果禁用自动换行,长行代码就需要手动结合水平滚动条来阅读 2、演示 启用自动换行 禁用自动换…

算法每日一练 (5)

💢欢迎来到张胤尘的技术站 💥技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥 文章目录 算法每日一练 (5)旋转链表题目描述解题思路解题代码c/…

51单片机-按键

1、独立按键 1.1、按键介绍 轻触开关是一种电子开关,使用时,轻轻按开关按钮就可使开关接通,当松开手时,开关断开。 1.2、独立按键原理 按键在闭合和断开时,触点会存在抖动现象。P2\P3\P1都是准双向IO口,…

BFS 和 DFS(深度优先搜索、广度优先搜索)

深度优先搜索(DFS)和广度优先搜索(BFS)是两种常用的图遍历算法,用于解决图相关的问题。它们在搜索问题中具有广泛的应用,如路径搜索、连通性检测等。 以下是具体区别: (图片引自&am…

推荐几款较好的开源成熟框架

一. 若依: 1. 官方网站:https://doc.ruoyi.vip/ruoyi/ 2. 若依SpringBootVueElement 的后台管理系统:https://gitee.com/y_project/RuoYi-Vue 3. 若依SpringBootVueElement 的后台管理系统:https://gitee.com/y_project/RuoYi-Cl…

根据音频中的不同讲述人声音进行分离音频 | 基于ai的说话人声音分离项目

0.研究背景 在实际的开发中可能会遇到这样的问题,老板让你把音频中的每个讲话人的声音分离成不同的音频片段。你可以使用au等专业的音频处理软件手动分离。但是这样效率太慢了,现在ai这么发达,我们能否借助ai之力来分离一条音频中的不同的说…