undolog会一直存在吗?什么时候删除?

典型回答 undolog是innodb中重要的一种日志,他的主要作用有两个: 1、事务回滚:当事务执行过程中需要执行ROLLBACK操作时,InnoDB可以使用undolog将数据恢复到事务开始前的状态。 2、MVCC:InnoDB使用undolog为实现MVCC提供支持,使得不同事务可以看到数据在不同时间点的一致性快照,从而提高并发性。 ✅如何理解MVCC? 当事务对数据进行修改(插入、更新、删除)时,会生成相应的undolog。每条记录会包含操作前的旧值。所以,根据操作类型,undolog分成两种类型: insert undolog:用于记录insert操作,用于事务回滚 update undolog:用于记录update和delete操作,不仅在事务回滚时需要,在快照读时也需要 对于insert undolog来说,它主要用于在事务回滚时。这种日志在事务提交就不再需要了。而update undolog还要用在MVCC场景用于做快照读,所以他是不能被立即清理的。 在mysql官网中(https://dev.mysql.com/doc/refman/8.4/en/optimizing-innodb-transaction-management.html )是这么描述的: 当行被update或delete时,行和关联的undolog不会立即物理删除,甚至不会在事务提交后立即删除。旧数据会一直保留到较早开始或同时开始的事务完成,以便这些事务可以访问修改或删除行的先前状态。因此,长时间运行的事务可以防止InnoDB清除由不同事务更改的数据。 InnoDB会在合适的时间进行统一清理,释放空间。这个过程称为**purge**。其实背后就是一个purge线程,purge线程在调度进行清理时,会做判断,对于insert undolog就直接清理了,但是对于update undolog,必须确保没有活跃事务需要这些日志用于一致性读。也就是说,即使事务已经提交,但只要还有其他事务在进行一致性读操作并依赖这些日志,update undolog就不能被清理。 那update undolog是如何判断有没有被其他事物依赖的呢? 其实很简单,就一句话:如果一个事务在所有当前活跃的读事务开始之前就已经完成并提交,那么这个事务的Undo Log就可以清理,因为这些读事务不需要它来查看历史数据。 具体来说就是,每当一个写事务开始时,InnoDB会分配一个递增的事务编号(trx_no),当写事务结束并提交时,这个事务的编号(trx_no)被用作提交序号。 当一个读事务开始时,它会创建一个读取视图(ReadView)。这个读取视图会记录当前看到的最大的事务提交序号,称为m_low_limit_no。这是这个读事务开始时数据库中已经提交的最大事务编号。 如果某个事务的编号(trx_no)小于所有活跃读事务的m_low_limit_no,说明这个事务在所有读事务开始之前就已经提交了。这意味着所有活跃的读事务都可以看到这个事务的结果,不需要再通过Undo Log来构建数据的历史版本。因此,这些Undo Log可以被清理。

March 22, 2026 · 1 min · santu

varchar(100)和varchar(10)有什么区别?

典型回答 基本差别 最简单的区别**VARCHAR(10)**最多只能存储 10 个字符。**VARCHAR(100)**最多可以存储 100 个字符。 这里是字符,不是字节哦!字节和字符之间的转换公式:字符数 * 字符集最大字节/字符,例如,**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">utf8mb4</font>** 最大 4 字节/字符,那么10个字符就占用**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">10 * 4 = 40 字节</font>** varchar在实际存储时只用实际字符长度 + 长度信息字节,不像 CHAR 会固定长度填充。 所以,如果他们同时存储"Hollis"的话,那么占用的都是6个字符数据,加上一个长度开销(根据编码方式不同)。这一点上没有什么不同的。 排序差别(重点) 如果,你用一个varchar来做排序,那么可能会有些细微的差别。 在《高性能MySQL》一书中,有这样一段描述: 较大的列会使用更多的内存,因为MySQL通常会在内部分配固定大小的内存来保存值。这对于使用内存临时表的排序或操作来说尤其糟糕。再利用磁盘临时表进行文件排序时也同样糟糕**。** 最好的策略是只分配真正需要的空间。 解释一下。 当 MySQL 执行排序(如 ORDER BY varchar_column)时,它通常需要在内存(sort_buffer)中存储待排序行的相关字段值。对于 VARCHAR 列,MySQL 在内存中为排序操作预留空间时,会考虑该列定义的最大长度 (**N**) 。 VARCHAR(10): 内存中可能为每个值预留 10 字符 的空间,则最多预留 10字符 + 长度字节。 VARCHAR(100): 内存中可能为每个值预留 100 字符 + 长度字节。 ...

March 22, 2026 · 1 min · santu

一次insert操作,MySQL的几种log的写入顺序?

典型回答 现需要了解下这几种log: ✅binlog、redolog和undolog区别? redolog:**是MySQL用于实现崩溃恢复和数据持久性的一种机制。**在事务进行过程中,MySQL会将事务做了什么改动到Redo Log中。当系统崩溃或者发生异常情况时,MySQL会利用Redo Log中的记录信息来进行恢复操作,将事务所做的修改持久化到磁盘中。 binlog:是MySQL用于记录数据库中的所有DDL语句和DML语句的一种二进制日志。它记录了所有对数据库结构和数据的修改操作,如INSERT、UPDATE和DELETE等。binlog主要用来对数据库进行数据备份、灾难恢复和数据复制等操作。binlog的格式分为基于语句的格式和基于行的格式。 undolog:**用于在事务回滚或系统崩溃时撤销(回滚)事务所做的修改。**当一个事务执行过程中,MySQL会将事务修改前的数据记录到Undo Log中。如果事务需要回滚,则会从Undo Log中找到相应的记录来撤销事务所做的修改。另外,Undo Log还支持MVCC(多版本并发控制)机制,用于在并发事务执行时提供一定的隔离性。 insert操作为什么要记录undolog,记录什么内容? insert也是有可能会滚的,所以undolog肯定要记录,这样才能基于undolog做撤销insert操作。那么insert操作的undolog都要记录哪些信息呢? insert的undolog需要明确的记录下来这是一个删除操作,所以insert应的 undo 日志类型为 **TRX_UNDO_INSERT_REC**。 同时存储的重要信息还有undo no、table id、主键各列信息<len,value>列表等。 其中undo type用于记录是一次insert操作,table id用于记录是在哪张表做的insert操作,主键各列信息<len,value>列表记录了具体是哪(几)条记录被insert了。 当执行一次insert操作时,MySQL InnoDB引擎的日志写入顺序如下: 1、写入undolog,先将事务修改前的数据记录到Undo Log中。 2、写入redolog,处于prepare阶段 (表示事务已修改但未提交)。 3、写入binlog,将binlog 内存日志数据写入文件缓冲区并刷新到磁盘中。 4、写入redolog,处于commit阶段。 所以写入顺序是: 1 undo log → redo log (prepare) → binlog → redo log (commit) 为什么这么个顺序? 首先,undolog要记录变更前的数据,所以一定要最先执行。 redolog和binlog的写入需要保证原子性,先写 redo log,后写 binlog,中间MySQL 崩溃,会导致主从数据不一致(因为binlog用于主从同步)。 而先写 binlog,后写 redo log,中间MySQL 崩溃,数据页没有更新,事务无法恢复。 所以需要用2阶段提交的方式写入。 ✅什么是事务的2阶段提交?

March 22, 2026 · 1 min · santu

为什么MySQL默认使用RR隔离级别?

对于数据库的默认隔离级别,Oracle默认的隔离级别是 RC,而MySQL默认的隔离级别是 RR。 那么,你知道为什么Oracle选择RC作为默认级别,而MySQL要选择RR作为默认的隔离级别吗? Oracle 的隔离级别 Oracle只支持ANSI/ISO SQL定义的Serializable和Read Committed,其实,根据Oracle官方文档给出的介绍,Oracle支持三种隔离级别: 即Oracle支持Read Committed、Serializable和Read-Only。 Read-Only只读隔离级别类似于Serializable隔离级别,但是只读事务不允许在事务中修改数据,除非用户是SYS。 在Oracle这三种隔离级别中,Serializable和Read-Only显然都是不适合作为默认隔离级别的,那么就只剩Read Committed这个唯一的选择了。 MySQL 的隔离级别 相比于Oracle,MySQL的默认隔离级别的可选范围就比较大了。 首先,我们先从四种隔离级别中排除Serializable和Read Uncommitted这两种,主要是因为这两个级别一个隔离级别太高,一个太低。太高的就会影响并发度,太低的就有脏读现象。 那么,剩下的RR和RC两种,怎么选? 在MySQL设计之初,他的定位就是提供一个稳定的关系型数据库。而为了要解决MySQL单点故障带来的问题,MySQL采用主从复制的机制。 所谓主从复制,其实就是通过搭建MySQL集群,整体对外提供服务,集群中的机器分为主服务器(Master)和从服务器(Slave),主服务器提供写服务,从服务器提供读服务。 为了保证主从服务器之间的数据的一致性,就需要进行数据同步,大致的同步过程如下,这里就不详细介绍了 MySQL在主从复制的过程中,数据的同步是通过bin log进行的,简单理解就是主服务器把数据变更记录到bin log中,然后再把bin log同步传输给从服务器,从服务器接收到bin log之后,再把其中的数据恢复到自己的数据库存储中。 那么,binlog里面记录的是什么内容呢?格式是怎样的呢? MySQL的bin log主要支持三种格式,分别是statement、row以及mixed。MySQL是在5.1.5版本开始支持row的、在5.1.8版本中开始支持mixed。 ✅MySQL的binlog有几种格式 statement和row最大的区别,当binlog的格式为statement时,binlog 里面记录的就是 SQL 语句的原文(这句话很重要!!!后面会用的到)。 因为MySQL早期只有statement这种bin log格式,这时候,如果使用提交读(Read Committed)、未提交读(Read Uncommitted)这两种隔离级别会出现问题。 举个例子,有一个数据库表t1,表中有如下两条记录: 1 2 3 4 5 6 7 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, KEY `b` (`b`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; insert into t1 values(10,1); 接着开始执行两个事务的写操作: ...

March 22, 2026 · 1 min · santu

乐观锁与悲观锁如何实现?

典型回答 在MySQL中,悲观锁是需要依靠数据库提供的锁机制实现的,在InnoDB引擎中,要使用悲观锁,需要先关闭MySQL数据库的自动提交属性,然后通过select ... for update来进行加锁。 在数据库中,悲观锁的流程如下: 在对记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。 其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。 我们举一个简单的例子,如淘宝下单过程中扣减库存的需求说明一下如何使用悲观锁: 1 2 3 4 5 6 7 8 //0.开始事务 begin; //1.查询出商品信息 select quantity from items where id=1 for update; //2.修改商品quantity为2 update items set quantity=2 where id = 1; //3.提交事务 commit; 以上,在对id = 1的记录修改前,先通过for update的方式进行加锁,然后再进行修改。这就是比较典型的悲观锁策略。 如果以上修改库存的代码发生并发,同一时间只有一个线程可以开启事务并获得id=1的锁,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。 上面我们提到,使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引的话,优化器在选择时候,如果发现锁表可能性能更好的话,有可能会直接锁表。 MySQL中的乐观锁主要通过CAS的机制来实现,一般通过version版本号来实现。 CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。 比如前面的扣减库存问题,通过乐观锁可以实现如下: 1 2 3 4 5 //查询出商品信息,quantity = 3 select quantity from items where id=1 //根据商品信息生成订单 //修改商品quantity为2 update items set quantity=2 where id=1 and quantity = 3; 以上,我们在更新之前,先查询一下库存表中当前库存数(quantity),然后在做update的时候,以库存数作为一个修改条件。当我们提交更新的时候,判断数据库表对应记录的当前库存数与第一次取出来的库存数进行比对,如果数据库表当前库存数与第一次取出来的库存数相等,则予以更新,否则认为是过期数据。 ...

March 22, 2026 · 1 min · santu

二级索引在索引覆盖时如何使用MVCC?

典型回答 在回答这个问题之前,需要有一定的前提储备知识,要不然可能你连这个问题都看不懂。 储备知识1:MySQL数据库中的每行记录中,除了保存了我们自己定义的一些字段以外,还有一些重要的隐式字段的:db_row_id(隐藏主键,如果我们没有给这个表创建主键,那么会以这个字段来创建聚簇索引。)、 db_trx_id(对这条记录做了最新一次修改的事务的ID)、以及 db_roll_ptr(回滚指针,指向这条记录的上一个版本,其实他指向的就是Undo Log中的上一个版本的快照的地址。 ) 储备知识2:在MVCC中,隐藏字段中的db_roll_ptr用来构建版本链,db_trx_id也是一个重要的用来判断快照可见性的一个字段。 ✅如何理解MVCC? 储备知识3:在Innodb 的聚簇索引(主键索引)中,叶子节点上保存的是整行记录。而在非聚簇索引(二级索引)中,叶子节点上值保存了主键的信息。 ✅什么是聚簇索引和非聚簇索引? 储备知识4:在一条查询语句中,如果能用到索引覆盖,则就会直接用二级索引进行检索,并不会回表。 ✅什么是回表,怎么减少回表的次数? 确保你针对以上知识点都了解了之后,那么问题来了。 如果某个查询语句的查询字段都包含在二级索引中,那么就会走索引覆盖,不用回表去读取聚簇索引的页记录。但是,版本链的头结点在聚簇索引中,不在二级索引中,通过二级索引的记录无法直接找到版本链。在这种情况下如何使用MVCC? 这个问题挺有意思的,确实,在你懂了以上几个知识点之后,可能就会陷入这个疑惑当中,那么具体是怎么回事呢?我在MySQL 官网中(https://dev.mysql.com/doc/refman/8.4/en/optimizing-innodb-transaction-management.html ),到了一个以下关于这个问题的描述: 翻译一下就是:如果发现二级索引页有一个 PAGE_MAX_TRX_ID 太新的,或者如果二级索引中的记录被删除标记, InnoDB可能需要使用聚集索引来查找记录。 也就是说,索引覆盖并不是用到了联合索引就一定会走的!在以上这种情况下,会回表,通过覆盖索引进行查询。上面的这个解释不是很清楚,我把过程在展开说一下: 在二级索引中,用一个额外的名为page_max_trx_id的变量来记录表示修改过该页的最大事务id。 如果当前查询得到的read_view的 up_limit_id > page_max_trx_id,说明在创建read_view时,最后一次更新二级索引的事务已经提交了,那就意味着二级索引里的提交对于当前查询都应该是可见的。这时候如果这个二级索引的记录没有被删除,那么就可以直接走索引覆盖查询。 up_limit_id来自于readview: ✅什么是ReadView,什么样的ReadView可见? 否则,就意味着数据可能被修改了,不能直接查询,而需要回表,通过聚簇索引进行查询。使用聚簇索引的时候,叶子节点行记录中设计包含了版本链的,就可以用到MVCC了。

March 22, 2026 · 1 min · santu

什么是buffer pool?

典型回答 我们都知道,MySQL的数据是存储在磁盘上面的(Memory引擎除外),但是如果每次数据的查询和修改都直接和磁盘交互的话,性能是很差的。 于是,为了提升读写性能,Innodb引擎就引入了一个中间层,就是buffer pool。 buffer是在内存上的一块连续空间,他主要的用途就是用来缓存数据页的,每个数据页的大小是16KB。 页是Innodb做数据存储的单元,无论是在磁盘,还是buffe pool中,都是按照页读取的,这也是一种’预读’的思想。 ✅介绍一下InnoDB的数据页,和B+树的关系是什么? 有了buffer pool之后,当我们想要做数据查询的时候,InnoDB会首先检查Buffer Pool中是否存在该数据。如果存在,数据就可以直接从内存中获取,避免了频繁的磁盘读取,从而提高查询性能。如果不存在再去磁盘中进行读取,磁盘中如果找到了的数据,则会把该数据所在的页直接复制一份到buffer pool中,并返回给客户端,后续的话再次读取就可以从buffer pool中就近读取了。 当需要修改的时候也一样,需要先在buffer pool中做修改,然后再把他写入到磁盘中。 但是因为buffer pool是基于内存的,所以空间不可能无限大,他的默认大小是128M,当然这个大小也不是完全固定的,我们可以调整,可以通过修改MySQL配置文件中的innodb_buffer_pool_size参数来调整Buffer Pool的大小。 1 2 3 SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; -- 查看buffer pool SET GLOBAL innodb_buffer_pool_size = 512M; -- 修改buffer pool 扩展知识 buffer pool和query cache的区别 在Innodb中,除了buffer pool,还有一个缓存层是用来做数据缓存,提升查询效率的,很多人搞不清楚他和buffer pool的区别是什么。 首先就是他们目的和作用不同。Buffer Pool用于缓存表和索引的数据页,从而加速读取操作;而Query Cache用于缓存查询结果,减少重复查询的执行时间。 Buffer Pool主要与存储引擎InnoDB相关,而Query Cache也支持其他的引擎,如MyISAM等。所以,Query Cache是位于Server层的优化技术,而Buffer Pool 是位于引擎层的优化技术。 需要注意的是,在MySQL 5.7版本中,Query Cache已经被标记为废弃,并在MySQL 8.0版本中彻底被移除了。 ...

March 22, 2026 · 1 min · santu

什么是InnoDB的页分裂和页合并

典型回答 ✅介绍一下InnoDB的数据页,和B+树的关系是什么? 先看上面的这个,了解下什么是InnoDB中的数据页。 InnoDB的数据页是InnoDB存储引擎中用于存储数据的基本单位,通常大小为16KB。B+树的每个节点都对应着一个数据页,包括根节点、非叶子节点和叶子节点。B+树通过节点之间的指针连接了不同层级的数据页,从而构建了一个有序的索引结构。 我们都是知道,B+树是按照索引字段建立的,并且在B+树中是有序的,假如有下面一个索引的树结构,其中的索引字段的值并不连续。 假如,现在我们插入一个新的一条记录,他的索引值是3,那么他就要按照顺序插入到页20中,在索引值为1,2的记录的后面。而如果这个索引页已经满了,那么就需要触发一次页分裂。 页分裂是指将该页面中的一部分索引记录移动到一个新的页面中,从而为新记录腾出空间。这样可以保持B+树的平衡和性能。 以下,就是一次页分裂的过程: 那么,当我们向Innodb中添加数据的时候,如果索引是随机无序的,那么就会导致页分裂。而且分裂这个动作还可能会引起连锁反应,从叶子节点沿着树结构一路分裂到根节点。 有分裂,就会有合并。在InnoDB中,当索引页面中的索引记录删除后,页面可能会变得过于稀疏。这时,为了节省空间和提高性能,可能会触发页合并操作。 页合并是指将两个相邻的索引页面合并成一个更大的页面,减少B+树的层级,从而提高查询性能。 扩展知识 页分裂(合并)的危害 首先,页分裂和合并是涉及大量数据移动和重组的操作。频繁进行这些操作会增加数据库的I/O负担和CPU消耗,影响数据库的整体性能。 分裂和合并可能导致B+树索引结构频繁调整,这个过程也会影响插入及删除操作的性能。 频繁的页分裂和合并可能会导致磁盘上存在较多的空间碎片,新分出的一个页一般会有很多空闲空间,使得数据库表占用更多的磁盘空间,而导致浪费。 如何避免页分裂 ✅char和varchar的区别? ✅MySQL的主键一定是自增的吗? 在上面两篇中,我们介绍过,使用varchar或者使用UUID作为主键的话,都会导致页分裂。 所以,尽量选择使用自增的字段作为索引,尤其是主键索引,这样可以很大程度的避免页分裂。 如果要插入大量数据,尽量使用批量插入的方式,而不是逐条插入。这样可以减少页分裂的次数。 频繁删除操作可能导致页面过于稀疏,从而触发页合并。所以,一般建议使用逻辑删除而不是物理删除。 逻辑删除:即在记录中添加一个标记来表示记录是否被删除(deleted = 0/1),而不是真正地从数据库中删除记录。 我们当然还可以根据实际情况,适当调整InnoDB的配置参数,如页大小、填充因子、叶子页合并的阈值等,以优化数据库性能。

March 22, 2026 · 1 min · santu

什么是OnlineDDL

典型回答 DDL,即Data Defination Language,是用于定义数据库结构的操作。DDL操作用于创建、修改和删除数据库中的表、索引、视图、约束等数据库对象,而不涉及实际数据的操作。以下是一些常见的DDL操作: CREATE ALTER DROP TRUNCATE 与DDL相对的是DML,即Data Manipulation Language,用于操作数据。即包括我们常用的INSERT、DELETE和UPDATE等。 在MySQL 5.6之前,所有的ALTER操作其实是会阻塞DML操作的,如:添加/删除字段、添加/删除索引等,都是会锁表的。 但是在MySQL 5.6中引入了Online DDL,OnLineDDL是MySQL5.6提出的加速DDL方案,**尽最大可能保证DDL期间不阻塞DML动作。但是需要注意,这里说的尽最大可能**意味着不是所有DDL语句都会使用OnlineDDL加锁。 Online DDL的优点就是可以减少阻塞,是MySQL的一种内置优化手段,但是需要注意的是,DDL在刚开始和快结束的时候,都需要获取MDL锁,而在获取锁的时候如果有事务未提交,那么DDL就会因为加锁失败而进入阻塞状态,也会造成性能影响。 还有就是,如果Online DDL操作失败,其回滚操作可能成本较高。以及长时间运行的Online DDL操作可能导致主从同步滞后。 但是需要注意的是,即使有了Online DDL,也不意味着就可以随意在业务高峰期进行DDL变更了: ✅MySQL做索引更新的时候,会锁表吗? 使用方式: 1 2 3 4 5 6 7 ALTER TABLE tbl_name RENAME INDEX old_index_name TO new_index_name, ALGORITHM=INPLACE, LOCK=NONE; ALTER TABLE tbl_name DROP COLUMN column_name, ALGORITHM=INSTANT; ALTER TABLE tbl_name MODIFY COLUMN col_name column_definition FIRST, ALGORITHM=INPLACE, LOCK=NONE; ALTER TABLE t1 ADD COLUMN (c2 INT GENERATED ALWAYS AS (c1 + 1) STORED), ALGORITHM=COPY; 即在SQL后增加ALGORITHM=INPLACE, LOCK=NONE;或者 ALGORITHM=INSTANT、ALGORITHM=COPY ...

March 22, 2026 · 2 min · santu

什么是ReadView,什么样的ReadView可见?

典型回答 ReadView 是 InnoDB 中一个至关重要的概念,他是实现MVCC的基础,同时他也是支持不同的事务隔离级别的基础,同时提高系统的并发能力和性能。 他到底是干啥的?简单来说,其实就是一句话:Read View 主要来帮我们解决可见性的问题的, 即他会来告诉我们本次事务应该看到哪个快照,不应该看到哪个**快照****。** 快照? 我们都知道,MySQL中用不同的事务隔离级别,比如我们常见的RR和RC,RR要求在一个事务中,多次读取的结果是保持一致的,而RC则要求每次都要读取到最新的值。 那么,具体如何实现的RR和RC的读数据的时候的不同现象,就是这个快照。 在可重复读(Repeatable Read)级别下,快照(ReadView)在事务开始后第一次查询时创建一次,并在整个事务期间保持不变。 在读已提交(Read Committed)级别下,快照(ReadView)会在每次查询时重新创建,以反映数据库中的最新提交更改。 ReadView的定义在MySQL的不同版本中不一样,拿MySQL 5.7 (8.0也一样)举例,ReadView定义如下 (https://github.com/xiaowei520/mysql-5.7.23-source-read/blob/0d99c6709f1d982aa8674dab3fc40848908478fb/storage/innobase/read/read0read.cc#L175 ): 这几个东西是干嘛的,有啥用,代表了啥?其实,在5.6(https://github.com/zhujzhuo/MySQL-5.6/blob/mysql-5.6.11/storage/innobase/include/read0read.h ) 中的ReadView的定义更加直观: 也就是说,在 Read View 中有几个重要的属性: trx_ids,表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。 low_limit_id,应该分配给下一个事务的id 值。 up_limit_id,未提交的事务中最小的事务 ID。 creator_trx_id,创建这个 Read View 的事务 ID。 trx_ids中包含了low_limit_id和up_limit_id的信息,其实trx_ids = [up_limit_id,low_limit_id)(但是需要注意,他并不一定连续,只是会包含up_limit_id,并且小于low_limit_id,比如他可能是5,7,8,11) ,并且你没看错,up_limit_id就是表示最低水位,low_limit_id就是表示最高水位,至于为啥这么叫,我也不知道。。。 网上还有些资料这里有min_trx_id、max_trx_id、m_ids等的说法,我翻了下源码5.6、5.7、8.0,并没有找到出处。就先不纠结了,以源码为准。 目前看到有关于max_trx_id的定义,但是是用在二级索引的MVCC支持中的。 也就是说,每一次读取数据的时候(RC情况下),都会生成一个ReadView,并且在其中记录上trx_ids(包含了[up_limit_id,low_limit_id))和creator_trx_id。 那么,一个事务应该看到哪些快照,不应该看到哪些快照该如何判断呢? 假如一个ReadView的内容为: 1 2 3 4 trx_ids = [5,6,8) low_limit_id = 8 up_limit_id = 5 creator_trx_id = 7 假设当前事务要读取某一个记录行,该记录行的 db_trx_id(即最新修改该行的事务ID)为 trx_id,那么,就有以下几种情况了: ...

March 22, 2026 · 1 min · santu

留言给博主