不可重复读和幻影读的区别是什么?
我读过维基百科上的隔离(数据库系统)文章,但我有一些怀疑。在下面的例子中,将会发生什么:不可重复读取和幻影读取?
# # # #事务
SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1
# # # #输出:
1----MIKE------29019892---------5000
# # # #事务B
UPDATE USERS SET amount=amount+5000 where ID=1 AND accountno=29019892;
COMMIT;
# # # #事务
SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1
另一个疑问是,在上面的示例中,应该使用哪个隔离级别?,为什么?
摘自维基百科(其中有非常详细的例子):
不可重复读取发生在这样的情况下:在事务过程中,一行被检索了两次,并且两次读取之间行中的值不同。
and
在事务过程中,当执行两个相同的查询,而第二个查询返回的行集合与第一个查询不同时,就会发生幻影读。
简单的例子:
User A runs the same query twice.
In between, User B runs a transaction and commits.
Non-repeatable read: The A row that user A has queried has a different value the second time.
Phantom read: All the rows in the query have the same value before and after, but different rows are being selected (because B has deleted or inserted some). Example: select sum(x) from table; will return a different result even if none of the affected rows themselves have been updated, if rows have been added or deleted.
在上面的示例中,要使用哪个隔离级别?
您需要什么样的隔离级别取决于您的应用程序。“更好的”隔离级别(例如降低并发性)的成本很高。
在您的示例中,您不会进行幻影读取,因为您只从单行(由主键标识)进行选择。您可以使用不可重复的读取,因此如果这是一个问题,您可能希望有一个隔离级别来防止这种情况。在Oracle中,事务A也可以发出SELECT FOR UPDATE,那么事务B在A完成之前不能更改行。
Non-repeatable read(fuzzy read) is that a transaction reads the same row at least twice but the same row's data is different between the 1st and 2nd reads because other transactions update the same row's data and commit at the same time(concurrently).
Phantom read is that a transaction reads the same table at least twice but the number of the same table's rows is different between the 1st and 2nd reads because other transactions insert or delete rows and commit at the same time(concurrently).
我用MySQL和2个命令提示符尝试了不可重复读取和幻影读取。
对于不可重复读和幻像读的实验,我设置read COMMITTED隔离级别发生不可重复读和幻像读:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
并且,我用id和名称创建了person表,如下所示。
人表:
首先,对于不可重复的读取,我在MySQL查询中执行了以下步骤:
Flow |
Transaction 1 (T1) |
Transaction 2 (T2) |
Explanation |
Step 1 |
BEGIN; |
|
T1 starts. |
Step 2 |
|
BEGIN; |
T2 starts. |
Step 3 |
SELECT * FROM person WHERE id = 2;
2 David |
|
T1 reads David . |
Step 4 |
|
UPDATE person SET name = 'Tom' WHERE id = 2; |
T2 updates David to Tom . |
Step 5 |
|
COMMIT; |
T2 commits. |
Step 6 |
SELECT * FROM person WHERE id = 2;
2 Tom |
|
T1 reads Tom instead of David after T2 commits.
*Non-repeatable read occurs!! |
Step 7 |
COMMIT; |
|
T1 commits. |
第二,对于幻影读取,我用MySQL查询执行了以下步骤:
Flow |
Transaction 1 (T1) |
Transaction 2 (T2) |
Explanation |
Step 1 |
BEGIN; |
|
T1 starts. |
Step 2 |
|
BEGIN; |
T2 starts. |
Step 3 |
SELECT * FROM person;
1 John 2 David |
|
T1 reads 2 rows. |
Step 4 |
|
INSERT INTO person VALUES (3, 'Tom'); |
T2 inserts the row with 3 and Tom to person table. |
Step 5 |
|
COMMIT; |
T2 commits. |
Step 6 |
SELECT * FROM person;
1 John 2 David 3 Tom |
|
T1 reads 3 rows instead of 2 rows after T2 commits.
*Phantom read occurs!! |
Step 7 |
COMMIT; |
|
T1 commits. |