設計一個支援高併發的分散式鎖,商品維度

躺沙灘上等死發表於2020-10-10
        try {
            Boolean product = redisTemplate.opsForValue().setIfAbsent("product-test001", "1", 11, TimeUnit.SECONDS);
            if (null == product) {
                // 自旋
                while (true) {
                    System.out.println("自旋開始" + Thread.currentThread().getName());
                    Thread.sleep(4000L);
                    // 加鎖成功
                    if (null != redisTemplate.opsForValue().setIfAbsent("product-test001", "1", 11, TimeUnit.SECONDS)) {
                        // 扣減庫存
                        System.out.println("自旋扣減庫存開始" + Thread.currentThread().getName());
                        Thread.sleep(10000L);
                        System.out.println("自旋扣減庫存成功" + Thread.currentThread().getName());
                        break;
                    }
                }
            }
            // 扣減庫存 模擬扣減庫存
            Thread.sleep(10000L);
        } finally {
            // 防止異常情況 必須釋放鎖
            redisTemplate.delete("product-test001");
        }


自旋開始http-nio-8081-exec-1
自旋開始http-nio-8081-exec-4
自旋開始http-nio-8081-exec-1
自旋開始http-nio-8081-exec-4
自旋開始http-nio-8081-exec-1
自旋扣減庫存開始http-nio-8081-exec-4
自旋開始http-nio-8081-exec-1
自旋開始http-nio-8081-exec-1
自旋扣減庫存成功http-nio-8081-exec-4
自旋開始http-nio-8081-exec-1
自旋扣減庫存開始http-nio-8081-exec-1
自旋扣減庫存成功http-nio-8081-exec-1

看上去好像沒有什麼問題 

場景1.在整個程式碼塊中發生異常了,就比如第一個setNx的時候超時了,可能有很多原因比如大的bigkey、比如很多很多的請求突然打到web,那麼這個時候就會發生直接去finally ,釋放了鎖。那麼在後續購買商品的請求訪問時,鎖失效 !

解決思路:使用執行緒的value進行比較,只有是本執行緒的加鎖的才能釋放。

那麼這個時候又有一個問題,假設這個執行緒因為操作庫存某些問題導致死了,那程式不就也死了?

設定了redis的超時時間,假設這個商品不能扣減庫存了,那也就這10秒不能操作,網際網路公司的業務完全能接受。

 

        String threadId = String.valueOf(Thread.currentThread().getId());
        try {
            // 獲取庫存數量 40
            Boolean product = redisTemplate.opsForValue().setIfAbsent("product-test001", threadId, 11, TimeUnit.SECONDS);
            if (null == product) {
                // 自旋
                while (true) {
                    System.out.println("自旋開始" + Thread.currentThread().getName());
                    Thread.sleep(4000L);
                    // 加鎖成功
                    if (null != redisTemplate.opsForValue().setIfAbsent("product-test001",threadId, 11, TimeUnit.SECONDS)) {
                        // 扣減庫存
                        System.out.println("自旋扣減庫存開始" + Thread.currentThread().getName());
                        Thread.sleep(10000L);
                        System.out.println("自旋扣減庫存成功" + Thread.currentThread().getName());
                        break;
                    }
                }
            }
            // 扣減庫存 模擬扣減庫存
            Thread.sleep(10000L);
        } finally {
            if(threadId.equals(redisTemplate.opsForValue().get("product-test001"))){
                // 防止異常情況 必須釋放鎖
                redisTemplate.delete("product-test001");
            }
        }

 

 

因為這款商品是爆品所以導致一旦開啟會有超高併發的可能存在,這個時候就會對redis的壓力非常大,怎麼設計?

使用分段鎖思想,比如將product_test1分成product_test1_001,product_test1_002,...,product_test1_100 根據某種規則讓他坐落在叢集的不同的槽位中 ,假設有10個叢集節點那麼,16384/10 儘量坐落在不同的節點,可以大幅度提高redis的吞吐量。

相關文章