日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql如何实现读提交锁_MySQL学习笔记(二)—MySQL事务及锁详解

發(fā)布時間:2025/3/21 数据库 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql如何实现读提交锁_MySQL学习笔记(二)—MySQL事务及锁详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、事務(wù)

數(shù)組庫的一組操作,要么全部成功,要么全部失敗

舉例:銀行轉(zhuǎn)賬 A賬戶向B賬戶轉(zhuǎn)100

A賬戶余額扣去100

B賬戶余額增加100

上述兩個操作要么全部成功,要么全部失敗,部分成功或失敗,數(shù)據(jù)就錯亂了

1. 事務(wù)的四大特征

原子性:事務(wù)是原子性操作,要么全部成功,要么全部失敗

一致性:多個事務(wù)對數(shù)據(jù)庫操作會保證數(shù)據(jù)一致性

隔離性:并發(fā)時,事務(wù)之間互不影響

持久性:事務(wù)提交之后對數(shù)據(jù)庫的影響是持久性的,不會因為數(shù)據(jù)庫宕機導(dǎo)致數(shù)據(jù)丟失

2. 并發(fā)事務(wù)帶來的問題

臟讀

在一個事務(wù)中,讀取了其他事務(wù)未提交的數(shù)據(jù)

不可重復(fù)讀

在一個事務(wù)中,同一行記錄被訪問了兩次卻得到了不同的結(jié)果

幻讀

在一個事務(wù)中,同一個范圍內(nèi)的記錄被讀取時,其他事務(wù)向這個范圍添加了新的記錄。

前面臟讀和不可重復(fù)讀容易理解,幻讀稍微難一點

假設(shè)圖一test開始是空表,事物1第一次查詢得到空表,事物2在事物1執(zhí)行期間插入一條數(shù)據(jù),

事物1第二次查詢由于滿足可重復(fù)讀,所以查詢結(jié)果依然為空,但是事物1插入同樣一條數(shù)據(jù),報重復(fù)主鍵錯誤

幻讀兩個要素:

可重復(fù)讀隔離級別下,快照讀看到的是一致性視圖,只有當(dāng)前讀才會產(chǎn)生幻讀

幻讀專指新插入發(fā)行,更新不算,將上述查詢后面加上For Update,就會將事務(wù)2插入的數(shù)據(jù)讀出來,這就是幻讀

3. 事務(wù)隔離級別

為了解決上述并發(fā)事務(wù)問題,MySQL數(shù)據(jù)庫提供了事務(wù)隔離級別

事物隔離級別

臟讀

不可重復(fù)讀

幻讀

讀未提交(read-uncommitted)

讀已提交(read-committed)

可重復(fù)讀(repeatable-read)

串行化(serializable)

可重復(fù)讀是MySQL默認級別

二、重要概念

1. MVCC和事務(wù)隔離的實現(xiàn)

同一數(shù)據(jù)庫記錄可以在系統(tǒng)中存在多個版本,這就是MVCC (多版本并發(fā)控制)

不同時刻開啟的事務(wù)會創(chuàng)建不同的視圖,后續(xù)直接從視圖讀取數(shù)據(jù),達到數(shù)據(jù)隔離,當(dāng)然數(shù)據(jù)隔離還需要數(shù)據(jù)庫鎖的幫助

InnoDB 里面每個事務(wù)有一個唯一的事務(wù) ID,叫作 transaction id,在事務(wù)開始的時

候向 InnoDB 的事務(wù)系統(tǒng)申請的,是按申請順序嚴格遞增

在MySQL中,每條記錄的更新都會記錄一條undo Log,記錄上最新的值通過回滾可以,都可以得到前一個狀態(tài)的值

上圖中,數(shù)據(jù)庫一行記錄有多個版本,每個版本有自己的 row trx_id,最新版本V4的k=22,是被row trx_id=25事務(wù)更新的,不同時刻啟動的事務(wù)看到不同的視圖,而V1,V2,V3不是物理上真實存在的,要想得到它們需要根據(jù)當(dāng)前版本和undo Log(回滾日志)計算,比如V1的值需要執(zhí)行U3,U2,U1才能得到

