問題描述
假設一個情況,這裡只是假設,真實的情況可能不會這樣設計,但是假如真的發生了....
鐵老大有一張這樣的ticket表,用來存放北京到上海的票。
| id |
uid |
start_addr |
end_addr |
book_time |
| 1 |
1300009860 |
上海 |
北京 |
13866660321 |
| 2 |
0 |
上海 |
北京 |
|
| 3 |
0 |
上海 |
北京 |
|
| 4 |
0 |
上海 |
北京 |
uid 不等於0 並且 book_time 不等於0,則表示已售出,不許再更改。
年底了,大家開始搶了,當某一個人開始搶的時候,鐵老大這樣做了,當A開始搶的時候,從表中所有uid=0的記錄中隨機取出一條,然後修改目前記錄的uid為A的uid,book_time為目前時間。
那麼好了,這個時候並發產生了,10000個人同時搶最後剩下的10張票,有2個人A,B同時讀取了uid=0 and id=2的記錄,然後A把uid修改為自己的,book_time修改timeA,系統通知A搶票成功 緊接著,B同樣修改了這條記錄為自己的,那麼這個時候A的票就失效了。
這裡就考慮使用鎖機制了,如果在A讀取了這條記錄後,並把這條記錄加上寫鎖,那麼別人就無法再讀取加鎖,也就不會修改多次了。
實驗結果
實驗了一下for update 加鎖,然後發現了一些問題。
1、當我們使用主鍵查詢去加鎖的時候,可以對這條記錄加行鎖
開啟一個終端:執行一下代碼:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from ticket where id = 2 for update;
那麼 id=2這個記錄就會被加行寫鎖。
令一個終端將無法讀取該記錄,但是卻可以讀取其他記錄
#可以讀取加鎖
mysql> select * from ticket where id = 3 for update;
#不可以讀取
mysql> select * from ticket where id = 2 for update;
2、但是當我們按非主鍵查詢會怎樣呢
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from ticket where uid = 0 limit 1 for update;
那麼這個時候,這個表會被鎖住,任何記錄都不能再讀取加鎖
//不可讀取,等待釋放鎖
mysql> select * from ticket where id = 1 for update;
//不可讀取等待釋放鎖
mysql> select * from ticket where uid = 0 limit 1 for update;
但是表鎖會減少並發。
結論
有沒有一種方法,可以在非主鍵欄位查詢一條記錄時上實現加行寫鎖,期待高手支招。