推薦學習:mysql影片教學
#對於MySQL中的樂觀鎖和悲觀鎖,可能很多的開發者還不是很熟悉,並不知道其中具體是如何實現的。本文就針對這個問題做一個實際案例演示,讓你徹底明白這兩種鎖的差異。
MySQL的中鎖定依範圍主要分為表格鎖定、行鎖定和頁面鎖定。其中myisam儲存引擎只支援表鎖,InnoDB不只支援行鎖,在一定程度上也支援表鎖。依行為可以分為共享鎖(讀鎖)、排他鎖(寫鎖)和意向鎖。按照思想分為樂觀鎖和悲觀鎖。
今天的文章示範一下實際中的樂觀鎖和悲觀鎖是如何運作的。
下面的SQL語句是表格的結構:
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;
插入模擬資料:##
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;表中資料。
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操作,此時我們不提交事務。
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操作,查看此時會發生什麼情況?
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作為版本好方式,使用時間戳方式同理)
#樂觀鎖定的實作原理:
客戶端一:
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之後,在執行。
客戶端二:
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,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適,之所以用悲觀鎖就是因為兩個用戶更新同一條數據的概率高,也就是衝突比較嚴重的情況下,所以才用悲觀鎖。悲觀鎖定比較適合強一致性的場景,但效率比較低,特別是讀的並發低。樂觀鎖則適用於讀多寫少,並發衝突少的場景。
推薦學習:mysql影片教學
#以上是MySQL樂觀鎖與悲觀鎖具體實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!