This article will take you to understand the transactions in MySQL and talk about the implementation principle of transaction isolation. I hope it can help you!
Speaking of database transactions, a lot of transaction-related knowledge must easily pop up in everyone's mind, such as the ACID characteristics of the transaction, isolation level, and solved problems (dirty read , non-repeatable read, phantom read), etc., but few people may really know how these features of transactions are implemented and why there are four isolation levels.
Today we will first talk about the implementation principle of transaction isolation in MySQL, and we will continue to publish articles to analyze the implementation principles of other features.
Of course MySQL is extensive and profound, and omissions in the article are inevitable. Criticisms and corrections are welcome.
Explanation
MySQL's transaction implementation logic is located at the engine layer, and not all engines support transactions. The following instructions are based on the InnoDB engine. .
Isolation refers to the fact that after different transactions are submitted and executed one after another, the final effect is serial. That is to say, for a transaction, it is During the execution process, the perceived data changes should only be caused by your own operations, and there should be no data changes caused by other transactions.
Isolation solves the problem of concurrent transactions.
The simplest way to implement isolation is that each transaction is executed serially. If the previous transaction has not been completed, subsequent transactions will wait. However, this implementation method is obviously not very efficient in concurrency and is not suitable for use in actual environments.
In order to solve the above problems and achieve different levels of concurrency control, SQL standard makers have proposed different isolation levels: uncommitted read (read uncommitted), committed read (read committed), repeatable read (repeatable) read), serialized read (serializable). The most advanced isolation level is serialized read, and in other isolation levels, since transactions are executed concurrently, some problems are more or less allowed. See the following matrix table:
Isolation level (: allowed to appear, -: not allowed to appear) | Dirty Read | ##Non-repeatable read | Fantasy read | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Implementation method | |
---|---|
The transaction does not lock the data currently being read; | The transaction is updating a certain At the moment of data (that is, the moment when an update occurs), a row-level shared lock must be added to it first, and it will not be released until the end of the transaction. |
The transaction adds a | row-level shared lock to the currently read data (locked only when read), once the row is read, the row-level shared lock is immediately released; The moment the transaction updates a certain data (that is, the moment the update occurs), it must first add a row-level exclusive lock , not released until the end of the transaction. |
The moment a transaction reads certain data (the moment it starts reading), it must first add | lines to it Level shared lock will not be released until the end of the transaction; When a transaction updates certain data (that is, the moment the update occurs), it must first add a row-level exclusive lock , It is not released until the end of the transaction. |
When a transaction reads data, it must first add a | table-level shared lock until the transaction ends Released only after When a transaction updates data, it must first add a table-level exclusive lock , and it will not be released until the end of the transaction. |
事务隔离级别 | 实现方式 |
---|---|
未提交读(RU) | 事务对当前被读取的数据不加锁,都是当前读; 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级共享锁,直到事务结束才释放。 |
提交读(RC) | 事务对当前被读取的数据不加锁,且是快照读; 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁(Record),直到事务结束才释放。 |
可重复读(RR) | 事务对当前被读取的数据不加锁,且是快照读; 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁(Record,GAP,Next-Key),直到事务结束才释放。 通过间隙锁,在这个级别MySQL就解决了幻读的问题 通过快照,在这个级别MySQL就解决了不可重复读的问题 |
序列化读(S) | 事务在读取数据时,必须先对其加表级共享锁 ,直到事务结束才释放,都是当前读; 事务在更新数据时,必须先对其加表级排他锁 ,直到事务结束才释放。 |
可以看到,InnoDB通过MVCC很好的解决了读写冲突的问题,而且提前一个级别就解决了标准级别下会出现的幻读问题,大大提升了数据库的并发能力。
不可重复读:前后多次读取一行,数据内容不一致,针对其他事务的update和delete操作。为了解决这个问题,使用行共享锁,锁定到事务结束(也就是RR级别,当然MySQL使用MVCC在RC级别就解决了这个问题)
幻读:当同一个查询在不同时间生成不同的行集合时就是出现了幻读,针对的是其他事务的insert操作,为了解决这个问题,锁定整个表到事务结束(也就是S级别,当然MySQL使用间隙锁在RR级别就解决了这个问题)
网上很多文章提到幻读和提交读的时候,有的说幻读包括了delete的情况,有的说delete应该属于提交读的问题,那到底真相如何呢?我们实际来看下MySQL的官方文档(如下)
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a
SELECT
) is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html
可以看到,幻读针对的是结果集前后发生变化,所以看起来delete的情况应该归为幻读,但是我们实际分析下上面列出的标准SQL在RR级别的实现原理就知道,标准SQL的RR级别是会对查到的数据行加行共享锁,所以这时候其他事务想删除这些数据行其实是做不到的,所以在RR下,不会出现因delete而出现幻读现象,也就是幻读不包含delete的情况。
网上很多文章会说MVCC或者MVCC+间隙锁解决了幻读问题,实际上MVCC并不能解决幻读问题。如以下的例子:
begin; #假设users表为空,下面查出来的数据为空 select * from users; #没有加锁 #此时另一个事务提交了,且插入了一条id=1的数据 select * from users; #读快照,查出来的数据为空 update users set name='mysql' where id=1;#update是当前读,所以更新成功,并生成一个更新的快照 select * from users; #读快照,查出来id为1的一条记录,因为MVCC可以查到当前事务生成的快照 commit;
可以看到前后查出来的数据行不一致,发生了幻读。所以说只有MVCC是不能解决幻读问题的,解决幻读问题靠的是间隙锁。如下:
begin; #假设users表为空,下面查出来的数据为空 select * from users lock in share mode; #加上共享锁 #此时另一个事务B想提交且插入了一条id=1的数据,由于有间隙锁,所以要等待 select * from users; #读快照,查出来的数据为空 update users set name='mysql' where id=1;#update是当前读,由于不存在数据,不进行更新 select * from users; #读快照,查出来的数据为空 commit; #事务B提交成功并插入数据
注意,RR级别下想解决幻读问题,需要我们显式加锁,不然查询的时候还是不会加锁的。
原文地址:https://segmentfault.com/a/1190000025156465
作者: X先生
【相关推荐:mysql视频教程】
The above is the detailed content of A brief analysis of the transaction isolation level in MySQL and a discussion of its implementation principles. For more information, please follow other related articles on the PHP Chinese website!