lock table 讀鎖定
成都創(chuàng)新互聯(lián)公司專注于廬陽網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供廬陽營銷型網(wǎng)站建設(shè),廬陽網(wǎng)站制作、廬陽網(wǎng)頁設(shè)計(jì)、廬陽網(wǎng)站官網(wǎng)定制、小程序開發(fā)服務(wù),打造廬陽網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供廬陽網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
如果一個(gè)線程獲得在一個(gè)表上的read鎖,那么該線程和所有其他線程只能從表中讀數(shù)據(jù),不能進(jìn)行任何寫操作。
lock tables user read;//讀鎖定表 unlock tables;//解鎖 lock tables user read local;//本地讀鎖定表,其他線程的insert未被阻塞,update操作被阻塞
lock table 寫鎖定
如果一個(gè)線程在一個(gè)表上得到一個(gè) write鎖,那么只有擁有這個(gè)鎖的線程可以從表中讀取和寫表。其它的線程被阻塞。
lock tables user write;//寫鎖定表 unlock tables;//解鎖
Yii中的用法實(shí)例
/** * 當(dāng)日單項(xiàng)內(nèi)容狀態(tài) */ public function getPointAready($marke,$dayTime){ $model = SysRun::model()-findByAttributes(array('syr_marking'=$marke,'syr_daytime'=$dayTime)); if(empty($model)){ //表寫鎖定 Yii::app()-db-createCommand()-setText("lock tables {{sys_run}} WRITE")-execute(); $model = new SysRun(); $model-syr_marking = $marke; $model-syr_daytime = $dayTime; $model-syr_val = 0; $model-syr_subval = 0; $model-save(); //表解鎖 Yii::app()-db-createCommand()-setText("unlock tables")-execute(); } return $model; }
更多關(guān)于Yii相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Yii框架入門及常用技巧總結(jié)》、《php優(yōu)秀開發(fā)框架總結(jié)》、《smarty模板入門基礎(chǔ)教程》、《php操作office文檔技巧總結(jié)(包括word,excel,access,ppt)》、《php面向?qū)ο蟪绦蛟O(shè)計(jì)入門教程》、《php字符串(string)用法總結(jié)》、《php+mysql數(shù)據(jù)庫操作入門教程》及《php常見數(shù)據(jù)庫操作技巧匯總》
首先需要明確的就是“幻讀”概念: 隔離級別是可重復(fù)讀,在一個(gè)事務(wù)中前后兩次查詢,查到了其他事務(wù)insert進(jìn)來的數(shù)據(jù)。
強(qiáng)調(diào)的是讀取到了其他事務(wù)插入進(jìn)來的數(shù)據(jù)。
下面來論證一下可重復(fù)讀下幻讀的解決方案
先明確一下,for update語法就是當(dāng)前讀,也就是查詢當(dāng)前已經(jīng)提交的數(shù)據(jù),并且是帶悲觀鎖的。沒有for update就是快照讀,也就是根據(jù)readView讀取的undolog中的數(shù)據(jù)。
如果按照以上猜想,那么整個(gè)執(zhí)行結(jié)果就違背了 可重復(fù)讀 的隔離級別了。
那么我們再假設(shè)select * from TABLE where d = 5 for update;這條語句鎖定的是所有被掃描到的數(shù)據(jù)。
這是因?yàn)門2階段的update會被阻塞住,畢竟所有被掃描到的記錄都被鎖定了。
按照上述推理過程,很顯然,即使鎖定所有掃描到的數(shù)據(jù)行,也依然存在幻讀的情況。違背了 可重復(fù)讀 的隔離級別。
針對這個(gè)情況,我們要解決幻讀的問題,那么就要求針對所有被掃描的記錄行以及還不存在的d=5的記錄行都給鎖住。
至此,當(dāng)前查詢結(jié)果完全滿足 可重復(fù)讀 的隔離級別。
通過以上推論,我們可以總結(jié)一下,在可重復(fù)讀的隔離級別下,解決幻讀除了需要鎖定所有掃描到的記錄行外,還需要鎖定行之間的間隙,也就是通過間隙鎖來解決幻讀的問題。
一個(gè)事務(wù)要更新一行,如果剛好有另外一個(gè)事務(wù)擁有這一行的行鎖,會被鎖住,進(jìn)入等待狀態(tài)。既然進(jìn)入了等待狀態(tài),那么等到這個(gè)事務(wù)自己獲取到行鎖要更新數(shù)據(jù)的時(shí)候,它讀到的值又是什么呢?
可重復(fù)讀隔離級別下,事務(wù)在啟動的時(shí)候就“拍了個(gè)整個(gè)庫的快照”。如果一個(gè)庫有100G,那么我啟動一個(gè)事務(wù),MySQL就要拷?100G的數(shù)據(jù)出來,這個(gè)過程得多慢啊。但是平時(shí)事務(wù)執(zhí)行起來卻是非??斓摹2皇侨靠截惓鰜砟鞘窃趺磳?shí)現(xiàn)的呢?
InnoDB里面每個(gè)事務(wù)有一個(gè)唯一的事務(wù)ID,叫作transaction id。它是在事務(wù)開始的時(shí)候向InnoDB的事務(wù)系統(tǒng)申請的,是按申請順序嚴(yán)格遞增的。
而每行數(shù)據(jù)也都是有多個(gè)版本的。每次事務(wù)更新數(shù)據(jù)的時(shí)候,都會生成一個(gè)新的數(shù)據(jù)版本,并且把transaction id賦值給這個(gè)數(shù) 據(jù)版本的事務(wù)ID,記為row trx_id。同時(shí),舊的數(shù)據(jù)版本要保留,并且在新的數(shù)據(jù)版本中,能夠有信息可以直接拿到它。
數(shù)據(jù)表中的一行記錄,其實(shí)可能有多個(gè)版本(row),每個(gè)版本有自己的row trx_id。
圖中虛線框里是同一行數(shù)據(jù)的4個(gè)版本,當(dāng)前最新版本是V4,k的值是22,它是被transaction id 為25的事務(wù)更新的,因此它的row trx_id也是25。語句更新會生成undo log(回滾日志),圖中的三個(gè)虛線箭頭,就是undo log。
按照可重復(fù)讀的定義,一個(gè)事務(wù)啟動的時(shí)候,能夠看到所有已經(jīng)提交的事務(wù)結(jié)果。但是之后,這個(gè)事務(wù)執(zhí)行期間,其他事務(wù)的更新對它不可?。
一個(gè)事務(wù)只需要在啟動的時(shí)候聲明說,“以我啟動的時(shí)刻為準(zhǔn),如果一個(gè)數(shù)據(jù)版本是在我啟動之前生成的,就認(rèn);如果是我啟動以后才生成的,我就不認(rèn),我必須要找到它的上一個(gè)版本”。
如果“上一個(gè)版本”也不可?,那就得繼續(xù)往前找。如果是這個(gè)事務(wù)自己更新的數(shù)據(jù),它自己還是要認(rèn)的。
在實(shí)現(xiàn)上, InnoDB為每個(gè)事務(wù)構(gòu)造了一個(gè)數(shù)組,用來保存這個(gè)事務(wù)啟動瞬間,當(dāng)前正在“活躍”的所有事務(wù)ID?!盎钴S”指的就 是,啟動了但還沒提交。數(shù)組里面事務(wù)ID的最小值記為低水位,當(dāng)前系統(tǒng)里面已經(jīng)創(chuàng)建過的事務(wù)ID的最大值加1記為高水位。 這個(gè)視圖數(shù)組和高水位,就組成了當(dāng)前事務(wù)的一致性視圖(read-view)。而數(shù)據(jù)版本的可?性規(guī)則,就是基于數(shù)據(jù)的row trx_id和這個(gè)一致性視圖的對比結(jié)果得到的。
InnoDB利用了“所有數(shù)據(jù)都有多個(gè)版本”的這個(gè)特性,實(shí)現(xiàn)了“秒級創(chuàng)建快照”的能力。
回到我們最開始的表格,看看最后執(zhí)行的結(jié)果是多少。做如下假設(shè):
事務(wù)A的視圖數(shù)組就是[99,100], 事務(wù)B的視圖數(shù)組是[99,100,101], 事務(wù)C的視圖數(shù)組是[99,100,101,102]。為了簡化分析,我先把其他干擾語句去掉,只畫出跟事務(wù)A查詢邏輯有關(guān)的操作:
第一個(gè)有效更新是事務(wù)C,把數(shù)據(jù)從(1,1)改成了(1,2)。這時(shí)候,這個(gè)數(shù)據(jù)的最新版本的row trx_id是102,而90這個(gè)版本已經(jīng)成為了歷史版本。 第二個(gè)有效更新是事務(wù)B,把數(shù)據(jù)從(1,2)改成了(1,3)。這時(shí)候,這個(gè)數(shù)據(jù)的最新版本(即row trx_id)是101,而102又成為了歷史版本。
事務(wù)B的update語句,如果按照一致性讀,好像結(jié)果不對哦?
事務(wù)B的視圖數(shù)組是先生成的,之后事務(wù)C才提交,不是應(yīng)該看不?(1,2)嗎,怎么能算出(1,3)來?
事務(wù)B在更新之前查詢一次數(shù)據(jù),這個(gè)查詢返回的k的值確實(shí)是1。 但是,當(dāng)它要去更新數(shù)據(jù)的時(shí)候,就不能再在歷史版本上更新了,否則事務(wù)C的更新就丟失了。因此,事務(wù)B此時(shí)的set k=k+1是在(1,2)的基礎(chǔ)上進(jìn)行的操作。 所以,這里就用到了這樣一條規(guī)則:更新數(shù)據(jù)都是先讀后寫的,而這個(gè)讀,只能讀當(dāng)前的值,稱為 “當(dāng)前讀” ( current read )。
在更新的時(shí)候,當(dāng)前讀拿到的數(shù)據(jù)是(1,2),更新后生成了新版本的數(shù)據(jù)(1,3),這個(gè)新版本的row trx_id是101。
所以,在執(zhí)行事務(wù)B查詢語句的時(shí)候,一看自己的版本號是101,最新數(shù)據(jù)的版本號也是101,是自己的更新,可以直接使用, 所以查詢得到的k的值是3。
select語句如果加鎖,也是當(dāng)前讀。
如果把事務(wù)A的查詢語句select * from t where id=1修改一下,加上lock in share mode 或 for update,也都可以讀到版本號是101的數(shù)據(jù),返回的k的值是3。下面這兩個(gè)select語句,就是分別加了讀鎖(S鎖,共享鎖)和寫鎖(X鎖,排他鎖)。
事務(wù)C’的不同是,更新后并沒有?上提交,在它提交前,事務(wù)B的更新語句先發(fā)起了。前面說過了,雖然事務(wù)C’還沒提交,但是(1,2)這個(gè)版本也已經(jīng)生成了,并且是當(dāng)前的最新版本。那么,事務(wù)B的更新語句會怎么處理呢?
兩階段鎖協(xié)議,事務(wù)C’沒提交,也就是說(1,2)這個(gè)版本上的寫鎖還沒釋放。 而事務(wù)B是當(dāng)前讀,必須要讀最新版本,而且必須加鎖,因此就被鎖住了,必須等到事務(wù)C’釋放這個(gè)鎖,才能繼續(xù)它的當(dāng)前讀。
回到最初的問題,事務(wù)的可重復(fù)讀的能力是怎么實(shí)現(xiàn)的?