MYSQL加鎖的測驗


 

 存儲引擎  支持的鎖定級別 

myisam      表級別 

memory     表級別 

  inndb       行級別 

 bdb:        頁級別 

 

lock鎖定類型

 鎖定方式            目的

 讀鎖                   自己與其他線程只能讀取該表

  寫鎖                  只有當前線程能夠對表進行寫入操作(其他線程也無法讀這部分數據)

 

 

讀鎖的英文叫法是shared locks,shared是共享的意思,共享鎖,就是所有用戶都可以共享進行讀(包括加鎖的用戶),不能寫。

 

寫鎖的英文叫法是Exclusive Locks,Exclusive是獨有、排外的意思,只有自己(加鎖的用戶)才能進行些寫入操作,其他用戶只能讀。有些書籍中有不同叫法,比如互斥鎖、排他鎖。

 

糾正一下:以前理解錯誤了。加了排他鎖定,其他事務是無法讀取數據的。網上的資料比較零散。

最近看了jim Gray那本《事務處理概念與技術》,里面提到排他鎖時:保留對該節點寫的權利,防止其他事務在該節點及其后代節點加{x,u,s,six,is,ix}鎖。

 

提到的s就是共享鎖了,既然防止其他事務加s鎖,那么其他線程是無法讀的(所有線程獲取數據之前必須先申請鎖才能操作)。文章下面加的試驗也會證明了這點。

 

ps:看來總結也好,加深了理解,如果不總結,也不會發現細節理解偏差。

 

 

 測試一:測驗讀鎖

 運行一個讀鎖:“lock table `cat` READ”

然后嘗試使用update語句操作 :

update `cat` set remark= 'ceshi'

 

報錯如下:

 

Table 'a' was locked with a READ lock and can't be updated

現在解鎖(針對當前線程鎖定的所有表解鎖):"unlock tables" 

 

然后再次運行update語句,報錯消失。完成更新操作。說明已經解除讀鎖了(可以進行更新) 

 

 測驗二:測驗寫鎖 

1、 我在一個客戶端SQLyog中運行一個寫鎖sql:LOCK TABLE cat WRITE;

2、另外開一個客戶端(線程),嘗試運行更新:

update `cat` set remark= 'jgjgjg'

 

情況:因為使用了寫鎖,只有自己能夠修改數據,其他線程無法執行update操作,此時服務器端的php程序一直等待數據庫給予響應結果,數據被鎖定了,根本沒法執行sql語句,處於等待中,數據庫並不會報錯,因為這本來就是一個很正常的情況,需要等待釋放鎖才能執行update操作,其實大並發環境下就是這種情況(很多用戶同時在執行sql操作,有的加了寫鎖定),於是速度就慢下來了。

結果phpmyadmin一直處於等待中,等待服務器給予客戶端(瀏覽器)數據,截圖如下:

 

 

 

 經驗:使用phpmyadmin無法模擬出一個線程的情況。因為每次php運行完畢后,與數據庫的連接就會自動斷開(php腳本特性)。

而SQLyog這樣的工具能夠保持連接不斷掉。所以,測驗寫鎖的時候完全可以使用同一個工具測驗出來。

 

 

 增加測驗:測驗加寫鎖后,其他事務無法讀數據的情況

同樣也是在SQLyog這樣的工具中運行:LOCK TABLE cat WRITE

 

因為加了x鎖后,其他事務是無法獲得s鎖,所以根本無法讀數據。phpmyadmin這邊查詢這個表,都是等待狀態。

 

 

 

 下面我在SQLyog釋放掉寫鎖后,phpmyadmin這邊就能讀取數據了

 

 

 

 

 

 

 

 

 

 

 

 

InnoDB使用行鎖定,BDB使用頁鎖定。對於這兩種存儲引擎,都可能存在死鎖。這是因為,在SQL語句處理期間,InnoDB自動獲得行鎖定和BDB獲得頁鎖定,而不是在事務啟動時獲得。

 

update語句默認比select語句優先權高。可以修改,在啟動mysql的時候加上參數,用--low-priority-updates啟動mysqld 

 

 

在大流量、大數量的網站,往往瓶頸不在於具體的語言,其實.net、java、php等語言速度的差別是有,但是很小,忽略不計。往往瓶頸在於數據庫。我是這樣理解:

 