undo Log日志如果一直存在,可能會嚴重占據(jù)磁盤空間,當(dāng)系統(tǒng)沒有比undo Log更早的視圖時,就會把undo Log刪除掉

長事務(wù)一般會保存很老的事務(wù)視圖,導(dǎo)致其它事務(wù)的undo Log無法刪除,所以在這個事務(wù)提交前,可能會導(dǎo)致大量undo Log存在,我們需要避免使用長事務(wù)

2. 視圖

用查詢語句定義的虛擬表,在調(diào)用的時候執(zhí)行查詢語句并生成結(jié)果。這是我們常說的視圖

InnoDB 用來實現(xiàn) MVCC 時用到的一致性讀視圖,即 consistent read view, 用于支持 RC(Read Committed,讀提交)和 RR(Repeatable Read,可重復(fù)讀)隔離級別的實現(xiàn)。沒有物理結(jié)構(gòu),僅僅是邏輯上用來定義在事務(wù)執(zhí)行期間能看到什么數(shù)據(jù)

3. 事務(wù)的起點

begin/start transaction 命令并不是一個事務(wù)的起點,在執(zhí)行到它們之后的第一個操作 InnoDB 表的語句,事務(wù)才真正啟動, start transaction with consistent snapshot 該命令可以立即啟動事務(wù)

4. 隔離級別與視圖的關(guān)系

“讀未提交”隔離級別下直接返回記錄上的最新值,沒有視圖概念

“讀提交”隔離級別,這個視圖是在每個 SQL 語句開始執(zhí)行的時候創(chuàng)建的

“可重復(fù)讀”隔離級別: 視圖是在事務(wù)啟動 (執(zhí)行第一條語句或者使用特定命令) 時創(chuàng)建的,整個事務(wù)存在期間都用同一個視圖

“串行化”隔離級別下直接用加鎖的方式來避免并行訪問

5. 當(dāng)前讀與快照讀

當(dāng)前讀,在事務(wù)執(zhí)行過程中可以讀到其它已已提交事務(wù)的最新數(shù)據(jù)

快照讀,在事務(wù)執(zhí)行過程中只能看到從事務(wù)起點創(chuàng)建的一致性視圖,并不能讀到其它已提交數(shù)據(jù)

在RR(可重復(fù)讀)級別下,快照讀滿足以下兩個規(guī)則:

讀取的記錄:更新的事務(wù)ID <= 當(dāng)前事務(wù)ID

讀取的記錄:刪除的事務(wù)ID > 當(dāng)前事務(wù)ID(小于的話數(shù)據(jù)都刪了,肯定讀不到)

三、MySQL鎖分類

按照不同維度可分為:

1)

悲觀鎖

樂觀鎖

2)

共享鎖(寫鎖)

排它鎖(讀鎖)

3)

意向共享鎖

意向互斥鎖

意向鎖其實不會阻塞全表掃描之外的任何請求

假設(shè)沒有意向鎖,兩個請求,一個修改數(shù)據(jù)某一行記錄,另一個需要修改該表所有行記錄,這時需要就需要對所有的行是否被鎖定進行掃描,引入意向鎖,只需要判斷該表有沒有意向鎖,等待修改單行事務(wù)提交,意向鎖釋放

4)

全局鎖

表鎖和元數(shù)據(jù)鎖(meta data lock 簡稱(MDL))

行鎖

全局鎖:對整個數(shù)據(jù)庫實例加鎖

作用: MyISAM不支持事務(wù)拿不到一致性視圖,需要加全局讀鎖做邏輯備份。加讀鎖期間數(shù)據(jù)庫只能讀,不能寫。

表鎖:使用lock tables 命令來鎖住整個表,一般不使用

MDL: 當(dāng)對表做增刪改查操作時,需要加MDL讀鎖;當(dāng)需要對表做結(jié)構(gòu)變更操作時需要加MDL寫鎖(見其它篇文章)

所以如果有兩個線程,一個對表做讀操操作,一個需要給表加字段,第二個操作會被阻塞。

在給表加字段的時候,如果該表請求頻繁,這時會無法獲取MDL寫鎖,同時會阻塞后續(xù)業(yè)務(wù)請求拿讀鎖。

