首頁  >  文章  >  資料庫  >  【MySQL】並發控制

【MySQL】並發控制

黄舟
黄舟原創
2017-02-25 10:25:261273瀏覽


無論何時,只有有多個查詢需要在同一時刻修改數據,都會產生並發控制的問題。這裡討論MySQL在兩個層面的同時控制:伺服器層與儲存引擎層。並發控制是一個內容龐大的議題,有大量的理論文獻對其進行詳細的論述。在此只是簡要地討論MySQL如何控制並發讀寫。

以unix系統的email box為例子,典型的mbox檔案格式是非常簡單的。一個mbox郵箱中的所有郵件都串行在一起,彼此首尾相連。這種格式對於讀取和肥西郵件資訊非常友好,同時投遞郵件也很容易,只要在文件末尾附加新的郵件內容即可。

但是如果兩個行程在同一時刻對同一個郵件信箱投遞郵件,會發生什麼事?顯然,郵箱的資料會被破壞,兩封郵件的內容會交叉地附加在郵箱文件的末端。設計娘好的郵箱投遞系統會透過鎖(lock)來防止資料損壞。如果客戶試圖投遞郵件,而郵箱已經被其他客戶鎖住,那麼就必須等待,直到鎖釋放才能進行投遞。

這種鎖的方案在實際​​應用環境中雖然運作良好,但是並不支援並發處理。因為在任意一個時刻,只有一個程序可以修改郵箱的數據,這在大容量的郵箱系統中是個問題。

讀取寫入鎖定

從郵箱讀取資料沒有這樣的麻煩,即使同一時刻多個使用者並發讀取也不會有什麼問題。因為讀取不會修改數據,所以不會出錯。但是如果某個客戶正在讀取郵箱,同時另一個使用者試圖刪除編號為25的郵件,會產生什麼結果?結論是不確定的,讀取的客戶可能會報錯退出,也可能讀取不到一致的郵箱資料。所以,為了安全起見,即使是讀取郵箱也需要特別注意。

如果把上述的郵箱當成資料庫中的一張表,把郵件當成表中的一行記錄,就很容易看出,同樣的問題依然存在。從很多方面來說,郵箱就是一張簡單的資料庫表。修改資料庫表中的記錄,並刪除或修改郵箱中的郵件訊息,十分類似。

解決這類經典的問題的方法就是並發控制(讀鎖和寫鎖)。其實非常簡單,在處理並發讀或寫入的時候,可以透過實作一個由兩種類型的鎖組成鎖定係統來解決問題。這兩種類型的鎖通常稱為共享鎖定(shared lock)排他鎖(exclusive lock),也叫讀鎖定(read lock)寫鎖(write lock)

這裡先不討論如何具體實現,描述一下鎖的概念如下:讀鎖是共享的,或者說是相互不阻塞的。多個客戶在同一時刻可以同時讀取相同資源,而互不干擾。寫鎖則是排他的,也就是說一個寫鎖會阻塞其他的寫鎖和讀鎖,這是出於安全策略的考慮,只有這樣,才能確保給定的時間裡,只有一個用戶能執行寫入,並防止其他使用者讀取正在寫入的相同資源。

在實際的資料庫系統中,每時每刻都在發生鎖定,當某個使用者在修改某一部分資料的時候,MySQL會透過鎖定來防止其他使用者讀取相同資料。大多數時候,MySQL鎖的內部管理都是透明的。

鎖定粒度

一種提供共享資源並發性的方式就是讓鎖定物件更有選擇性。盡量只鎖定需要修改的部分數據,而不是所有的資源。更理想的方式是,只對會修改的資料片(具體到鎖定所修改的欄位)進行精確的鎖定。任何時候,在給定的資源上,鎖定的資料量越少,則係統的並發成都越高,只要互相之間不發生衝突即可。

問題是加鎖也需要消耗資源。鎖的各種操作,包括取得鎖,檢查鎖是否已解除,釋放鎖等,都會增加系統的開銷。如果系統花費大量的時間來管理鎖,而不是存取數據,那麼系統的效能可能因此受到影響。

所謂的鎖策略,就是在鎖的開銷和資料的安全性之間尋求平衡,這種平衡當然有會影響到效能,大多數商業資料庫系統沒有提供更多的選擇,一般都是在表上施加行級鎖(row-level lock),並以各種複雜的方式來實現,以便在鎖比較多的情況下盡可能地提供更好的性能。

