畅购商城项目改造--redis分布式锁解决缓存击穿的问题
程序员文章站
2022-05-04 12:52:01
...
分布式锁主流的实现方案:
- 基于数据库实现分布式锁
- 基于缓存(Redis等)
- 基于Zookeeper
每一种分布式锁解决方案都有各自的优缺点:
-
性能:redis最高
-
可靠性:zookeeper最高
但是畅购项目并没有使用zookeeper进行开发,功能由Eureka接替,我也没有在项目实现改造。下一篇文章再说利用模板设计模式实现分布式锁的问题。
借助于redis中的命令setnx(key, value),key不存在就新增,存在就什么都不做。同时有多个客户端发送setnx命令,只有一个客户端可以成功,返回1(true);其他的客户端返回0(false)。 -
多个客户端同时获取锁(setnx)
-
获取成功,执行业务逻辑,执行完成释放锁(del)
-
其他客户端等待重试
基本思想就是这样改造,为了解决 缓存击穿 击穿是一个热点key失效,执行redis的setnx命令
uuid是保证给自己的锁唯一标志,防止误删。
同时string script是lua脚本的执行命令,保证原子性。String uuid = UUID.randomUUID().toString(); Boolean aBoolean = this.redisTemplate.opsForValue().setIfAbsent("lock", uuid,10,TimeUnit.SECONDS); Content content=new Content(); //判断是否拿到锁 if(aBoolean){ String jsonContent = redisTemplate.boundValueOps("content_" + id).get(); if(jsonContent!=null){ List<Content> contents; contents = JSON.parseObject(jsonContent, List.class); return contents; } content.setCategoryId(id); content.setStatus("1"); // 2. 释放锁 del String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; this.redisTemplate.execute(new DefaultRedisScript<>(script), Arrays.asList("lock"), Arrays.asList(uuid)); }else { //其他请求尝试获取锁 findByCategory(id); } return contentMapper.select(content);
备注:其实最好的方式还是redission加上AOP环绕通知,做成注解实现,具体查阅google