http://suene.iteye.com/blog/1756295
可以使用 @Lock , 里面填入的是 JPA 的 LockModeType
*- @Lock(LockModeType.PESSIMISTIC_READ)
- public List<User> findByUsername(String username);
對應的 sql 就是:
- select * from t_user where username=? lock in share mode
*
- @Lock(LockModeType.PESSIMISTIC_WRITE)
- public List<User> findByUsername(String username);
對應的 sql 就是:
- select * from t_user where username=? for update
以上是jpa對於鎖的實現,可見:
PESSIMISTIC_READ | 共享鎖 | |
PESSIMISTIC_WRITE | 排他鎖 | |
http://blog.csdn.net/cug_jiang126com/article/details/50544728
深入理解SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE
概念和區別
SELECT ... LOCK IN SHARE MODE走的是IS鎖(意向共享鎖),即在符合條件的rows上都加了共享鎖,這樣的話, 其他session可以讀取這些記錄,也可以繼續添加IS鎖,但是無法修改這些記錄直到你這個加鎖的session執行完成(否則直接鎖等待超時)。SELECT ... FOR UPDATE 走的是IX鎖(意向排它鎖),即在符合條件的rows上都加了排它鎖, 其他session也就無法在這些記錄上添加任何的S鎖或X鎖。如果不存在一致性非鎖定讀的話,那么其他session是無法讀取和修改這些記錄的,但是innodb有非鎖定讀(快照讀並不需要加鎖),for update之后並不會阻塞其他session的快照讀取操作,除了select ...lock in share mode和select ... for update這種顯示加鎖的查詢操作。
通過對比,發現for update的加鎖方式無非是比lock in share mode的方式多阻塞了select...lock in share mode的查詢方式,並不會阻塞快照讀。
應用場景
在我看來,SELECT ... LOCK IN SHARE MODE的應用場景適合於 兩張表存在關系時的寫操作 ,拿mysql官方文檔的例子來說,一個表是child表,一個是parent表,假設child表的某一列child_id映射到parent表的c_child_id列,那么從業務角度講,此時我直接insert一條child_id=100記錄到child表是存在風險的, 因為剛insert的時候可能在parent表里刪除了這條c_child_id=100的記錄,那么業務數據就存在不一致的風險。正確的方法是再插入時執行select * from parent where c_child_id=100 lock in share mode,鎖定了parent表的這條記錄,然后執行insert into child(child_id) values (100)就ok了。但是如果是同一張表的應用場景, 舉個例子,電商系統中計算一種商品的剩余數量,在產生訂單之前需要確認商品數量>=1,產生訂單之后應該將商品數量減1。
1 select amount from product where product_name='XX';
2 update product set amount=amount-1 where product_name='XX';
顯然1的做法是是有問題,因為如果1查詢出amount為1,但是這時正好其他session也買了該商品並產生了訂單,那么amount就變成了0,那么這時第二步再執行就有問題。
那么采用lock in share mode可行嗎,也是不合理的,因為兩個session同時鎖定該行記錄時,這時兩個session再update時必然會產生死鎖導致事務回滾。以下是操作范例(按時間順序)
session1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_jjj lock in share mode;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj |
+-----+------------+
2 rows in set (0.00 sec)
session2(同樣鎖定了相同的行)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_jjj lock in share mode;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj |
+-----+------------+
2 rows in set (0.00 sec)
session1(這時session1再update時就會引起鎖等待)
mysql> update test_jjj set name='jjj1' where name='jjj'; 等待session2鎖
session2( 這時session2同樣update后,等待session1的鎖,就會檢測到死鎖,回滾session2, 注意執行時間不要超過session1的鎖等待超時檢測時間,即不要超過innodb_lock_wait_timeout設置的值)
mysql> update test_jjj set name='jjj1' where name='jjj';
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
session1(此時session1執行完成)
mysql> update test_jjj set name='jjj1' where name='jjj';
Query OK, 1 row affected (29.20 sec)
Rows matched: 1 Changed: 1 Warnings: 0
通過該案例可知lock in share mode的方式在這個場景中不適用,我們需要使用for update的方式直接加X鎖,從而短暫地阻塞session2的select...for update操作 ;以下是操作范例
session1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_jjj for update;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj1 |
+-----+------------+
2 rows in set (0.00 sec)
session2(此時session2處於鎖等待狀態,得不到結果)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_jjj for update;
session1(這時session1 update之后提交,可完成)
mysql> update test_jjj set name='jjj1' where name='jjj';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
session2( session1提交之后session2剛才的查詢結果就出來了 ,也就可以再次update往下執行了)
mysql> select * from test_jjj for update;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj1 |
+-----+------------+
2 rows in set (37.19 sec)
mysql> select * from test_jjj for update;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj1 |
+-----+------------+
2 rows in set (37.19 sec)
mysql> update test_jjj set name='jjj1' where name='jjj';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
通過對比,lock in share mode適用於兩張表存在業務關系時的一致性要求,for update適用於操作同一張表時的一致性要求。