mysql中的get_lock鎖機制解析
mysql> SELECT GET_LOCK('MySQL', 10) ; 返回結果爲1,說明成功得到鎖
+-----------------+
| ci_session_lock |
+-----------------+
| 1 |
+-----------------+
1 row in set (0.00 sec)
mysql>select RELEASE_LOCK('MySQL') ;
+----------------------------------------------------------+
| RELEASE_LOCK('MySQL') |
+----------------------------------------------------------+
| 1 |
+----------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT IS_USED_LOCK('MySQL') ; 返回結果爲當前連接ID,表示名稱爲'MySQL'的鎖正在被使用。
+-----------------+
| ci_session_lock |
+-----------------+
| 8 |
+-----------------+
1 row in set (0.00 sec)
mysql> SELECT IS_FREE_LOCK('MySQL') ; 返回結果爲0,說明名稱爲'MySQL'的鎖正在被使用。
+-----------------+
| ci_session_lock |
+-----------------+
| 0 |
+-----------------+
1 row in set (0.00 sec)
創建表:
create table test_lock(
id int,
name varchar(50),
address varchar(50)
);
insert into test_lock values(1,'tt','aaaaaaaaaaaaaaaaaaaa');
A1:
select get_lock('key_lock', 100);
update test_lock set name = 'tt2', address = 'aaaaaaaaaaaaaaaaaaaa' where id = 1; #只更新name列
select release_lock('key_lock');
A2:
select get_lock('key_lock', 100);
update test_lock set name = 'tt', address = 'bbbbbbbbbbbbbbbbbbbbbbb' where id = 1; #只更新address列
select release_lock('key_lock');
執行A1的第一條語句後執行A2的第一條語句會發現一直卡在那裏,直接執行了A1的最後一條語句(或過期或斷線)爲止才能繼續執行A2的語句。
如果按上面的順序分別執行了A1和A2中的語句,則最後表中的數據是按A2的第二語句更新那樣的顯示,這種結果當然不是我們想要的,實際項目中,應該是先查出來id爲1的數據,把某列更新的值set進去(針對Java),再執行更新,這樣所有的更新結果都是正確的。
-
優缺點分析
(1)這種方式對於更新所有列比較有效,但是得把查詢的語句也放在鎖內執行;
(2)這種方式當客戶端無故斷線了會自動釋放鎖,比較好,不像redis鎖那樣,如果加完鎖斷了,那麼鎖一直在;
(3)這種方式是針對鎖內的所有操作加鎖,並不針對特定表或特定行,所以使用了同一個Key的鎖但不同的操作都會共用一把鎖,會導致效率低下;
(4)如果查詢語句放在鎖之前,則數據可能是舊的,更新之後會把查詢之後更新之前別的客戶端更新的數據覆蓋掉; - 舉個Java例子的僞代碼
A1客戶端:
execute("select get_lock('key_lock', 100)");
User user = queryUser(1);
user.setName("tt1");
updateUser(user);
execute("select release_lock('key_lock')");
A2客戶端:
execute("select get_lock('key_lock', 100)");
User user = queryUser(1);
user.setAddress("bbbbbbbbbbbbbb");
updateUser(user);
execute("select release_lock('key_lock')");
按照以上這種方式操作則兩條更新語句都是正確的,最後的結果應該是
name = ‘tt1’,
address = ‘bbbbbbbbbbbbbb’