MySQL樂(lè)觀鎖和悲觀鎖具體實(shí)現(xiàn)
前言
對(duì)于MySQL中的樂(lè)觀鎖和悲觀鎖,可能很多的開(kāi)發(fā)者還不是很熟悉,并不知道其中具體是如何實(shí)現(xiàn)的。本文就針對(duì)這個(gè)問(wèn)題做一個(gè)實(shí)際案例演示,讓你徹底明白這兩種鎖的區(qū)別。
鎖分類
MySQL的中鎖按照范圍主要分為表鎖、行鎖和頁(yè)面鎖。其中myisam存儲(chǔ)引擎只支持表鎖,InnoDB不僅僅支持行鎖,在一定程度上也支持表鎖。按照行為可以分為共享鎖(讀鎖)、排他鎖(寫(xiě)鎖)和意向鎖。按照思想分為樂(lè)觀鎖和悲觀鎖。
今天的文章演示一下實(shí)際中的樂(lè)觀鎖和悲觀鎖是如何操作的。
表結(jié)構(gòu)
下面的SQL語(yǔ)句是表的結(jié)構(gòu):
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 '數(shù)據(jù)版本號(hào)', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
插入模擬數(shù)據(jù):
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;
表中數(shù)據(jù)。
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
悲觀鎖
悲觀鎖,比較消極的一種鎖處理方式。直接在操作數(shù)據(jù)時(shí),搶占鎖。其他的事務(wù)在進(jìn)行時(shí)就會(huì)等待,直到占有鎖的事務(wù)釋放鎖為止。
這種處理方式能保證數(shù)據(jù)的最大一致性,但是容易導(dǎo)致鎖超時(shí)、并發(fā)程度低等問(wèn)題。 首先我們開(kāi)啟事務(wù)一,并且對(duì)id=1的數(shù)據(jù)進(jìn)行update操作,此時(shí)我們不提交事務(wù)。
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
接著我們開(kāi)啟事務(wù)二,對(duì)id=1的數(shù)據(jù)進(jìn)行update操作,查看此時(shí)會(huì)發(fā)生什么情況?
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;
我們執(zhí)行完update語(yǔ)句之后,就處于等待狀態(tài),SQL語(yǔ)句也不會(huì)馬上被執(zhí)行,這是因?yàn)槭聞?wù)一沒(méi)有commit,也就沒(méi)有釋放id=1的數(shù)據(jù)對(duì)應(yīng)的寫(xiě)鎖。
效果如下圖:
通過(guò)上面的例子,我們就能比較直觀的感受到悲觀鎖的實(shí)現(xiàn)過(guò)程是如何的。
樂(lè)觀鎖
樂(lè)觀鎖認(rèn)為數(shù)據(jù)一般情況下不會(huì)造成沖突,只有當(dāng)數(shù)據(jù)去執(zhí)行修改情況時(shí),才會(huì)針對(duì)數(shù)據(jù)沖突做處理。這里是如何發(fā)現(xiàn)沖突了呢?常規(guī)的方式,都是在數(shù)據(jù)行上加一個(gè)版本號(hào)或者時(shí)間戳等字段。(本文使用version作為版本好方式,使用時(shí)間戳方式同理)
樂(lè)觀鎖的實(shí)現(xiàn)原理:
- 一個(gè)事務(wù)在讀取數(shù)據(jù)時(shí),將對(duì)應(yīng)的版本號(hào)字段讀取出來(lái),假設(shè)此時(shí)的版本號(hào)是1。
- 另外一個(gè)事務(wù)也是執(zhí)行同樣的讀取操作。當(dāng)事務(wù)一提交時(shí),對(duì)版本號(hào)執(zhí)行+1,此時(shí)該數(shù)據(jù)行的版本號(hào)就是2。
- 第二個(gè)事務(wù)執(zhí)行修改操作時(shí),針對(duì)業(yè)務(wù)數(shù)據(jù)做條件,并默認(rèn)增加一個(gè)版本號(hào)作為where條件。此時(shí)修改語(yǔ)句中的版本號(hào)字段是不滿足where條件,該事務(wù)執(zhí)行失敗。通過(guò)這種方式來(lái)達(dá)到鎖的功能。
客戶端一:
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 = '事務(wù)一', 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 | 事務(wù)一 | 1 | 18228937997@163.com | 18228937997 | 2 | +----+--------+-----+---------------------+-------------+---------+ 1 row in set Time: 0.009s
執(zhí)行update語(yǔ)句的順序應(yīng)該在客戶端二執(zhí)行了select之后,在執(zhí)行。
客戶端二:
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 = '事務(wù)二', 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 | 事務(wù)一 | 1 | 18228937997@163.com | 18228937997 | 2 | +----+--------+-----+---------------------+-------------+---------+ 1 row in set Time: 0.012s
此時(shí)根據(jù)update返回的結(jié)構(gòu),可以看出受影響的行數(shù)為0,同時(shí)select查詢之后,返現(xiàn)數(shù)據(jù)也是事務(wù)一的數(shù)據(jù)。
適用場(chǎng)景
悲觀鎖:比較適合寫(xiě)入操作比較頻繁的場(chǎng)景,如果出現(xiàn)大量的讀取操作,每次讀取的時(shí)候都會(huì)進(jìn)行加鎖,這樣會(huì)增加大量的鎖的開(kāi)銷,降低了系統(tǒng)的吞吐量。
樂(lè)觀鎖:比較適合讀取操作比較頻繁的場(chǎng)景,如果出現(xiàn)大量的寫(xiě)入操作,數(shù)據(jù)發(fā)生沖突的可能性就會(huì)增大,為了保證數(shù)據(jù)的一致性,應(yīng)用層需要不斷的重新獲取數(shù)據(jù),這樣會(huì)增加大量的查詢操作,降低了系統(tǒng)的吞吐量。
總結(jié)
兩種所各有優(yōu)缺點(diǎn),讀取頻繁使用樂(lè)觀鎖,寫(xiě)入頻繁使用悲觀鎖。
像樂(lè)觀鎖適用于寫(xiě)比較少的情況下,即沖突真的很少發(fā)生的時(shí)候,這樣可以省去了鎖的開(kāi)銷,加大了系統(tǒng)的整個(gè)吞吐量。但如果經(jīng)常產(chǎn)生沖突,上層應(yīng)用會(huì)不斷的進(jìn)行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適,之所以用悲觀鎖就是因?yàn)閮蓚€(gè)用戶更新同一條數(shù)據(jù)的概率高,也就是沖突比較嚴(yán)重的情況下,所以才用悲觀鎖。
悲觀鎖比較適合強(qiáng)一致性的場(chǎng)景,但效率比較低,特別是讀的并發(fā)低。樂(lè)觀鎖則適用于讀多寫(xiě)少,并發(fā)沖突少的場(chǎng)景。
到此這篇關(guān)于MySQL樂(lè)觀鎖和悲觀鎖具體實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)MySQL樂(lè)觀鎖和悲觀鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用mysql記錄從url返回的http GET請(qǐng)求數(shù)據(jù)操作
這篇文章主要介紹了使用mysql記錄從url返回的http GET請(qǐng)求數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01在MySQL中實(shí)現(xiàn)二分查找的詳細(xì)教程
這篇文章主要介紹了在MySQL中實(shí)現(xiàn)二分查找的詳細(xì)教程,來(lái)自計(jì)算機(jī)研究生考試原題,需要的朋友可以參考下2015-05-05使用Canal實(shí)現(xiàn)MySQL主從同步的流程步驟
這篇文章主要介紹了如何使用Canal實(shí)現(xiàn)MySQL主從同步效果,文中通過(guò)代碼示例和圖文結(jié)合的方式給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-04-04mysql中索引與FROM_UNIXTIME的問(wèn)題
這篇文章主要介紹了mysql中索引與FROM_UNIXTIME的問(wèn)題的相關(guān)資料,需要的朋友可以參考下2017-05-05MySQL中union和join語(yǔ)句使用區(qū)別的辨析教程
這篇文章主要介紹了MySQL中union和join語(yǔ)句的用法區(qū)別,舉例說(shuō)明了union和join在連接操作上的不同作用,需要的朋友可以參考下2015-12-12MySQL?數(shù)據(jù)庫(kù)的約束及數(shù)據(jù)表的設(shè)計(jì)原理
這篇文章主要介紹了MySQL?數(shù)據(jù)庫(kù)的約束及數(shù)據(jù)表的設(shè)計(jì)原理,數(shù)據(jù)庫(kù)中的約束即是對(duì)插入數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行限定,為了保證數(shù)據(jù)的有效性和完整性,下面一起來(lái)了解具體的詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2021-12-12MySQL?8.0.35數(shù)據(jù)庫(kù)下載安裝以及環(huán)境變量的配置方法
很多朋友剛開(kāi)始接觸mysql數(shù)據(jù)庫(kù)服務(wù)器,這篇文章主要給大家介紹了關(guān)于MySQL?8.0.35數(shù)據(jù)庫(kù)下載安裝以及環(huán)境變量的配置方法,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12