天天看點

8種分布式鎖解決方案推薦

本文會陸續更新;先寫下大綱和劣勢

部落客:隻針對JAVA其他語言未知未測;

redis 2x版本鎖:3x版本已移除

redis setnx鎖:1.死鎖問題,2.哨兵模式會有多個線程同時擷取鎖,舍棄 3.無法動态控制程式執行時間

redis+lua鎖:1.哨兵模式會有多個線程同時擷取鎖,舍棄

redLock算法鎖:1.主觀念抛棄了主從叢集

redisson鎖:是對redis鎖加強,需要續約,不好把控續約時間,有SpringBoot整合版;

spring-boot-klock-starter:一個開源項目基于redisson鎖

以上全是基于redis的鎖,redis主從模式下全部不推薦,全部會導緻多個線程(程序)同時擷取鎖;但可以考慮TTL(鎖過期時間)>從節點上位時間的方案,但是TTL可能很久,也非常耗性能與時間,是以全部抛棄;

mysql單機實作比較粗暴;集分庫表很麻煩,方案不多不推薦

zookeper臨時節點鎖:目前2020/3/17看來是比較好的方案,性能消耗比redis單機高,需要持續監聽節點,還支援叢集高可用;

總結:

如果對性能要求較高,redLock,是你的選擇,如果可控時間redisson是你的選擇(需要保證多數以上成功,且叢集必須要高可用)

反之zookeper

注意事項:

1.使用redisson注意版本,一定要測試!!!

2.使用redisson一定要自定義權衡逾時時間(預設30s),太短會持續續約消耗性能,太長會阻塞大量線程等待擷取鎖;在大量線程阻塞等待時鎖注意使用redistemplte時,redis逾時時間小心超過等待時間導緻逾時(不合理将會導緻大量異常傳回逾時),那麼逾時後會不會導緻程式混亂,這時候需要認認真真的控制代碼(ps:不想煩躁方案:直接鎖根方法,并控制代碼反應時間)

3.盡量使用redis線程池技術,可間接避免擷取逾時;使用線程池時,注意redis包版本對應的線程池,以及redission支援的線程池

特别注意:

部落客表述不清楚的地方可以指出來,我沒辦法寫出所有的細節考慮(文筆不好);比如:網絡抖動,記憶體爆滿,交換區爆滿,丢失等等組合情況,也一般比較少遇到是以不寫了。可以提問喲

下面是redisson單機整合版和zk,我已經測試過的了

加入依賴

<dependencies>
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
    </dependency>
</dependencies>
           

直接注入就可使用,需要注意的是與redistemplate可能有沖突,記得測試喲~

@Slf4j
@Service
public class RedissonLock implements LockClient {

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 擷取自定義時間鎖
     *
     * @param key
     */
    @Override
    public void lock(String key,int outTime){
        redissonClient.getLock(key).lock(outTime, TimeUnit.SECONDS);
    }

    /**
     * 釋放鎖
     *
     * @param key
     */
    @Override
    public void unLock(String key) {
        redissonClient.getLock(key).unlock();
    }


    //5秒,自動續約
    public void lock(String key) {
//        redissonClient.getLock(key).lock();
        redissonClient.getLock(key).lock(5L, TimeUnit.SECONDS);
    }


    /**
     * 嘗試擷取鎖
     *
     * @param lockKey
     * @param waitTime  最多等待時間
     * @param leaseTime 釋放時間
     * @return
     */
    @Transactional
    public boolean tryLock(String lockKey, int waitTime, int leaseTime) {
        boolean b = false;
        try {
            b = redissonClient.getLock(lockKey).tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("擷取分布式鎖異常:", e);
        }
        return b;
    }
           

繼續閱讀