天天看點

redistemplate分布式鎖實作_SpringBoot2.0實戰(14)整合Redis之實作分布式鎖相關知識目标操作步驟源碼位址參考擴充

相關知識

分布式鎖

分布式鎖是控制分布式系統之間同步通路共享資源的一種方式,在分布式系統中,如果不同的應用之間共享一個或一組資源,那麼通路這些資源的時候,往往需要互斥來防止彼此幹擾來保證一緻性,在這種情況下,便需要使用到分布式鎖。

  • 互斥性。在分布式環境下,同一時間隻有一個用戶端能持有鎖。
  • 具備鎖失效機制,防止死鎖。例如鎖的持有者在持有鎖期間崩潰而沒有主動解鎖,鎖需要在規定時間後自動失效,以保證後續可用。
  • 具備可重入性,防止死鎖。
  • 解鈴還須系鈴人。釋放鎖與加鎖應該為相同用戶端,不能把别人加的鎖給解了。

Redis 分布式鎖實作原理

實作原理可參考 http://www.redis.cn/topics/distlock.html。

Redis 是單線程的,是以 Redis 指令具有原子性,Redis 提供了以下幾個指令

  • setnx,意思是待建立的鍵如果已存在,則建立失敗,否則建立成功,實作互斥性。
  • expire,給鍵加上過期時間,實作自動失效機制
  • set key value [EX|PX] [NX],可以将上面兩個指令的動作,在這一個指令實作,其中,EX|PX 是過期時間,EX 使用秒,PX 使用毫秒,NX 表示 setnx 的意思

因為鎖的實作中擁有比較、加鎖等一系列操作,為保證原子性,需要引入 Lua 腳本

加鎖流程:

redistemplate分布式鎖實作_SpringBoot2.0實戰(14)整合Redis之實作分布式鎖相關知識目标操作步驟源碼位址參考擴充

釋放鎖流程

redistemplate分布式鎖實作_SpringBoot2.0實戰(14)整合Redis之實作分布式鎖相關知識目标操作步驟源碼位址參考擴充

目标

整合 Redis 實作分布式鎖

操作步驟

添加依賴

依賴以及配置見上章https://blog.csdn.net/qq_34479912/article/details/105719636

編碼

鎖是針對某個資源的狀态,保證其通路的互斥性,在實際使用當中,這個狀态一般是一個字元串。使用 Redis 實作鎖,主要是将狀态放到 Redis 當中,利用其原子性,當其他線程通路時,如果 Redis 中已經存在這個狀态,就不允許之後的一些操作。spring boot使用Redis的操作主要是通過RedisTemplate(或StringRedisTemplate )來實作。

1.1 将鎖狀态放入 Redis:

redisTemplate.opsForValue().setIfAbsent("lockkey", "value"); // setIfAbsent如果鍵不存在則新增,存在則不改變已經有的值。

1.2 設定鎖的過期時間

redisTemplate.expire("lockkey", 30000, TimeUnit.MILLISECONDS);

1.3 删除/解鎖

redisTemplate.delete("lockkey");

這麼就是簡單實作,但是1.1和1.2這麼做,這兩步違背了原子性,也就是一旦鎖被建立,而沒有設定過期時間,則鎖會一直存在。

1.4 擷取鎖

redisTemplate.opsForValue().get("lockkey");

1.5 解決方案

spring data的 RedisTemplate 當中并沒有這樣的方法。但是在jedis當中是有這種原子操作的方法的,需要通過 RedisTemplate 的 execute 方法擷取到jedis裡操作指令的對象.

String result = template.execute(new RedisCallback() {            @Override            public String doInRedis(RedisConnection connection) throws DataAccessException {                JedisCommands commands = (JedisCommands) connection.getNativeConnection();                return commands.set(key, "鎖定的資源", "NX", "PX", 3000);            }        });
           

注意: Redis 從2.6.12版本開始 set 指令支援 NX 、 PX 這些參數來達到 setnx 、 setex 、 psetex 指令的效果,文檔參見: SET — Redis 指令參考

NX: 表示隻有當鎖定資源不存在的時候才能 SET 成功。利用 Redis 的原子性,保證了隻有第一個請求的線程才能獲得鎖,而之後的所有線程在鎖定資源被釋放之前都不能獲得鎖。

鎖的進階

模拟一個比較常見的秒殺場景,這時候就需要用到鎖。

建立RedisLockHelper

redistemplate分布式鎖實作_SpringBoot2.0實戰(14)整合Redis之實作分布式鎖相關知識目标操作步驟源碼位址參考擴充

這個是Redis加鎖和解鎖的工具類,裡面使用的主要是兩個指令,SETNX和GETSET。

SETNX指令 将key設定值為value,如果key不存在,這種情況下等同SET指令。 當key存在時,什麼也不做

GETSET指令 先查詢出原來的值,值不存在就傳回nil。然後再設定值 對應的Java方法在代碼中提示了。 注意一點的是,Redis是單線程的!是以在執行GETSET和SETNX不會存在并發的情況。

建立Controller模拟秒殺場景

redistemplate分布式鎖實作_SpringBoot2.0實戰(14)整合Redis之實作分布式鎖相關知識目标操作步驟源碼位址參考擴充

源碼位址

本章源碼 :https://github.com/caiyuanzi-song/boot.git

參考

https://www.cnblogs.com/toutou/archive/2019/02/11/redis_lock.html

擴充

Redis 相關資料

  • spring-data-redis文檔: https://docs.spring.io/spring-data/redis/docs/2.0.1.RELEASE/reference/html/#new-in-2.0.0
  • Redis 文檔: https://redis.io/documentation
  • Redis 中文文檔: http://www.redis.cn/commands.html