问题:你知道 redis 分布式锁吗?有哪些实现方案?你谈谈对 redis 分布式锁的理解, 删除key 的时候有什么问题?
锁的概述:
1、 JVM 层面的加锁, 就是单机版本
2、 分布式为服务架构,拆分后各个为服务之间为了避免冲突和数据故障而加的一种锁,分布式锁
上面两种是两个不同的概念
分布式锁的实现:
1、mysql
2、zookeeper
3、redis
一般的互联网公司,都是喜欢使用 redis 来作为分布式锁
核心概览:
redis --- redlock ===> redisson lock/unlock
如果我们自己写:setnx + lua 脚本
开源实现: redisCluster -- redisson
分布式锁的常见问题和思考!
1、redis 除了拿来组缓存,还有那些基于 redis 的什么用法?
2、redis 做分布式锁的时候有需要注意的问题?
3、如果 redis 是单点部署,会带来什么问题?
4、集群模式下,比如主从模式,有没有什么问题?
5、你简单的介绍一下 redlock , 看看你的理解?
6、redis 分布式锁如何续期,如何理解看门狗概念?
实践案例 (Spring Boot + Redis)
使用场景:
多个服务之间 + 保证同一个时刻内,同一个用户只能有一个请求(防止关键业务出现数据冲突和并发错误)
依赖文件
plugins { id 'org.springframework.boot' version '2.6.3' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group 'io.zhengsh' version '1.0-SNAPSHOT' repositories { mavenCentral() maven { url 'https://repo.spring.io/milestone' } maven { url 'https://repo.spring.io/snapshot' } } dependencies { //implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.apache.commons:commons-pool2' //implementation 'org.redisson:redisson-spring-boot-starter:3.16.8' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() }
YML 配置
server: port: 11111 spring: redis: database: 0 host: 127.0.0.1 passowrd: lettuce: pool: max-active: 8 max-wait: -1 max-idle: 8 min-idle: 0
Redis 配置类
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } }
Controller 控制器
@RestController public class GoodsController { private Logger logger = LoggerFactory.getLogger(GoodsController.class); @Autowired private StringRedisTemplate stringRedisTemplate; @Value("${server.port}") private Integer serverPort; @GetMapping("/byGoods") public String buyGoods() { String result = stringRedisTemplate.opsForValue().get("goods:001"); int goodsNumber = result == null ? 0 : Integer.valueOf(result); if (goodsNumber > 0) { int realNumber = goodsNumber - 1; stringRedisTemplate.opsForValue().set("goods:001", String.valueOf(realNumber)); logger.info("成功买到商品, 库存还剩下:{} 件|服务提供者 serverPort : {}", realNumber, serverPort); return "成功买到商品, 库存还剩下:" + realNumber + " 件|服务提供者 serverPort : " + serverPort; } logger.info("商品已经售完/活动结束/调用超时, 欢迎下次光临, serverPort:{}", serverPort); return "商品已经售完/活动结束/调用超时, 欢迎下次光临, serverPort:" + serverPort; } }
启动类文件
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
接口访问