「mysql是怎样运行的」第24章 一条记录的多幅面孔---事务的隔离级别与MVCC

news2025/1/15 6:58:43

「mysql是怎样运行的」第24章 一条记录的多幅面孔—事务的隔离级别与MVCC

文章目录

  • 「mysql是怎样运行的」第24章 一条记录的多幅面孔---事务的隔离级别与MVCC
    • 一、事前准备
    • 二、事务的隔离级别
      • 事务并发执行遇到的问题
      • SQL标准中的四种隔离级别
      • MySQL中支持的四种隔离级别
    • 三、MVCC原理
      • 版本链
      • ReadView
      • MVCC小结
    • 四、关于purge

一、事前准备

为了故事的顺利发展,我们需要创建一个表:

 CREATE TABLE hero (
        number INT,
        name VARCHAR(100),
        country varchar(100),
        PRIMARY KEY (number)
    ) Engine=InnoDB CHARSET=utf8;

小贴士: 注意我们把这个hero表的主键命名为number,而不是id,主要是想和后边要用到的事务id做区别,大家 不用大惊小怪哈~

然后向这个表里插入一条数据:

INSERT INTO hero VALUES(1, '刘备', '蜀');

现在表里的数据就是这样的:

image-20230130004109663


二、事务的隔离级别

我们知道 MySQL 是一个 客户端/服务器 架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接, 每个客户端与服务器连接上之后,就可以称之为一个会话( Session )。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分,也就是对于服务器来说可能同时处理多个事务。在事务简介的章节中我们说过事务有一个称之为 隔离性 的特性,理论上在某个事务对某个数据进行访问时,其他事务应该进行排队,当该事务提交之后,其他事务才可以继续访问这个数据。但是这样子的话对性能影响太大, 我们既想保持事务的隔离性 ,又想让服务器在处理访问同一数据的多个事务时性能尽量高些,鱼和熊掌不可得兼,舍一部分隔离性而取性能者也


事务并发执行遇到的问题

怎么个舍弃法呢?我们先得看一下访问相同数据的事务在不保证串行执行(也就是执行完一个再执行另一个)的情况下可能会出现哪些问题:

  • 脏写( Dirty Write )

如果一个事务修改了另一个未提交事务修改过的数据,那就意味着发生了 脏写 ,示意图如下:

image-20230130203457986

如上图, Session A 和 Session B 各开启了一个事务, Session B 中的事务先将 number 列为 1 的记录的 name 列更新为 ‘关羽’ ,然后 Session A 中的事务接着又把这条 number 列为 1 的记录的 name 列更新为 张飞 。如果之后 Session B 中的事务进行了回滚,那么 Session A 中的更新也将不复存在,这种现象就称之为 脏写 。这时 Session A 中的事务就很懵逼,我明明把数据更新了,最后也提交事务了,怎么到最后说自己啥也没干呢?


  • 脏读( Dirty Read )

如果一个事务读到了另一个未提交事务修改过的数据,那就意味着发生了 脏读 ,示意图如下:

image-20230130204126816

如上图, Session A 和 Session B 各开启了一个事务, Session B 中的事务先将 number 列为 1 的记录的 name 列更新为 ‘关羽’ ,然后 Session A 中的事务再去查询这条 number 为 1 的记录,如果du到列 name 的值为 ‘关羽’ ,而 Session B 中的事务稍后进行了回滚,那么 Session A 中的事务相当于读到了一个不存在的数据,这种现象就称之为 脏读


  • 不可重复读(Non-Repeatable Read)

如果一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那就意味着发生了不可重复读 ,示意图如下:

image-20230130205009562

如上图,我们在 Session B 中提交了几个隐式事务(注意是隐式事务,意味着语句结束事务就提交了),这些事务都修改了 number 列为 1 的记录的列 name 的值,每次事务提交之后,如果 Session A 中的事务都可以查看到最新的值,这种现象也被称之为 不可重复读


  • 幻读(Phantom)

如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了 幻读 ,示意图如 下:

image-20230131120120451

如上图, Session A 中的事务先根据条件 number > 0 这个条件查询表 hero ,得到了 name 列值为 ‘刘 备’ 的记录;之后Session B 中提交了一个隐式事务,该事务向表 hero 中插入了一条新记录;之后Session A 中的事务再根据相同的条件 number > 0 查询表 hero ,得到的结果集中包含Session B 中的事 务新插入的那条记录,这种现象也被称之为 幻读

