分布式锁死锁问题

现象 接口偶发卡死,请求一直等待不返回。 排查后发现某个 key 长时间存在,业务线程始终获取不到锁。 原始实现 使用 setnx 实现分布式锁: Boolean lock = redisTemplate.opsForValue() .setIfAbsent("order_lock", "1"); if (Boolean.TRUE.equals(lock)) { try { // 执行业务 } finally { redisTemplate.delete("order_lock"); } } 问题原因 当服务在执行过程中异常退出(例如进程重启), finally 块没有执行,锁未删除。 由于没有设置过期时间,key 会一直存在。 后续请求全部获取锁失败,形成类似“死锁”的情况。 调整方案 增加过期时间: Boolean lock = redisTemplate.opsForValue() .setIfAbsent("order_lock", "1", 30, TimeUnit.SECONDS); 避免锁永久存在。 进一步问题 如果业务执行超过 30 秒, 锁提前过期,其他线程可能获取锁,造成并发执行。 改进方式 使用唯一标识作为 value: String requestId = UUID.randomUUID().toString(); Boolean lock = redisTemplate.opsForValue() .setIfAbsent("order_lock", requestId, 30, TimeUnit.SECONDS); 释放锁时校验 value: ...

May 21, 2024 · 1 min · Faithlv

缓存击穿简单复现

现象 某个热点数据设置了过期时间。 过期瞬间并发请求同时打到数据库,数据库压力突然升高。 简单模拟 伪代码如下: public String getData(String key) { String value = redisTemplate.opsForValue().get(key); if (value == null) { value = queryFromDb(key); redisTemplate.opsForValue().set(key, value, 30, TimeUnit.SECONDS); } return value; } 当 key 过期时,多个线程同时进入 if 判断。 并发测试 简单用多线程模拟: ExecutorService pool = Executors.newFixedThreadPool(20); for (int i = 0; i < 20; i++) { pool.execute(() -> { getData("hot_key"); }); } 在 key 失效瞬间,可以看到数据库查询被执行多次。 ...

March 19, 2024 · 1 min · Faithlv