一、為什么需要分布式鎖
- 1.在java單機服務(wù)中,jvm內(nèi)部有一個全局的鎖監(jiān)視器,只有一個線程能獲取到鎖,可以實現(xiàn)線程之間的互斥
- 2.當有多個java服務(wù)時,會有多個jvm,也會有多個鎖監(jiān)視器,這樣沒辦法使得多個jvm之間的線程互斥,所以無法使用jvm內(nèi)部的鎖監(jiān)視器,也就是synchronized關(guān)鍵字無法用于此場景
- 3.需要在多個jvm外部加一個鎖監(jiān)視器,這樣保證不同的jvm的線程保持互斥
- 4.滿足分布式系統(tǒng)或者集群模式下多個進程都可見并且互斥的鎖
- 5.分布式鎖滿足的特性:多進程可見、互斥、高可用、高性能、安全
- 6.分布式鎖是否可重入
- 7.分布式鎖是否公平
二、分布式鎖的實現(xiàn)方案
- 1.mysql實現(xiàn):利用mysql內(nèi)部的寫操作(本身的互斥鎖),可以開啟一個事務(wù),執(zhí)行業(yè)務(wù),業(yè)務(wù)執(zhí)行完之后,提交事務(wù)釋放鎖,當業(yè)務(wù)出現(xiàn)異常,事務(wù)回滾,釋放鎖。可用性好,性能一般,斷開連接也會釋放鎖,是安全的
- 2.redis實現(xiàn):利用setnx的互斥命令,當key存在的時候set值會失敗,當key不存在的時候才會set成功,刪除key之后,鎖便能釋放。redis集群,可用性得到保障,高性能。給key加上過期時間,即使服務(wù)器宕機,redis重啟,key到期失效鎖也會自動失效,解決安全性問題。key的過期時間也是有講究的,如果過短,業(yè)務(wù)還未處理完便釋放了鎖,會有問題。如果過長,無效的等待時間會變多。
- 3.zookeeper實現(xiàn):利用節(jié)點的唯一性和有序性實現(xiàn)互斥。zookeeper可以集群,可用性好,zookeeper是強一致性,性能上比redis差,節(jié)點是臨時節(jié)點,服務(wù)器宕機,節(jié)點斷開連接會自動釋放
三、redis分布式鎖
3.1 簡單實現(xiàn)
- 1.獲取鎖:setnx lock thread,lock不存在則設(shè)置key,lock存在則設(shè)置不了lock
- 2.釋放鎖:del key,如果服務(wù)宕機,沒有del key,會導(dǎo)致key一直存在,造成死鎖
- 3.設(shè)置過期時間:expire lock 10,lock在10秒后自動過期
- 4.若setnx lock thread之后,還沒來得及expire lock 10,服務(wù)器宕機,仍然會死鎖,是用一個set命令保證原子性,即set lock thread ex 10 nx
- 5.獲取鎖失敗會有兩種機制,一是繼續(xù)等待,等到有線程釋放鎖為止,阻塞式獲取,二是非阻塞是獲取,如果獲取失敗,會嘗試再獲取一次,如果還是失敗則立即結(jié)束返回結(jié)果。阻塞式實現(xiàn)比較麻煩,對cpu也會產(chǎn)生壓力
- 6.非阻塞式分布式鎖實現(xiàn)過程:嘗試獲取鎖,獲取鎖成功,執(zhí)行業(yè)務(wù),業(yè)務(wù)如果超時或者宕機則釋放鎖,業(yè)務(wù)執(zhí)行完畢也會釋放鎖
- 7.誤刪鎖情況:線程1獲取到鎖,由于業(yè)務(wù)處理時間過長,鎖超時釋放,此時線程2獲取到鎖,執(zhí)行業(yè)務(wù),業(yè)務(wù)時間也長,線程1此時業(yè)務(wù)處理完畢,然后釋放了鎖,線程3此時拿到鎖執(zhí)行業(yè)務(wù),那么此時線程2和線程3的執(zhí)行業(yè)務(wù)就是非互斥的,造成并發(fā)不安全。
- 8.對于無刪除鎖情況,鎖的value中應(yīng)該存一份線程的標識(可以用uuid,若是線程的id,集群情況下會發(fā)生線程id重復(fù)的情況),實現(xiàn)過程:線程1獲取到鎖,并存入線程標識,獲取鎖成功,開始執(zhí)行業(yè)務(wù),若業(yè)務(wù)超時或服務(wù)宕機,自動釋放鎖,若業(yè)務(wù)執(zhí)行完畢且沒有超時,判斷鎖標識是否是線程1的,如果是則釋放鎖
- 9.非原子操作情況:線程1獲取到鎖,然后處理業(yè)務(wù),業(yè)務(wù)處理完畢,準備釋放鎖,判斷鎖標識是否是線程1的,判斷是的,開始釋放鎖,此時線程1阻塞(jvm發(fā)生FullGC),導(dǎo)致redis鎖超時釋放,GC完畢,線程2獲取到鎖,開始執(zhí)行業(yè)務(wù),業(yè)務(wù)時間較長,線程1阻塞結(jié)束,釋放掉了鎖,線程3獲取到鎖,執(zhí)行業(yè)務(wù),此時線程2和線程3的業(yè)務(wù)處理產(chǎn)生并發(fā)問題。所以需要線程1判斷鎖標識的操作和釋放鎖的操作要保證原子性,需要用lua腳本實現(xiàn)
- 10.還有其它問題,例如不可重入,同一個線程無法多次獲取同一個鎖;不可重試,獲取鎖只嘗試一次就返回false,沒有重試機制;超時釋放,超時釋放可以避免死鎖,但業(yè)務(wù)處理時間過長,導(dǎo)致鎖超時釋放,會存在安全隱患;主從一致性問題,如果redis提供了主從集群,線程1在主機上設(shè)置了鎖,主從同步存在延遲,當主機宕機時,從機沒有同步到主機上的鎖數(shù)據(jù),線程2讀從機時未發(fā)現(xiàn)到鎖,線程2也能拿到鎖,此時從機被推選為主機,并緩存了線程2的鎖,此時線程1和線程2在業(yè)務(wù)處理上存在并發(fā)問題
3.2 成熟的實現(xiàn)
- 1.開源框架redission
文章來源地址http://www.zghlxwxcb.cn/news/detail-424272.html
文章來源:http://www.zghlxwxcb.cn/news/detail-424272.html
到了這里,關(guān)于【redis】redis分布式鎖的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!