有的同学会有疑问,那如果 Session B 中是删除了一些符合 number > 0 的记录而不是插入新记录,那 Session A 中之后再根据 number > 0 的条件读取的记录变少了,这种现象算不算 幻读 呢?明确说一下,这种现象不属于幻读幻读强调的是一个事务按照某个相同条件多次读取记录时,后读取时读到了之前没有读到的记录。

小贴士:
那对于先前已经读到的记录,之后又读取不到这种情况,算啥呢?其实这相当于对每一条记录都发生了不可重复读的现象。幻读只是重点强调了读取到了之前读取没有获取到的记录。


SQL标准中的四种隔离级别

我们上边介绍了几种并发事务执行过程中可能遇到的一些问题,这些问题也有轻重缓急之分,我们给这些问题按照严重性来排一下序:

脏写 > 脏读 > 不可重复读 > 幻读

我们上边所说的舍弃一部分隔离性来换取一部分性能在这里就体现在:设立一些隔离级别,隔离级别越低,越严 重的问题就越可能发生。有一帮人(并不是设计 MySQL 的大叔们)制定了一个所谓的 SQL标准 ,在标准中设立了 4个 隔离级别 :

  • READ UNCOMMITTED :未提交读。
  • READ COMMITTED :已提交读。
  • REPEATABLE READ :可重复读。
  • SERIALIZABLE :可串行化。

SQL标准 中规定,针对不同的隔离级别,并发事务可以发生不同严重程度的问题,具体情况如下:

image-20230131194024371

也就是说:

  • READ UNCOMMITTED 隔离级别下,可能发生脏读 、 不可重复读 和 幻读 问题。
  • READ COMMITTED 隔离级别下,可能发生 不可重复读 和 幻读 问题,但是不可以发生 脏读 问题。
  • REPEATABLE READ 隔离级别下,可能发生 幻读 问题,但是不可以发生 脏读 和 不可重复读 的问题。
  • SERIALIZABLE 隔离级别下,各种问题都不可以发生。

脏写 是怎么回事儿?怎么里边都没写呢?这是因为脏写这个问题太严重了,不论是哪种隔离级别,都不允许脏写的情况发生。


MySQL中支持的四种隔离级别

不同的数据库厂商对 SQL标准 中规定的四种隔离级别支持不一样,比方说 Oracle 就只支持 READ COMMITTED 和 SERIALIZABLE 隔离级别。本书中所讨论的 MySQL 虽然支持4种隔离级别,但与 SQL标准 中所规定的各级隔离级 别允许发生的问题却有些出入,MySQL在REPEATABLE READ隔离级别下,是可以禁止幻读问题的发生的(关于如何禁止我们之后会详细说明的)。
MySQL 的默认隔离级别为 REPEATABLE READ ,我们可以手动修改一下事务的隔离级别。


三、MVCC原理

版本链

我们前边说过,对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列( row_id 并不是必要的,我们创建的表中有主键或者非NULL的UNIQUE键时都不会包含 row_id 列):

  • trx_id :每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的 事务id 赋值给 trx_id 隐藏列。
  • roll_pointer :每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到 undo日志 中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息

比方说我们的表 hero 现在只包含一条记录:

image-20230131205654041

假设插入该记录的 事务id 为 80 ,那么此刻该条记录的示意图如下所示:

image-20230131205827211

小贴士:
实际上insert undo只在事务回滚时起作用,当事务提交后,该类型的undo日志就没用了,它占用的Undo Log Segment也会被系统回收(也就是该undo日志占用的Undo页面链表要么被重用,要么被释放)。 虽然真正的insert undo日志占用的存储空间被释放了,但是roll_pointer的值并不会被清除,roll_pointer属性占用7个字节,第一个比特位就标记着它指向的undo日志的类型,如果该比特位的值为1时,就代表着它指向的undo日志类型为insert undo。所以我们之后在画图时都会把insert undo给去掉, 大家留意一下就好了。

假设之后两个事务id 分别为 100 、 200 的事务对这条记录进行 UPDATE 操作,操作流程如下:

image-20230131210919872

