基于SpringBoot實(shí)現(xiàn)分布式鎖的三種方法
我來(lái)詳細(xì)講解Spring Boot中實(shí)現(xiàn)分布式鎖的幾種方式,包括手寫(xiě)Redis鎖和使用Redisson框架。
一、基于Redis原生命令實(shí)現(xiàn)分布式鎖
1. 基礎(chǔ)版Redis分布式鎖
@Component
public class RedisDistributedLock {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String LOCK_PREFIX = "distributed:lock:";
private static final long DEFAULT_EXPIRE_TIME = 30000; // 30秒
private static final long DEFAULT_WAIT_TIME = 10000; // 10秒
private static final long DEFAULT_SLEEP_TIME = 100; // 100ms
/**
* 嘗試獲取分布式鎖(簡(jiǎn)單版)
* @param lockKey 鎖的key
* @param value 鎖的值(通常用UUID)
* @param expireTime 鎖的過(guò)期時(shí)間(ms)
* @return 是否獲取成功
*/
public boolean tryLock(String lockKey, String value, long expireTime) {
String key = LOCK_PREFIX + lockKey;
// 使用SET命令,通過(guò)NX參數(shù)實(shí)現(xiàn)"不存在時(shí)設(shè)置",EX參數(shù)設(shè)置過(guò)期時(shí)間
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);
return Boolean.TRUE.equals(result);
}
/**
* 釋放鎖
* 需要確保是自己加的鎖才能釋放(防止釋放別人的鎖)
*/
public boolean releaseLock(String lockKey, String value) {
String key = LOCK_PREFIX + lockKey;
String currentValue = redisTemplate.opsForValue().get(key);
// 通過(guò)Lua腳本確保原子性:判斷值是否匹配,匹配則刪除
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(luaScript);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(
redisScript,
Collections.singletonList(key),
value
);
return result != null && result == 1;
}
/**
* 獲取鎖(支持重試)
*/
public boolean lockWithRetry(String lockKey, String value,
long expireTime, long waitTime) {
long endTime = System.currentTimeMillis() + waitTime;
while (System.currentTimeMillis() < endTime) {
if (tryLock(lockKey, value, expireTime)) {
return true;
}
try {
Thread.sleep(DEFAULT_SLEEP_TIME);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
return false;
}
}
2. 可重入鎖實(shí)現(xiàn)
@Component
public class RedisReentrantLock {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String LOCK_PREFIX = "reentrant:lock:";
private static final ThreadLocal<Map<String, Integer>> LOCK_COUNT =
ThreadLocal.withInitial(HashMap::new);
/**
* 可重入鎖實(shí)現(xiàn)
*/
public boolean tryReentrantLock(String lockKey, String clientId,
long expireTime) {
String key = LOCK_PREFIX + lockKey;
Map<String, Integer> lockCountMap = LOCK_COUNT.get();
int count = lockCountMap.getOrDefault(lockKey, 0);
// 重入:當(dāng)前線程已持有鎖
if (count > 0) {
lockCountMap.put(lockKey, count + 1);
return true;
}
// 嘗試獲取鎖
String luaScript =
"if redis.call('exists', KEYS[1]) == 0 then " +
" redis.call('hset', KEYS[1], 'owner', ARGV[1]) " +
" redis.call('hset', KEYS[1], 'count', 1) " +
" redis.call('pexpire', KEYS[1], ARGV[2]) " +
" return 1 " +
"elseif redis.call('hget', KEYS[1], 'owner') == ARGV[1] then " +
" local count = redis.call('hget', KEYS[1], 'count') " +
" redis.call('hset', KEYS[1], 'count', tonumber(count) + 1) " +
" redis.call('pexpire', KEYS[1], ARGV[2]) " +
" return 1 " +
"else " +
" return 0 " +
"end";
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(Long.class);
Long result = redisTemplate.execute(
script,
Collections.singletonList(key),
clientId,
String.valueOf(expireTime)
);
if (result != null && result == 1) {
lockCountMap.put(lockKey, 1);
return true;
}
return false;
}
/**
* 釋放可重入鎖
*/
public boolean releaseReentrantLock(String lockKey, String clientId) {
String key = LOCK_PREFIX + lockKey;
Map<String, Integer> lockCountMap = LOCK_COUNT.get();
int count = lockCountMap.getOrDefault(lockKey, 0);
if (count <= 0) {
return false;
}
if (count > 1) {
// 重入次數(shù)減1
lockCountMap.put(lockKey, count - 1);
return true;
}
// 最后一次重入,釋放鎖
String luaScript =
"if redis.call('hget', KEYS[1], 'owner') == ARGV[1] then " +
" local current = redis.call('hget', KEYS[1], 'count') " +
" if tonumber(current) > 1 then " +
" redis.call('hset', KEYS[1], 'count', tonumber(current) - 1) " +
" redis.call('pexpire', KEYS[1], ARGV[2]) " +
" return 0 " +
" else " +
" redis.call('del', KEYS[1]) " +
" return 1 " +
" end " +
"else " +
" return -1 " +
"end";
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(Long.class);
Long result = redisTemplate.execute(
script,
Collections.singletonList(key),
clientId,
"30000"
);
if (result != null && result == 1) {
lockCountMap.remove(lockKey);
if (lockCountMap.isEmpty()) {
LOCK_COUNT.remove();
}
return true;
}
return false;
}
}
二、使用Redisson實(shí)現(xiàn)分布式鎖(推薦生產(chǎn)環(huán)境使用)
1. 添加依賴
<!-- pom.xml -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.5</version>
</dependency>
2. 配置Redisson
# application.yml
spring:
redis:
host: localhost
port: 6379
database: 0
timeout: 3000
redisson:
config: |
singleServerConfig:
address: "redis://${spring.redis.host}:${spring.redis.port}"
database: ${spring.redis.database}
connectionPoolSize: 64
connectionMinimumIdleSize: 24
idleConnectionTimeout: 10000
connectTimeout: ${spring.redis.timeout}
timeout: ${spring.redis.timeout}
retryAttempts: 3
retryInterval: 1500
3. Redisson分布式鎖服務(wù)
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedissonDistributedLock {
@Autowired
private RedissonClient redissonClient;
/**
* 可重入鎖(最常用)
*/
public boolean tryReentrantLock(String lockKey, long waitTime,
long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 公平鎖
*/
public boolean tryFairLock(String lockKey, long waitTime,
long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getFairLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 聯(lián)鎖(多個(gè)鎖同時(shí)獲?。?
*/
public boolean tryMultiLock(String[] lockKeys, long waitTime,
long leaseTime, TimeUnit unit) {
RLock[] locks = new RLock[lockKeys.length];
for (int i = 0; i < lockKeys.length; i++) {
locks[i] = redissonClient.getLock(lockKeys[i]);
}
RLock multiLock = redissonClient.getMultiLock(locks);
try {
return multiLock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 紅鎖(RedLock,多個(gè)Redis實(shí)例)
*/
public boolean tryRedLock(String lockKey, long waitTime,
long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 釋放鎖
*/
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
/**
* 強(qiáng)制釋放鎖
*/
public void forceUnlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.forceUnlock();
}
/**
* 自動(dòng)續(xù)期的鎖(看門狗機(jī)制)
*/
public void lockWithWatchdog(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(); // 默認(rèn)30秒,看門狗會(huì)自動(dòng)續(xù)期
}
}
4. 使用AOP簡(jiǎn)化分布式鎖使用
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
/** 鎖的key,支持SpEL表達(dá)式 */
String key();
/** 鎖類型,默認(rèn)可重入鎖 */
LockType lockType() default LockType.REENTRANT;
/** 等待時(shí)間(秒) */
long waitTime() default 5;
/** 持有時(shí)間(秒),-1表示使用看門狗自動(dòng)續(xù)期 */
long leaseTime() default -1;
/** 時(shí)間單位 */
TimeUnit timeUnit() default TimeUnit.SECONDS;
enum LockType {
REENTRANT, // 可重入鎖
FAIR, // 公平鎖
READ, // 讀鎖
WRITE, // 寫(xiě)鎖
MULTI, // 聯(lián)鎖
RED // 紅鎖
}
}
@Aspect
@Component
@Slf4j
public class DistributedLockAspect {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedissonDistributedLock redissonDistributedLock;
@Around("@annotation(distributedLock)")
public Object around(ProceedingJoinPoint joinPoint,
DistributedLock distributedLock) throws Throwable {
String lockKey = parseKey(distributedLock.key(), joinPoint);
RLock lock = getLock(lockKey, distributedLock.lockType());
boolean locked = false;
try {
// 嘗試獲取鎖
if (distributedLock.leaseTime() == -1) {
// 使用看門狗自動(dòng)續(xù)期
lock.lock();
} else {
locked = lock.tryLock(
distributedLock.waitTime(),
distributedLock.leaseTime(),
distributedLock.timeUnit()
);
}
if (!locked && distributedLock.leaseTime() != -1) {
throw new RuntimeException("獲取分布式鎖失敗: " + lockKey);
}
log.info("獲取分布式鎖成功: {}", lockKey);
return joinPoint.proceed();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("獲取分布式鎖被中斷", e);
} finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
log.info("釋放分布式鎖: {}", lockKey);
}
}
}
private String parseKey(String keySpEL, ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 解析SpEL表達(dá)式
if (keySpEL.startsWith("#")) {
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(keySpEL);
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("methodName", method.getName());
// 設(shè)置參數(shù)
Object[] args = joinPoint.getArgs();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
context.setVariable(parameters[i].getName(), args[i]);
}
return expression.getValue(context, String.class);
}
return keySpEL;
}
private RLock getLock(String lockKey, DistributedLock.LockType lockType) {
switch (lockType) {
case FAIR:
return redissonClient.getFairLock(lockKey);
case READ:
return redissonClient.getReadWriteLock(lockKey).readLock();
case WRITE:
return redissonClient.getReadWriteLock(lockKey).writeLock();
case MULTI:
case RED:
// 簡(jiǎn)化處理,實(shí)際使用需要多個(gè)實(shí)例
return redissonClient.getLock(lockKey);
case REENTRANT:
default:
return redissonClient.getLock(lockKey);
}
}
}
三、使用Spring Integration實(shí)現(xiàn)分布式鎖
1. 添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
2. 配置Redis鎖注冊(cè)表
@Configuration
public class RedisLockConfiguration {
@Bean
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory connectionFactory) {
// 過(guò)期時(shí)間10秒
return new RedisLockRegistry(connectionFactory, "distributed-lock", 10000L);
}
@Bean
public LockRegistry lockRegistry(RedisLockRegistry redisLockRegistry) {
return redisLockRegistry;
}
}
3. 使用Spring Integration鎖
@Service
@Slf4j
public class OrderService {
@Autowired
private LockRegistry lockRegistry;
public void createOrder(String orderId) {
Lock lock = lockRegistry.obtain("order:" + orderId);
boolean locked = false;
try {
// 嘗試獲取鎖,最多等待5秒
locked = lock.tryLock(5, TimeUnit.SECONDS);
if (!locked) {
throw new RuntimeException("系統(tǒng)繁忙,請(qǐng)稍后重試");
}
// 執(zhí)行業(yè)務(wù)邏輯
processOrder(orderId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("訂單處理被中斷", e);
} finally {
if (locked) {
lock.unlock();
}
}
}
private void processOrder(String orderId) {
// 訂單處理邏輯
log.info("處理訂單: {}", orderId);
}
}
四、實(shí)際應(yīng)用示例
1. 商品秒殺場(chǎng)景
@Service
@Slf4j
public class SeckillService {
@Autowired
private RedissonClient redissonClient;
@Autowired
private StringRedisTemplate redisTemplate;
private static final String SECKILL_PREFIX = "seckill:product:";
private static final String LOCK_PREFIX = "seckill:lock:";
/**
* 秒殺下單(防超賣)
*/
public boolean seckillOrder(Long productId, Integer quantity, Long userId) {
String lockKey = LOCK_PREFIX + productId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 嘗試獲取鎖,等待100ms,持有鎖3秒
if (!lock.tryLock(100, 3000, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("搶購(gòu)太火爆,請(qǐng)重試");
}
// 檢查庫(kù)存
String stockKey = SECKILL_PREFIX + productId + ":stock";
Integer stock = Integer.valueOf(
redisTemplate.opsForValue().get(stockKey)
);
if (stock == null || stock < quantity) {
throw new RuntimeException("庫(kù)存不足");
}
// 扣減庫(kù)存
Long newStock = redisTemplate.opsForValue().decrement(stockKey, quantity);
if (newStock < 0) {
// 庫(kù)存不足,恢復(fù)庫(kù)存
redisTemplate.opsForValue().increment(stockKey, quantity);
throw new RuntimeException("庫(kù)存不足");
}
// 創(chuàng)建訂單
createOrder(productId, quantity, userId);
return true;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("系統(tǒng)異常", e);
} finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 使用注解簡(jiǎn)化版
*/
@DistributedLock(
key = "'seckill:lock:' + #productId",
waitTime = 1,
leaseTime = 3,
timeUnit = TimeUnit.SECONDS
)
public boolean seckillOrderWithAnnotation(Long productId, Integer quantity, Long userId) {
// 業(yè)務(wù)邏輯,無(wú)需關(guān)心鎖的獲取和釋放
String stockKey = SECKILL_PREFIX + productId + ":stock";
Integer stock = Integer.valueOf(
redisTemplate.opsForValue().get(stockKey)
);
if (stock == null || stock < quantity) {
throw new RuntimeException("庫(kù)存不足");
}
Long newStock = redisTemplate.opsForValue().decrement(stockKey, quantity);
if (newStock < 0) {
redisTemplate.opsForValue().increment(stockKey, quantity);
throw new RuntimeException("庫(kù)存不足");
}
createOrder(productId, quantity, userId);
return true;
}
private void createOrder(Long productId, Integer quantity, Long userId) {
// 創(chuàng)建訂單邏輯
log.info("用戶{}搶購(gòu)商品{},數(shù)量{}", userId, productId, quantity);
}
}
2. 定時(shí)任務(wù)防重復(fù)執(zhí)行
@Component
@Slf4j
public class ScheduledTasks {
@Autowired
private RedissonClient redissonClient;
/**
* 分布式定時(shí)任務(wù),確保集群中只有一個(gè)實(shí)例執(zhí)行
*/
@Scheduled(cron = "0 */5 * * * ?") // 每5分鐘執(zhí)行一次
public void syncDataTask() {
String lockKey = "task:sync:data";
RLock lock = redissonClient.getLock(lockKey);
// 不等待,獲取不到鎖直接返回
boolean locked = lock.tryLock();
if (!locked) {
log.info("其他節(jié)點(diǎn)正在執(zhí)行數(shù)據(jù)同步任務(wù)");
return;
}
try {
log.info("開(kāi)始執(zhí)行數(shù)據(jù)同步任務(wù)");
// 執(zhí)行業(yè)務(wù)邏輯
syncData();
log.info("數(shù)據(jù)同步任務(wù)完成");
} finally {
lock.unlock();
}
}
private void syncData() {
// 數(shù)據(jù)同步邏輯
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
五、配置和最佳實(shí)踐
1. Redisson配置類
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private String redisPort;
@Value("${spring.redis.password:}")
private String password;
@Value("${spring.redis.database:0}")
private int database;
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
// 單節(jié)點(diǎn)模式
config.useSingleServer()
.setAddress(String.format("redis://%s:%s", redisHost, redisPort))
.setDatabase(database)
.setPassword(StringUtils.hasText(password) ? password : null)
.setConnectionPoolSize(64)
.setConnectionMinimumIdleSize(10)
.setIdleConnectionTimeout(10000)
.setConnectTimeout(3000)
.setTimeout(3000)
.setRetryAttempts(3)
.setRetryInterval(1500)
.setPingConnectionInterval(30000)
.setKeepAlive(true);
// 鎖配置
config.setLockWatchdogTimeout(30000L); // 看門狗超時(shí)時(shí)間
return Redisson.create(config);
}
}
2. 分布式鎖工具類
@Component
@Slf4j
public class DistributedLockUtil {
@Autowired
private RedissonClient redissonClient;
/**
* 執(zhí)行帶鎖的方法
*/
public <T> T executeWithLock(String lockKey, long waitTime, long leaseTime,
TimeUnit unit, Supplier<T> supplier) {
RLock lock = redissonClient.getLock(lockKey);
boolean locked = false;
try {
locked = lock.tryLock(waitTime, leaseTime, unit);
if (!locked) {
throw new DistributedLockException("獲取分布式鎖失敗: " + lockKey);
}
return supplier.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new DistributedLockException("獲取分布式鎖被中斷", e);
} finally {
if (locked && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 執(zhí)行帶鎖的方法(無(wú)返回值)
*/
public void executeWithLock(String lockKey, long waitTime, long leaseTime,
TimeUnit unit, Runnable runnable) {
executeWithLock(lockKey, waitTime, leaseTime, unit, () -> {
runnable.run();
return null;
});
}
/**
* 執(zhí)行帶鎖的方法(快速失?。?
*/
public <T> Optional<T> tryExecuteWithLock(String lockKey, long waitTime,
long leaseTime, TimeUnit unit,
Supplier<T> supplier) {
try {
return Optional.ofNullable(
executeWithLock(lockKey, waitTime, leaseTime, unit, supplier)
);
} catch (DistributedLockException e) {
log.warn("獲取鎖失敗,跳過(guò)執(zhí)行: {}", lockKey);
return Optional.empty();
}
}
public static class DistributedLockException extends RuntimeException {
public DistributedLockException(String message) {
super(message);
}
public DistributedLockException(String message, Throwable cause) {
super(message, cause);
}
}
}
六、測(cè)試分布式鎖
@SpringBootTest
@Slf4j
class DistributedLockTest {
@Autowired
private RedissonDistributedLock redissonDistributedLock;
@Autowired
private DistributedLockUtil distributedLockUtil;
private final AtomicInteger counter = new AtomicInteger(0);
@Test
void testConcurrentLock() throws InterruptedException {
int threadCount = 10;
String lockKey = "test:concurrent:lock";
CountDownLatch latch = new CountDownLatch(threadCount);
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
for (int i = 0; i < threadCount; i++) {
executor.submit(() -> {
try {
boolean locked = redissonDistributedLock
.tryReentrantLock(lockKey, 2, 5, TimeUnit.SECONDS);
if (locked) {
try {
// 模擬業(yè)務(wù)處理
Thread.sleep(100);
int value = counter.incrementAndGet();
log.info("線程 {} 獲取鎖成功,計(jì)數(shù): {}",
Thread.currentThread().getName(), value);
} finally {
redissonDistributedLock.unlock(lockKey);
}
} else {
log.warn("線程 {} 獲取鎖失敗", Thread.currentThread().getName());
}
} catch (Exception e) {
log.error("線程執(zhí)行異常", e);
} finally {
latch.countDown();
}
});
}
latch.await();
executor.shutdown();
assertEquals(threadCount, counter.get());
}
@Test
void testLockWithUtil() {
String lockKey = "test:util:lock";
String result = distributedLockUtil.executeWithLock(
lockKey,
2,
5,
TimeUnit.SECONDS,
() -> {
// 業(yè)務(wù)邏輯
return "success";
}
);
assertEquals("success", result);
}
}
總結(jié)與建議
1.選擇方案
- 簡(jiǎn)單場(chǎng)景:使用Spring Integration的
RedisLockRegistry - 生產(chǎn)環(huán)境:推薦使用Redisson,功能最全,穩(wěn)定性最好
- 特殊需求:需要精細(xì)控制時(shí),可以使用原生Redis命令自定義
2.最佳實(shí)踐
- 鎖的key要有業(yè)務(wù)含義,如
order:create:{orderId} - 一定要設(shè)置合理的過(guò)期時(shí)間,防止死鎖
- 釋放鎖時(shí)要檢查是否當(dāng)前線程持有
- 使用Lua腳本保證原子性
- 考慮鎖的可重入性
- 生產(chǎn)環(huán)境使用Redis集群或哨兵模式
3.注意事項(xiàng)
- 避免鎖粒度過(guò)大,影響并發(fā)性能
- 避免鎖持有時(shí)間過(guò)長(zhǎng)
- 實(shí)現(xiàn)鎖的自動(dòng)續(xù)期(看門狗機(jī)制)
- 考慮鎖等待超時(shí)和快速失敗
- 添加監(jiān)控和告警機(jī)制
4.常見(jiàn)問(wèn)題解決
- 鎖提前過(guò)期:使用Redisson的看門狗機(jī)制
- 鎖誤刪:每個(gè)鎖設(shè)置唯一value,釋放時(shí)驗(yàn)證
- 鎖不可重入:使用Redisson或?qū)崿F(xiàn)可重入邏輯
- Redis集群故障:使用RedLock算法(多個(gè)Redis實(shí)例)
這樣實(shí)現(xiàn)的分布式鎖既安全又可靠,可以滿足大多數(shù)業(yè)務(wù)場(chǎng)景的需求。
以上就是基于SpringBoot實(shí)現(xiàn)分布式鎖的三種方法的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot分布式鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot使用Zookeeper實(shí)現(xiàn)分布式鎖的實(shí)現(xiàn)示例
- SpringBoot項(xiàng)目中實(shí)現(xiàn)Redis分布式鎖的方法與最佳實(shí)踐
- SpringBoot中使用Zookeeper實(shí)現(xiàn)分布式鎖的案例
- SpringBoot3+Redis實(shí)現(xiàn)分布式鎖的配置方法
- Springboot中使用Redis實(shí)現(xiàn)分布式鎖的示例代碼
- Springboot使用redisson實(shí)現(xiàn)分布式鎖的代碼示例
- 基于SpringBoot+Redis實(shí)現(xiàn)分布式鎖
- 如何在SpringBoot中使用Redis實(shí)現(xiàn)分布式鎖
- SpringBoot整合分布式鎖redisson的示例代碼
相關(guān)文章
mybatis的xml中使用@符號(hào)調(diào)用類方法示例
這篇文章主要為大家介紹了mybatis的xml中使用@符號(hào)調(diào)用類方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Java方法重載和方法重寫(xiě)的區(qū)別到底在哪?
今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Java方法重載和方法重寫(xiě)的區(qū)別到底在哪展開(kāi),文中有非常詳細(xì)的解釋,需要的朋友可以參考下2021-06-06
MyBatis-Flex BaseMapper的接口基本用法小結(jié)
本文主要介紹了MyBatis-Flex BaseMapper的接口基本用法小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02
Java獲取接口的所有實(shí)現(xiàn)類方法總結(jié)示例
這篇文章主要給大家介紹了關(guān)于Java獲取接口的所有實(shí)現(xiàn)類方法的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-06-06
SpringBoot中cache使用的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot中cache使用的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-10-10
SpringBoot中@ConfigurationProperties注解的使用與源碼詳解
這篇文章主要介紹了SpringBoot中@ConfigurationProperties注解的使用與源碼詳解,@ConfigurationProperties注解用于自動(dòng)配置綁定,可以將application.properties配置中的值注入到bean對(duì)象上,需要的朋友可以參考下2023-11-11
一文帶你掌握springBoot如何做到優(yōu)雅停機(jī)的
在分布式系統(tǒng)中,服務(wù)的優(yōu)雅停機(jī)(Graceful Shutdown)是確保業(yè)務(wù)連續(xù)性的重要機(jī)制,下面就跟隨小編一起來(lái)深入了解下springBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的具體方式吧2025-04-04
SpringBoot升級(jí)3.2報(bào)錯(cuò)Invalid value type for
這篇文章給大家介紹了SpringBoot升級(jí)3.2報(bào)錯(cuò)Invalid value type for attribute ‘factoryBeanObjectType‘: java.lang.String的解決方案,文中有詳細(xì)的原因分析,需要的朋友可以參考下2023-12-12
SpringBoot升級(jí)指定jackson版本的問(wèn)題
這篇文章主要介紹了SpringBoot升級(jí)指定jackson版本,本文給大家分享了漏洞通告及修改Springboot中jackson版本的問(wèn)題,需要的朋友可以參考下2022-08-08

