MYSQL加鎖的測驗

 

 存儲引擎  支持的鎖定級別 php

myisam      表級別 java

memory     表級別 mysql

  inndb       行級別 程序員

 bdb:        頁級別 sql

 

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語句,報錯消失。完成更新操做。說明已經解除讀鎖了(能夠進行更新) 

 

 測驗二:測驗寫鎖 

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

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

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(這是一個內存緩存區)要大,存不下了,就會保持到磁盤去。

方案:

一、能夠考慮把這個myisam存儲引擎調整爲innodb,innodb就支持行鎖。

 

二、可是我以爲本質仍是要減小left join查詢,須要這樣查詢,拆分紅多條sql,分步驟來執行會更好。

 

在互聯網的應用結構中。聯表查詢,仍是要慎重,由於數據庫是共享資源,不少應用程序都要來訪問。一旦一個線程鎖定數據表了,就會形成其餘線程操做數據被阻塞了。

 

因此思想是儘可能快速完成查詢。傳統的數據庫開發中使用複合查詢,儘可能一條sql能夠完成不少事情的思想。在互聯網是不要這樣子作。

 

拆分紅多條sql進行查詢,應用程序編碼仍是增長了不少複雜度,原本一條sql丟入到query($sql)中就完成,如今要分紅多個步驟。程序員會以爲麻煩。

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

相關文章
相關標籤/搜索