小贴士: 能不能在两个事务中交叉更新同一条记录呢?哈哈,这不就是一个事务修改了另一个未提交事务修改过的数据,沦为了脏写了么?InnoDB使用锁来保证不会有脏写情况的发生,也就是在第一个事务更新了某条记录后,就会给这条记录加锁,另一个事务再次更新时就需要等待第一个事务提交了,把锁释放之后才可以继续更新。关于锁的更多细节我们后续的文章中再唠叨哈~

每次对记录进行改动,都会记录一条 undo日志 ,每条 undo日志 也都有一个 roll_pointer 属性( INSERT 操作 对应的 undo日志 没有该属性,因为该记录并没有更早的版本),可以将这些 undo日志 都连起来,串成一个链表,所以现在的情况就像下图一样:

image-20230131211052031

对该记录每次更新后,都会将旧值放到一条 undo日志 中,就算是该记录的一个旧版本,随着更新次数的增多, 所有的版本都会被 roll_pointer 属性连接成一个链表,我们把这个链表称之为 版本链 ,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的 事务id ,这个信息很重要,我们稍后就会用到。


ReadView

对于使用 READ UNCOMMITTED 隔离级别的事务来说,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了;对于使用 SERIALIZABLE 隔离级别的事务来说,设计 InnoDB 的大叔规定使用加锁的方式来访 问记录(加锁是啥我们后续文章中说哈);对于使用 READ COMMITTED 和 REPEATABLE READ 隔离级别的事务来 说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交, 是不能直接读取最新版本的记录的,核心问题就是:需要判断一下版本链中的哪个版本是当前事务可见的。为此,设计 InnoDB 的大叔提出了一个 ReadView 的概念,这个 ReadView 中主要包含4个比较重要的内容:

  • m_ids :表示在生成 ReadView 时当前系统中活跃的读写事务的 事务id 列表
  • min_trx_id :表示在生成 ReadView 时当前系统中活跃的读写事务中最小的事务id ,也就是 m_ids 中的最小值。
  • max_trx_id :表示生成 ReadView 时系统中应该分配给下一个事务的id 值

小贴士:

注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1,2,3这三 个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括1和2,mi n_trx_id的值就是1,max_trx_id的值就是4。

  • creator_trx_id :表示生成该 ReadView 的事务的 事务id

小贴士:

我们前边说过,只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会 为事务分配事务id,否则在一个只读事务中的事务id值都默认为0

有了这个 ReadView ,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

  • 如果被访问版本的 trx_id 属性值与 ReadView 中的 creator_trx_id 值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  • 如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生 成 ReadView 前已经提交,所以该版本可以被当前事务访问。
  • 如果被访问版本的 trx_id 属性值大于 ReadView 中的 max_trx_id 值,表明生成该版本的事务在当前事务生 成 ReadView 后才开启,所以该版本不可以被当前事务访问。
  • 如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,那就需要判断一下 trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。

如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录。

在 MySQL 中, READ COMMITTED 和 REPEATABLE READ 隔离级别的的一个非常大的区别就是它们生成ReadView的 时机不同。我们还是以表 hero 为例来,假设现在表 hero 中只有一条由 事务id 为 80 的事务插入的一条记录:

image-20230131214858426

接下来看一下READ COMMITTED 和 REPEATABLE READ所谓的生成ReadView的时机不同到底不同在哪里。

EAD COMMITTED ——每次读取数据前都生成一个ReadView

比方说现在系统里有两个 事务id 分别为 100 、 200 的事务在执行:

# Transaction 100
BEGIN;
UPDATE hero SET name = '关羽' WHERE number = 1;
UPDATE hero SET name = '张飞' WHERE number = 1;
    # Transaction 200
    BEGIN;
# 更新了一些别的表的记录 ...

小贴士: 再次强调一遍,事务执行过程中,只有在第一次真正修改记录时(比如使用INSERT、DELETE、UPDATE语 句),才会被分配一个单独的事务id,这个事务id是递增的。所以我们才在Transaction 200中更新一 些别的表的记录,目的是让它分配事务id。

此刻,表 hero 中 number 为 1 的记录得到的版本链表如下所示:

image-20230131215113096

假设现在有一个使用 READ COMMITTED 隔离级别的事务开始执行:

# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200未提交
SELECT * FROM hero WHERE number = 1; # 得到的列name的值为'刘备'

