1、请讲解一下事务的隔离级别1.1、什么是事务事务就是把一组sql语句打包成一个整体在这组sql语句中要么全部执行成功要么全部失败。这组sql可以是一条也可以是多条。事务是数据库管理系统执行过程中的一个逻辑单元包含了一组数据库操作要么全部成功执行要么全部失败回滚以保证数据的一致性、完整性和可靠性举个例子来说的话比如转账。张三和李四都有1000元钱现在张三向李四转100元那么张三剩下900元李四剩下1100元钱。在这个例子中1张三和李四的钱的总和一定是2000元钱不能超过或比这个钱少。2不能出现张三钱减少而李四钱不增多的情况。3转账后的余额应当保存到存储介质中方便提取。4张三和李四的转账不能因为其他转账而收到干扰。1.2、事务的ACID特性原子性、一致性、隔离性、持久性。1.2.1、原子性一个事务中的所有操作要么全部成功要么全部失败不会出现执行一半的情况如果事务在执行过程中发生了错误会回滚到事务开始前的状态就像这个事务重来没有执行过一样。START TRANSACTION; UPDATE accounts SET balance balance - 100 WHERE user_id 1; UPDATE accounts SET balance balance 100 WHERE user_id 2; -- 如果第二条语句失败第一条也会回滚 COMMIT;简短回答原子性要求事务的所有操作要么全部提交成功要么全部失败回滚对于一个事务中的操作不能只执行其中一部分。1.2.2、一致性在事务开始之前和事务结束之后数据库的完整性不会被破坏。这表明了写入的数据必须完全符合所有的预设规则包括数据的精度、关联性以及关于事务执行过程中服务器崩溃和如何恢复。一致性确保事务从一个一致的状态转换到另一个一致的状态。比如在银行转账事务中无论发生什么转账前后两个账户的总金额应保持不变。假如A账户100 块给 B 账户10 块转了10块钱不管成功与否A和B的总金额都是110块。-- 假设 A 账户余额为 100B 账户余额为 10 -- 转账前状态 SELECT balance FROM accounts WHERE user_id A; -- 100 SELECT balance FROM accounts WHERE user_id B; -- 10 -- 转账操作 START TRANSACTION; UPDATE accounts SET balance balance - 10 WHERE user_id A; UPDATE accounts SET balance balance 10 WHERE user_id B; COMMIT; -- 转账后状态 SELECT balance FROM accounts WHERE user_id A; -- 90 SELECT balance FROM accounts WHERE user_id B; -- 20 -- 总金额仍然是 110简短回答一致性确保数据的状态从一个一致状态转变为另一个一致状态。一致性与业务规则有关比如银行转账不论事务成功还是失败转账双方的总金额应该是不变的。1.2.3、隔离性数据库运行多个并发事务同时对数据进行读写和修改隔离性可以防止多个事务并发执行时由于交叉执行导致数据不一致的情况。事务可以指定不同的隔离级别以权衡在不同场景下数据库性能和安全。隔离性意味着并发执行的事务是彼此隔离的一个事务的执行不会被其他事务干扰。事务之间是井水不犯河水的。隔离性主要是为了解决事务并发执行时可能出现的脏读、不可重复读、幻读等问题。比如说在读未提交的隔离级别下会出现脏读现象一个事务C读取了事务B尚未提交的修改数据。如果事务B最终回滚事务C读取的数据就是无效的“脏数据”。-- 会话 A -- 创建模拟并发的测试表 DROP TABLE IF EXISTS accounts; CREATE TABLE accounts ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), balance DECIMAL(10,2) ); -- 插入测试数据 INSERT INTO accounts (name, balance) VALUES (王二, 1000.00), (张三, 2000.00), (李四, 3000.00); -- 会话B 中设置隔离级别为读未提交 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; START TRANSACTION; -- 在会话 B 中更新数据但不提交 UPDATE accounts SET balance balance - 500 WHERE name王二; -- 会话C 是读为提交级别读取数据得到 500 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT * FROM accounts WHERE name王二; -- 继续别的操作基于 500 -- 会话 B 的事务回滚导致会话 A 读到的数据其实是脏数据 ROLLBACK;通过升级隔离级别为读已提交可以解决脏读的问题。读已提交是指只能读到别的事务“已经提交”的数据读不到未提交的脏数据。一句话解释你在事务里查询数据只能看到别人已经提交完成的数据看不到别人正在改、还没提交的中间数据。-- 会话 B 修改为读已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 执行第一次查询 1000 SELECT * FROM accounts WHERE name王二; -- 会话 C 中设置隔离级别为读已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 在会话 C 中更新数据但不提交 START TRANSACTION; UPDATE accounts SET balance balance 200 WHERE name王二; -- 会话 B 中再次读取数据结果仍然为 1000 SELECT * FROM accounts WHERE name王二; -- 会话 C 中回滚事务 ROLLBACK; -- 会话 B 中再次读取数据结果仍然为 1000 SELECT * FROM accounts WHERE name王二;但会出现不可重复读的问题事务B第一次读取某行数据值为X期间事务C修改该数据为Y并提交事务B再次读取时发现值变为Y导致两次读取结果不一致。-- 会话 B 修改为读已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 执行第一次查询 1000 START TRANSACTION; SELECT * FROM accounts WHERE name王二; -- 会话 C 中设置隔离级别为读已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 在会话 C 中更新数据并提交 START TRANSACTION; UPDATE accounts SET balance balance 200 WHERE name王二; -- 会话 C 提交事务 COMMIT; -- 会话 B 中再次读取数据结果仍然为 1200 SELECT * FROM accounts WHERE name王二;可以通过升级隔离级别为可重复读来解决不可重复读的问题。可重复读是指同一个事务内多次读取同一行数据结果完全一样不受其他事务提交的影响。定义是是数据库事务的隔离级别保证在同一个事务内多次读取同一数据结果保持一致不受其他已提交事务的修改影响能避免脏读、不可重复读是 InnoDB 的默认隔离级别。用最通俗的话讲你开启一个事务后不管别人怎么改、怎么提交你在这个事务里反复查同一条数据查到的永远是你刚开启事务时的那个版本这就叫可重复读。-- 会话 B 修改为可重复读 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 开始事务并执行第一次查询 1000 START TRANSACTION; SELECT * FROM accounts WHERE name王二; -- 会话 C 中设置隔离级别为可重复读 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 在会话 C 中更新数据并提交 START TRANSACTION; UPDATE accounts SET balance balance 200 WHERE name王二; -- 会话 C 提交事务 COMMIT; -- 会话 B 中再次读取数据结果仍然为 1000 SELECT * FROM accounts WHERE name王二;但可重复读级别下仍然会出现幻读的问题事务B第一次查询获得2条数据事务C 新增1条数据并提交后事务B再次查询时仍然为2条数据但可以更新新增的数据再次查询时就发现有3条数据了。通俗的来说幻读就是在同一个事务里第一次查询返回10条别人插入/删除了数据并提交你再查变成11条或9条→像幻觉一样多/少了行这就是幻读。-- 会话 B 修改为可重复读 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 执行第一次查询查到 2 条记录 START TRANSACTION; SELECT * FROM accounts WHERE balance 1000; -- 会话 C 中设置隔离级别为可重复读 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 在会话 C 中新增数据并提交 START TRANSACTION; INSERT INTO accounts (name, balance) VALUES (王五, 4000); -- 会话 C 提交事务 COMMIT; -- 会话 B 中再次读取数据结果仍然为 2 条 SELECT * FROM accounts WHERE balance 1000; -- 会话 B 中尝试更新王五的余额为 5000竟然成功了 UPDATE accounts SET balance 5000 WHERE name王五; -- 会话 B 中再次读取数据发现 3 条记录 SELECT * FROM accounts WHERE balance 1000;可以通过升级隔离级别为串行化来解决幻读的问题。隔离级别是否会脏读是否会不可重复读是否会幻读Read Uncommitted读未提交✅ 可能✅ 可能✅ 可能Read Committed读已提交❌ 不会✅ 可能✅ 可能Repeatable Read可重复读❌ 不会❌ 不会✅ 可能但 InnoDB 已解决Serializable可串行化❌ 不会❌ 不会❌ 不会简短回答多个并发事务之间需要相互隔离即一个事务的执行不能被其他事务干扰。1.2.4、追问企业一般不适用Serializable可串行化因为使用Serializable会导致性能较差哪InnoDB是怎么样去解决这个问题的呢InnoDB可重复读RR级别下通过Next-Key Lock临键锁 彻底解决了幻读。临键锁会锁住查询范围的所有数据与间隙阻止其他事务插入新数据从而避免幻读。1.2.5、持久性事务处理结束之后对数据的修改将永久的保存到存储介质中即便是系统故障了也不会消失。持久性确保事务一旦提交它对数据所做的更改就是永久性的即使系统发生崩溃数据也能恢复到最近一次提交的状态。MySQL的持久性是通过InnoDB引擎的redo log实现的。在事务提交时InnoDB会先将修改操作写入redo log并刷盘持久化。崩溃后InnoDB会通过redo log恢复数据从而保证事务提交成功的数据不会丢失。简短回答一旦事务提交则其所做的修改将永久保存到MySQL中。即使发生系统崩溃修改的数据也不会丢失。1.3、为什么使用事务事务具备的ACID特性是我们使用事务的原因在我们日常的业务场景中有大量的需求要用事务来保证。支持事务的数据库能够简化我们的编程模型, 不需要我们去考虑各种各样的潜在错误和并发问题在使用事务过程中要么提交要么回滚不用去考虑网络异常服务器宕机等其他因素因此 我们经常接触的事务本质上是数据库对ACID模型的⼀个实现是为应用层服务的。2、ACID靠什么来保证的呢一句话总结ACID中的原子性主要通过Undo Log来实现持久性通过Redo Log来实现隔离性由MVCC和锁机制来实现一致性则由其他三大特性共同保证。2.1、详细说说如何保证原子性事务对数据进行修改前会记录一份快照到Undo Log如果事务中有任何一步执行失败系统会读取Undo Log将所有操作回滚恢复到事务开始前的状态从而保证事务要么全部成功要么全部失败。1BEGIN; 2UPDATE user SET balance balance - 100 WHERE id 1; 写入 Undo Log记录 id1 的原始余额 500 3UPDATE user SET balance balance 100 WHERE id 2; 写入 Undo Log记录 id2 的原始余额 300 4COMMIT; 清空 Undo Log事务成功 ❗如果失败 执行 ROLLBACK根据 Undo Log 把数据还原2.2、详细说说如何保证持久性MySQL 的持久性主要由预写Redo Log、双写机制、两阶段提交以及Checkpoint刷盘机制共同保证。当事务提交时MySQL会先将事务的修改操作写入Redo Log并强制刷盘然后再将内存中的数据页刷入磁盘。这样即使系统崩溃重启后也能通过Redo Log重放恢复数据。在将数据页写入到磁盘时如果发生崩溃可能会导致数据页不完整。InnoDB的数据页大小为16KB通常大于操作系统的 4KB页大小。为了解决只写入部分的问题MySQL采用了双写机制脏盘刷页时先将数据页写入到一个双写缓冲区中2M的连续空间然后再将其写入到磁盘的实际位置。崩溃恢复时如果发现数据页不完整会从双写缓冲区中恢复副本确保数据页的完整性。在涉及主从复制时MySQL通过两阶段提交保证Redo Log和Binlog的一致性第一阶段写入Redo Log并标记为prepare状态第二阶段写入Binlog再提交Redo Log为commit状态。崩溃恢复时如果发现Redo Log是 prepare但Binlog完整则会提交事务反之会回滚避免主从不一致。另外由于Redo Log的容量有限Checkpoint机制会定期将内存中的脏页刷到磁盘这样能减少崩溃恢复时需要处理的Redo Log数量。2.3、请详细说说如何保证隔离性隔离性主要通过锁机制和MVCC来实现。比如说一个事务正在修改某条数据时MySQL会通过临键锁来防止其他事务同时进行修改避免数据冲突。同时临键锁可以防止幻读现象的发生。比如事务A查询id 10的记录那么临键锁不仅会锁住id10的行还会锁住10后面的“间隙”防止其他事务插入id15的数据。假如表中的主键有id:5, 10, 15, 20, 25那么InnoDB会对以下区间和记录加锁加锁对象类型锁定含义(10, 15]临键锁锁住 id15 和前间隙防止插入11~14(15, 20]临键锁锁住了 id20 和前间隙(20, 25]临键锁锁住了 id25 和前间隙(25, ∞)间隙锁锁住尾部防止插入30等MVCC 主要用来优化读操作通过保存数据的历史版本让读操作不需要加锁就能直接读取快照提高读的并发性能。不同的隔离级别对应不同的实现策略比如说在可重复读隔离级别下事务第一次查询时会生成一个Read View之后所有读操作都复用这个视图保证多次读取的结果一致。2.3、如何保证一致性MySQL的一致性并不是靠某一个机制单独保证的而是原子性、隔离性和持久性协同作用的结果。2.4、事务会不会自动提交是的MySQL默认开启了事务自动提交模式。每条单独的SQL语句都会被视为一个独立的事务处理单元SQL语句执行成功后会自动执行 COMMIT执行失败时会自动ROLLBACK。可通过SELECTautocommit;查看当前会话的自动提交状态。如果需要执行多条SQL语句可以将它们放在一个事务中使用START TRANSACTION开启事务执行完所有SQL语句后手动提交。START TRANSACTION; UPDATE accounts SET balance balance - 100 WHERE user_id 1; UPDATE accounts SET balance balance 100 WHERE user_id 2; COMMIT;3、事务的隔离级别有那些隔离级别定义了一个事务可能受其他事务影响的程度MySQL支持四种隔离级别分别是读未提交、读已提交、可重复读和串行化。读未提交会出现脏读读已提交会出现不可重复读可重复读是InnoDB默认的隔离级别可以避免脏读和不可重复读但会出现幻读。不过通过MVCC和临键锁能够防止大多数并发问题。串行化最安全但性能较差通常不推荐使用。3.1、详细说说读未提交事务可以读取其他未提交事务修改的数据。也就是说如果未提交的事务一旦回滚读取到的数据就会变成了“脏数据”通常不会使用。3.2、什么是读已提交读已提交避免了脏读但可能会出现不可重复读即同一事务内多次读取同一数据结果会不同因为其他事务提交的修改对当前事务是可见的。是 Oracle、SQL Server 等数据库的默认隔离级别。3.3、什么是可重复读可重复读能确保同一事务内多次读取相同数据的结果一致即使其他事务已提交修改。是 MySQL 默认的隔离级别避免了“脏读”和“不可重复读”通过 MVCC 和临键锁也能在一定程度上避免幻读。-- Session A: START TRANSACTION; SELECT balance FROM accounts WHERE id1; --返回500 -- Session B: UPDATE accounts SET balance balance 100 WHERE id1; COMMIT; -- Session A再次查询: SELECT balance FROM accounts WHERE id1; --仍返回500(可重复读) -- Session A更新后查询: UPDATE accounts SET balance balance 50 WHERE id1; --基于最新值550更新为600 SELECT balance FROM accounts WHERE id1; --返回6003.4、什么是串行化串行化是最高的隔离级别通过强制事务串行执行来解决“幻读”问题。但会导致大量的锁竞争问题实际应用中很少用。3.5、A 事务未提交B 事务上查询到的是旧值还是新值如果B是普通的SELECT也就是快照读它读的是旧值即事务A修改前的快照并且不会阻塞如果B是当前读比如SELECT … FOR UPDATE它会被阻塞直到事务A提交或回滚。-- 会话 A 中更新王二的余额 START TRANSACTION; UPDATE accounts SET balance 8000 WHERE name 王二; -- 此时并没有 COMMIT -- 会话 B 中查询王二的余额 SELECT * FROM accounts WHERE name 王二; -- 会话 B 会读取到 旧值 1000 -- 会话 C 中使用当前读查询王二的余额 SELECT * FROM accounts WHERE name 王二 FOR UPDATE; -- 会话 C 会被阻塞直到会话 A 提交或回滚3.6、怎么更改事务的隔离级别MySQL支持通过SET语句修改事务隔离级别包括全局级别、当前会话但一般不建议在生产环境中随意修改隔离级别。测试环境下可以使用SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; 可以修改当前会话的隔离级别。使用SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; 可以修改全局隔离级别影响新的连接但不会改变现有会话。4、事务的隔离级别是如何实现的读未提交通过行锁共享锁确保一个事务在更新行数据但没有提交的情况下其他事务不能更新该行数据但不会阻止脏读意味着事务2可以在事务1提交之前读取到事务1修改的数据。读已提交会在更新数据前加行级排他锁不允许其他事务写入或者读取未提交的数据也就意味着事务2不能在事务1提交之前读取到事务1修改的数据从而解决脏读的问题。另外读已提交会在每次读取数据前都生成一个新的ReadView所以会出现不可重复读的问题。可重复读只在第一次读操作时生成ReadView后续读操作都会使用这个ReadView从而避免不可重复读的问题。另外对于当前读操作可重复读会通过临键锁来锁住当前行和前间隙防止其他事务在这个范围内插入数据从而避免幻读的问题。串行化级别下事务在读操作时会先加表级共享锁在写操作时会先加表级排他锁。直到事务结束后才释放锁这样就能确保事务之间不会相互干扰。5、请详细说说幻读呢幻读是指在同一个事务中多次执行相同的范围查询结果却不同。这种现象通常发生在其他事务在两次查询之间插入或删除了符合当前查询条件的数据。比如说事务A在第一次查询某个条件范围的数据行后事务B插入了一条新数据且符合条件范围事务A再次查询时发现多了一条数据。我们来验证一下先创建测试表插入测试数据。CREATE TABLE user_info ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键id, name VARCHAR(32) NOT NULL DEFAULT COMMENT 姓名, gender VARCHAR(32) NOT NULL DEFAULT COMMENT 性别, email VARCHAR(32) NOT NULL DEFAULT COMMENT 邮箱, PRIMARY KEY (id) ) ENGINEINNODB DEFAULT CHARSETutf8mb4 COMMENT用户信息表; -- 插入测试数据 INSERT INTO user_info (id, name, gender, email) VALUES (1, Curry, 男, curry163.com), (2, Wade, 男, wade163.com), (3, James, 男, james163.com); COMMIT;然后我们在事务A中执行查询SELECT * FROM user_info WHERE id 1;在事务B中插入数据INSERT INTO user_info (name, gender, email) VALUES (wanger, 女, wanger163.com);再在事务A中修改刚刚插入的数据 update user_info set gender男 where id 4;最后在事务A中再次查询SELECT * FROM user_info WHERE id 1;。5.1、如何避免幻读MySQL 在可重复读隔离级别下通过MVCC和临键锁可以在一定程度上避免幻读。比如说在查询时显示加锁利用临键锁锁定查询范围防止其他事务插入新的数据。START TRANSACTION; SELECT * FROM user_info WHERE id 1 FOR UPDATE; -- 加临键锁 COMMIT;其他事务在插入数据时会被阻塞直到当前事务提交或回滚。5.1.1、对这个进行解释。如果查询语句中包含显式加锁如 FOR UPDATEInnoDB z会使用当前读直接读取最新的数据并加锁。在范围查询时InnoDB不仅会对符合条件的记录加行锁还会对相邻的索引间隙加间隙锁从而形成临键锁。临键锁可以防止其他事务在间隙中插入新数据从而避免幻读。比如说在执行查询的事务中不要尝试去更新其他事务插入/删除的数据利用快照读来避免幻读。使用SELECT查询时如果没有显式加锁InnoDB会使用MVCC提供一致性视图。每个事务在启动时都会生成一个Read View用来确定哪些数据对当前事务可见。其他事务在当前事务启动后插入的新数据不会被当前事务看到因此不会出现幻读。5.1.2、什么是当前读呢当前读是指读取记录的最新已提交版本并且在读取时对记录加锁确保其他并发事务不能修改当前记录。比如SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE以及UPDATE、DELETE都属于当前读。5.1.3、为什么UPDATE和DELETE也属于当前读因为更新、删除这些操作本质上不仅是写操作还需要在写之前读取数据然后才能修改或删除。为了保证修改的是最新的数据并防止并发冲突InnoDB必须读取最新版本的数据并加锁因此UPDATE和DELETE也属于当前读。SQL语句是否当前读是否加锁SELECT * FROM user WHERE id1❌ 否❌ 否SELECT * FROM user WHERE id1FOR UPDATE✅ 是✅ 加排他锁SELECT * FROM user WHERE id1 LOCK IN SHARE MODE✅ 是✅ 加共享锁UPDATE user SET ... WHERE id1✅ 是✅ 加排他锁DELETE FROM user WHERE id1✅ 是✅ 加排他锁5.1.4、什么是快照读呢快照读是InnoDB通过MVCC实现的一种非阻塞读方式。当事务执行SELECT查询时InnoDB 并不会直接读当前最新的数据而是根据事务开始时生成的Read View去判断每条记录的可见性从而读取符合条件的历史版本。SQL是否快照读说明SELECT * FROM t WHERE id1✅ 是快照读SELECT * FROM t WHERE id1 FOR UPDATE❌ 否当前读读取最新版本并加锁UPDATE / DELETE❌ 否当前读必须读取当前版本并加锁INSERT❌ 否写操作不存在历史版本6、MVCC了解吗MVCC指的是多版本并发控制每次修改数据时都会生成一个新的版本而不是直接在原有数据上进行修改。并且每个事务只能看到在它开始之前已经提交的数据版本。这样的话读操作就不会阻塞写操作写操作也不会阻塞读操作从而避免加锁带来的性能损耗。其底层实现主要依赖于Undo Log和Read View。每次修改数据前先将记录拷贝到Undo Log并且每条记录会包含三个隐藏列DB_TRX_ID用来记录修改该行的事务IDDB_ROLL_PTR 用来指向Undo Log中的前一个版本DB_ROW_ID用来唯一标识该行数据仅无主键时生成。每次读取数据时都会生成一个ReadView其中记录了当前活跃事务的ID集合、最小事务 ID、最大事务ID等信息通过与DB_TRX_ID进行对比判断当前事务是否可以看到该数据版本。6.1、请详细说说什么是版本链版本链是指InnoDB中同一条记录的多个历史版本通过DB_ROLL_PTR字段将它们像链表一样串起来用来支持MVCC的快照读。假设有一张hero表表中有这样一行记录name为张三city为帝都插入这行记录的事务 id是80。此时DB_TRX_ID的值就是 80DB_ROLL_PTR的值就是指向这条 insert undo 日志的指针。接下来如果有两个DB_TRX_ID分别为100、200的事务对这条记录进行了update操作那么这条记录的版本链就会变成下面这样也就是说当更新一行数据时InnoDB不会直接覆盖原有数据而是创建一个新的数据版本并更新 DB_TRX_ID和DB_ROLL_PTR使它们指向前一个版本和相关的undo日志。这样老版本的数据就不会丢失可以通过版本链找到。由于undo日志会记录每一次的 update并且新插入的行数据会记录上一条undo日志的指针所以可以通过DB_ROLL_PTR这个指针找到上一条记录这样就形成了一个版本链。6.2、请详细说说什么是ReadViewReadView是InnoDB为每个事务创建的一份“可见性视图”用于判断在执行快照读时哪些数据版本是当前这个事务可以看到的哪些不能看到。当事务开始执行时InnoDB会为该事务创建一个ReadView这个ReadView会记录4个重要的信息1creator_trx_id创建该ReadView的事务ID。2m_ids所有活跃事务的ID列表活跃事务是指那些已经开始但尚未提交的事务。3min_trx_id所有活跃事务中最小的事务ID。它是 m_ids 数组中最小的事务ID。4max_trx_id 事务ID的最大值加一。换句话说它是下一个将要生成的事务ID。6.3、ReadView是如何判断记录的某个版本是否可见的会通过三个步骤来判断①、如果某个数据版本的DB_TRX_ID小于min_trx_id则该数据版本在生成ReadView之前就已经提交因此对当前事务是可见的。②、如果DB_TRX_ID大于max_trx_id则表示创建该数据版本的事务在生成ReadView之后开始因此对当前事务不可见。③、如果DB_TRX_ID在min_trx_id和max_trx_id之间需要判断DB_TRX_ID是否在m_ids列表中不在表示创建该数据版本的事务在生成 ReadView 之后已经提交因此对当前事务也是可见的。在表示事务仍然活跃或者在当前事务生成 ReadView 之后才开始因此是不可见的。举个实际的例子。读事务开启了一个ReadView这个ReadView 里面记录了当前活跃事务的ID列表444、555、665以及最小事务ID444和最大事务ID666。当然还有自己的事务ID520也就是creator_trx_id。它要读的这行数据的写事务ID是x也就是DB_TRX_ID。①如果x 110显然在 ReadView 生成之前就提交了所以这行数据是可见的。②如果x 667显然是未知世界所以这行数据对读操作是不可见的。③如果x 519虽然519大于444小于666但是519不在活跃事务列表里所以这行数据是可见的。因为 519是在 520 生成 ReadView 之前就提交了。④如果x 555虽然555大于444小于666但是555在活跃事务列表里所以这行数据是不可见的。因为 555不确定有没有提交。6.4、可重复读和读已提交在 ReadView 上的区别是什么可重复读在第一次读取数据时生成一个ReadView这个ReadView会一直保持到事务结束这样可以保证在事务中多次读取同一行数据时读取到的数据是一致的。读已提交每次读取数据前都生成一个ReadView这样就能保证每次读取的数据都是最新的。6.5、如果两个AB事务并发修改一个变量那么A读到的值是什么怎么分析。事务A在读取时是否能读到事务B的修改取决于A是快照读还是当前读。如果是快照读InnoDB会使用MVCC的ReadView判断记录版本是否可见若事务B尚未提交或在A的视图不可见则A会读到旧值如果是当前读则需要加锁若B已提交可直接读取否则A会阻塞直到B结束。