MySQL中的樂觀鎖和悲觀鎖具體怎麼實現的?

 文章介紹

對於MySQL中的樂觀鎖和悲觀鎖,可能不少的開發者還不是很熟悉,並不知道其中具體是如何實現的。本文就針對這個問題作一個實際案例演示,讓你完全明白這兩種鎖的區別。mysql

相關文章

以前針對MySQL中的鎖單獨分享過一篇文章,對於MySQL鎖還不夠了解的能夠仔細閱讀如下該文。sql

鎖分類

MySQL的中鎖按照範圍主要分爲表鎖、行鎖和頁面鎖。其中myisam存儲引擎只支持表鎖,InnoDB不只僅支持行鎖,在必定程度上也支持表鎖。按照行爲能夠分爲共享鎖(讀鎖)、排他鎖(寫鎖)和意向鎖。按照思想分爲樂觀鎖和悲觀鎖。併發

今天的文章演示一下實際中的樂觀鎖和悲觀鎖是如何操做的。ide

表結構

下面的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;

插入模擬數據spa

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;

表中數據。code

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

悲觀鎖

悲觀鎖,比較消極的一種鎖處理方式。直接在操做數據時,搶佔鎖。其餘的事務在進行時就會等待,直到佔有鎖的事務釋放鎖爲止。blog

這種處理方式能保證數據的最大一致性,可是容易致使鎖超時、併發程度低等問題。 首先咱們開啓事務一,而且對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操做,查看此時會發生什麼狀況?ci

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的數據對應的寫鎖。效果以下圖:

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

經過上面的例子,咱們就能比較直觀的感覺到悲觀鎖的實現過程是如何的。

樂觀鎖

樂觀鎖認爲數據通常狀況下不會形成衝突,只有當數據去執行修改狀況時,纔會針對數據衝突作處理。這裏是如何發現衝突了呢?常規的方式,都是在數據行上加一個版本號或者時間戳等字段。(本文使用version做爲版本好方式,使用時間戳方式同理)

樂觀鎖的實現原理:

  1. 一個事務在讀取數據時,將對應的版本號字段讀取出來,假設此時的版本號是1。

  2. 另一個事務也是執行一樣的讀取操做。當事務一提交時,對版本號執行+1,此時該數據行的版本號就是2。

  3. 第二個事務執行修改操做時,針對業務數據作條件,並默認增長一個版本號做爲where條件。此時修改語句中的版本號字段是不知足where條件,該事務執行失敗。經過這種方式來達到鎖的功能。

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

客戶端一

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,這樣反卻是下降了性能,因此這種狀況下用悲觀鎖就比較合適,之因此用悲觀鎖就是由於兩個用戶更新同一條數據的機率高,也就是衝突比較嚴重的狀況下,因此才用悲觀鎖。

悲觀鎖比較適合強一致性的場景,但效率比較低,特別是讀的併發低。樂觀鎖則適用於讀多寫少,併發衝突少的場景。

 

相關文章
相關標籤/搜索