我們用 redis 來實作這把分布式的鎖,redis 速度快、支援事務、可持久化的特點非常适合建立分布式鎖。
鎖,簡單來說就是存于 redis 中一個唯一的 key。一般而言,redis 用 <code>set</code> 指令來完成一個 key 的設定(加鎖),使用 <code>get</code> 指令擷取 key 的資訊(檢查鎖)。由于網絡延遲的存在,簡單的使用 <code>set</code> 和 <code>get</code> 指令可能會帶來如下問題:
線程 A 檢查鎖是否存在(get)–>否–>加鎖(set),在 A 發起加鎖指令但是還沒有加鎖成功的時候,可能線程 B 已經完成了 <code>set</code> 操作,鎖被 B 獲得,但是 A 也發起了加鎖請求,由于 <code>set</code> 指令并不檢查 key 的存在,B 的鎖很可能會被 A 的 <code>set</code> 操作破壞。
幸運的是,redis 提供了另一個指令 <code>setx</code> : 當指定的 key 不存在時,設定 key 的值為指定 value,如果存在,不做任何操作,成功則傳回 1,失敗則傳回 0。也就是隻要指令傳回成功,線程就能正确獲得鎖,不需要再做類似 <code>get</code> 檢查操作。
使用 <code>setx</code> 可以消除網絡延遲對鎖設定的影響。
加鎖成功并操作完成時候,就需要加鎖線程對鎖進行釋放,以讓出資源的控制權。釋放鎖,簡單來說就是删除 redis 中這個唯一的 key,但是一定要保證删除的這個 key 是該線程建立的,因而鎖建立時必須攜帶執行線程的唯一特征以标示建立者的身份。
如果加鎖的線程出現異常 crash 了而不能及時删除鎖,則會導緻鎖一直無法被正确釋放,資源處于一直被占有,别的線程處于一直等待的狀态。為了避免這樣的情況發生,鎖一定要在異常發生之後 可以自己釋放,以讓出資源的控制權,可以使用 redis 的逾時機制來達到這個目的。逾時時間視不同的業務場景而定,一般是最大允許等待時間。需要注意的是,隻有在加鎖成功之後才可以對 key 設定 TTL,否則很容易導緻 key 被多個線程不斷設定 TTL 而無法過期。
redis 提供了 pipeline 和事務操作來保證多個指令可以在一個事務内全部完成進而減少多次網絡請求帶來的開銷,watch 指令又可以在事務開始執行之前對所要操作的 key 執行監測,進而保證了事務的完整性和一緻性。是以,為了防止鎖篡改,可以在加鎖完成之後對鎖進行 watch 操作,一旦鎖發生變化,則終止事務,復原操作。
不論是通信故障或是伺服器故障而導緻的鎖伺服器無法響應,此時都會導緻用戶端加鎖和釋放鎖的請求無法完成,是以一定要有相應的應急處理,以確定程式流程的完整體驗,加強用戶端的健壯性。比如相應的逾時提示,異常告警等。
1.隻有鎖正确釋放才算是整個事務的完整結束,如果鎖釋放失敗,比如被篡改、鎖伺服器異常等,不同的業務可以根據自己的需求進行變動和調整。
2.設定 TTL 一定要是加鎖成功之後,否則所有擷取鎖的用戶端都會嘗試 TTL 導緻鎖無法過期。
3.鎖的過期時間也就是擷取鎖的用戶端的最大等待時間,這個時間根據執行的事務能夠容忍的最長時間為限
<code>轉自:https://gold.xitu.io/entry/57bae53f5bbb500063fedf31</code>
<code></code>
<code>本文轉自張昺華-sky部落格園部落格,原文連結:</code>http://www.cnblogs.com/bonelee/p/6430789.html,如需轉載請自行聯系原作者