標籤:

為什麼MySQL在innodb引擎中即使使用了MVCC機制仍然會出現丟失更新?

環境:

mysql&> status
--------------
c:/Program Files/wamp/bin/mysql/mysql5.6.17/bin/mysql.exe Ver 14.14 Distrib 5.6.17, for Win64 (x86_64)

Connection id: 4
Current database: hello_mysql
Current user: root@localhost
SSL: Not in use
Using delimiter: ;
Server version: 5.6.17 MySQL Community Server (GPL)
Protocol version: 10
Connection: localhost via TCP/IP
Server characterset: utf8
Db characterset: utf8
Client characterset: utf8
Conn. characterset: utf8
TCP port: 3306
Uptime: 13 hours 8 min 25 sec

Threads: 2 Questions: 400 Slow queries: 0 Opens: 104 Flush tables: 1 Open tables: 96 Queries per second avg: 0.008
--------------

mysql&> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

測試:

CREATE DATABASE IF NOT EXISTS `hello_mysql`;
USE `hello_mysql`;

CREATE TABLE IF NOT EXISTS `foo` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `foo` (`a`, `b`) VALUES
(1, 7),
(2, 3);

事務1:

start transaction;
select * from foo where a = 1 ;

事務2:

start transaction;
select * from foo where a = 1 ;

兩個事務獲取的b的值都是7,現假設需要更新b的值,需要根據當前的b值和業務邏輯計算得到更新的b值

事務1:

update foo set b = 6 where a = 1;
-- 假設6是根據b=7和業務邏輯計算得出
commit;

事務1提交成功,事務2:

update foo set b = 6 where a = 1;
-- 假設6是根據b=7和業務邏輯計算得出
commit;

此時事務2仍提交成功。

我的問題是,Innodb引擎已經具有MVCC機制,通過向每行數據添加了隱藏的事務版本號欄位以控制並發讀寫,在這樣的情況下,為什麼仍然需要在業務層中(如Hibernate中通過向實體類添加version欄位避免上述情況發生)自行實現版本控制解決丟失更新的問題?還是說MVCC不是用來解決丟失更新的?


1. 這樣OK
Transaction 1 | Transaction 2
T0: Update |
T1: Commit |
T2: |Update
T3: |Commit

2. 這樣transaction 2 會等待
Transaction 1 | Transaction 2
T0: Update |
T1: |Update
T2 |(Wait for lock) --time out--&> ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
T3: Commit |

單純Select對鎖沒影響的吧。


mvcc在innodb中只負責解決讀寫衝突,把普通select語句變成快照讀。寫衝突仍然是靠鎖來解決的。因此要解決你說的丟失更新,要用select...for update主動加x鎖。

當然mvcc不是說完全不能解決丟失更新,比如postgresql的serializable隔離級別下,遇到寫寫衝突直接向客戶端返回異常,保證只有一個事務可以更新成功。


朋友你的理解有點偏差,你的測試,autocommit?

mvcc是多版本協議,它的基本療效是讓並發控制爽,嚴格來說mysql並沒有完全貫徹二階段封鎖的要求,另外加了自己的東西來保證事務可串列化提高並發,對於不同事務對同一個記錄的並發讀取更新,保存舊版本回滾指針這些主要是為了方便回滾,萬一有人幹了壞事不承認又不想擔責把東西還原回去呢?另外RR隔離級別下innodb默認是next key lock,即record lock + gap lock來實現所謂行級鎖。mysql有個主線程專門在後面偷偷幹壞事,譬如purge無用的undo頁啊做checkpoint啊合併插入緩衝啊之類,你可以理解它為mvcc的助手,清道工行政工作身兼多職。

你說的丟失更新是另外一個討論東東,可以去了解一下關係型資料庫怎麼去做checkpoint而mysql怎麼做的fuzzy checkpoint怎麼做崩潰恢復的,有哪些參數影響mysql使用什麼策略刷臟頁刷緩存到磁碟的,另外也注意一下系統與硬碟也有緩存哈。

不說了,我下地鐵了,,,


推薦閱讀:

TAG:資料庫 | MySQL |