解決方法:在 alter table語句里面設(shè)定等待時間,如果在指定的等待時間里面能夠拿到 MDL 寫鎖最好,拿不到也不要阻塞后面的業(yè)務(wù)語句,先放棄。

行鎖: 在引擎層實現(xiàn),MyISAM不支持行鎖。

重要概念: 兩階段加鎖

在數(shù)據(jù)庫更新時會給掃描的數(shù)據(jù)行加行鎖,更新結(jié)束不會立馬釋放行鎖,需要等到事務(wù)提交才會釋放行鎖。

由于兩階段鎖的存在,所以在一個事務(wù)中,更新語句如果放在前面,會阻塞其它事務(wù)對表的更新,影響并發(fā)。對于更新頻繁的語句盡量放在事務(wù)的靠后部分

死鎖

解決方案:

超時等待

發(fā)起死鎖檢測,主動回滾其中某個事務(wù)

超時等待的時間根據(jù)業(yè)務(wù)執(zhí)行時間制定,太短誤傷,太長會影響并發(fā)量

死鎖檢測有額外負擔(dān),在事務(wù)被鎖住,需要查看其依賴的線程是否被鎖住,一直循環(huán),最后判斷出現(xiàn)死鎖,在多個線程并發(fā)修改同一行數(shù)據(jù)時,時間復(fù)雜度會變成O(n^2),會導(dǎo)致CPU利用率很高,卻執(zhí)行不了幾個事務(wù)。一般通過控制并發(fā)來解決

5)

記錄鎖(record Lock)

間隙鎖(Gap lock)

next-key

在另一篇文章中詳細講解了加鎖情況

數(shù)據(jù)庫的行鎖實際上record Lock,會對掃描的行加鎖,如果沒有走索引,掃描全表,會鎖住整個表的所有行。

例:

CREATE TABLE `t` (

`id` int(11) NOT NULL,

`c` int(11) DEFAULT NULL,

`d` int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `c` (`c`)

) ENGINE=InnoDB;

語句1:select * from t where id >3 for update;

語句2:select * from t where d > 3 for update;

語句1會走主鍵索引,對掃描到的行數(shù)加鎖

語句2不走索引,掃描全表,對所有行加record lock

在可重復(fù)讀的隔離級別下:

每次開啟事務(wù),會生成一致性視圖,看不到其它事務(wù)已經(jīng)提交的修改,在前面已經(jīng)提過

更新語句先讀后寫,這個讀是當(dāng)前讀,就算我們對所有數(shù)據(jù)加上record lock,也不能阻止數(shù)據(jù)的插入。這樣我們在當(dāng)前讀中還是會讀到插入的數(shù)據(jù),形成幻讀。

如何避免幻讀?

使用Gap lock + record lock

間隙鎖是對索引記錄中的一段連續(xù)區(qū)域的鎖

SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE;

這個語句阻止其他事務(wù)向表中插入 id = 15 的記錄,因為整個范圍都被間隙鎖鎖定

雖然間隙鎖中也分為共享鎖和互斥鎖,不過它們之間并不是互斥的,也就是不同的事務(wù)可以同時持有一段相同范圍的共享鎖和互斥鎖,它唯一阻止的就是其他事務(wù)向這個范圍中添加新的記錄

間隙鎖的引入,可能會導(dǎo)致同樣的語句鎖住更大的范圍,但是它只在可重復(fù)讀級別下才會生效

Next-Key是記錄鎖和記錄前的間隙鎖的結(jié)合,每個 next-key lock 是前開后閉區(qū)間

select * from t where id = 5

會加上(4, 5]的next-key,同時會加上(5, 6]的間隙鎖

next-key的加鎖原則是鎖定的是當(dāng)前值和前面的范圍

注:一般生產(chǎn)都會設(shè)置讀已提交級別,這個時候為了防止binlog和數(shù)據(jù)庫數(shù)據(jù)不一致需要設(shè)置binlog格式為row,在代碼中使用鎖來解決并發(fā)問題。數(shù)據(jù)庫應(yīng)該盡可能簡單,不管是語句,還是隔離級別,保證數(shù)據(jù)庫的性能。

參考

丁奇老師 MySQL45講

《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的mysql如何实现读提交锁_MySQL学习笔记(二)—MySQL事务及锁详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。