这个 SELECT1 的执行过程如下:

  • 在执行 SELECT 语句时会先生成一个 ReadView , ReadView 的 m_ids 列表的内容就是 [100, 200] , min_trx_id 为 100 , max_trx_id 为 201 , creator_trx_id 为 0 。
  • 然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 ‘张飞’ ,该版本的 trx_id 值为 100 ,在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。
  • 下一个版本的列 name 的内容是 ‘关羽’ ,该版本的 trx_id 值也为 100 ,也在 m_ids 列表内,所以也不符合要求,继续跳到下一个版本。
  • 下一个版本的列 name 的内容是 ‘刘备’ ,该版本的 trx_id 值为 80 ,小于 ReadView 中的 min_trx_id 值100 ,所以这个版本是符合要求的,最后返回给用户的版本就是这条列 name 为 ‘刘备’ 的记录。

之后,我们把 事务id 为 100 的事务提交一下,就像这样:

 # Transaction 100
BEGIN;
UPDATE hero SET name = '关羽' WHERE number = 1;
UPDATE hero SET name = '张飞' WHERE number = 1;
COMMIT;

然后再到 事务id 为 200 的事务中更新一下表 hero 中 number 为 1 的记录:

# Transaction 200
BEGIN;
# 更新了一些别的表的记录 ...
UPDATE hero SET name = '赵云' WHERE number = 1; UPDATE hero SET name = '诸葛亮' WHERE number = 1;

此刻,表 hero 中 number 为 1 的记录的版本链就长这样:

image-20230131215530618

然后再到刚才使用 READ COMMITTED 隔离级别的事务中继续查找这个 number 为 1 的记录,如下:

# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200均未提交
SELECT * FROM hero WHERE number = 1; # 得到的列name的值为'刘备'
# SELECT2:Transaction 100提交,Transaction 200未提交
SELECT * FROM hero WHERE number = 1; # 得到的列name的值为'张飞'

这个 SELECT2 的执行过程如下:

  • 在执行 SELECT 语句时会又会单独生成一个 ReadView ,该 ReadView 的 m_ids 列表的内容就是 [200] ( 事 务id 为 100 的那个事务已经提交了,所以再次生成快照时就没有它了), min_trx_id 为 200 ,
  • max_trx_id 为 201 , creator_trx_id 为 0 。 然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 ‘诸葛亮’ ,该版本的trx_id 值为 200 ,在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。
  • 下一个版本的列 name 的内容是 ‘赵云’ ,该版本的 trx_id 值为 200 ,也在 m_ids 列表内,所以也不符合 要求,继续跳到下一个版本。
  • 下一个版本的列 name 的内容是 ‘张飞’ ,该版本的 trx_id 值为 100 ,小于 ReadView 中的 min_trx_id 值200 ,所以这个版本是符合要求的,最后返回给用户的版本就是这条列 name 为 ‘张飞’ 的记录。

以此类推,如果之后 事务id 为 200 的记录也提交了,再此在使用 READ COMMITTED 隔离级别的事务中查询表 hero 中 number 值为 1 的记录时,得到的结果就是 ‘诸葛亮’ 了,具体流程我们就不分析了。总结一下就是:使用READ COMMITTED隔离级别的事务在每次查询开始时都会生成一个独立的ReadView。

REPEATABLE READ —— 在第一次读取数据时生成一个ReadView

对于使用 REPEATABLE READ 隔离级别的事务来说,只会在第一次执行查询语句时生成一个 ReadView ,之后的查询就不会重复生成了。我们还是用例子看一下是什么效果。

比方说现在系统里有两个 事务id 分别为 100 、 200 的事务在执行:

# Transaction 100
BEGIN;
UPDATE hero SET name = '关羽' WHERE number = 1;
UPDATE hero SET name = '张飞' WHERE number = 1;
# Transaction 200
BEGIN;
# 更新了一些别的表的记录 ...

此刻,表 hero 中 number 为 1 的记录得到的版本链表如下所示:

image-20230131232230909

假设现在有一个使用 REPEATABLE READ 隔离级别的事务开始执行:

# 使用REPEATABLE READ隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200未提交
SELECT * FROM hero WHERE number = 1; # 得到的列name的值为'刘备' 

