零、概览
当我们谈论隔离级别的时候,我们在谈论什么?看官网的innodb engine的文档目录(本文涉及的内容主要是InnoDB Multi-Versioning和InnoDB Locking and Transaction Model两个章节),谈隔离级别的时候,主要谈不同隔离级别下的读写问题。
隔离性描绘的是当前事务能够看到其他事务的中间状态的能力,这个能力通过设置当前事务的隔离级别来描绘。不同level的隔离级别描绘的隔离能力是不同的,本质是不同的隔离级别在读时走MVCC建立和读取快照read view的策略不同、写时加锁的策略也不同。
所以MySQL官网文档中给出了两种读——一致性读(普通select一致性读在不同的隔离级别下走MVCC的策略)和锁定读(select for share、select for update和update、delete锁定读在不同的隔离级别下写数据记录的加锁策略)。
一、本文要讨论的内容
所以本文要讨论的读(一致性读)和写(锁定读、update、delete)内容范围如下:
- 读:Consistent reads 一致性读(consistent non-lock read一致性非锁定读简称),也就是不同隔离级别下的MVCC策略
- RC:读不加锁写加锁,select的时候读线程不会被其他写线程加锁阻塞,所以在RC隔离级别下select防脏读,要讨论读什么时候读取最新的记录、什么时候应该去读回滚段版本链最新版本数据
- RR:读不加锁写加锁,select的时候读线程不会被其他写线程加锁阻塞,所以在RR隔离级别下做到防脏读、防不可重复读和防虚读,要讨论什么时候可以读最新的记录,什么时候读取回滚段版本链的记录,以及怎么选择版本链数据记录的版本
- 两个核心知识点:回滚段中由undo log组成的版本链和快照read view
- 写:locking reads 和 update、delete,也就是不同隔离级别下写加锁的策略。谈隔离级别为什么要讨论写呢?
- RC:RC可以读到已提交的事务,也就意味着在同一个事务中多次update、delete、select for share、select for update影响的记录不会包括正在活动的未提交事务,对于其他未提交的事务,会对其当前正在操作的记录的索引加写锁,当前事务锁定读或写会被阻塞等待,防止了读到未提交;已提交的事务不加锁,所以可以读到已提交的事务结果
- RR:RR除了防读未提交,还可重复读和防虚读,也就意味着在同一个事务中多次update、delete、select for share、select for update影响的记录应该保持一致,这个一致不仅仅是影响的记录相同、不能增减记录,还包括记录的数据没被其他事务修改,这也是在《MySQL隔离级别之加锁策略篇》要分析的内容
- 例如update table_name set name='new name' where id>10,id是主键,同一个事务中第一次update影响的记录集合是{11,15,17},RR下第二次同样的update也只能影响{11,15,17}的记录集,否则就破坏了RR的性质。怎么做到的,《MySQL隔离级别之加锁策略篇》再展开分析
- 两个核心知识点:锁(记录锁、gap lock、next-key lock)和不同隔离级别的加锁策略
二、文章结构概览和说明
本文要讨论的的两个核心观点:
- 核心观点一:讨论隔离性就是讨论不同隔离级别下的读写问题
- 核心观点二:读(MVCC)和写(加锁策略)
- 读:讨论隔离性的读就是讨论不同隔离级别下的MVCC策略
- 写:讨论隔离性的写就是讨论不同隔离级别下锁定读和写的加锁策略
为了要说清楚MySQL事务中的隔离性,对于本文要讨论的隔离性的两个核心观点并非无中生有,更不是胡编乱造,在MySQL官网文档中就已经明确提出来了。本文两个核心观点的官网内容,参见本文第三部分“隔离性读写的官网版本”和第四部分“读(MVCC)和写(加锁策略)的官网版本”。更多本文涉及的官网文档资料,参见第五部分“官网资料”,更多讨论,欢迎留言!
要讨论清楚MySQL隔离性,就要讨论清楚不同隔离级别下读的MVCC策略和写时的加锁策略。
本文只是MySQL隔离性的开头,讲清楚要讨论的内容范围,然后再分开讨论清楚,后续目录安排如下:
-
《MySQL隔离性之MVCC篇》(核心知识点:版本链和快照read view)
-
《MySQL隔离性之加锁策略篇》(核心知识点:锁和加锁策略)
三、隔离性读写的官网版本
对于本文观点“讨论隔离性就是讨论不同隔离级别下的读写问题”这一说法,并非无中生有,更不是胡编乱造,在MySQL官网文档就已经明确提出来了(官网地址:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html):
3.1 RR
- 读:Consistent reads within the same transaction read the snapshot established by the first read. This means that if you issue several plain (nonlocking) SELECT statements within the same transaction, these SELECT statements are consistent also with respect to each other.
- 写:For locking reads (SELECT with FOR UPDATE or FOR SHARE), UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition.
- For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it.
- For other search conditions, InnoDB locks the index range scanned, using gap locks or next-key locks to block insertions by other sessions into the gaps covered by the range.
3.2 RC
- 读:Each consistent read, even within the same transaction, sets and reads its own fresh snapshot.
- 写:For locking reads (SELECT with FOR UPDATE or FOR SHARE), UPDATE statements, and DELETE statements, InnoDB locks only index records, not the gaps before them, and thus permits the free insertion of new records next to locked records. Gap locking is only used for foreign-key constraint checking and duplicate-key checking.
2.3 RU和serializable
RU和Serializable在后续文章中在给出
四、读(MVCC)和写(加锁策略)的官网版本
本文中读和写的观点:
- 读:Consistent reads 一致性读(consistent non-lock read一致性非锁定读简称),也就是不同隔离级别下的MVCC策略
- 写:locking reads 和 update、delete,也就是不同隔离级别下写加锁的策略
对于本文中讨论隔离性的读(MVCC)和写(加锁策略)的观点也并非无众生有,更不是胡编乱造,在MySQL官网文档中就已经明确提出来了:
读的观点:讨论隔离性的读就是讨论不同隔离级别下的MVCC策略
- 隔离级别和一致性读的关系参见本文第二部分摘抄自MySQL官网文挡“读”部分的内容
- 一致性读和MVCC的关系:A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time.
- consistent reads:https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html
- multi-versioning :https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html
- 查看MySQL MVCC的词条更直观:This technique lets
InnoDB
transactions with certain isolation levels perform consistent read operations; that is, to query rows that are being updated by other transactions, and see the values from before those updates occurred. 官网此条地址:https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_mvcc
写的观点:讨论隔离性的写就是讨论不同隔离级别下锁定读和写的加锁策略
- 隔离级别和加锁策略的关系参见本文第二部分摘抄自MySQL官网文档“写”部分的内容
- locking reads:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html
- innodb locking:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
五、官网资料
官方文档链接地址:
- 隔离级别:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
- MVCC
- 多版本(回滚段中undo log版本链的结构):https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html
- 一致性读(RC和RR下的read view策略):https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html
- MVCC的目的:对于不同隔离级别,提供不同程度的读写并发和一致性
- 锁和锁定读
- 锁:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
- 锁定读:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html
- 不同隔离级别下的加锁策略:参看上面【隔离级别】词条的链接
- 加锁的目的:事务能够按照不同隔离级别的要求正确并发,对于RR也就是事务中多次加锁读的结果要保持一致,也就意味着事务中同一个update、delete和加锁读语句多次执行通过加锁读看到的结果都是一样的
- bug
- 事务的锁定读将当前事务之后完成的记录的较大的tx_id修改为当前事务tx_id,然后再通过MVCC就能看到更多的记录,因为本来应该大于read view中最大tx_id的事务的id被修改成了当前事务的tx_id。
- 简单来说:锁定读修改了记录的trx_id,导致一致性读走MVCC的时候破坏了RR的性质
- 参见该文最后部分示例:https://my.oschina.net/alchemystar/blog/1927425
- 本文涉及的官网文档的两个章节