概述
Mysql數(shù)據(jù)庫在處理并發(fā)中下了很多功夫,鎖是為了更好的保護數(shù)據(jù)的正確和可靠,Mvcc是維持一個數(shù)據(jù)的多個版本,使得讀寫操作沒有沖突的解決并發(fā)的數(shù)據(jù)庫方案。
鎖
當數(shù)據(jù)訪問多了,就會出現(xiàn)并發(fā)的問題,Mysql鎖設(shè)計的初衷是處理并發(fā)問題。作為多用戶共享的資源,當出現(xiàn)并發(fā)訪問的時候,數(shù)據(jù)庫需要合理地控制資源的訪問規(guī)則。而鎖就是用來實現(xiàn)這些訪問規(guī)則的重要數(shù)據(jù)結(jié)構(gòu)。
(資料圖)
根據(jù)加鎖的范圍,MySQL 里面的鎖大致可以分成全局鎖、表級鎖和行鎖三類。
全局鎖
全局鎖就是對整個數(shù)據(jù)庫實例加鎖,當你需要讓整個庫處于只讀狀態(tài)的時候,可以使用這個命令,之后其他線程的以下語句會被阻塞:數(shù)據(jù)更新語句(數(shù)據(jù)的增刪改)、數(shù)據(jù)定義語句(包括建表、修改表結(jié)構(gòu)等)和更新類事務(wù)的提交語句。
全局鎖命令:
//加鎖Flush tables with read lock;//釋放鎖命令unlock tables;
全局鎖的典型使用場景是,做全庫邏輯備份。也就是把整庫每個表都 select 出來存成文本。
mysql> UPDATE runoob_tbl SET runoob_title="學習 C++" WHERE runoob_id=1;2013 - Lost connection to server during querymysql> INSERT INTO runoob_tbl (runoob_title, runoob_author, submission_date) VALUES ("學習 PHP", "菜鳥教程", NOW());2013 - Lost connection to server during querymysql> SELECT * FROM runoob_tbl;+-----------+--------------+---------------+-----------------+| runoob_id | runoob_title | runoob_author | submission_date |+-----------+--------------+---------------+-----------------+| 1 | Go 學習 | 菜鳥教程 | 2023-03-22 |+-----------+--------------+---------------+-----------------+1 row in set (0.01 sec)
釋放全局鎖后,所有的進程得到釋放,需要注意的是如果是終端操作需要清空鏈接緩存,或者打開新鏈接重試,mysql釋放鎖在當前的鏈接中是不生效的。
mysql> use mysql2;Database changedmysql> INSERT INTO runoob_tbl (runoob_title, runoob_author, submission_date) VALUES ("學習 PHP", "菜鳥教程", NOW());Query OK, 1 row affected (0.03 sec)
表級鎖
MySQL 里面表級別的鎖有兩種:一種是表鎖,一種是元數(shù)據(jù)鎖(meta data lock,MDL)。
表鎖是最常用的處理并發(fā)的方式。而對于 InnoDB 這種支持行鎖的引擎,一般不使用 lock tables 命令來控制并發(fā),畢竟鎖住整個表的影響面還是太大。
另一類表級的鎖是 MDL(metadata lock)。
//加鎖lock tables 表名 ... read/write;//釋放鎖unlock tables;
表級鎖分讀鎖和寫鎖,1)讀鎖,在進行讀鎖時,讀不會受到影響但是會阻塞其他進程的insert、update操作。
mysql> lock tables runoob_tbl read;Query OK, 0 rows affected (0.05 sec) mysql> select * from runoob_tbl;+-----------+--------------+---------------+-----------------+| runoob_id | runoob_title | runoob_author | submission_date |+-----------+--------------+---------------+-----------------+| 1 | 學習 C++ | 菜鳥教程 | 2023-03-22 || 2 | 學習 PHP | 菜鳥教程 | 2023-03-22 || 3 | 學習 PHP | 菜鳥教程 | 2023-03-22 |+-----------+--------------+---------------+-----------------+3 rows in set (0.02 sec)//寫操作mysql> INSERT INTO runoob_tbl (runoob_title, runoob_author, submission_date) VALUES ("學習 PHP", "菜鳥教程", NOW());2013 - Lost connection to server during querymysql> UPDATE runoob_tbl SET runoob_title="學習 C++" WHERE runoob_id=1;2013 - Lost connection to server during query
2)寫鎖,對指定表加了寫鎖,會阻塞右側(cè)客戶端的讀和寫。
mysql> lock tables runoob_tbl write;Query OK, 0 rows affected (0.02 sec)mysql> unlock tables;Query OK, 0 rows affected (0.03 sec)
行鎖
MySQL 的行鎖是在引擎層由各個引擎自己實現(xiàn)的,但并不是所有的引擎都支持行鎖,比如 MyISAM 引擎就不支持行鎖。
行鎖就是針對數(shù)據(jù)表中行記錄的鎖。這很好理解,比如事務(wù) A 更新了一行,而這時候事務(wù) B 也要更新同一行,則必須等事務(wù) A 的操作完成后才能進行更新。
兩階段鎖協(xié)議:在 InnoDB 事務(wù)中,行鎖是在需要的時候才加上的,但并不是不需要了就立刻釋放,而是要等到事務(wù)結(jié)束時才釋放。這個就是兩階段鎖協(xié)議。
每個新來的被堵住的線程,都要判斷會不會由于自己的加入導(dǎo)致了死鎖,這是一個時間復(fù)雜度是 O(n) 的操作,要耗費大量的 CPU 資源,應(yīng)該在邏輯上進行優(yōu)化。
Mvcc
因為加鎖會影響效率,MVCC全稱多版本并發(fā)控制(Multiversion concurrency control, MCC 或 MVCC),是數(shù)據(jù)庫管理系統(tǒng)常用的一種并發(fā)控制,理念是維持一個數(shù)據(jù)的多個版本,使得讀寫操作沒有沖突的解決并發(fā)的數(shù)據(jù)庫方案。
當前讀和快照讀
Mvcc 把事務(wù)的執(zhí)行語句分為當前讀和快照讀。
當前讀:總是讀取最新的版本的記錄。快照讀:讀取歷史版本的記錄,歷史版本保存在undo Log(回滾日志)中,快照讀就是MySQL為我們實現(xiàn)MVCC理想模型的其中一個具體非阻塞讀功能。//select * from Table //快照讀//Insert Update Delete //當前讀//Select ... lock in share mode //當前讀//Select ... for update //當前讀
事務(wù)的隔離解決有四種,可重復(fù)讀(RR)、讀已提交(RC)、讀未提交、序列化,查看全局隔離權(quán)限語句,舊版的myql使用tx開頭,否則報錯1193 - Unknown system variable "tx_isolation"
。
mysql> show variables like "transaction_isolation";+-----------------------+-----------------+| Variable_name | Value |+-----------------------+-----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+-----------------+1 row in set (0.07 sec)mysql> select @@transaction_isolation;+-------------------------+| @@transaction_isolation |+-------------------------+| REPEATABLE-READ |+-------------------------+
如果沒有設(shè)置隔離級別,可使用下面語句進行設(shè)置。
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;Query OK, 0 rows affected (0.03 sec)
MVCC的實現(xiàn)原理
MVCC的目的就是多版本并發(fā)控制,在數(shù)據(jù)庫中的實現(xiàn),就是為了解決讀寫沖突,它的實現(xiàn)原理主要是依賴記錄中的 3個隱式字段,undo日志 ,Read View 來實現(xiàn)的。
每行記錄除了我們自定義的字段外,還有數(shù)據(jù)庫隱式定義的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段。
DB_TRX_ID : 創(chuàng)建或最后修改記錄的事務(wù)IDDB_ROW_ID : 隱藏主鍵DB_ROLL_PTR :Undo Log里的回滾指針,7byte大小read-view:事務(wù)在快照時產(chǎn)生的讀視圖。
trx_list : 系統(tǒng)活躍的事務(wù)IDup_limit_id : 列表中事務(wù)最小的IDlow_limit_id : 系統(tǒng)尚未分配的下一個事務(wù)IDMvcc判斷的規(guī)則:
1.比較DB_TRX_ID和up_limit_id,如果小于,則當前事務(wù)能看到DB_TRX_ID的記錄,如果大于和等于,則進入下一個判斷
2.比較DB_TRX_ID和low_limit_id,如果大于等于則代表DB_TRX_ID的記錄在read-view生成后出現(xiàn)的,那么對于當前事務(wù)不可見。如果小于,則進入下一個判斷。
3.判斷DB_TRX_ID是否在活躍事務(wù)中,如果在,代表read-view生成時,事務(wù)還在活躍狀態(tài),修改的數(shù)據(jù)當前的事務(wù)是看不到的,如果不在,說明事務(wù)在read-view之前就commit了,那么修改的結(jié)果就是可見的。
可重復(fù)讀(repeatable read):每次進行快照讀時都生成讀視圖。
讀已提交(read committed):只有第一次時生成讀視圖,之后沒次都使用第一次時的讀視圖。
Mysql ( Innodb引擎 ) 保證數(shù)據(jù)的一致性
1.執(zhí)行更新語句update table set a= 1 where id = 2
2.將id=2
的行上的列值改為1
3.將修改更新到內(nèi)存中
4.記錄在第N個Page的地方做修改,并將這行記錄狀態(tài)為prepare
5.修改好了,可以提交事務(wù)了
6.寫入binlog
7.commit,提交事務(wù)
8.將redo log里這個事務(wù)的相關(guān)記錄狀態(tài)置為commit狀態(tài)
這個數(shù)據(jù)提交過程,就是兩階段提交,在恢復(fù)數(shù)據(jù)時,用binlog和redolog兩部分來比較做數(shù)據(jù)恢復(fù)就可以了。
事務(wù)
事務(wù)的特性:
原子性:UndoLog 隔離性:Mvcc持久性:RedoLog一致性:以上3個共同保證了一致性問題
1.既然有了redolog,為什么還要有binlog呢?
redolog依賴于搜索引擎層,并不是每一個引擎都有redolog,binlog是屬于MysqlServer層。
關(guān)鍵詞: