MySQL——事务管理
一、认识事务1.引入若MySQL的CURD不加控制会出现的问题对于以上的问题CURD满足以下条件买票的过程是原子的买票互相不能影响买完票要永久有效买前和买后都要是确定的状态而事务就是来解决这种问题的2.事务的概念事务的定义事务是由一组逻辑上相关的DML语句如INSERT、UPDATE、DELETE组成的操作集合。事务中的SQL语句要么全部成功执行要么全部失败回滚不存在部分执行的情况。MySQL提供事务机制确保这一整体性并规定不同客户端看到的数据状态可以不同。事务的四大属性ACID1. 原子性Atomicity一个事务中的所有操作要么全部完成要么全部不完成。如果在执行过程中发生错误系统会将事务回滚Rollback到开始前的状态就像该事务从未执行过一样。2. 一致性Consistency事务开始前和结束后数据库的完整性没有被破坏。所有写入的数据必须符合预设的规则如数据类型、约束、触发器等确保数据的精确性、串联性和业务逻辑的正确性。3. 隔离性Isolation允许多个事务并发访问同一份数据时彼此之间不会互相干扰。通过事务隔离级别来避免因交叉执行导致的数据不一致问题。隔离级别包括读未提交Read Uncommitted读提交Read Committed可重复读Repeatable Read串行化Serializable4. 持久性Durability事务一旦提交其对数据库的修改就是永久性的。即使系统发生故障如断电、崩溃已提交的数据也不会丢失。ACID 之间的关系原子性、隔离性、持久性是因一致性是果。正是由于事务具备了原子、隔离、持久三大特性才能最终保证数据库在事务执行前后始终处于一致状态。事务的本质事务不仅仅是SQL语句的集合更是一种从业务逻辑出发、在数据库层面保证数据正确性与完整性的机制。它的存在使得开发者可以将复杂的多步操作作为一个不可分割的单元来执行从而简化并发控制和错误恢复的处理。3.事务的必要性当业务操作涉及操作量大、复杂度高的数据时通常需要多条SQL共同完成例如银行转账先查询、再扣款、再加款。这些SQL在逻辑上相互依赖只有组合在一起才有业务意义因此必须作为一个整体进行管理。在高并发场景下多个事务可能同时访问同一份数据若不加以控制会引发数据不一致、执行失败等问题。4.事务的版本支持在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务 MyISAM 不支持。mysql show engines \G -- 行显示 *************************** 1. row *************************** Engine: InnoDB -- 引擎名称 Support: DEFAULT -- 默认引擎 Comment: Supports transactions, row-level locking, and foreign keys -- 描述 Transactions: YES -- 支持事务 XA: YES Savepoints: YES -- 支持事务保存点 *************************** 5. row *************************** Engine: MyISAM Support: YES Comment: MyISAM storage engine Transactions: NO -- MyISAM不支持事务 XA: NO Savepoints: NO5.事务的提交方式事务的提交方式常见的有两种自动提交手动提交查看事务提交方式show variables like autocommit;修改事务提交方式set autocommit0; #禁止自动提交 set autocommit1; #开启自动提交6.事务操作演示准备工作将mysql的默认隔离级别设置成读未提交便于演示具体操作后面专门会讲现在以使用为主设置隔离级别成功后需要quit退出MySQL再重新登入创建演示表create table if not exists account( id int primary key, name varchar(50) not null default , blance decimal(10,2) not null default 0.0 )ENGINEInnoDB DEFAULT CHARSETUTF8;①事务的常规操作查看连接mysqld(服务器)的mysql(客户端)数量show processlist;我们启动两个终端两个客户端一个执行事务操作一个查看用户表信息左边使用begin 或 start transaction启动一个事务在左终端插入一条数据由于我们将隔离等级设置成读未提交所以在左终端使用commit提交之前右终点就能查看到事务插入的信息然后我们在事务中使用savepoint命令创建一个保存点然后继续插入然后我们可以在事务中使用rollback加上保存点名就可以滚回到保存点这时在右终端查看数据时就看不到我们插入的第二条信息了如果直接使用rollback不加保存点名则直接回滚到事务开始小结begin 或 start transaction命令可以启动一个事务。savepoint 保存点命令可以在事务中创建指定名称的保存点。rollback to 保存点命令可以让事务回滚到指定保存点。rollback命令可以直接让事务回滚到最开始。commit命令可以提交事务提交事务后就不能回滚了。②演示原子性如果左终端的事务在提交之前因为某些原因与MySQL断开连接那么MySQL会自动让事务回滚到开始并且提交③演示持久性当事务commit提交之后即使左终端因为某些原因断开了与MySQL的连接已经提交的数据不会回滚注使用启动事务都必须要手动使用commit命令提交数据才会被持久化与autocommit无关④单条SQL与事务的关系全局变量autocommit影响的是单条SQL语句InnoDB中的每一条SQL都会默认被封装成事务autocommit为ON则单条SQL语句执行后会自动被提交如果为OFF则SQL语句执行后需要使用commit进行手动提交当autocommit设置为自动提交时不需要commit只要执行完单条SQL那么数据就已经具备了持久化我们前面的博客的所有数据操作SQL都可以看作是自动提交的单条事务SQL当autocommit设置为手动提交时不论是否使用begin启动事务所有的单条SQL都看作是事务只有手动执行commit后数据才会持久化举例说明下面两种操作效果相同二、事务的隔离级别1.认识隔离级别隔离性的由来MySQL服务可能同时被多个客户端进程线程访问每个访问都以事务方式进行。一个事务由多条SQL构成存在执行前、执行中、执行后三个阶段。原子性保证用户层面看到的是事务要么尚未执行、要么已经完成的状态执行中出现问题可以随时回滚。然而所有事务都有执行过程当多个事务各自执行多条SQL时仍可能出现互相影响的情况例如多个事务同时访问同一张表甚至同一行记录。为了保证事务执行过程中尽量不受干扰数据库引入了隔离性这一重要特征。隔离级别的概念由于实际应用中允许事务受到不同程度的干扰数据库进一步提出了隔离级别的概念以便在不同场景下平衡数据一致性与并发性能。隔离级别主要解决的是读写并发带来的数据不一致问题——即当一个事务在读取数据时另一个事务可能正在修改同一份数据需要根据隔离级别来决定读操作能看到什么程度的数据。对于写写并发由于两个事务同时对同一数据进行修改必然产生冲突无论隔离级别如何都必须通过锁机制强制串行执行以保证数据正确性而对于读读并发多个事务同时读取数据不会相互影响因此无需任何隔离措施。四种隔离级别读未提交Read Uncommitted在该隔离级别下所有事务都可以看到其他事务尚未提交的执行结果。这相当于没有任何隔离性会引发脏读、幻读、不可重复读等多种并发问题实际生产中几乎不使用仅在极少数极端场景下才有应用。读提交Read Committed该隔离级别满足隔离的简单定义一个事务只能看到其他已提交事务所做的改变。它是大多数数据库的默认隔离级别但不是MySQL的默认级别。这种级别会引起不可重复读问题即同一事务中多次执行SELECT可能得到不同的结果。可重复读Repeatable Read这是MySQL的默认隔离级别。它确保同一个事务在执行过程中多次读取操作数据时会看到同样的数据行从而解决了不可重复读的问题。但该级别下仍然存在幻读问题。串行化Serializable这是事务的最高隔离级别。它通过强制事务排序使事务之间不可能相互冲突从而解决了幻读问题。实现方式是在每个读取的数据行上加共享锁但会导致超时和锁竞争问题该级别过于极端实际生产中基本不使用。隔离级别的实现方式隔离性基本上都是通过锁机制实现的不同的隔离级别对锁的使用方式不同。常见的锁类型包括表锁行锁读锁共享锁写锁排他锁间隙锁GAPNext-Key锁GAP锁与行锁的组合2.隔离级别的查看与设置虽然一个稳定的数据库通常会选择一种默认隔离级别但默认级别可能无法满足上层业务需求因此数据库提供了上述四种隔离级别供用户根据实际场景自行选择和设置。①查看全局隔离级别select global.transaction_isolation;②查看会话隔离级别select session.transaction_isolation; #或者 select transaction_isolation;③设置会话隔离级别set session transaction isolation level {read uncommitted/read committed/repeatable read/serializable};设置会话的隔离级别只影响到当前会话新建的对话仍然采用全局隔离级别④设置全局隔离级别set global transaction isolation level {read uncommitted/read committed/repeatable read/serializable};设置全局隔离级别时修改后的值不会影响当前已经存在的会话只有新启动的会话才会生效。也就是说每个会话在启动时会将当前的全局隔离级别作为其初始值之后该会话的隔离级别便与全局设置无关了。3.四种隔离级别演示①读未提交首先启动两个终端隔离级别都设置成读未提交接着双方都启动一个事务左边的事务插入数据后没有提交右边的事务就已经能看到插入结果了②读已提交首先将两个终端的隔离级别都设置成读已提交两个终端都启动一个事务左边事务添加的数据在没有提交之前右边事务无法看到当左端事务commit提交之后右端事务才能看到添加后的数据③可重复读首先将两个终端的隔离级别都设置为可重复读两个终端都启动事务左端事务添加的数据在commit提交之前右端事务看不到数据这和上面是一样的由于隔离级别是可重复读即使左端事务提交了修改右端事务仍然看不到数据只有右端事务也提交右端事务结束后才能看到左端事务添加的数据④串行化首先把两个终端的隔离级别都设置为串行化如果这两个事务中有一个事务要对表进行写操作那么这个事务立即被阻塞只有访问这张表的其它事务都提交后这个被阻塞的事务才会被唤醒一旦一个事务对表进行CURD操作时此事务会被放入等待队列被阻塞直到另一个事务提交但是如果此事务阻塞时间过长将会由于锁等待超时退出当前事务等待时间是可以修改的⑤隔离级别总结1. 读未提交Read Uncommitted特点一个事务在执行过程中另一个事务能够立即看到该事务未提交的数据。实现方式几乎没有加锁并发效率高但问题严重。存在的问题脏读一个事务读到另一个未提交事务的更新数据不可重复读、幻读适用场景实际生产中几乎不使用仅在极少数极端场景下有应用。2. 读提交Read Committed特点一个事务只能看到其他已提交事务所做的改变。实现方式读操作不加锁使用MVCC写操作加锁。存在的问题不可重复读同一事务内多次执行SELECT可能得到不同结果因为其他事务提交了修改说明这是大多数数据库的默认隔离级别但不是MySQL的默认级别。3. 可重复读Repeatable Read特点确保同一事务在执行过程中多次读取操作数据时会看到同样的数据行。实现方式读操作不加锁使用MVCC写操作加锁。解决的问题解决了不可重复读问题。存在的问题在一般数据库中会存在幻读问题但MySQL在该级别下通过Next-Key锁GAP锁行锁解决了幻读问题说明这是MySQL的默认隔离级别一般情况下不建议修改。4. 串行化Serializable特点通过强制事务排序使事务之间不可能相互冲突。实现方式读操作加共享锁多个事务可并发读写操作加排他锁写操作会阻塞其他事务解决的问题解决了脏读、不可重复读、幻读所有问题。缺点会导致超时和锁竞争并发性能极低实际生产中基本不使用。总结核心问题问题定义特点脏读一个事务在执行中读到另一个执行中事务的更新或其他操作但未commit的数据读到未提交的数据不可重复读同一事务内同样的查询条件在不同时间段读取到了不同的值由于其他事务修改或删除了数据重点在修改和删除读出的值不一样幻读同一事务内同样的查询条件多次读取时记录数不一致由于其他事务插入了新数据重点在新增读出的记录数不一样幻读说明总述幻读是指在同一个事务中使用相同的查询条件前后两次执行时由于其他事务插入了新数据导致读出的记录数不一致仿佛出现了幻觉它本质上属于不可重复读的一种特殊情形。详细说明在可重复读隔离级别下一个事务无论何时执行相同的查询看到的数据都是一致的这就是“可重复读”的含义。但是一般的数据库在可重复读级别下并无法阻止其他事务插入新数据。原因在于隔离性主要是通过给已有数据加锁来实现的而新插入的数据在插入前并不存在所以普通的锁无法提前锁定一个“还不存在的记录”这就导致了一个问题——一个事务在查询时可能会读到另一个事务刚刚插入的新数据从而使前后两次查询的结果记录数不一致就好像凭空多出了几条记录这种现象就叫做幻读。MySQL 在可重复读级别下通过使用Next-Key 锁即间隙锁 GAP 与行锁的组合解决了幻读问题。这种锁机制不仅能锁定已有的数据行还能锁定数据行之间的“间隙”从而防止其他事务在这个间隙中插入新数据保证了查询结果的一致性。四种隔离级别对比隔离级别选择原则隔离级别越严格安全性越高但数据库并发性能越低因为锁竞争越激烈需要在安全性与并发性能之间寻找平衡点MySQL默认使用可重复读级别一般情况下不建议修改数据库提供四种隔离级别供用户根据业务场景自行选择和设置4.一致性当数据库只包含事务成功提交的结果时数据库就处于一种一致性状态而事务执行完的结果就是使数据库从一个一致性状态变到另一个一致性状态一致性需要原子性来保证事务执行过程中如果发生错误则已经做出的操作需要回滚到最开始状态就像该事务没有执行过一样一致性需要持久性来保证事务提交后对数据的修改必须是永久的即使服务器异常退出也不会丢失一致性需要隔离性来保证多个事务并发执行时不会有数据不一致的问题一致性也与用户的业务逻辑强相关如果用户的业务逻辑不合理或存在问题那么可能也会让数据库处于不一致状态三、隔离性的底层实现1.数据库的并发场景数据库并发场景主要分为以下三类读-读并发不存在任何问题不需要并发控制。读-写并发这是数据库中最常见的并发场景存在线程安全问题可能引发事务隔离性问题如脏读、不可重复读、幻读等异常。在解决读-写并发时既要考虑线程安全也要兼顾并发性能。写-写并发存在线程安全问题可能导致更新丢失问题具体包括两类第一类更新丢失回滚丢失一个事务的回滚覆盖了另一个已提交事务的更新数据第二类更新丢失覆盖丢失一个事务的提交覆盖了另一个已提交事务的更新数据。2. 多版本并发控制MVCC概述多版本并发控制Multi-Version Concurrency Control简称MVCC是一种用于解决读-写冲突的无锁并发控制机制。它通过为每个修改保存多个版本使读操作无需等待写操作写操作也无需等待读操作从而在保证事务隔离性的同时提升数据库的并发性能。3.MVCC的核心实现依赖理解MVCC需要掌握以下三个前提知识三个记录隐藏字段undo日志Read View读视图3.1 三个记录隐藏字段在 InnoDB 存储引擎中数据库表中的每条记录除了用户自定义的字段外还包含若干隐藏字段用于支持 MVCC 和事务回滚等机制。主要包括DB_TRX_ID6 字节记录最近一次修改插入或更新该记录的事务 ID。DB_ROLL_PTR7 字节回滚指针指向该记录的上一个版本通常存储在 undo log 中用于构建版本链。DB_ROW_ID6 字节隐含的自增 ID。如果表没有定义主键InnoDB 会自动以该字段生成聚簇索引若表有主键则 DB_ROW_ID 不会作为索引使用。此外还有一个删除标志delete flag隐藏字段用于标记记录是否被删除当执行 DELETE 或更新操作时并不立即物理删除而是修改该标志便于事务回滚和 MVCC 读取历史版本。举例说明假设我们向一张只有姓名和年龄字段的表中插入一条记录该插入操作的事务ID为9那么系统会自动为该记录添加三个隐藏字段DB_TRX_ID填入9表示创建该记录的事务IDDB_ROW_ID填入1作为隐式自增主键因为这是插入的第一条记录DB_ROLL_PTR设置为 null表示该记录没有历史版本这三个隐藏字段是 MVCC 实现的核心基础此外还存在其他隐藏字段如删除标志但图中未画出。3.2 undo日志undo 日志的基本概念undo log回滚日志是 MySQL 三大日志之一主要用于保证事务的原子性支持对已执行操作进行回滚支持 MVCC记录数据的历史版本构建版本链undo log 可以理解为 MySQL 内存中的一段缓冲区日志缓冲区用于临时存储日志数据后续在适当时机刷新到磁盘。MVCC 模拟版本链的形成以更新操作为例演示版本链的构建过程1. 初始插入事务 ID 为 9 的事务插入一条记录张三28记录中DB_TRX_ID 9DB_ROW_ID 1隐式主键DB_ROLL_PTR null无历史版本2. 第一次更新事务 10事务 10 将 name 从“张三”改为“李四”先给记录加行锁修改前将原记录拷贝到 undo log写时拷贝修改原记录的name为“李四”修改原记录的DB_TRX_ID 10修改原记录的DB_ROLL_PTR指向 undo log 中副本的地址事务 10 提交释放锁此时最新记录为“李四”版本链形成最新记录 → undo log 副本张三3. 第二次更新事务 11事务 11 将 age 从 28 改为 38先给最新记录加行锁修改前将当前最新记录拷贝到 undo log采用头插方式修改原记录的age为 38修改原记录的DB_TRX_ID 11修改原记录的DB_ROLL_PTR指向新副本的地址事务 11 提交释放锁此时版本链为最新记录李四38→ 上一版本李四28→ 最初版本张三28核心机制每次修改都采用写时拷贝将当前版本拷贝到 undo log 后再修改多个版本通过回滚指针形成历史版本链回滚就是用 undo log 中的历史版本覆盖当前数据insert 与 delete 的版本处理1. delete 操作记录被删除时并非物理删除先将记录拷贝到 undo log将隐藏字段中的删除标志delete flag设置为 1回滚时将标志改回 0数据恢复2. insert 操作插入前没有数据因此没有历史版本为支持回滚插入的数据也会被放入 undo log事务提交后undo log 中的 insert 记录可以被清空3. select 操作select 本身不修改数据因此无需为 select 维护版本但 select 读取时可以选择读取最新版本当前读或历史版本快照读当前读 vs 快照读类型定义操作示例当前读读取最新的记录增INSERT、删DELETE、改UPDATE、以及SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE快照读读取历史版本普通的SELECT语句在特定隔离级别下当前读需要加锁以保证数据一致性快照读通过 MVCC 读取历史版本无需加锁可实现读写并行提高并发性能隔离级别与读取方式的关系读未提交RU和串行化Serializable下SELECT为当前读读提交RC和可重复读RR下SELECT可能为当前读或快照读具体取决于隔离级别的实现机制隔离性存在的根本原因事务从BEGIN到CURD到COMMIT存在执行前、执行中、执行后三个阶段。多个事务并发执行时CURD 操作会交织在一起。为了在并发场景下保证每个事务“有先有后”并看到自己该看到的内容就需要引入隔离性与隔离级别。MVCC 通过无锁的快照读使得读操作无需等待写操作写操作也无需等待读操作在保证隔离性的同时大大提升了数据库的并发性能。3.3 Read View读视图Read View 的定义与作用Read View读视图是 MySQL 在事务执行快照读时生成的一个数据结构用于判断当前事务能够看到数据版本链中的哪一个版本。它本质上是事务可见性判断的依据生成时机是事务首次进行快照读的时刻而非事务启动时。Read View 在 MySQL 源码中是一个类其核心成员包括字段含义m_ids生成 Read View 时系统中活跃事务 ID 的列表即尚未提交的事务m_up_limit_idm_ids列表中的最小事务 ID也称为“低水位”m_low_limit_id生成 Read View 时系统尚未分配的下一个事务 ID即当前已出现的最大事务 ID 1也称为“高水位”m_creator_trx_id创建该 Read View 的当前事务的 ID下面是ReadView中的部分源码字段class ReadView { private: /** 高水位大于等于这个ID的事务均不可见*/ trx_id_t m_low_limit_id; /** 低水位小于这个ID的事务均可见 */ trx_id_t m_up_limit_id; /** 创建该 Read View 的事务ID*/ trx_id_t m_creator_trx_id; /** 创建视图时的活跃事务id列表*/ ids_t m_ids; /** 配合purge标识该视图不需要小于m_low_limit_no的UNDO LOG * 如果其他视图也不需要则可以删除小于m_low_limit_no的UNDO LOG*/ trx_id_t m_low_limit_no; /** 标记视图是否被关闭*/ bool m_closed; };可见性判断规则当事务进行快照读时会遍历版本链中的各个版本每个版本记录都有DB_TRX_ID创建或最后修改该版本的事务 ID。通过将DB_TRX_ID当前事务的 ID与 Read View 的字段进行比较判断该版本是否可见若DB_TRX_ID m_up_limit_idm_ids列表中的最小事务 ID说明该版本由生成 Read View 时已经提交的事务修改因此可见。若DB_TRX_ID m_low_limit_id当前已出现的最大事务 ID 1说明该版本由生成 Read View 时尚未启动的事务修改因此不可见。若m_up_limit_id DB_TRX_ID m_low_limit_id说明该版本由生成 Read View 时正处于活跃状态或刚好提交的事务修改。此时如果DB_TRX_ID在m_ids列表中表示该事务仍然活跃未提交则不可见如果DB_TRX_ID不在m_ids列表中表示该事务在生成 Read View 前已经提交则可见。特殊规则若DB_TRX_ID m_creator_trx_id即该版本由当前事务自己修改则可见。判断流程源码逻辑id当前事务的 ID在changes_visible函数中判断流程如下若id m_up_limit_id或id m_creator_trx_id返回true可见。若id m_low_limit_id返回false不可见。若m_ids为空返回true可见。否则在m_ids列表中二分查找id若找到表示该事务仍活跃返回false不可见若未找到返回true可见。若当前版本不可见则通过回滚指针DB_ROLL_PTR沿版本链向下遍历继续判断上一个版本直到找到可见版本为止。示例说明假设初始记录为张三28nameageDB_TRX_ID(创建该记录的事务ID)DB_ROW_ID(隐式主键)DB_ROLL_PTR(回滚指针)张三28null1null依次启动事务操作时间自顶向下事务1 [id1]事务2 [id2]事务 3 [id3]事务4 [id4]事务开始事务开始事务开始事务开始.........修改且已提交进行中快照读进行中.........其中事务 4 在事务 2 快照读之前已提交事务 1 和事务 3 仍在活跃。事务 2 进行快照读时生成的 Read Viewm_ids [1, 3]活跃事务m_up_limit_id 1m_ids列表中的最小事务 IDm_low_limit_id 5最大事务 ID 为 4下一个为 5m_creator_trx_id 2当前事务的 ID此时版本链是判断是否能看到事务 4 提交后的版本4 1否。4 5否。4在m_ids中吗否因为事务 4 已提交。结论可见。因此事务 2 的快照读能够看到事务 4 提交的最新版本。总结Read View 是快照读的可见性依据在事务首次快照读时生成之后不再变化。通过m_up_limit_id低水位和m_low_limit_id高水位将事务 ID 划分为三个区间小于低水位已提交可见大于等于高水位未启动不可见位于中间通过m_ids判断是否活跃活跃则不可见否则可见。版本链遍历保证了事务只看到自己或已提交事务的修改从而实现 MVCC 的无锁快照读提升并发性能。4.RR 与 RC 的本质区别4.1 核心区别隔离级别Read View 生成时机快照读可见性可重复读RR事务中第一次快照读时生成一个 Read View之后整个事务内的所有快照读都复用这个 Read View只能看到 Read View 生成前已提交的事务所做的修改之后其他事务提交的修改不可见读提交RC事务中每次快照读都会重新生成一个新的 Read View每次快照读都能看到当前已提交的最新数据因此可能看到其他事务在不同时间提交的修改4.2 RR 级别下快照读的特点首次快照读决定后续事务中第一次执行快照读如普通SELECT时MySQL 会生成一个 Read View记录此时系统中所有活跃事务的 ID。后续所有快照读都使用同一个 Read View。对后续提交的修改不可见如果其他事务在第一次快照读之后提交了修改由于 Read View 未更新这些修改对当前事务的快照读仍然不可见。这保证了同一事务内多次快照读结果一致即可重复读。当前读与快照读的分离在 RR 级别下使用SELECT ... LOCK IN SHARE MODE或SELECT ... FOR UPDATE执行的是当前读读取最新数据会加锁不受快照读 Read View 的限制可以立即看到其他事务已提交的修改。4.3 RC 级别下快照读的特点每次快照读独立生成 Read View事务中每次执行快照读普通SELECT都会重新生成一个 Read View其中包含当前系统最新的活跃事务信息。可见其他事务的提交由于每次快照读都基于最新的 Read View因此如果其他事务在两次快照读之间提交了修改第二次快照读就能看到这些修改导致同一事务内多次读取结果不同即不可重复读。4.4 RR 与 RC 对不可重复读的影响RR 级别通过复用同一个 Read View避免了不可重复读问题。RC 级别每次快照读都生成新的 Read View所以无法避免不可重复读。4.5 快照读与当前读的补充说明快照读普通SELECT通过 MVCC 读取历史版本无锁。当前读SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE、INSERT、UPDATE、DELETE等总是读取最新版本需要加锁。在 RR 级别下如果事务在第一次快照读之后其他事务提交了修改快照读仍然看不到但当前读可以看到。示例首先将两个终端的隔离级别都设置为可重复读两个终端都启动事务左端事务添加的数据在commit提交之前右端事务看不到数据由于隔离级别是可重复读即使左端事务提交了修改右端事务使用普通SELECT仍然看不到数据快照读但是左端事务提交了修改右端事务使用SELECT ... LOCK IN SHARE MODE可以看到数据当前读4.6 总结RR 与 RC 的本质区别在于 Read View 的生成策略RR事务中第一个快照读创建 Read View之后复用保证可重复读。RC每次快照读都创建新的 Read View因此可能产生不可重复读。这一差异直接决定了两种隔离级别在并发读写时对数据一致性的保障程度以及性能上的权衡。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2440093.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!