mysql從零單排之快照讀與當(dāng)前讀的實(shí)現(xiàn)
了解完MVCC會(huì)不會(huì)有這樣的疑問:MVCC實(shí)現(xiàn)了讀不阻塞寫,寫不阻塞讀的高效并發(fā)控制,寫的時(shí)候讀是不是拿到實(shí)時(shí)數(shù)據(jù),讀的時(shí)候可能拿到的不是實(shí)時(shí)寫的數(shù)據(jù)?
關(guān)鍵區(qū)分:兩種讀操作
MySQL InnoDB 實(shí)際上有兩種讀模式,MVCC 只適用于其中一種:
| 讀類型 | 名稱 | 實(shí)現(xiàn)方式 | 是否阻塞寫 | 數(shù)據(jù)一致性 |
|---|---|---|---|---|
| 快照讀 | Snapshot Read (Consistent Read) | MVCC,讀歷史版本 | 不阻塞 | 事務(wù)開始時(shí)的快照 |
| 當(dāng)前讀 | Current Read (Locking Read) | 加鎖讀最新版本 | 會(huì)阻塞 | 最新已提交數(shù)據(jù) |
-- 快照讀(普通 SELECT)-- 使用 MVCC SELECT * FROM user WHERE id = 1; -- 當(dāng)前讀(加鎖 SELECT)-- 不使用 MVCC,直接讀最新版本 SELECT * FROM user WHERE id = 1 FOR UPDATE; -- 排他鎖 SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE; -- 共享鎖
你的問題:
答案取決于業(yè)務(wù)場景:
場景1:讀歷史版本就夠了(大多數(shù)場景)
銀行查詢賬戶余額(只讀場景):
事務(wù)A(轉(zhuǎn)賬):UPDATE account SET balance = 900 WHERE id = 1; -- 扣100
事務(wù)B(查詢):SELECT balance FROM account WHERE id = 1; -- 讀快照├─? 事務(wù)B 讀到的 1000元 是"過時(shí)"的嗎?技術(shù)上是的
├─? 但這是事務(wù)B 啟動(dòng)時(shí)的準(zhǔn)確快照,業(yè)務(wù)上完全合理
└─? 用戶只是查余額,不需要看到實(shí)時(shí)變化(實(shí)時(shí)變化可能還在處理中)
場景2:必須讀最新數(shù)據(jù)(需要當(dāng)前讀)
庫存扣減(不能超賣):
事務(wù)A:UPDATE stock SET count = count - 1 WHERE id = 1; -- 剩9件
事務(wù)B:SELECT count FROM stock WHERE id = 1 FOR UPDATE; -- 必須知道最新值!├─? 事務(wù)B 用 FOR UPDATE,強(qiáng)制讀最新版本(當(dāng)前讀)
├─? 如果 count 已經(jīng)是 0,事務(wù)B 就知道賣完了
└─? 這會(huì)阻塞直到事務(wù)A提交,但保證了業(yè)務(wù)正確性
一致性模型對比
┌─────────────────────────────────────────────────────────────┐ │ 一致性光譜 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 最終一致性 ?────────────────────────────? 強(qiáng)一致性 │ │ │ │ 快照讀(MVCC) 當(dāng)前讀(加鎖) │ │ ├─ 讀歷史版本 ├─ 讀最新版本 │ │ ├─ 可能"過時(shí)"但自洽 ├─ 實(shí)時(shí)但可能阻塞 │ │ └─ 適合:報(bào)表、查詢 └─ 適合:金融交易、庫存 │ │ │ └─────────────────────────────────────────────────────────────┘
為什么 MVCC 這樣設(shè)計(jì)?
┌─────────────────────────────────────────────────────────┐ │ 數(shù)據(jù)庫設(shè)計(jì)哲學(xué):不同場景需要不同的一致性級(jí)別 │ ├─────────────────────────────────────────────────────────┤ │ │ │ 場景1:淘寶商品列表頁 │ │ ├─ 100萬人同時(shí)瀏覽 │ │ ├─ 庫存數(shù)字差幾個(gè)沒關(guān)系 │ │ └─ 用 MVCC 快照讀 → 高并發(fā),不阻塞商家改庫存 │ │ │ │ 場景2:下單扣庫存 │ │ ├─ 必須精確知道當(dāng)前庫存 │ │ ├─ 不能超賣 │ │ └─ 用當(dāng)前讀 FOR UPDATE → 阻塞等待,保證正確性 │ │ │ │ 同一系統(tǒng),兩種策略,各取所需 │ │ │ └─────────────────────────────────────────────────────────┘
代碼示例對比
-- 假設(shè)表:account(id, balance)
┌─────────────────────────────────────────────────────────────┐
│ 事務(wù)A(轉(zhuǎn)賬) │
├─────────────────────────────────────────────────────────────┤
│ START TRANSACTION; │
│ -- 扣減轉(zhuǎn)賬賬戶 │
│ UPDATE account SET balance = balance - 100 WHERE id = 1; │
│ -- 此時(shí) id=1 的 balance 內(nèi)存中有新版本,但未提交 │
│ │
│ -- 做一些其他處理(耗時(shí)較長) │
│ ... │
│ │
│ COMMIT; │
└─────────────────────────────────────────────────────────────┘
│
│ 并發(fā)執(zhí)行
▼
┌─────────────────────────────────────────────────────────────┐
│ 事務(wù)B(查詢余額)- 快照讀 │
├─────────────────────────────────────────────────────────────┤
│ START TRANSACTION; │
│ SELECT balance FROM account WHERE id = 1; -- 讀快照 │
│ -- 結(jié)果:1000(事務(wù)A修改前的值) │
│ -- 原理:TRX_ID 判斷,事務(wù)A未提交,對事務(wù)B不可見 │
│ COMMIT; │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 事務(wù)C(必須確認(rèn)轉(zhuǎn)賬結(jié)果)- 當(dāng)前讀 │
├─────────────────────────────────────────────────────────────┤
│ START TRANSACTION; │
│ SELECT balance FROM account WHERE id = 1 FOR UPDATE; │
│ -- 結(jié)果:等待... 直到事務(wù)A提交后返回 900 │
│ -- 原理:加排他鎖,強(qiáng)制讀最新版本,阻塞等待 │
│ COMMIT; │
└─────────────────────────────────────────────────────────────┘總結(jié)
| 你的顧慮 | MVCC 的答案 |
|---|---|
| 「寫的時(shí)候讀,數(shù)據(jù)不準(zhǔn)確」 | 快照讀 故意讀"舊"數(shù)據(jù),換取不阻塞 |
| 「但我需要準(zhǔn)確數(shù)據(jù)」 | 用 當(dāng)前讀 (FOR UPDATE),犧牲并發(fā)換準(zhǔn)確 |
| 「到底準(zhǔn)不準(zhǔn)」 | 快照讀在事務(wù)開始時(shí)是一致的、自洽的,只是不是最新的 |
MVCC 不是萬能藥,而是給開發(fā)者選擇權(quán):
要并發(fā)?用快照讀。要準(zhǔn)確?用當(dāng)前讀。
這也是為什么 InnoDB 同時(shí)提供兩種機(jī)制,而不是一刀切。
到此這篇關(guān)于mysql從零單排之快照讀與當(dāng)前讀的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)mysql 快照讀與當(dāng)前讀內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Workbench連接不上阿里云服務(wù)器Ubuntu的Mysql解決方法(已測)
這兩天為了解決workbench連接不上阿里云服務(wù)器的問題,搞得頭大,網(wǎng)上搜到的教程都大同小異,但唯獨(dú)到我這就是行不通。不過好在最后終于解決了,記錄一下這個(gè)坑爹的過程,另外腳本之家小編特把這些問題整理了一下,看完這一篇文章基本上就解決了2020-02-02
解決數(shù)據(jù)庫有數(shù)據(jù)但查詢出來的值為Null問題
這篇文章主要介紹了解決數(shù)據(jù)庫有數(shù)據(jù)但查詢出來的值為Null問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
MySQL數(shù)據(jù)備份之mysqldump的使用方法
mysqldump常用于MySQL數(shù)據(jù)庫邏輯備份,這篇文章主要給大家介紹了關(guān)于MySQL數(shù)據(jù)備份之mysqldump使用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-11-11
mysql觸發(fā)器實(shí)現(xiàn)oracle物化視圖示例代碼
mysql觸發(fā)器實(shí)現(xiàn)oracle物化視圖即不是基于基表的虛表,而是根據(jù)表實(shí)際存在的實(shí)表,需要的朋友可以參考下2014-02-02
MySQL數(shù)據(jù)權(quán)限的實(shí)現(xiàn)詳情
這篇文章主要介紹了MySQL數(shù)據(jù)權(quán)限的實(shí)現(xiàn)詳情,文章通過實(shí)際案例,從代碼實(shí)戰(zhàn)的角度來實(shí)現(xiàn)這樣的一個(gè)數(shù)據(jù)權(quán)限。具體詳細(xì)介紹,具有一定的參考價(jià)值2022-08-08