这个 SELECT1 的执行过程如下:

  • 在执行 SELECT 语句时会先生成一个 ReadView , ReadView 的 m_ids 列表的内容就是 [100, 200] , min_trx_id 为 100 , max_trx_id 为 201 , creator_trx_id 为 0 。
  • 然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 ‘张飞’ ,该版本的 trx_id 值为 100 ,在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。
  • 下一个版本的列 name 的内容是 ‘关羽’ ,该版本的 trx_id 值也为 100 ,也在 m_ids 列表内,所以也不符 合要求,继续跳到下一个版本。
  • 下一个版本的列 name 的内容是 ‘刘备’ ,该版本的 trx_id 值为 80 ,小于 ReadView 中的 min_trx_id 值100 ,所以这个版本是符合要求的,最后返回给用户的版本就是这条列 name 为 ‘刘备’ 的记录。

之后,我们把 事务id 为 100 的事务提交一下,就像这样:

# Transaction 100
BEGIN;
UPDATE hero SET name = '关羽' WHERE number = 1;
UPDATE hero SET name = '张飞' WHERE number = 1;
COMMIT;

然后再到 事务id 为 200 的事务中更新一下表 hero 中 number 为 1 的记录:

# Transaction 200
BEGIN;
# 更新了一些别的表的记录 ...
UPDATE hero SET name = '赵云' WHERE number = 1; UPDATE hero SET name = '诸葛亮' WHERE number = 1;

此刻,表 hero 中 number 为 1 的记录的版本链就长这样:

image-20230201124023170

然后再到刚才使用 REPEATABLE READ 隔离级别的事务中继续查找这个 number 为 1 的记录,如下:

# 使用REPEATABLE READ隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200均未提交
SELECT * FROM hero WHERE number = 1; # 得到的列name的值为'刘备'
# SELECT2:Transaction 100提交,Transaction 200未提交
SELECT * FROM hero WHERE number = 1; # 得到的列name的值仍为'刘备'

这个 SELECT2 的执行过程如下:

  • 因为当前事务的隔离级别为 REPEATABLE READ ,而之前在执行 SELECT1 时已经生成过 ReadView 了,所以此 时直接复用之前的 ReadView ,之前的 ReadView 的 m_ids 列表的内容就是 [100, 200] , min_trx_id 为100 , max_trx_id 为 201 , creator_trx_id 为 0 。
  • 然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 ‘诸葛亮’ ,该版本的trx_id 值为 200 ,在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。
  • 下一个版本的列 name 的内容是 ‘赵云’ ,该版本的 trx_id 值为 200 ,也在 m_ids 列表内,所以也不符合 要求,继续跳到下一个版本。
  • 下一个版本的列 name 的内容是 ‘张飞’ ,该版本的 trx_id 值为 100 ,而 m_ids 列表中是包含值为 100 的事务id 的,所以该版本也不符合要求,同理下一个列 name 的内容是 ‘关羽’ 的版本也不符合要求。继续跳 到下一个版本。
  • 下一个版本的列 name 的内容是 ‘刘备’ ,该版本的 trx_id 值为 80 ,小于 ReadView 中的 min_trx_id 值100 ,所以这个版本是符合要求的,最后返回给用户的版本就是这条列 c 为 ‘刘备’ 的记录。

也就是说两次 SELECT 查询得到的结果是重复的,记录的列 c 值都是 ‘刘备’ ,这就是 可重复读 的含义。如果我们之后再把 事务id 为 200 的记录提交了,然后再到刚才使用 REPEATABLE READ 隔离级别的事务中继续查找这 个 number 为 1 的记录,得到的结果还是 ‘刘备’ ,具体执行过程大家可以自己分析一下。


MVCC小结

从上边的描述中我们可以看出来,所谓的 **MVCC (Multi-Version Concurrency Control ,多版本并发控制)**指的就 是在使用 READ COMMITTD 、 REPEATABLE READ 这两种隔离级别的事务在执行普通的 SEELCT 操作时访问记录的版 本链的过程,这样子可以使不同事务的读-写 、 写-读操作并发执行,从而提升系统性能。 READ COMMITTD 、

REPEATABLE READ 这两个隔离级别的一个很大不同就是:生成ReadView的时机不同,READ COMMITTD在每一 次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作 前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。

小贴士: 我们之前说执行DELETE语句或者更新主键的UPDATE语句并不会立即把对应的记录完全从页面中删除,而是执行一个所谓的delete mark操作,相当于只是对记录打上了一个删除标志位,这主要就是为MVCC服务的,大家可以对比上边举的例子自己试想一下怎么使用。 另外,所谓的MVCC只是在我们进行普通的SEELCT查询时才生效,截止到目前我们所见的所有SELECT语句都算是普通的查询,至于啥是个不普通的查询,我们稍后再说哈~


四、关于purge

大家有没有发现两件事儿:

  • 我们说 insert undo 在事务提交之后就可以被释放掉了,而 update undo 由于还需要支持 MVCC ,不能立即删除掉。
  • 为了支持 MVCC ,对于 delete mark 操作来说,仅仅是在记录上打一个删除标记,并没有真正将它删除掉。

随着系统的运行,在确定系统中包含最早产生的那个 ReadView 的事务不会再访问某些 update undo日志以及被打了删除标记的记录后,有一个后台运行的 purge线程会把它们真正的删除掉。关于更多的purge细节,我们将放到纸质书中进行详细唠叨,不见不散哈~


笔记整理:

mysql是怎样运行的

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

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

相关文章

C++多态原理

请看下面的程序&#xff0c;该程序演示了多态类对象存储空间的大小。 #include <iostream> using namespace std; class A {public:int i;virtual void func() {}virtual void func2() {} }; class B : public A {int j;void func() {} }; int main() {cout << si…

VBA之正则表达式(41)-- 快速标记两个星号之后的字符

实例需求&#xff1a;工作表中的数据保存在A列~G列&#xff0c;现需要识别D列中包含超过两个星号的内容&#xff0c;并将第3个星号及其之后的字符设置为红色字体&#xff0c;如图所示。 示例代码如下。 Sub Demo1()Dim objRegExp As ObjectDim objMatch As ObjectDim strMatch…

【10】linux命令每日分享——cp复制文件和目录

大家好&#xff0c;这里是sdust-vrlab&#xff0c;Linux是一种免费使用和自由传播的类UNIX操作系统&#xff0c;Linux的基本思想有两点&#xff1a;一切都是文件&#xff1b;每个文件都有确定的用途&#xff1b;linux涉及到IT行业的方方面面&#xff0c;在我们日常的学习中&…

Altium Designer v23.2.1.34 PCB板设计软件中文特别版

Altium Designer 中文特别版是一款功能强大的电子设计自动化(EDA)软件,它提供了一个完整的设计环境,可以帮助电子工程师快速设计、模拟、仿真和制造电子产品。Altium Designer 中文特别版拥有丰富的功能,包括电路设计、PCB设计、回流焊接、3D设计、仿真等,可以帮助用户快…

【深度学习 01】安装环境详解之 miniconda

欢迎关注『youcans的深度学习课』系列&#xff0c;持续更新中… 【深度学习 01】安装环境之 miniconda 【深度学习 02】PyTorch CPU版本安装与环境配置 【深度学习 01】 安装环境详解之 miniconda 1. conda/Anaconda/miniconda conda可以理解为一个工具&#xff0c;也是一个可…

TensorFlow-Keras - 一文搞懂 TF 常用矩阵计算方法

目录 一.引言 二.tf.multiply 1.常规乘法 2.乘以标量 3.不规则乘法 三.tf.matmul 1.常规矩阵相乘 2.多维矩阵相乘 四.tf.tensordot 1.axes1 2.axesN 3.axesTuple 4.axesArray(Tuple()) 五.K.dot 六.K.batch_dot 1.不指定 axes 2.指定 axes 为数字 3.指定 axe…

【观察】连续八年霸榜云数据库“领导者”,揭秘亚马逊云科技背后的“统治力”...

日前&#xff0c;全球市场分析机构 Gartner发布《2022 云数据库管理系统魔力象限》报告。其中&#xff0c;在Gartner本次魔力象限报告评估的20家供应商中&#xff0c;亚马逊云科技在纵轴“执行能力”和横轴“愿景完整性”两个维度分别处于最高、最右位置&#xff0c;这也是亚马…

Zigbee物联网组网

物联网的核心和基础是互联网&#xff0c;物联网是在互联网基础上的延伸和扩展的网络&#xff0c;然而在物联网当中基于海量数据的无线传感网是物联网极具代表的网络之一&#xff0c;其用户端延伸和扩展到了任何物品与物品之间&#xff0c;进行信息交换和通信。 硬件设备及连接&…

PPC Insights系列:洞见安全多方图联邦

开放隐私计算开放隐私计算开放隐私计算OpenMPC是国内第一个且影响力最大的隐私计算开放社区。社区秉承开放共享的精神&#xff0c;专注于隐私计算行业的研究与布道。社区致力于隐私计算技术的传播&#xff0c;愿成为中国 “隐私计算最后一公里的服务区”。183篇原创内容公众号知…

