數據庫往往是多個用戶在連接使用的,那麼**如何保證數據併發訪問的一致性、有效性呢?**首先我們看一下MySQL的鎖機制。
根據加鎖的範圍,MySQL中的鎖可分爲三類:
- 全局鎖;
- 表級鎖;
- 行鎖;
1. 全局鎖
MySQL全局鎖會關閉所有打開的表,並使用全局讀鎖鎖定所有表,其命令爲:
flush tables with read lock;
當執行上面這個命令後,所有的表都變成只讀狀態,數據更新或者字段更新都將會被阻塞,可以使用下面命令解鎖:
unlock tables;
全局鎖一般會在什麼時候用到呢?
全局鎖一般用在整個庫做備份(mysqldump)時。也就是說在整個備份過程中,整個庫都是隻讀的。
- 如果是主庫備份,會導致業務不能修改數據;
- 如果是從庫備份,會導致主從延遲;
好在mysqldump包含一個參數–single-transaction,可以在一個事務中創建一致性快照,然後在進行所有表的備份。因此增加這個參數的情況下,備份期間可以進行數據修改。但是需要所有表都是innodb表。所以這也是建議使用InnoDB引擎的原因之一。
2. 表級鎖
表級鎖有兩種:表鎖和元數據鎖。
2.1 表鎖
表鎖使用場景:
- 事務需要更新某張大表的大部分或全部數據。如果使用默認的行級鎖,不僅事務執行效率低,而且可能造成其它事務長時間鎖等待或鎖衝突,這種情況下可以考慮使用表鎖來提高事務執行速度;
- 事務設計多個表,比較複雜,可能會引起死鎖,導致大量事務回滾,可以考慮表鎖避免死鎖;
其中表鎖又分爲表讀鎖和表寫鎖,命令分別是:
表讀鎖:
lock tables t1 read;
lock tables t1 write;
- 對錶執行lock tables t1 read(表讀鎖)時,當前線程和其它線程都可以讀,本線程寫會報錯,其它線程寫會等待;
- 對錶執行lock tables t1 write(表寫鎖)時,當前線程可以讀寫,其它線程讀寫都會造成阻塞;
2.2 元數據鎖
在MySQL中,DDL是不屬於事務範疇的。如果事務和DDL並行執行同一張表,可能會出現事務特性被破壞等bug。爲了解決這個問題,MySQL引入了元數據鎖(MDL鎖)。MDL鎖的出現解決了同一張表上事務和DDL並行執行時可能導致數據不一致的問題。很多情況下,我們需要考慮MDL的存在,否則可能導致長時間鎖等待甚至連接被打滿的情況。
比如:執行如下SQL1語句,表示10秒後返回結果:
select id, a, b, SLEEP(10) from t1 limit 1;
在此期間,執行如下兩條SQL語句:
SQL2:
alter table t1 add column c int;
SQL3:
select id, a, b from t1 limit 1;
可以看出,SQL1屬於慢SQL,在慢SQL期間,如果執行了DDL語句,即執行了SQL2,SQL2會等待(原因是SQL1在執行期間對t1加了MDL鎖,SQL2又會跟SQL1爭搶MDL鎖),再執行SQL3,SQL3也會等待,因爲SQL1沒執行結束的話,其他的所有SQL查詢都會等待。
如果出現這種情況,可以kill掉SQL1或者終止SQL2的DDL操作,讓其他查詢操作恢復。
因此在工作中應該避免慢查詢、儘量保證事務及時提交,同時避免在業務高峯執行DDL操作。
3. 總結
-
全局鎖會讓所有的表變成只讀狀態,所有更新操作都會被阻塞,通常進行備份操作(mysqldump)時會用到全局鎖;
-
表級鎖分爲表鎖和元數據鎖,表鎖又分爲表讀鎖和表寫鎖,兩者的區別是:
表讀鎖:本線程和其他線程可以讀,本線程寫會報錯,其他線程寫會等待; 表寫鎖:本線程可以讀寫,其他線程讀寫都會阻塞;
-
爲了保證事務和DDL並行執行數據一致性,引入了MDL鎖,應該注意的幾點是:
儘量避免慢查詢; 事務要及時提交; 避免在業務高峯執行DDL操作;