$conn->query("update where ....");

...php其他代碼

 

//php需要等待數據庫(比如mysql)給予返回結果,才能繼續往下面執行代碼,但里mysql被鎖定了,一直沒有執行完畢。所以卡死了,一直在等待中。這就是為什么語言不是瓶頸,瓶頸往往在數據庫(數據量大的時候,數據庫壓力很大),這就是同步執行,需要等到上面代碼執行完畢才能繼續執行,以前聽過阿里巴巴的分享,使用一種異步執行方案。並行加載數據,那么代碼的執行耗時取決於最耗時的操作(因為所有操作都是並行開始運行)



 

其他測驗辦法:還可以開多個mysql命令行界面來測驗。每個界面就是一個session。

 

 

 

 

 

關於大表查詢少用left join的原因

進行select查操作,加的是共享鎖(對整個表還是對行,要看什么存儲引擎)。共享鎖,則其他線程只能讀數據。那么insert,update操作就會阻塞,等待select操作完成后釋放掉共享鎖后,這些線程才能進行寫數據操作。

如果使用left join進行關聯查詢,一張表的數據量大,就會導致copyingd table操作了,意思是要先復制一張臨時表,在臨時表上面進行計算操作。這個時間比較長,就會阻塞掉其他線程的寫入操作,一直處在等待狀態。需要進行寫入的線程越多,那么越多的線程等待。

 

解決辦法是:要迅速的查詢,拆分成多步驟來操作,這樣就算是加讀鎖的時候,也是針對當前操作的表。操作釋放后,可以快速的釋放掉鎖。

 

我想到用現實生活中哲學來理解:一次干多件事情,當然是爽,但是非常揪心,搞的復雜,影響的面廣,大面積的受到影響。如果拆分成小部分,一次干一小部分,那么受到影響的面積小。逐個逐個來分解任務。

 

在實際場景中,left join導致的阻塞其他線程的寫操作的場景,剛好被我遇到了:

 

上面是show processlist命令查看出來的,這個命令可以查看mysql當前有哪些查詢線程,會列出查詢的語句出來。

 

上面情況顯示,好幾個insert操作被阻塞掉,一直在延遲,time項如果我沒理解錯的話,就是已經等待了這么多秒數了。

信息顯示:waiting for table level lock。更新操作需要先獲取鎖(myisam存儲引擎只支持表級別的鎖,所以先獲取表鎖)。

 

 

問:到底是什么情況導致無法獲取表鎖?

肯定是其他線程在對別加了鎖,一直沒有執行完畢。從下面這張圖能看出是什么原因:

顯示的狀態欄表示Coping to tmp table,正在將數據復制到臨時表(tmp table)。左側顯示是查詢語句,是left join。

貌似進入死鎖狀態了,a想要獲取表的鎖,才能繼續操作。b線程也要獲取,但是有個線程一直沒有釋放掉。

 

什么情況下會出現 copying to tmp table的操作呢?

有英文這樣解釋:

Copying to tmp table on disk The temporary result set was larger than tmp_table_size and the thread is now changing the in memory-based temporary table to a disk based one to save memory.

 

臨時結果比tmp_table_size(這是一個內存緩存區)要大,存不下了,就會保持到磁盤去。

方案:

1、可以考慮把這個myisam存儲引擎調整為innodb,innodb就支持行鎖。

 

2、但是我覺得本質還是要減少left join查詢,需要這樣查詢,拆分成多條sql,分步驟來執行會更好。

 

在互聯網的應用結構中。聯表查詢,還是要慎重,因為數據庫是共享資源,很多應用程序都要來訪問。一旦一個線程鎖定數據表了,就會造成其他線程操作數據被阻塞了。

 

所以思想是盡量快速完成查詢。傳統的數據庫開發中使用復合查詢,盡量一條sql可以完成很多事情的思想。在互聯網是不要這樣子做。

 

拆分成多條sql進行查詢,應用程序編碼還是增加了很多復雜度,本來一條sql丟入到query($sql)中就完成,現在要分成多個步驟。程序員會覺得麻煩。

不過,為了性能優化,是要這樣子。數據庫資源畢竟有限。有時候程序員調整一下思路,對於數據庫壓力的緩解是很大的。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM