先針對本身之前錯誤的思惟作個記錄, 你們能夠直接跳過html
鎖
來控制併發, MySQL也不例外, 也有不少和鎖相關的概念(留到後面會單獨整理一篇筆記出來), 因此一提到高併發產生的問題, 我會不自覺地提出一個疑問: 如今併發出問題了, 那怎麼用鎖的相關知識來解決?
;死鎖
, 鎖衝突
, 行鎖
,表鎖
, 讀鎖
, 寫鎖
, 樂觀鎖
, 悲觀鎖
......等等 N多鎖相關的名詞(後面的筆記會把全部本身遇到的, 所有整理並進行分析), 大量的篇幅, 高深晦澀的描述, 直接致使我意識裏認爲嗯, 鎖真tm高大上, 真tm高端, 確定tm就是它了
;髒讀
,不可重複讀
,幻讀
的資料中, 應該大篇幅的描述如何用鎖相關的知識來解決這些問題, 然而略失落了, 資料卻是提了點兒鎖的知識, 但更多的是用事務的哪一個隔離級別來解決這些問題, 鎖
哪兒去了?髒讀
,不可重複讀
,幻讀
這幾個問題的時候, 一上去就全亂了, 好比 髒讀
, 若是老是以MySQL鎖的相關知識做爲前提來分析, 就會陷入誤區 '事務A讀取數據的時候確定會加S鎖的, 事務B天然是沒法對未完成的事務A中的數據進行修改的, 我Ca, 這種髒讀的場景根本就不成立嘛!', 那爲何不提鎖, 而是用隔離級別來解決。暈了幾天以後,終於稍微醒了點......mysql
參考美團技術博客
![]()
隔離級別(鎖)
天然是不能做爲前置知識點的, 而是最終問題的解決方案!(未提交讀)sql
髒讀(Dirty Read)
的出現。雖然不多使用, 但仍是有必要了解一下, 它這個隔離級別到底是怎麼隔離的, 居然還能允許不少問題的存在? (老兄虧你還算個隔離級別, 怎麼辦事兒的...) 網上相關資料五花八門, 下面列幾個出來(但願你看完不要激動):segmentfault
說實話, 資料查到這份兒上, 我已經快崩潰了, 就READ UNCOMMITTED
這個隔離級別:併發
先準備一張測試表test_transaction
:app
DROP TABLE IF EXISTS `test_transaction`; CREATE TABLE `test_transaction` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `user_name` char(20) NOT NULL COMMENT '姓名', `age` tinyint(3) NOT NULL COMMENT '年齡', `gender` tinyint(1) NOT NULL COMMENT '1:男, 2:女', `desctiption` text NOT NULL COMMENT '簡介', PRIMARY KEY (`id`), KEY `name_age_gender_index` (`user_name`,`age`,`gender`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; INSERT INTO `test_transaction` VALUES (1, '金剛狼', 127, 1, '我有一雙鐵爪'); INSERT INTO `test_transaction` VALUES (2, '鋼鐵俠', 120, 1, '我有一身鐵甲'); INSERT INTO `test_transaction` VALUES (3, '綠巨人', 0, 2, '我有一身肉');
以下:高併發
mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 1 | 金剛狼 | 127 | 2 | 我有一雙鐵爪 | | 2 | 鋼鐵俠 | 120 | 1 | 我有一身鐵甲 | | 3 | 綠巨人 | 0 | 2 | 我有一身肉 | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec)
先查看當前會話(當前客戶端)事務的隔離級別: SELECT @@SESSION.tx_isolation;
能夠看到: REPEATABLE READ
是InnoDB存儲引擎的默認事務隔離級別sqlserver
mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.00 sec) mysql>
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
客戶端1開啓事務,並執行一個查詢'讀取數據':性能
mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 row in set (0.00 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from test_transaction where id=2; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 2 | 鋼鐵俠 | 120 | 1 | 我有一身鐵甲 | +----+-----------+-----+--------+--------------------+ 1 row in set (0.00 sec) mysql>
注意, 客戶端1此時的事務並未提交測試
客戶端2開啓事務, 並修改客戶端1查詢的數據
mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 row in set (0.00 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update test_transaction set user_name='鋼鐵俠-託尼' where id=2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql>
若是說客戶端1在讀取記錄的時候加了S鎖, 那麼客戶端2是不能加X鎖對該記錄進行更改的
, 因此能夠得出結論: 要麼是客戶端1讀取記錄的時候沒有加S鎖, 要麼是客戶端2更改記錄的時候不加任何鎖(這樣即便客戶端1加了S鎖,對它這個不加鎖的事務也迫不得已), 那麼到底是哪中狀況致使的? 下面繼續進行分析...rollback
事務, 再到客戶端1中查詢,發現user_name又變成了'鋼鐵俠', 那以前獨到'鋼鐵俠-託尼'就是髒數據了, 這就是一次 髒讀
客戶端1開啓事務, 而後對數據作修改
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update test_transaction set user_name='鋼鐵俠-rymuscle' where id=2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql>
注意, 客戶端1此時的事務並未提交
客戶端2開啓事務, 對相同的數據行作修改
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update test_transaction set user_name='鋼鐵俠-rym' where id=2; ....阻塞等待了
最終會以下:
注意: 在上面的過程, 在客戶端2阻塞階段, 你能夠經過一個新的客戶端來分析, 客戶端2在鎖等待的狀況下的 加鎖狀況
和 事務狀態
:
select * from information_schema.INNODB_LOCKS;
select * from information_schema.INNODB_TRX;
接下來你確定會納悶 "既然該隔離級別下事務在修改數據的時候加的是x鎖, 而且是事務完成後才釋放, 那以前的測試客戶端2在事務中修改完數據以後, 爲何事務還沒完成, 也就是x鎖還在, 結果客戶端1卻能讀取到客戶端2修改的數據"?這徹底不符合排他鎖的特性啊(要知道,排他鎖會阻塞除當前事務以外的其餘事務的讀,寫操做)
ansactions running at the READ UNCOMMITTED level do not issue shared locks to prevent other transactions from modifying data read by the current transaction. READ UNCOMMITTED transactions are also not blocked by exclusive locks that would prevent the current transaction from reading rows that have been modified but not committed by other transactions. When this option is set, it is possible to read uncommitted modifications, which are called dirty reads. Values in the data can be changed and rows can appear or disappear in the data set before the end of the transaction. This option has the same effect as setting NOLOCK on all tables in all SELECT statements in a transaction. This is the least restrictive of the isolation levels.
在 READ UNCOMMITTED 級別運行的事務不會發出共享鎖來防止其餘事務修改當前事務讀取的數據, 既然不加共享鎖了, 那麼當前事務所讀取的數據天然就能夠被其餘事務來修改。 並且當前事務要讀取其餘事務未提交的修改, 也不會被排他鎖阻止, 由於排他鎖會阻止其餘事務再對其鎖定的數據加讀寫鎖, **可是好笑的是, 事務在該隔離級別下去讀數據的話根本什麼鎖都不加, 這就讓排他鎖沒法排它了, 由於它連鎖都沒有**。 這就致使了事務能夠讀取未提交的修改, 稱爲髒讀。
因此能夠得出: READ UNCOMMITTED
隔離級別下, 讀不會加任何鎖。而寫會加排他鎖,併到事務結束以後釋放。
參考資料:
-《高性能MySQL》