NetCore使用SkyWalking

官网中文文档&#xff1a;SkyWalking 极简入门 | Apache SkyWalking一、引用依赖新建一个项目&#xff1a;Cbf.SkyWalking.ServiceInstancenuget安装&#xff1a;SkyAPM.Agent.AspNetCore二、launchSettings.json添加这两行配置或者在这里添加这两行也行&#xff1a;三、需要添…

界面组件DevExpress WinForms v22.2 - 全面升级数据展示功能

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

某马程序员NodeJS速学笔记

文章目录前言一、什么是Node.js?二、fs文件系统模块三、Http模块四、模块化五、开发属于自己的包模块加载机制六、Express1.初识ExpressGET/POSTnodemon2.路由模块化3.中间件中间件分类自定义中间件4. 跨域问题七、Mysql模块安装与配置基本使用Web开发模式Session认证JWT八、大…

MySQL查询操作

系列文章目录前言一、简单查询SELECT子句SELECT后面之间跟列名DISTINCT,ALL列表达式列更名WHERE子句WHERE子句中可以使用的查询条件比较运算BETWEEN...AND...集合查询&#xff1a;IN模糊查询LIKE空值比较&#xff1a;IS NULL多重条件查询SELECT 的基本结构ORDER BY子句排序聚集…

vue导出excel

1、下载依赖 npm install --save xlsx file-saverps&#xff1a;我下载完依赖后再运行会报错&#xff0c;偶尔情况&#xff0c;没找到原因&#xff0c;只需要卸载重新npm install就好 2、新建一个写公共js方法的文件**&#xff0c;如果你有的话 &#xff0c;写在里面就好 然…

Mysql 用户管理、权限管理(含用户密码修改)

和Linux 系统一样&#xff0c;也有着自己独有的用户管理系统&#xff0c;MySQL所有的用户信息都被保存在mysql数据库中的user表中。 目录 1、用户信息 2、用户管理 (1) 创建用户 (2) 删除用户 (3) 修改用户密码 3、用户权限管理 (1) 赋予权限&#xff08;grant&#xff…

Mysql5.7+Orch+proxysql+keepalive

架构设计 通过gtid配置MySQL主从&#xff0c;通过orch实现高可用&#xff0c;orch通过raft实现自身的高可用&#xff0c;通过proxysql实现读写分离&#xff0c;proxysql可自身可以配置集群&#xff0c;通过keepalive实现虚拟IP漂移&#xff0c;keepalive可以自身配置集群 配…

蓝桥杯嵌入式LED流马灯(使用HAL_Delay、操作寄存器、使用定时器)

1.STM32CubeMX的配置 将PC8-PC15设置为GPIO_Output模式&#xff0c;将PD2也设置为GPIO_Output模式。 通过原理图知&#xff0c;LED&#xff08;PC8-PC15&#xff09;低电平有效&#xff0c;锁存器(PD2)高电平有效。 初始化时我将LED设置为高电平&#xff0c;熄灭状态&#xff0…

MyBatis - 09 - 自定义映射resultMap

文章目录1 准备工作1.1 建表1.2 创建实体类1.3 引出一个问题方案1方案2方案32.完整代码项目结构EmpMapper接口Emp类SqlSessionUtils工具类EmpMapper.xmljdbc.propertieslog4j.xmlmybatis-config.xmlResultMapTest完整代码在后面 1 准备工作 1.1 建表 t_emp 添加测试数据&…

AOP在PowerJob中的使用,缓存锁保证并发安全,知识细节全总结

这是一篇简简单单的文章&#xff0c;需要你简简单单看一眼就好&#xff0c;如果有不明白的地方&#xff0c;欢迎留言讨论。 在之前的文章中出现过一次AOP的使用&#xff0c;就是在运行任务之前&#xff0c;需要判断一下&#xff0c;触发该任务执行的server&#xff0c;是不是数…

[神经网络]图神经网络(GNN)

一、概述 1.图 图用来表示一些实体(entities)之间的关系(实体表示为点(node)&#xff0c;关系表示为边(edge))。 关系分为有方向和无方向 2.数据的图表示 以图像文件为例&#xff0c;我们可以用邻接矩阵来表示一张图像。每个点表示一个像素点&#xff0c;若一个像素点有x个相邻…