MySQL資料庫——多使用者併發訪問鎖
多使用者共享資源,出現併發訪問的時候,需要合理控制資源的訪問規則。鎖就是用來實現這些訪問規則的重要資料結構。
根據加鎖的範圍,鎖可以大致分成全域性鎖、表級鎖、行鎖。
(1)全域性鎖就是對整個資料庫例項加鎖。
業務的更新不只是增刪改資料DML,還有可能是加欄位等修改表結構的操作DDL。
全域性讀鎖,使用Flush tables with read LOCK,做全庫邏輯備份,即把整個庫的每個表都select出來存成文字。這樣在備份過程中整個庫完全處於只讀的狀態,這樣業務就得停擺,而且備份期間從庫不能執行主庫同步的binlog,會導致主從延遲。
場景:使用者購買一個課程,業務邏輯就要扣掉他的餘額,然後往已購課程裡面加上一門課。
順序:先備份賬戶餘額,然後使用者購買,然後備份已購課程。會發現錢沒少,課多了。
也就是說,不加鎖的話,備份系統得到的不是一個邏輯時間點,這個檢視是邏輯不一致的。
官方自帶的邏輯備份工具是mysqldump,當mysqldump使用引數-single-transaction的時候,導資料之前就會啟動一個事務,確保拿到一致性檢視。由於MVCC的支援,這個過程資料可以正常更新。但是這個一致性讀的前提是引擎支援隔離級別(暗戳戳說MyISAM不行)
(2)表級鎖(一般不支援行鎖的才會想用這種)
一種是表鎖,一種是元資料鎖。
a、表鎖的語法:lock tables ...read/write。可以用unlock tables主動釋放鎖,也可以在客戶端斷開的時候自動釋放。
除了會限制別的執行緒的讀寫之外,也限定了本執行緒接下來的操作物件。
例如:執行緒A執行了lock tables t1 read, t2 write;
其他執行緒寫t1、讀寫t2的語句都會被阻塞。
同時,執行緒A執行unlock tables之前,也只能執行讀t1、讀寫t2的操作。不允許寫t1,也不能訪問其他表。
鎖住整個表還是不好用的,影響面太大了。
b、元資料鎖MDL。MDL的作用是保證讀寫的正確性,不需要顯示使用,訪問表的時候會被自動加上。
對一個表做增刪改查操作的時候,加MDL讀鎖。對錶做結構變更操作的時候,加MDL寫鎖。
事務中的MDL鎖,在語句執行開始時申請,但是語句結束之後並不會馬上釋放,而會等到整個事務提交之後再釋放。
(3)行鎖
行鎖是在引擎層由各個引擎自己實現的。
不是所有的引擎都支援行鎖,比如MyISAM就不支援。不支援行鎖就只能用表鎖,表鎖的話,同一張表在任何時刻都只能有一個更新在執行,會影響業務併發度。
如果使用了行鎖,可以在一個表設定很多鎖,並通過減少鎖衝突來提高業務併發度。
行鎖是針對資料表中記錄得鎖。比如鎖定一行,只有在事務A操作完成後事務B才能執行。
兩階段鎖:有兩個事務都要執行update操作,只有事務Acommit之後事務B才能執行操作。
因為在InnoDB事務中,行鎖是在需要的時候加上的,等待事務結束才能釋放。這就是兩階段鎖協議。
如果事務需要鎖很多行,要把最可能造成鎖衝突,最可能影響併發度的鎖儘量往後放。
當併發系統中不同執行緒出現迴圈資源依賴,涉及的執行緒都在等待別的執行緒釋放資源的時候,就會導致幾個執行緒都無限等待,即死鎖。
解決方案:直接進入等待,直到超時。這個超時時間通過innodb_lock_wait_timeout來設定。也可以主動發起死鎖檢測,發現死鎖就主動回滾死鎖鏈的事務,執行其他事務。將引數innodb_deadlock_detect設定成on就表示開啟這個邏輯。