共計 2683 個字符,預計需要花費 7 分鐘才能閱讀完成。
本篇內容主要講解“redis 中的分布式鎖有哪些特點”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓丸趣 TV 小編來帶大家學習“redis 中的分布式鎖有哪些特點”吧!
分布式鎖的特點
1. 獨占性
不論在任何情況下都只能有一個線程持有鎖。
2. 高可用
redis 集群環境不能因為某一個節點宕機而出現獲取鎖或釋放鎖失敗。
3. 防死鎖
必須有超時控制機制或者撤銷操作。
4. 不亂搶
自己加鎖,自己釋放。不能釋放別人加的鎖。
5. 重入性
同一線程可以多次加鎖。
redis 單機怎么實現
一般情況下都是使用 setnx+lua 腳本實現。
直接貼代碼
package com.fandf.test.redis;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
* redis 單機鎖
*
* @author fandongfeng
* @date 2023/3/29 06:52
*/
@Slf4j
@Service
public class RedisLock {
@Resource
RedisTemplate String, Object redisTemplate;
private static final String SELL_LOCK = kill:
/**
* 模擬秒殺
*
* @return 是否成功
*/
public String kill() {
String productId = 123
String key = SELL_LOCK + productId;
// 鎖 value, 解鎖時 用來判斷當前鎖是否是自己加的
String value = IdUtil.fastSimpleUUID();
// 加鎖 十秒鐘過期 防死鎖
Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, value, 10, TimeUnit.SECONDS);
if (!flag) {
return 加鎖失敗
}
try {
String productKey = good123
// 獲取商品庫存
Integer stock = (Integer) redisTemplate.opsForValue().get(productKey);
if (stock == null) {
// 模擬錄入數據, 實際應該加載時從數據庫讀取
redisTemplate.opsForValue().set(productKey, 100);
stock = 100;
}
if (stock = 0) {
return 賣完了,下次早點來吧
}
// 扣減庫存, 模擬隨機賣出數量
int randomInt = RandomUtil.randomInt(1, 10);
redisTemplate.opsForValue().decrement(productKey, randomInt);
// 修改 db, 可以丟到隊列里慢慢處理
return 成功賣出 + randomInt + 個,庫存剩余 + redisTemplate.opsForValue().get(productKey) + 個
} finally {
// // 這種方法會存在刪除別人加的鎖的可能
// redisTemplate.delete(key);
// if(value.equals(redisTemplate.opsForValue().get(key))){
// // 因為 if 條件的判斷和 delete 不是原子性的,// //if 條件判斷成功后,恰好鎖到期自己解鎖
// // 此時別的線程如果持有鎖了,就會把別人的鎖刪除掉
// redisTemplate.delete(key);
// }
// 使用 lua 腳本保證判斷和刪除的原子性
String luaScript =
if (redis.call( get ,KEYS[1]) == ARGV[1]) then +
return redis.call(del ,KEYS[1]) +
else +
return 0 +
end
redisTemplate.execute(new DefaultRedisScript (luaScript, Boolean.class), Collections.singletonList(key), value);
}
}
}
進行單元測試,模擬一百個線程同時進行秒殺
package com.fandf.test.redis;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;
* @Description:
* @author: fandongfeng
* @date: 2023-3-24 16:45
*/
@SpringBootTest
class SignServiceTest {
@Resource
RedisLock redisLock;
正文完