為什麼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 secThreads: 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使用什麼策略刷臟頁刷緩存到磁碟的,另外也注意一下系統與硬碟也有緩存哈。不說了,我下地鐵了,,,
推薦閱讀: