術(shù)式之后皆為邏輯,一切皆為需求和實(shí)現(xiàn)。希望此文能從需求、現(xiàn)狀和解決方式的角度幫大家理解隔離級(jí)別。
專業(yè)從事網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站,高端網(wǎng)站制作設(shè)計(jì),成都微信小程序,網(wǎng)站推廣的成都做網(wǎng)站的公司。優(yōu)秀技術(shù)團(tuán)隊(duì)竭力真誠(chéng)服務(wù),采用HTML5+CSS3前端渲染技術(shù),響應(yīng)式網(wǎng)站設(shè)計(jì),讓網(wǎng)站在手機(jī)、平板、PC、微信下都能呈現(xiàn)。建站過(guò)程建立專項(xiàng)小組,與您實(shí)時(shí)在線互動(dòng),隨時(shí)提供解決方案,暢聊想法和感受。
隔離級(jí)別的產(chǎn)生
在串型執(zhí)行的條件下,數(shù)據(jù)修改的順序是固定的、可預(yù)期的結(jié)果,但是并發(fā)執(zhí)行的情況下,數(shù)據(jù)的修改是不可預(yù)期的,也不固定,為了實(shí)現(xiàn)數(shù)據(jù)修改在并發(fā)執(zhí)行的情況下得到一個(gè)固定、可預(yù)期的結(jié)果,由此產(chǎn)生了隔離級(jí)別。
所以隔離級(jí)別的作用是用來(lái)平衡數(shù)據(jù)庫(kù)并發(fā)訪問與數(shù)據(jù)一致性的方法。
事務(wù)的4種隔離級(jí)別
READ UNCOMMITTED ? ? ? 未提交讀,可以讀取未提交的數(shù)據(jù)。READ COMMITTED ? ? ? ? 已提交讀,對(duì)于鎖定讀(select with for update 或者 for share)、update 和 delete 語(yǔ)句, ? ? ? ? ? ? ? ? ? ? ? InnoDB 僅鎖定索引記錄,而不鎖定它們之間的間隙,因此允許在鎖定的記錄旁邊自由插入新記錄。 ? ? ? ? ? ? ? ? ? ? ? Gap locking 僅用于外鍵約束檢查和重復(fù)鍵檢查。REPEATABLE READ ? ? ? ?可重復(fù)讀,事務(wù)中的一致性讀取讀取的是事務(wù)第一次讀取所建立的快照。SERIALIZABLE ? ? ? ? ? 序列化
在了解了 4 種隔離級(jí)別的需求后,在采用鎖控制隔離級(jí)別的基礎(chǔ)上,我們需要了解加鎖的對(duì)象(數(shù)據(jù)本身間隙),以及了解整個(gè)數(shù)據(jù)范圍的全集組成。
數(shù)據(jù)范圍全集組成
SQL 語(yǔ)句根據(jù)條件判斷不需要掃描的數(shù)據(jù)范圍(不加鎖);
SQL 語(yǔ)句根據(jù)條件掃描到的可能需要加鎖的數(shù)據(jù)范圍;
以單個(gè)數(shù)據(jù)范圍為例,數(shù)據(jù)范圍全集包含:(數(shù)據(jù)范圍不一定是連續(xù)的值,也可能是間隔的值組成)
1. 數(shù)據(jù)已經(jīng)填充了整個(gè)數(shù)據(jù)范圍:(被完全填充的數(shù)據(jù)范圍,不存在數(shù)據(jù)間隙)
整形,對(duì)值具有唯一約束條件的數(shù)據(jù)范圍 1~5 ,
已有數(shù)據(jù)1、2、3、4、5,此時(shí)數(shù)據(jù)范圍已被完全填充;
整形,對(duì)值具有唯一約束條件的數(shù)據(jù)范圍 1 和 5 ,
已有數(shù)據(jù)1、5,此時(shí)數(shù)據(jù)范圍已被完全填充;
2. 數(shù)據(jù)填充了部分?jǐn)?shù)據(jù)范圍:(未被完全填充的數(shù)據(jù)范圍,是存在數(shù)據(jù)間隙)
整形的數(shù)據(jù)范圍 1~5 ,
已有數(shù)據(jù) 1、2、3、4、5,但是因?yàn)闆]有唯一約束,
所以數(shù)據(jù)范圍可以繼續(xù)被 1~5 的數(shù)據(jù)重復(fù)填充;
整形,具有唯一約束條件的數(shù)據(jù)范圍 1~5 ,
已有數(shù)據(jù) 2,5,此時(shí)數(shù)據(jù)范圍未被完全填充,還可以填充 1、3、4 ;
3. 數(shù)據(jù)范圍內(nèi)沒有任何數(shù)據(jù)(存在間隙)
如下:
整形的數(shù)據(jù)范圍 1~5 ,數(shù)據(jù)范圍內(nèi)當(dāng)前沒有任何數(shù)據(jù)。
在了解了數(shù)據(jù)全集的組成后,我們?cè)賮?lái)看看事務(wù)并發(fā)時(shí),會(huì)帶來(lái)的問題。
無(wú)控制的并發(fā)所帶來(lái)的問題
并發(fā)事務(wù)如果不加以控制的話會(huì)帶來(lái)一些問題,主要包括以下幾種情況。
1. 范圍內(nèi)已有數(shù)據(jù)更改導(dǎo)致的:
更新丟失:當(dāng)多個(gè)事務(wù)選擇了同一行,然后基于最初選定的值更新該行時(shí),
由于每個(gè)事物不知道其他事務(wù)的存在,最后的更新就會(huì)覆蓋其他事務(wù)所做的更新;
臟讀: 一個(gè)事務(wù)正在對(duì)一條記錄做修改,這個(gè)事務(wù)完成并提交前,這條記錄就處于不一致狀態(tài)。
這時(shí),另外一個(gè)事務(wù)也來(lái)讀取同一條記錄,如果不加控制,
第二個(gè)事務(wù)讀取了這些“臟”數(shù)據(jù),并據(jù)此做了進(jìn)一步的處理,就會(huì)產(chǎn)生提交的數(shù)據(jù)依賴關(guān)系。
這種現(xiàn)象就叫“臟讀”。
2. 范圍內(nèi)數(shù)據(jù)量發(fā)生了變化導(dǎo)致:
不可重復(fù)讀:一個(gè)事務(wù)在讀取某些數(shù)據(jù)后的某個(gè)時(shí)間,再次讀取以前讀過(guò)的數(shù)據(jù),
卻發(fā)現(xiàn)其讀出的數(shù)據(jù)已經(jīng)發(fā)生了改變,或者某些記錄已經(jīng)被刪除了。
這種現(xiàn)象就叫“不可重復(fù)讀”。
幻讀:一個(gè)事務(wù)按相同的查詢條件重新讀取以前檢索過(guò)的數(shù)據(jù),
卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象稱為“幻讀”。
可以簡(jiǎn)單的認(rèn)為滿足條件的數(shù)據(jù)量變化了。
因?yàn)闊o(wú)控制的并發(fā)會(huì)帶來(lái)一系列的問題,這些問題會(huì)導(dǎo)致無(wú)法滿足我們所需要的結(jié)果。因此我們需要控制并發(fā),以實(shí)現(xiàn)我們所期望的結(jié)果(隔離級(jí)別)。
MySQL 隔離級(jí)別的實(shí)現(xiàn)
InnoDB 通過(guò)加鎖的策略來(lái)支持這些隔離級(jí)別。
行鎖包含:
Record Locks
索引記錄鎖,索引記錄鎖始終鎖定索引記錄,即使表中未定義索引,
這種情況下,InnoDB 創(chuàng)建一個(gè)隱藏的聚簇索引,并使用該索引進(jìn)行記錄鎖定。
Gap Locks
間隙鎖是索引記錄之間的間隙上的鎖,或者對(duì)第一條記錄之前或者最后一條記錄之后的鎖。
間隙鎖是性能和并發(fā)之間權(quán)衡的一部分。
對(duì)于無(wú)間隙的數(shù)據(jù)范圍不需要間隙鎖,因?yàn)闆]有間隙。
Next-Key Locks
索引記錄上的記錄鎖和索引記錄之前的 gap lock 的組合。
假設(shè)索引包含 10、11、13 和 20。
可能的next-key locks包括以下間隔,其中圓括號(hào)表示不包含間隔端點(diǎn),方括號(hào)表示包含端點(diǎn):
(負(fù)無(wú)窮大, 10] ? ?(10, 11] ? ?(11, 13] ? ?(13, 20] ? ?(20, 正無(wú)窮大) ? ? ? ?對(duì)于最后一個(gè)間隔,next-key將會(huì)鎖定索引中最大值的上方,
左右滑動(dòng)進(jìn)行查看
"上確界"偽記錄的值高于索引中任何實(shí)際值。
上確界不是一個(gè)真正的索引記錄,因此,實(shí)際上,這個(gè) next-key 只鎖定最大索引值之后的間隙。
基于此,當(dāng)獲取的數(shù)據(jù)范圍中,數(shù)據(jù)已填充了所有的數(shù)據(jù)范圍,那么此時(shí)是不存在間隙的,也就不需要 gap lock。
對(duì)于數(shù)據(jù)范圍內(nèi)存在間隙的,需要根據(jù)隔離級(jí)別確認(rèn)是否對(duì)間隙加鎖。
默認(rèn)的 REPEATABLE READ 隔離級(jí)別,為了保證可重復(fù)讀,除了對(duì)數(shù)據(jù)本身加鎖以外,還需要對(duì)數(shù)據(jù)間隙加鎖。
READ COMMITTED 已提交讀,不匹配行的記錄鎖在 MySQL 評(píng)估了 where 條件后釋放。
對(duì)于 update 語(yǔ)句,InnoDB 執(zhí)行 "semi-consistent" 讀取,這樣它會(huì)將最新提交的版本返回到 MySQL,
以便 MySQL 可以確定該行是否與 update 的 where 條件相匹配。
總結(jié)延展:
唯一索引存在唯一約束,所以變更后的數(shù)據(jù)若違反了唯一約束的原則,則會(huì)失敗。
當(dāng) where 條件使用二級(jí)索引篩選數(shù)據(jù)時(shí),會(huì)對(duì)二級(jí)索引命中的條目和對(duì)應(yīng)的聚簇索引都加鎖;所以其他事務(wù)變更命中加鎖的聚簇索引時(shí),都會(huì)等待鎖。
行鎖的增加是一行一行增加的,所以可能導(dǎo)致并發(fā)情況下死鎖的發(fā)生。
例如,
在 session A 對(duì)符合條件的某聚簇索引加鎖時(shí),可能 session B 已持有該聚簇索引的 Record Locks,而 session B 正在等待 session A 已持有的某聚簇索引的 Record Locks。
session A 和 session B 是通過(guò)兩個(gè)不相干的二級(jí)索引定位到的聚簇索引。
session A 通過(guò)索引 idA,session B通過(guò)索引 idB 。
當(dāng) where 條件獲取的數(shù)據(jù)無(wú)間隙時(shí),無(wú)論隔離級(jí)別為 rc 或 rr,都不會(huì)存在間隙鎖。
比如通過(guò)唯一索引獲取到了已完全填充的數(shù)據(jù)范圍,此時(shí)不需要間隙鎖。
間隙鎖的目的在于阻止數(shù)據(jù)插入間隙,所以無(wú)論是通過(guò) insert 或 update 變更導(dǎo)致的間隙內(nèi)數(shù)據(jù)的存在,都會(huì)被阻止。
rc 隔離級(jí)別模式下,查詢和索引掃描將禁用 gap locking,此時(shí) gap locking 僅用于外鍵約束檢查和重復(fù)鍵檢查(主要是唯一性檢查)。
rr 模式下,為了防止幻讀,會(huì)加上 Gap Locks。
事務(wù)中,SQL 開始則加鎖,事務(wù)結(jié)束才釋放鎖。
就鎖類型而言,應(yīng)該有優(yōu)化鎖,鎖升級(jí)等,例如rr模式未使用索引查詢的情況下,是否可以直接升級(jí)為表鎖。
就鎖的應(yīng)用場(chǎng)景而言,在回放場(chǎng)景中,如果確定事務(wù)可并發(fā),則可以考慮不加鎖,加快回放速度。
鎖只是并發(fā)控制的一種粒度,只是一個(gè)很小的部分:
從不同場(chǎng)景下是否需要控制并發(fā),(已知無(wú)交集且有序的數(shù)據(jù)的變更,MySQL 的 MTS 相同前置事務(wù)的多事務(wù)并發(fā)回放)
并發(fā)控制的粒度,(鎖是一種邏輯粒度,可能還存在物理層和其他邏輯粒度或方式)
相同粒度下的優(yōu)化,(鎖本身存在優(yōu)化,如IX、IS類型的優(yōu)化鎖)
粒度加載的安全性能(如獲取行鎖前,先獲取頁(yè)鎖,頁(yè)鎖在執(zhí)行獲取行鎖操作后即釋放,無(wú)論是否獲取成功)等多個(gè)層次去思考并發(fā)這玩意。
MySQL 中事務(wù)的隔離級(jí)別一共分為四種,分別如下:
序列化(SERIALIZABLE):如果隔離級(jí)別為序列化,則用戶之間通過(guò)一個(gè)接一個(gè)順序地執(zhí)行當(dāng)前的事務(wù),這種隔離級(jí)別提供了事務(wù)之間最大限度的隔離。
可重復(fù)讀(REPEATABLE READ):在可重復(fù)讀在這一隔離級(jí)別上,事務(wù)不會(huì)被看成是一個(gè)序列。不過(guò),當(dāng)前正在執(zhí)行事務(wù)的變化仍然不能被外部看到,也就是說(shuō),如果用戶在另外一個(gè)事務(wù)中執(zhí)行同條 SELECT 語(yǔ)句數(shù)次,結(jié)果總是相同的。(因?yàn)檎趫?zhí)行的事務(wù)所產(chǎn)生的數(shù)據(jù)變化不能被外部看到)。
提交讀(READ COMMITTED):READ COMMITTED 隔離級(jí)別的安全性比 REPEATABLE READ 隔離級(jí)別的安全性要差。處于 READ COMMITTED 級(jí)別的事務(wù)可以看到其他事務(wù)對(duì)數(shù)據(jù)的修改。也就是說(shuō),在事務(wù)處理期間,如果其他事務(wù)修改了相應(yīng)的表,那么同一個(gè)事務(wù)的多個(gè) SELECT 語(yǔ)句可能返回不同的結(jié)果。
未提交讀(READ UNCOMMITTED):READ UNCOMMITTED 提供了事務(wù)之間最小限度的隔離。除了容易產(chǎn)生虛幻的讀操作和不能重復(fù)的讀操作外,處于這個(gè)隔離級(jí)的事務(wù)可以讀到其他事務(wù)還沒有提交的數(shù)據(jù),如果這個(gè)事務(wù)使用其他事務(wù)不提交的變化作為計(jì)算的基礎(chǔ),然后那些未提交的變化被它們的父事務(wù)撤銷,這就導(dǎo)致了大量的數(shù)據(jù)變化。
應(yīng)用環(huán)境
與其他的大型數(shù)據(jù)庫(kù)例如?Oracle、DB2、SQL Server等相比,MySQL自有它的不足之處,但是這絲毫也沒有減少它受歡迎的程度。對(duì)于一般的個(gè)人使用者和中小型企業(yè)來(lái)說(shuō),MySQL提供的功能已經(jīng)綽綽有余,而且由于 MySQL是開放源碼軟件,因此可以大大降低總體擁有成本。
mysql事務(wù)隔離級(jí)別如下:
1.讀取未提交(READ-UNCOMMITTED):最低的隔離級(jí)別,允許讀取尚未提交的數(shù)據(jù)變更,可能造成臟讀、不可重復(fù)讀、幻讀。
2.讀取已提交(READ-COMMITTED):允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以避免臟讀,但是可能造成不可重復(fù)、幻讀。
3.可重復(fù)讀(REPEATABLE-READ):對(duì)同一字段多次讀取的結(jié)果都是一致的,除非本身事務(wù)修改,可以避免臟讀和不可重復(fù)讀,但是可能造成幻讀。
4.可串行化(SERIALIZABLE):最高的隔離級(jí)別,完全服從ACID的隔離級(jí)別,所以的事務(wù)依次執(zhí)行,可以避免臟讀、不可重復(fù)讀、幻讀。
事務(wù)的特性:
1.原子性:事務(wù)最小的執(zhí)行單位,不允許分割。事務(wù)的原子性確保動(dòng)作要么全部執(zhí)行,要么全部不執(zhí)行。
2.一致性:執(zhí)行事務(wù)的前后,數(shù)據(jù)保持一致。例如轉(zhuǎn)賬的業(yè)務(wù)中,無(wú)論事務(wù)是否成功,轉(zhuǎn)賬者和收款人的總額應(yīng)該是不變的。
3.隔離性:并發(fā)訪問數(shù)據(jù)庫(kù)時(shí),一個(gè)用戶的事務(wù)不應(yīng)該被其他事務(wù)所影響,各并發(fā)事務(wù)之間數(shù)據(jù)庫(kù)是獨(dú)立的。
4.持久性:一個(gè)事務(wù)被提交后,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變是持久的,即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有影響。
分別是原子性、一致性、隔離性、持久性。
原子性是指事務(wù)包含的所有操作要么全部成功,要么全部失敗回滾,因此事務(wù)的操作如果成功就必須要完全應(yīng)用到數(shù)據(jù)庫(kù),如果操作失敗則不能對(duì)數(shù)據(jù)庫(kù)有任何影響。
一致性是指事務(wù)必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài),也就是說(shuō)一個(gè)事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)。舉例來(lái)說(shuō),假設(shè)用戶A和用戶B兩者的錢加起來(lái)一共是1000,那么不管A和B之間如何轉(zhuǎn)賬、轉(zhuǎn)幾次賬,事務(wù)結(jié)束后兩個(gè)用戶的錢相加起來(lái)應(yīng)該還得是1000,這就是事務(wù)的一致性。
隔離性是當(dāng)多個(gè)用戶并發(fā)訪問數(shù)據(jù)庫(kù)時(shí),比如同時(shí)操作同一張表時(shí),數(shù)據(jù)庫(kù)為每一個(gè)用戶開啟的事務(wù),不能被其他事務(wù)的操作所干擾,多個(gè)并發(fā)事務(wù)之間要相互隔離。關(guān)于事務(wù)的隔離性數(shù)據(jù)庫(kù)提供了多種隔離級(jí)別,稍后會(huì)介紹到。
持久性是指一個(gè)事務(wù)一旦被提交了,那么對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)的改變就是永久性的,即便是在數(shù)據(jù)庫(kù)系統(tǒng)遇到故障的情況下也不會(huì)丟失提交事務(wù)的操作。例如我們?cè)谑褂肑DBC操作數(shù)據(jù)庫(kù)時(shí),在提交事務(wù)方法后,提示用戶事務(wù)操作完成,當(dāng)我們程序執(zhí)行完成直到看到提示后,就可以認(rèn)定事務(wù)已經(jīng)正確提交,即使這時(shí)候數(shù)據(jù)庫(kù)出現(xiàn)了問題,也必須要將我們的事務(wù)完全執(zhí)行完成。否則的話就會(huì)造成我們雖然看到提示事務(wù)處理完畢,但是數(shù)據(jù)庫(kù)因?yàn)楣收隙鴽]有執(zhí)行事務(wù)的重大錯(cuò)誤。這是不允許的。
在數(shù)據(jù)庫(kù)操作中,在并發(fā)的情況下可能出現(xiàn)如下問題:
正是為了解決以上情況,數(shù)據(jù)庫(kù)提供了幾種隔離級(jí)別。
數(shù)據(jù)庫(kù)事務(wù)的隔離級(jí)別有4個(gè),由低到高依次為Read uncommitted(未授權(quán)讀取、讀未提交)、Read committed(授權(quán)讀取、讀提交)、Repeatable read(可重復(fù)讀?。?、Serializable(序列化),這四個(gè)級(jí)別可以逐個(gè)解決臟讀、不可重復(fù)讀、幻象讀這幾類問題。
雖然數(shù)據(jù)庫(kù)的隔離級(jí)別可以解決大多數(shù)問題,但是靈活度較差,為此又提出了悲觀鎖和樂觀鎖的概念。
悲觀鎖,它指的是對(duì)數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來(lái)自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度。因此,在整個(gè)數(shù)據(jù)處理過(guò)程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫(kù)提供的鎖機(jī)制。也只有數(shù)據(jù)庫(kù)層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)的數(shù)據(jù)訪問層中實(shí)現(xiàn)了加鎖機(jī)制,也無(wú)法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù)。
商品t_items表中有一個(gè)字段status,status為1代表商品未被下單,status為2代表商品已經(jīng)被下單(此時(shí)該商品無(wú)法再次下單),那么我們對(duì)某個(gè)商品下單時(shí)必須確保該商品status為1。假設(shè)商品的id為1。
如果不采用鎖,那么操作方法如下:
但是上面這種場(chǎng)景在高并發(fā)訪問的情況下很可能會(huì)出現(xiàn)問題。例如當(dāng)?shù)谝徊讲僮髦校樵兂鰜?lái)的商品status為1。但是當(dāng)我們執(zhí)行第三步Update操作的時(shí)候,有可能出現(xiàn)其他人先一步對(duì)商品下單把t_items中的status修改為2了,但是我們并不知道數(shù)據(jù)已經(jīng)被修改了,這樣就可能造成同一個(gè)商品被下單2次,使得數(shù)據(jù)不一致。所以說(shuō)這種方式是不安全的。
在上面的場(chǎng)景中,商品信息從查詢出來(lái)到修改,中間有一個(gè)處理訂單的過(guò)程,使用悲觀鎖的原理就是,當(dāng)我們?cè)诓樵兂鰐_items信息后就把當(dāng)前的數(shù)據(jù)鎖定,直到我們修改完畢后再解鎖。那么在這個(gè)過(guò)程中,因?yàn)閠_items被鎖定了,就不會(huì)出現(xiàn)有第三者來(lái)對(duì)其進(jìn)行修改了。需要注意的是,要使用悲觀鎖,我們必須關(guān)閉mysql數(shù)據(jù)庫(kù)的自動(dòng)提交屬性,因?yàn)镸ySQL默認(rèn)使用autocommit模式,也就是說(shuō),當(dāng)你執(zhí)行一個(gè)更新操作后,MySQL會(huì)立刻將結(jié)果進(jìn)行提交。我們可以使用命令設(shè)置MySQL為非autocommit模式: set autocommit=0;
設(shè)置完autocommit后,我們就可以執(zhí)行我們的正常業(yè)務(wù)了。具體如下:
上面的begin/commit為事務(wù)的開始和結(jié)束,因?yàn)樵谇耙徊轿覀冴P(guān)閉了mysql的autocommit,所以需要手動(dòng)控制事務(wù)的提交。
上面的第一步我們執(zhí)行了一次查詢操作: select status from t_items where id=1 for update; 與普通查詢不一樣的是,我們使用了 select…for update 的方式,這樣就通過(guò)數(shù)據(jù)庫(kù)實(shí)現(xiàn)了悲觀鎖。此時(shí)在t_items表中,id為1的那條數(shù)據(jù)就被我們鎖定了,其它的事務(wù)必須等本次事務(wù)提交之后才能執(zhí)行。這樣我們可以保證當(dāng)前的數(shù)據(jù)不會(huì)被其它事務(wù)修改。需要注意的是,在事務(wù)中,只有 SELECT ... FOR UPDATE 或 LOCK IN SHARE MODE 操作同一個(gè)數(shù)據(jù)時(shí)才會(huì)等待其它事務(wù)結(jié)束后才執(zhí)行,一般 SELECT ... 則不受此影響。拿上面的實(shí)例來(lái)說(shuō),當(dāng)我執(zhí)行 select status from t_items where id=1 for update; 后。我在另外的事務(wù)中如果再次執(zhí)行 select status from t_items where id=1 for update; 則第二個(gè)事務(wù)會(huì)一直等待第一個(gè)事務(wù)的提交,此時(shí)第二個(gè)查詢處于阻塞的狀態(tài),但是如果我是在第二個(gè)事務(wù)中執(zhí)行 select status from t_items where id=1; 則能正常查詢出數(shù)據(jù),不會(huì)受第一個(gè)事務(wù)的影響。
使用 select…for update 會(huì)把數(shù)據(jù)給鎖住,不過(guò)我們需要注意一些鎖的級(jí)別,MySQL InnoDB默認(rèn)Row-Level Lock,所以只有「明確」地指定主鍵或者索引,MySQL 才會(huì)執(zhí)行Row lock (只鎖住被選取的數(shù)據(jù)) ,否則MySQL 將會(huì)執(zhí)行Table Lock (將整個(gè)數(shù)據(jù)表單給鎖住)。舉例如下:
1、 select * from t_items where id=1 for update;
這條語(yǔ)句明確指定主鍵(id=1),并且有此數(shù)據(jù)(id=1的數(shù)據(jù)存在),則采用row lock。只鎖定當(dāng)前這條數(shù)據(jù)。
2、 select * from t_items where id=3 for update;
這條語(yǔ)句明確指定主鍵,但是卻查無(wú)此數(shù)據(jù),此時(shí)不會(huì)產(chǎn)生lock(沒有元數(shù)據(jù),又去lock誰(shuí)呢?)。
3、 select * from t_items where name='手機(jī)' for update;
這條語(yǔ)句沒有指定數(shù)據(jù)的主鍵,那么此時(shí)產(chǎn)生table lock,即在當(dāng)前事務(wù)提交前整張數(shù)據(jù)表的所有字段將無(wú)法被查詢。
4、 select * from t_items where id0 for update; 或者 select * from t_items where id1 for update; (注:在SQL中表示不等于)
上述兩條語(yǔ)句的主鍵都不明確,也會(huì)產(chǎn)生table lock。
5、 select * from t_items where status=1 for update; (假設(shè)為status字段添加了索引)
這條語(yǔ)句明確指定了索引,并且有此數(shù)據(jù),則產(chǎn)生row lock。
6、 select * from t_items where status=3 for update; (假設(shè)為status字段添加了索引)
這條語(yǔ)句明確指定索引,但是根據(jù)索引查無(wú)此數(shù)據(jù),也就不會(huì)產(chǎn)生lock。
樂觀鎖( Optimistic Locking ) 相對(duì)悲觀鎖而言,樂觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會(huì)造成沖突,所以只會(huì)在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè),如果發(fā)現(xiàn)沖突了,則返回用戶錯(cuò)誤的信息,讓用戶決定如何去做。實(shí)現(xiàn)樂觀鎖一般來(lái)說(shuō)有以下2種方式:
mysql的4種事務(wù)隔離級(jí)別,如下所示:
1、未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會(huì)話中未提交事務(wù)修改的數(shù)據(jù)。
2、提交讀(Read Committed):只能讀取到已經(jīng)提交的數(shù)據(jù)。Oracle等多數(shù)數(shù)據(jù)庫(kù)默認(rèn)都是該級(jí)別 (不重復(fù)讀)。
3、可重復(fù)讀(Repeated Read):可重復(fù)讀。在同一個(gè)事務(wù)內(nèi)的查詢都是事務(wù)開始時(shí)刻一致的,InnoDB默認(rèn)級(jí)別。在SQL標(biāo)準(zhǔn)中,該隔離級(jí)別消除了不可重復(fù)讀,但是還存在幻象讀,但是innoDB解決了幻讀。
4、串行讀(Serializable):完全串行化的讀,每次讀都需要獲得表級(jí)共享鎖,讀寫相互都會(huì)阻塞。
相關(guān)簡(jiǎn)介
MySQL是一個(gè)關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),由瑞典MySQL AB?公司開發(fā),屬于?Oracle?旗下產(chǎn)品。MySQL 是最流行的關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)之一,在?WEB?應(yīng)用方面,MySQL是最好的?RDBMS?(Relational Database Management System,關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)) 應(yīng)用軟件之一。
MySQL是一種關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),關(guān)系數(shù)據(jù)庫(kù)將數(shù)據(jù)保存在不同的表中,而不是將所有數(shù)據(jù)放在一個(gè)大倉(cāng)庫(kù)內(nèi),這樣就增加了速度并提高了靈活性。
MySQL所使用的 SQL 語(yǔ)言是用于訪問數(shù)據(jù)庫(kù)的最常用標(biāo)準(zhǔn)化語(yǔ)言。MySQL 軟件采用了雙授權(quán)政策,分為社區(qū)版和商業(yè)版,由于其體積小、速度快、總體擁有成本低,尤其是開放源碼這一特點(diǎn),一般中小型網(wǎng)站的開發(fā)都選擇 MySQL 作為網(wǎng)站數(shù)據(jù)庫(kù)。
我們?cè)O(shè)想一個(gè)場(chǎng)景,這個(gè)場(chǎng)景中我們需要插入多條相關(guān)聯(lián)的數(shù)據(jù)到數(shù)據(jù)庫(kù),不幸的是,這個(gè)過(guò)程可能會(huì)遇到下面這些問題:
上面的任何一個(gè)問題都可能會(huì)導(dǎo)致數(shù)據(jù)的不一致性。為了保證數(shù)據(jù)的一致性,系統(tǒng)必須能夠處理這些問題。事務(wù)就是我們抽象出來(lái)簡(jiǎn)化這些問題的首選機(jī)制。事務(wù)的概念起源于數(shù)據(jù)庫(kù),目前,已經(jīng)成為一個(gè)比較廣泛的概念。
何為事務(wù)? 一言蔽之, 事務(wù)是邏輯上的一組操作,要么都執(zhí)行,要么都不執(zhí)行。
事務(wù)最經(jīng)典也經(jīng)常被拿出來(lái)說(shuō)例子就是轉(zhuǎn)賬了。假如小明要給小紅轉(zhuǎn)賬 1000 元,這個(gè)轉(zhuǎn)賬會(huì)涉及到兩個(gè)關(guān)鍵操作,這兩個(gè)操作必須都成功或者都失敗。
事務(wù)會(huì)把這兩個(gè)操作就可以看成邏輯上的一個(gè)整體,這個(gè)整體包含的操作要么都成功,要么都要失敗。這樣就不會(huì)出現(xiàn)小明余額減少而小紅的余額卻并沒有增加的情況。
大多數(shù)情況下,我們?cè)谡務(wù)撌聞?wù)的時(shí)候,如果沒有特指 分布式事務(wù) ,往往指的就是 數(shù)據(jù)庫(kù)事務(wù) 。
數(shù)據(jù)庫(kù)事務(wù)在我們?nèi)粘i_發(fā)中接觸的最多了。如果你的項(xiàng)目屬于單體架構(gòu)的話,你接觸到的往往就是數(shù)據(jù)庫(kù)事務(wù)了。
那數(shù)據(jù)庫(kù)事務(wù)有什么作用呢?
簡(jiǎn)單來(lái)說(shuō),數(shù)據(jù)庫(kù)事務(wù)可以保證多個(gè)對(duì)數(shù)據(jù)庫(kù)的操作(也就是 SQL 語(yǔ)句)構(gòu)成一個(gè)邏輯上的整體。構(gòu)成這個(gè)邏輯上的整體的這些數(shù)據(jù)庫(kù)操作遵循: 要么全部執(zhí)行成功,要么全部不執(zhí)行 。
另外,關(guān)系型數(shù)據(jù)庫(kù)(例如: MySQL 、 SQL Server 、 Oracle 等)事務(wù)都有 ACID 特性:
ACID
這里要額外補(bǔ)充一點(diǎn): 只有保證了事務(wù)的持久性、原子性、隔離性之后,一致性才能得到保障。也就是說(shuō) A、I、D 是手段,C 是目的!
在典型的應(yīng)用程序中,多個(gè)事務(wù)并發(fā)運(yùn)行,經(jīng)常會(huì)操作相同的數(shù)據(jù)來(lái)完成各自的任務(wù)(多個(gè)用戶對(duì)同一數(shù)據(jù)進(jìn)行操作)。并發(fā)雖然是必須的,但可能會(huì)導(dǎo)致以下的問題。
不可重復(fù)讀和幻讀區(qū)別 :不可重復(fù)讀的重點(diǎn)是修改比如多次讀取一條記錄發(fā)現(xiàn)其中某些列的值被修改,幻讀的重點(diǎn)在于新增或者刪除比如多次查詢同一條查詢語(yǔ)句(DQL)時(shí),記錄發(fā)現(xiàn)記錄增多或減少了。
SQL 標(biāo)準(zhǔn)定義了四個(gè)隔離級(jí)別:
隔離級(jí)別臟讀不可重復(fù)讀幻讀 READ-UNCOMMITTED READ-COMMITTED REPEATABLE-READ SERIALIZABLE
MySQL 的隔離級(jí)別基于鎖和 MVCC 機(jī)制共同實(shí)現(xiàn)的。
SERIALIZABLE 隔離級(jí)別,是通過(guò)鎖來(lái)實(shí)現(xiàn)的。除了 SERIALIZABLE 隔離級(jí)別,其他的隔離級(jí)別都是基于 MVCC 實(shí)現(xiàn)。
不過(guò), SERIALIZABLE 之外的其他隔離級(jí)別可能也需要用到鎖機(jī)制,就比如 REPEATABLE-READ 在當(dāng)前讀情況下需要使用加鎖讀來(lái)保證不會(huì)出現(xiàn)幻讀。
MySQL InnoDB 存儲(chǔ)引擎的默認(rèn)支持的隔離級(jí)別是 REPEATABLE-READ(可重讀) 。我們可以通過(guò) SELECT @@tx_isolation; 命令來(lái)查看,MySQL 8.0 該命令改為 SELECT @@transaction_isolation;
從上面對(duì) SQL 標(biāo)準(zhǔn)定義了四個(gè)隔離級(jí)別的介紹可以看出,標(biāo)準(zhǔn)的 SQL 隔離級(jí)別定義里,REPEATABLE-READ(可重復(fù)讀)是不可以防止幻讀的。
但是!InnoDB 實(shí)現(xiàn)的 REPEATABLE-READ 隔離級(jí)別其實(shí)是可以解決幻讀問題發(fā)生的,主要有下面兩種情況:
因?yàn)楦綦x級(jí)別越低,事務(wù)請(qǐng)求的鎖越少,所以大部分?jǐn)?shù)據(jù)庫(kù)系統(tǒng)的隔離級(jí)別都是 READ-COMMITTED ,但是你要知道的是 InnoDB 存儲(chǔ)引擎默認(rèn)使用 REPEATABLE-READ 并不會(huì)有任何性能損失。
InnoDB 存儲(chǔ)引擎在分布式事務(wù)的情況下一般會(huì)用到 SERIALIZABLE 隔離級(jí)別。