而MySQL則提供多種選擇,每種MySQL儲存引擎都可以實現自己的鎖定策略和鎖定粒度。在儲存引擎的設計中,鎖定管理是個非常重要的決定。將鎖定粒度固定在某個級別,可以為某些特定的應用場景提供更好的效能。但是同事卻會失去對另外一些應用場景的良好支持。好在Mysql支援多個儲存引擎的架構,所以不需要單一的通用解決方案。以下介紹兩種最重要的鎖定策略。

表格鎖定(table lock)

表格鎖定是MySQL中最基本的鎖定策略,也是開銷最小的策略。表鎖非常類似前文所描述的郵箱加鎖機制:它會鎖定整張表。一個使用者在對錶進行寫入操作(插入,刪除,更新等等)前,需要先取得寫鎖,這個會阻塞其他使用者對該表的所有讀寫操作。只有沒有寫鎖的時候,其他讀取的使用者才能獲得讀鎖,讀鎖之間是不互相阻塞的。

在特定的場景中,表鎖定也可能有良好的效能。例如,read local表鎖支援某些類型的並發寫入操作。另外,寫鎖也比讀鎖有更高的優先權,因此一個寫鎖請求可能會被插入到讀鎖佇列的前面(寫鎖可以插入到鎖佇列中讀鎖的前面,反之讀鎖則不能插入寫鎖的前面)。

儘管儲存引擎可以管理自己的鎖,Mysql本身還是會使用各種有效的表鎖來實現不同的目的。例如伺服器會諸如alter table之類的語句使用表鎖,而忽略儲存引擎的鎖定機制。

【備註:鎖定機制是儲存引擎管理的,但MySQL本身也會有時候強制管理這個鎖定機制】

行級鎖定(row lock)

行級鎖定可以最大程度地支援並發處理(同時也帶來了最大的鎖定開銷)。眾多周知,在InnoDB和XtraDB,以及其他一些儲存引擎中實作了行級鎖定。 行級鎖定只在儲存引擎層實作,而MySQL伺服器層(如果有必要,請回顧上篇的邏輯架構圖)沒有實作。伺服器層完全不了解儲存引擎中的鎖實作。在本章的後續內容以及全書中,所有的儲存引擎都以自己的方式顯示了鎖定機制

無論何時,只有有多個查詢需要在同一時刻修改數據,都會產生並發控制的問題。這裡討論MySQL在兩個層面的同時控制:伺服器層與儲存引擎層。並發控制是一個內容龐大的議題,有大量的理論文獻對其進行詳細的論述。在此只是簡要地討論MySQL如何控制並發讀寫。

以unix系統的email box為例子,典型的mbox檔案格式是非常簡單的。一個mbox郵箱中的所有郵件都串行在一起,彼此首尾相連。這種格式對於讀取和肥西郵件資訊非常友好,同時投遞郵件也很容易,只要在文件末尾附加新的郵件內容即可。

但是如果兩個行程在同一時刻對同一個郵件信箱投遞郵件,會發生什麼事?顯然,郵箱的資料會被破壞,兩封郵件的內容會交叉地附加在郵箱文件的末端。設計娘好的郵箱投遞系統會透過鎖(lock)來防止資料損壞。如果客戶試圖投遞郵件,而郵箱已經被其他客戶鎖住,那麼就必須等待,直到鎖釋放才能進行投遞。

這種鎖的方案在實際​​應用環境中雖然運作良好,但是並不支援並發處理。因為在任意一個時刻,只有一個程序可以修改郵箱的數據,這在大容量的郵箱系統中是個問題。

讀取寫入鎖定

從郵箱讀取資料沒有這樣的麻煩,即使同一時刻多個使用者並發讀取也不會有什麼問題。因為讀取不會修改數據,所以不會出錯。但是如果某個客戶正在讀取郵箱,同時另一個使用者試圖刪除編號為25的郵件,會產生什麼結果?結論是不確定的,讀取的客戶可能會報錯退出,也可能讀取不到一致的郵箱資料。所以,為了安全起見,即使是讀取郵箱也需要特別注意。

如果把上述的郵箱當成資料庫中的一張表,把郵件當成表中的一行記錄,就很容易看出,同樣的問題依然存在。從很多方面來說,郵箱就是一張簡單的資料庫表。修改資料庫表中的記錄,並刪除或修改郵箱中的郵件訊息,十分類似。

