最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

MySQL事務(wù)原理分析——所以鎖

2023-02-09 16:27 作者:后端攻城獅哇  | 我要投稿

共享鎖

共享鎖也成為讀鎖,針對同一份數(shù)據(jù),多個事務(wù)的讀操作可以同時進行而不會互相影響,相互不阻塞的。

  • 通過下面命令加共享鎖

SELECT...LOCK IN SHARE MODE
#或
SELECT...FOR SHARE;#(8.0新增語法)


排他鎖

排他鎖也叫寫鎖,當(dāng)一個事務(wù)對一份數(shù)據(jù)執(zhí)行寫入,即加上排他鎖后,其他事務(wù)對同一份數(shù)據(jù)進行讀寫操作會阻塞,直到前一個事務(wù)提交。

  • 通過下面的命令加排他鎖

SELECT ... FOR UPDATE;

  • DELETE、UPDATE、INSERT等操作也相當(dāng)于加排他鎖

共享鎖和排他鎖是否會發(fā)生阻塞如下圖所示:

共享鎖排他鎖共享鎖不阻塞阻塞排他鎖阻塞阻塞


行級鎖

行級鎖是鎖住行,粒度小,性能較高,但是行級鎖只在存儲引擎層實現(xiàn),行級鎖分為3種,記錄鎖、間隙鎖和臨鍵鎖。

假如下面的一個表test_lock:

idab000444888161616323232


  • id是主鍵索引

  • a是普通索引

  • b是普通列


記錄鎖 (Record Locks)

記錄鎖是僅僅鎖住一條記錄,鎖的粒度最小。

什么時候會加記錄鎖?

當(dāng)用唯一索引進行等職查詢時,且查詢的記錄是存在的時候,會加記錄鎖。

會話1會話2會話3begin;select * from test_lock where id = 16 for update;update test_lock set a = 100 where id = 16;(阻塞)insert into test_lock value(9, 9, 9);(正常)

  • 會話1對id=16記錄加了行鎖

  • 會話2阻塞,無法對這條記錄進行修改操作

  • 會話3正常插入


間隙鎖(Gap Locks)

大家還記得并發(fā)事務(wù)中會出現(xiàn)"幻讀"的問題嗎?就是事務(wù)期間,其他事務(wù)添加一條數(shù)據(jù),再次讀取突然多出一條記錄。為了解決這樣的問題,我們是不是可以對一段區(qū)間的數(shù)據(jù)加鎖,加上鎖以后,其他事務(wù)添加數(shù)據(jù)時必須阻塞。像這樣的鎖就叫做間隙鎖,即鎖定一個區(qū)間,左開右開。

什么情況會加 間隙鎖 ?

  • 在用唯一索引進行等值查詢時,當(dāng)查詢記錄不存在時,會加間隙鎖。

select * from test_lock where id = 10 for update;

id=10位于8到16區(qū)間,由于10這條記錄不存在,所以加的間隙鎖,鎖定(8, 16)的區(qū)間。

  • 唯一鎖引使用范圍查詢的時候,會加間歇鎖。

select * from test_lock where id <10 and id> 8 for update;

id <10 and id> 8是一個范圍查詢,會鎖定范圍(8,16)。

  • 普通索引等值查詢時,如果記錄存在,會額外添加一個間隙鎖。

select * from test_lock where a = 8 for update;

由于a=8記錄存在,會對范圍(4,8]添加臨鍵鎖,這個后面會提到,同時額外向下遍歷到第一個不符合條件的值才能停止,因此間隙鎖的范圍是(8,16)

  • 通索引等值查詢時,如果記錄不存在,會加一個間隙鎖。

select * from test_lock where a = 10 for update;

此種情況鎖定的范圍為(8,16)


臨鍵鎖(Next-Key Locks)

如果想要同時集合上面的記錄鎖和間隙鎖,也就是既想鎖住某條記錄,又想阻止其他事務(wù)在該記錄前邊的間隙插入新記錄,所以InnoDB就提出了臨鍵鎖(Next-Key Locks),默認(rèn)鎖定的范圍是左開右閉。InnoDB存儲引擎默認(rèn)的鎖單位就是臨鍵鎖(Next-Key Locks),怎么理解呢?

也就是鎖加鎖都是按照臨鍵鎖加鎖,但是會根據(jù)一定的規(guī)律退化為記錄鎖和間隙鎖。具體規(guī)律如下:

唯一索引等值查詢:

  • 當(dāng)查詢的記錄是存在的,臨鍵鎖會退化成「記錄鎖」。

  • 當(dāng)查詢的記錄是不存在的,臨鍵鎖 會退化成「間隙鎖」。

非唯一索引等值查詢:

  • 當(dāng)查詢的記錄存在時,除了會加 臨鍵鎖外,還額外加間隙鎖,也就是會加兩把鎖。

  • 當(dāng)查詢的記錄不存在時,只會加 臨鍵鎖,然后會退化為間隙鎖,也就是只會加一把鎖。

InnoDB存儲引擎中,如果一個表查詢或者更新沒有走索引,這時候還會創(chuàng)建行級鎖嗎? 答案是不會,這時候會升級為表級鎖。


頁級鎖

頁級鎖是 MySQL 中鎖定粒度介于行級鎖和表級鎖中間的一種鎖。表級鎖速度快,但沖突多,行級沖突少,但速度慢。因此,采取了折衷的頁級鎖,一次鎖定相鄰的一組記錄。BDB (BerkeleyDB)存儲引擎 支持頁級鎖。

特點

  • 開銷和加鎖時間界于表鎖和行鎖之間

  • 會出現(xiàn)死鎖

  • 鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般


表級鎖

表鎖會鎖定整張表,它是MySQL中最基本的鎖策略,并不依賴于存儲引擎,表鎖是開銷最小的策略。因為表級鎖一次會將整個表鎖定,所以可以很好的避免死鎖問題。但是表鎖的并發(fā)度很差,那表鎖都有哪幾種呢?


表級別的S鎖、X鎖

  • LOCK TABLES t READ :InnoDB存儲引擎會對表 t 加表級別的 S鎖 。

  • LOCK TABLES t WRITE:InnoDB存儲引擎會對表 t 加表級別的 X鎖 。


意向鎖

意向鎖也是一種表鎖,表示某個事務(wù)正在鎖定一行或者將要鎖定一行,表明一個意圖。它不與行級鎖沖突。那它究竟有啥作用?

意向鎖是在當(dāng)事務(wù)加表鎖時發(fā)揮作用。比如一個事務(wù)想要對表加排他鎖,如果沒有意向鎖的話,那么該事務(wù)在加鎖前需要判斷當(dāng)前表的每一行是否已經(jīng)加了鎖,如果表很大,遍歷每行進行判斷需要耗費大量的時間。如果使用意向鎖的話,那么加表鎖前,只需要判斷當(dāng)前表是否有意向鎖即可,這樣加快了對表鎖的處理速度。

意向鎖分為兩種:

  • 意向共享鎖(intention shared lock, IS):事務(wù)有意向?qū)Ρ碇械哪承┬屑庸蚕礞i(S鎖)

  • 意向排他鎖(intention exclusive lock, IX):事務(wù)有意向?qū)Ρ碇械哪承┬屑优潘i(X鎖)


自增鎖(AUTO-INC LOCK)

我們都知道在使用創(chuàng)建表的時候有自增主鍵AUTO_INCREMENT屬性,那它是怎么實現(xiàn)自增的呢?

AUTO-INC鎖是當(dāng)向使用含有AUTO_INCREMENT列的表中插入數(shù)據(jù)時需要獲取的一種特殊的表級鎖,在執(zhí)行插入語句時就在表級別加一個AUTO-INC鎖,然后為每條待插入記錄的AUTO_INCREMENT修飾的列分配遞增的值,在該語句執(zhí)行結(jié)束后,再把AUTO-INC鎖釋放掉。


元數(shù)據(jù)鎖(MDL鎖)

元數(shù)據(jù)鎖可以用來保證讀寫的正確性。比如,如果一個查詢正在遍歷一個表中的數(shù)據(jù)而執(zhí)行期間另一個線程對這個 表結(jié)構(gòu)做變更 ,增加了一列,那么查詢線程拿到的結(jié)果跟表結(jié)構(gòu)對不上,肯定是不行的。

  • 當(dāng)對一個表做增刪改查操作的時候,加 MDL讀鎖;

  • 當(dāng)要對表做結(jié)構(gòu)變更操作的時候,加 MDL 寫鎖。

MDL讀鎖和讀鎖之間可以共享兼容,讀鎖和寫鎖之間不兼容,會互相阻塞。


全局鎖

全局鎖就是對 整個數(shù)據(jù)庫實例 加鎖。當(dāng)你需要讓整個庫處于 只讀狀態(tài) 的時候,可以使用這個命令,之后 其他線程的以下語句會被阻塞:數(shù)據(jù)更新語句(數(shù)據(jù)的增刪改)、數(shù)據(jù)定義語句(包括建表、修改表結(jié) 構(gòu)等)和更新類事務(wù)的提交語句。

全局鎖的典型使用場是做全庫邏輯備份。

## 全局鎖命令
flush tables with read lock


悲觀鎖

悲觀鎖是總以最壞的情況假設(shè),比如操作一條數(shù)據(jù),總認(rèn)為也有其他線程要拿這條數(shù)據(jù),那就給這條數(shù)據(jù)上排他鎖,讓其他事務(wù)或者線程阻塞,類似于Java中 synchronized 和 ReentrantLock 等獨占鎖的思想。

適用場景:

悲觀鎖適寫操作多的場景,因為寫的操作具有 排它性 。采用悲觀鎖的方式,可以在數(shù)據(jù)庫層 面阻止其他事務(wù)對該數(shù)據(jù)的操作權(quán)限,防止讀 - 寫和寫 - 寫的沖突。

實現(xiàn)思路:

以秒殺商品為例,為了防止超賣,需要加鎖。

#第1步: for update 方式查出商品庫存
select quantity from items where id 1001 for update;
#第2步:如果庫存大于0,則根據(jù)商品信息生產(chǎn)訂單
insert into orders (item_id)values(1001);
#第3步:修改商品的庫存,um表示購買數(shù)量
update items set quantity quantity-num where id 1001;

select····for update是MySQL中悲觀鎖。此時在items表中,id為1001的那條數(shù)據(jù)就被我們鎖定了,其他的要執(zhí)行select quantity from items where id=1001 for update;語句的事務(wù)必須等本次事務(wù)提交之后才能執(zhí)行。這樣我們可以保證每次事務(wù)能拿到最新的庫存數(shù)量,從而不會超賣,但是這樣的性能很差。


樂觀鎖

樂觀鎖認(rèn)為一個事務(wù)發(fā)生并發(fā)的概率很小,就不加通過數(shù)據(jù)庫加鎖實現(xiàn),因為加鎖性能比較差,而是通過程序?qū)崿F(xiàn),那如何數(shù)據(jù)沒有被其他事務(wù)修改了呢?會在更新數(shù)據(jù)的時候判斷數(shù)據(jù)的版本或者時間戳是否發(fā)生變化。

實現(xiàn)思路:


版本號機制實現(xiàn)樂觀鎖

  • 數(shù)據(jù)表中新增一個version字段

  • 更新前讀取出version字段

  • 進行業(yè)務(wù)邏輯操作,更新數(shù)據(jù), UPDATE ... SET version=version+1 WHERE version=version,版本+1

  • 如果有其他線程更新了數(shù)據(jù),那么上面的修改不成功,返回值為0,表示已經(jīng)被人更新了,我們可以根據(jù)0去做業(yè)務(wù)操作


時間戳機制實現(xiàn)樂觀鎖

時間戳和版本號機制一樣,也是在更新提交的時候,將當(dāng)前數(shù)據(jù)的時間戳和更新之前取得的時間戳進行 比較,如果兩者一致則更新成功,否則就是版本沖突。

適用場景:

樂觀鎖適合讀操作多的場景,相對來說寫的操作比較少。它的優(yōu)點在于 程序?qū)崿F(xiàn) ,不存在死鎖問題。


C++后端開發(fā) 面試題、學(xué)習(xí)資料、教學(xué)視頻和學(xué)習(xí)路線圖(資料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費分享有需要的可以自行添加學(xué)習(xí)交流群739729163 領(lǐng)取

MySQL事務(wù)原理分析——所以鎖的評論 (共 條)

分享到微博請遵守國家法律
亚东县| 新巴尔虎右旗| 梓潼县| 襄城县| 开原市| 山阳县| 平邑县| 泽普县| 杭州市| 濮阳县| 柞水县| 威信县| 彝良县| 南澳县| 蚌埠市| 油尖旺区| 东阿县| 黄山市| 孝昌县| 红河县| 江津市| 抚顺市| 公主岭市| 克什克腾旗| 湄潭县| 库车县| 成安县| 景谷| 广宁县| 辽中县| 新泰市| 亳州市| 随州市| 大安市| 永仁县| 宣威市| 巨鹿县| 永登县| 涿鹿县| 布拖县| 宜宾市|