時間:2022-09-11來源:www.farandoo.com作者:電腦系統城
于MySQL中的樂觀鎖和悲觀鎖,可能很多的開發者還不是很熟悉,并不知道其中具體是如何實現的。本文就針對這個問題做一個實際案例演示,讓你徹底明白這兩種鎖的區別。
MySQL的中鎖按照范圍主要分為表鎖、行鎖和頁面鎖。其中myisam存儲引擎只支持表鎖,InnoDB不僅僅支持行鎖,在一定程度上也支持表鎖。按照行為可以分為共享鎖(讀鎖)、排他鎖(寫鎖)和意向鎖。按照思想分為樂觀鎖和悲觀鎖。
今天的文章演示一下實際中的樂觀鎖和悲觀鎖是如何操作的。
下面的SQL語句是表的結構:
1 2 3 4 5 6 7 8 9 |
CREATE TABLE `demo`.` user ` ( `id` int (10) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT, ` name ` varchar (255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , `sex` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `email` varchar (255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL , `mobile` varchar (20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL , `version` int (1) NULL DEFAULT 1 COMMENT '數據版本號' , PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic ; |
插入模擬數據:
1 2 3 4 5 6 7 8 9 |
BEGIN ; INSERT INTO ` user ` VALUES (0000000001, '張三' , 0, '18228937997@163.com' , '18228937997' , 1); INSERT INTO ` user ` VALUES (0000000002, '李四' , 0, '1005349393@163.com' , '15683202302' , 1); INSERT INTO ` user ` VALUES (0000000003, '李四1' , 0, '1005349393@163.com' , '15683202302' , 1); INSERT INTO ` user ` VALUES (0000000004, '李四2' , 0, '1005349393@163.com' , '15683202302' , 1); INSERT INTO ` user ` VALUES (0000000005, '李四3' , 0, '1005349393@163.com' , '15683202302' , 1); INSERT INTO ` user ` VALUES (0000000006, '李四4' , 0, '1005349393@163.com' , '15683202302' , 1); INSERT INTO ` user ` VALUES (0000000007, '李四55' , 0, '1005349393@163.com' , '15683202302' , 1); COMMIT ; |
表中數據。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
mysql root@127.0.0.1:demo> select * from user ; + ----+--------+-----+---------------------+-------------+---------+ | id | name | sex | email | mobile | version | + ----+--------+-----+---------------------+-------------+---------+ | 1 | 張三 | 0 | 18228937997@163.com | 18228937997 | 2 | | 2 | 李四 | 0 | 1005349393@163.com | 15683202302 | 1 | | 3 | 李四1 | 0 | 1005349393@163.com | 15683202302 | 1 | | 4 | 李四2 | 0 | 1005349393@163.com | 15683202302 | 1 | | 5 | 李四3 | 0 | 1005349393@163.com | 15683202302 | 1 | | 6 | 李四4 | 0 | 1005349393@163.com | 15683202302 | 1 | | 7 | 李四55 | 0 | 1005349393@163.com | 15683202302 | 1 | + ----+--------+-----+---------------------+-------------+---------+ 7 rows in set Time : 0.011s |
悲觀鎖,比較消極的一種鎖處理方式。直接在操作數據時,搶占鎖。其他的事務在進行時就會等待,直到占有鎖的事務釋放鎖為止。
這種處理方式能保證數據的最大一致性,但是容易導致鎖超時、并發程度低等問題。 首先我們開啟事務一,并且對id=1的數據進行update操作,此時我們不提交事務。
1 2 3 4 5 6 |
mysql root@127.0.0.1:demo> begin ; Query OK, 0 rows affected Time : 0.002s mysql root@127.0.0.1:demo> update ` user ` set name = '張三111111' where id = 1; Query OK, 1 row affected Time : 0.004s |
接著我們開啟事務二,對id=1的數據進行update操作,查看此時會發生什么情況?
1 2 3 4 |
mysql root@127.0.0.1:demo> begin ; Query OK, 0 rows affected Time : 0.002s mysql root@127.0.0.1:demo> update ` user ` set sex = 1 where id = 1; |
我們執行完update語句之后,就處于等待狀態,SQL語句也不會馬上被執行,這是因為事務一沒有commit,也就沒有釋放id=1的數據對應的寫鎖。
效果如下圖:
通過上面的例子,我們就能比較直觀的感受到悲觀鎖的實現過程是如何的。
樂觀鎖認為數據一般情況下不會造成沖突,只有當數據去執行修改情況時,才會針對數據沖突做處理。這里是如何發現沖突了呢?常規的方式,都是在數據行上加一個版本號或者時間戳等字段。(本文使用version作為版本好方式,使用時間戳方式同理)
樂觀鎖的實現原理:
客戶端一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
mysql root@127.0.0.1:demo> select * from user where id = 1; + ----+------------+-----+---------------------+-------------+---------+ | id | name | sex | email | mobile | version | + ----+------------+-----+---------------------+-------------+---------+ | 1 | 張三111111 | 0 | 18228937997@163.com | 18228937997 | 1 | + ----+------------+-----+---------------------+-------------+---------+ 1 row in set Time : 0.012s mysql root@127.0.0.1:demo> update ` user ` set name = '事務一' , version = version + 1 where id = 1 and version = 1; Query OK, 1 row affected Time : 0.008s mysql root@127.0.0.1:demo> select * from user where id = 1; + ----+--------+-----+---------------------+-------------+---------+ | id | name | sex | email | mobile | version | + ----+--------+-----+---------------------+-------------+---------+ | 1 | 事務一 | 1 | 18228937997@163.com | 18228937997 | 2 | + ----+--------+-----+---------------------+-------------+---------+ 1 row in set Time : 0.009s |
執行update語句的順序應該在客戶端二執行了select之后,在執行。
客戶端二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
mysql root@127.0.0.1:demo> select * from user where id = 1; + ----+------------+-----+---------------------+-------------+---------+ | id | name | sex | email | mobile | version | + ----+------------+-----+---------------------+-------------+---------+ | 1 | 張三111111 | 1 | 18228937997@163.com | 18228937997 | 1 | + ----+------------+-----+---------------------+-------------+---------+ 1 row in set Time : 0.015s mysql root@127.0.0.1:demo> update ` user ` set name = '事務二' , version = version + 1 where id = 1 and version = 1; Query OK, 0 rows affected Time : 0.003s mysql root@127.0.0.1:demo> select * from user where id = 1; + ----+--------+-----+---------------------+-------------+---------+ | id | name | sex | email | mobile | version | + ----+--------+-----+---------------------+-------------+---------+ | 1 | 事務一 | 1 | 18228937997@163.com | 18228937997 | 2 | + ----+--------+-----+---------------------+-------------+---------+ 1 row in set Time : 0.012s |
此時根據update返回的結構,可以看出受影響的行數為0,同時select查詢之后,返現數據也是事務一的數據。
悲觀鎖:比較適合寫入操作比較頻繁的場景,如果出現大量的讀取操作,每次讀取的時候都會進行加鎖,這樣會增加大量的鎖的開銷,降低了系統的吞吐量。
樂觀鎖:比較適合讀取操作比較頻繁的場景,如果出現大量的寫入操作,數據發生沖突的可能性就會增大,為了保證數據的一致性,應用層需要不斷的重新獲取數據,這樣會增加大量的查詢操作,降低了系統的吞吐量。
兩種所各有優缺點,讀取頻繁使用樂觀鎖,寫入頻繁使用悲觀鎖。
像樂觀鎖適用于寫比較少的情況下,即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生沖突,上層應用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適,之所以用悲觀鎖就是因為兩個用戶更新同一條數據的概率高,也就是沖突比較嚴重的情況下,所以才用悲觀鎖。
2022-09-11
SQL Server服務器監控2022-09-11
系統城分享CentOS7下安裝MySQL5.7.39的詳細過程2022-06-20
Ubuntu安裝Mysql+啟用遠程連接的完整過程一、實驗目的 二、實驗要求 三、實現內容及步驟 1、學生表:student 2、寢室表:dormitory 3、管理員表:admin 四、實驗總結...
2022-06-20