做項目時由於業務邏輯的需要,必須對數據表的一行或多行加入行鎖,舉個最簡單的例子,圖書借閱系統。假設id=1的這本書庫存為1,但是有2個人同時來借這本書,此處的邏輯為
Select restnum from book where id =1 ; -- 如果 restnum 大於 0 ,執行 update Update book set restnum=restnum-1 where id=1 ;問題就來了,當2個人同時來借的時候,有可能第一個人執行select語句的時候,第二個人插了進來,在第一個人沒來得及更新book表的時候,第二個人查到數據了,其實是髒數據,因為第一個人會把restnum值減1,因此第二個人本來應該是查到id=1的書restnum為了,因此不會執行update,而會告訴它id=1的書沒有庫存了,可是數據庫哪懂這些,數據庫只負責執行一條條SQL語句,它才不管中間有沒有其他sql語句插進來,它也不知道要把一個session的sql語句執行完再執行另一個session的。因此會導致並發的時候restnum最後的結果為-1,顯然這是不合理的,所以,才出現鎖的概念,MySQL使用innodb引擎可以通過索引 對數據行加鎖。以上借書的語句變為:
Begin; Select restnum from book where id =1 for update ; -- 給 id=1 的行加上排它鎖且 id 有索引 Update book set restnum=restnum-1 where id=1 ; Commit; 這樣,第二個人執行到select語句的時候就會處於等待狀態直到第一個人執行commit。從而保證了第二個人不會讀到第一個人修改前的數據。
*****************************************************************************************************
文章的意思是想講行級鎖的應用,不過針對上面舉的那個例子的那個場景(其實這種場景還是經常出現的比如,秒殺系統等)完全可以用修改下sql語句即可。
update book set restnum=restnum-1 where id=1 and restnum>0;
只要在原來的update 語句之後加一個 restnum>0 ,問題即可解決
Begin; Select restnum from book where id =1 for update ; Update book set restnum=restnum-1 where id=1 ; Commit; update book set restnum=restnum-1 where id=1 and restnum>0;