假設,有一個線上做業系統,當閱卷時,會從數據庫取出第一個未評閱的做業。
評閱完成後,會把做業狀態改成「已評閱」:
這樣沒什麼問題。git
若是是兩我的同時評閱呢?若是B獲取做業時,A已經把做業1評閱完畢,此時B獲取的是做業2。這樣,彷佛看上去也沒什麼問題。(①②③④表明時序):
github
可是,這是不現實的,B不可能每次都等到A評閱完成以後再發起請求。
若是AB同時評閱,就會出現問題(①②③④表明時序):
sql
A和B前後從後臺獲取一個做業,假設A先發起請求,那麼當B獲取做業時,A尚未評閱完成,此時做業1仍是未評閱狀態。
因而B也會獲取到做業1,而不是做業2。接下來,A評閱完成,此時做業1變成已評閱狀態。再輪到B評閱做業1時,系統就報錯了。數據庫
所以,目前的系統,因爲出現了併發問題,沒法實現多人同時評閱做業。segmentfault
鎖的好處之一,就是在必定程度上解決了併發問題,上面提到的多人閱卷,就是一個生動的例子,此外還有其餘場景,好比:取錢。
數據庫的鎖和事務是相關的,關於這部份內容,張喜碩學長在《MySQL RR 與 鎖》中有詳細的介紹。多線程
值得注意的是,因爲是第一次接觸,而且這個項目比較簡單,所以不必使用Mysql中真正的事務和鎖,而是用代碼來實現相似「鎖」效果。具體邏輯以下:併發
(下圖①②③④表明時序)spa
這樣,就成功解決了併發問題,不管有多少人同時評閱,也不會衝突了。線程
以上的解決方式還存在問題,若是A獲取做業1以後,並無提交分數(好比A掉線了、或者A沒有點擊評閱按鈕),做業1的鎖,就永遠不會被解除了。blog
這就致使,做業1不會再被別人獲取到。
所以,須要爲鎖自動加上倒計時,一旦時間結束,若是閱卷人仍然沒有評閱的話,就自動解鎖,清空CurrentUserId。(下圖①②③④⑤表明時序)
這樣,即便在A獲取做業以後沒有完成閱卷,也不會遺漏掉任何做業。
本文中,咱們經過手寫代碼,實現了Mysql中「讀鎖」(共享鎖)的功能(事實上,這段代碼有點麻煩,涉及到改字段、多線程、定時任務...因此文中只談思路)。
隨着知識瞭解的愈來愈多,咱們思考問題的角度,已經從如何生產一個新項目,變成了如何更新已經上線的項目。後者的要求更高,這須要咱們進行更新時,必須考慮和原有數據的無縫銜接。
相信不久後的未來,咱們就能學會使用真正的鎖和事務來處理併發問題了。
本文做者: 河北工業大學夢雲智開發團隊 - 劉宇軒