解決這類經典的問題的方法就是並發控制(讀鎖和寫鎖)。其實非常簡單,在處理並發讀或寫入的時候,可以透過實作一個由兩種類型的鎖組成鎖定係統來解決問題。這兩種類型的鎖通常稱為共享鎖定(shared lock)排他鎖(exclusive lock),也叫讀鎖定(read lock)寫鎖(write lock)

這裡先不討論如何具體實現,描述一下鎖的概念如下:讀鎖是共享的,或者說是相互不阻塞的。多個客戶在同一時刻可以同時讀取相同資源,而互不干擾。寫鎖則是排他的,也就是說一個寫鎖會阻塞其他的寫鎖和讀鎖,這是出於安全策略的考慮,只有這樣,才能確保給定的時間裡,只有一個用戶能執行寫入,並防止其他使用者讀取正在寫入的相同資源。

在實際的資料庫系統中,每時每刻都在發生鎖定,當某個使用者在修改某一部分資料的時候,MySQL會透過鎖定防止其他使用者讀取相同資料。大多數時候,MySQL鎖的內部管理都是透明的。

鎖定粒度

一種提供共享資源並發性的方式就是讓鎖定物件更有選擇性。盡量只鎖定需要修改的部分數據,而不是所有的資源。更理想的方式是,只對會修改的資料片(具體到鎖定所修改的欄位)進行精確的鎖定。任何時候,在給定的資源上,鎖定的資料量越少,則係統的並發成都越高,只要互相之間不發生衝突即可。

問題是加鎖也需要消耗資源。鎖的各種操作,包括取得鎖,檢查鎖是否已解除,釋放鎖等,都會增加系統的開銷。如果系統花費大量的時間來管理鎖,而不是存取數據,那麼系統的效能可能因此受到影響。

所謂的鎖策略,就是在鎖的開銷和資料的安全性之間尋求平衡,這種平衡當然有會影響到效能,大多數商業資料庫系統沒有提供更多的選擇,一般都是在表上施加行級鎖(row-level lock),並以各種複雜的方式來實現,以便在鎖比較多的情況下盡可能地提供更好的性能。

而MySQL則提供多種選擇,每種MySQL儲存引擎都可以實現自己的鎖定策略和鎖定粒度。在儲存引擎的設計中,鎖定管理是個非常重要的決定。將鎖定粒度固定在某個級別,可以為某些特定的應用場景提供更好的效能。但是同事卻會失去對另外一些應用場景的良好支持。好在Mysql支援多個儲存引擎的架構,所以不需要單一的通用解決方案。以下介紹兩種最重要的鎖定策略。

表格鎖定(table lock)

表格鎖定是MySQL中最基本的鎖定策略,並且是開銷最小的策略。表鎖非常類似前文所描述的郵箱加鎖機制:它會鎖定整張表。一個使用者在對錶進行寫入操作(插入,刪除,更新等等)前,需要先取得寫鎖,這個會阻塞其他使用者對該表的所有讀寫操作。只有沒有寫鎖的時候,其他讀取的使用者才能獲得讀鎖,讀鎖之間是不互相阻塞的。

在特定的場景中,表鎖定也可能有良好的效能。例如,read local表鎖支援某些類型的並發寫入操作。另外,寫鎖也比讀鎖有更高的優先權,因此一個寫鎖請求可能會被插入到讀鎖佇列的前面(寫鎖可以插入到鎖佇列中讀鎖的前面,反之讀鎖則不能插入寫鎖的前面)。

儘管儲存引擎可以管理自己的鎖,Mysql本身還是會使用各種有效的表鎖來實現不同的目的。例如伺服器會諸如alter table之類的語句使用表鎖,而忽略儲存引擎的鎖定機制。

【備註:鎖定機制是儲存引擎管理的,但MySQL本身也會有時候強制管理這個鎖定機制】

行級鎖定(row lock)

行級鎖定可以最大程度地支援並發處理(同時也帶來了最大的鎖定開銷)。眾多周知,在InnoDB和XtraDB,以及其他一些儲存引擎中實作了行級鎖定。 行級鎖定只在儲存引擎層實作,而MySQL伺服器層(如果有必要,請回顧上篇的邏輯架構圖)沒有實作。伺服器層完全不了解儲存引擎中的鎖實作。在本章的後續內容以及全書中,所有的儲存引擎都以自己的方式顯示了鎖定機制

 以上就是【MySQL】同時控制的內容,更多相關內容請關注PHP中文網(www.php.cn)!


#
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn