廊坊建设网站,北京免费网站设计,青海省交通建设工程质量监督站网站,软件开发项目管理办法目录 2.1. 基本实现2.2. 防死锁2.3. 防误删2.4. redis中的lua脚本2.4.1 redis 并不能保证2.4.2 lua介绍 2.5. 使用lua保证删除原子性 2.1. 基本实现
借助于redis中的命令setnx(key, value)#xff0c;key不存在就新增#xff0c;存在就什么都不做。同时有多个客户端发送setn… 目录 2.1. 基本实现2.2. 防死锁2.3. 防误删2.4. redis中的lua脚本2.4.1 redis 并不能保证2.4.2 lua介绍 2.5. 使用lua保证删除原子性 2.1. 基本实现
借助于redis中的命令setnx(key, value)key不存在就新增存在就什么都不做。同时有多个客户端发送setnx命令只有一个客户端可以成功返回1true其他的客户端返回0false。
多个客户端同时获取锁setnx获取成功执行业务逻辑执行完成释放锁del其他客户端等待重试
改造StockService方法 /*** redis setnx实现分布式锁最基本的哪一种 */public void deduct() {// 加锁setnxBoolean lock this.redisTemplate.opsForValue().setIfAbsent(lock, 111);if (!lock) {// 没有获取到锁进行重试try {Thread.sleep(50);this.deduct();} catch (InterruptedException e) {e.printStackTrace();}} else {try {// 1. 查询库存信息String stockStr redisTemplate.opsForValue().get(stock: 1001);// 2. 判断库存是否充足if (stockStr ! null stockStr.length() ! 0) {Long stock Long.parseLong(stockStr);if (stock 0) {redisTemplate.opsForValue().set(stock: 1001, String.valueOf(stock - 1));}}} finally {// 解锁this.redisTemplate.delete(lock);}}}使用 jmeter 进行压测 查看库存数量
上述代码优化不断重试的过程中一直进行递归最终导致栈的溢出。
解决
/*** while循环代替递归解决不断重试可能导致的栈溢出的问题*/public void deduct() {// 加锁setnxwhile (this.redisTemplate.opsForValue().setIfAbsent(lock1, 1)) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}try {// 1. 查询库存信息String stockStr redisTemplate.opsForValue().get(stock: 1001);// 2. 判断库存是否充足if (stockStr ! null stockStr.length() ! 0) {Long stock Long.parseLong(stockStr);if (stock 0) {redisTemplate.opsForValue().set(stock: 1001, String.valueOf(stock - 1));}}} finally {// 解锁this.redisTemplate.delete(lock1);}}}
2.2. 防死锁
问题setnx刚刚获取到锁当前服务器宕机导致del释放锁无法执行进而导致锁无法锁无法释放死锁 解决给锁设置过期时间自动释放锁。
设置过期时间两种方式
通过expire设置过期时间缺乏原子性如果在setnx和expire之间出现异常锁也无法释放使用set指令设置过期时间set key value ex 3 nx既达到setnx的效果又设置了过期时间 2.3. 防误删
持有锁的线程在锁的内部出现了阻塞导致他的锁自动释放这时其他线程线程2来尝试获得锁就拿到了这把锁然后线程2在持有锁执行过程中线程1反应过来继续执行而线程1执行过程中走到了删除锁逻辑此时就会把本应该属于线程2的锁进行删除这就是误删别人锁的情况说明
解决 setnx获取锁时设置一个指定的唯一值例如uuid释放前获取这个值判断是否自己的锁
问题删除操作缺乏原子性。 场景
index1执行删除时查询到的lock值确实和uuid相等index1执行删除前lock刚好过期时间已到被redis自动释放index2获取了lockindex1执行删除此时会把index2的lock删除
解决方案没有一个命令可以同时做到判断 删除所有只能通过其他方式实现LUA脚本
2.4. redis中的lua脚本
2.4.1 redis 并不能保证
redis采用单线程架构可以保证单个命令的原子性但是无法保证一组命令在高并发场景下的原子性。
如果redis客户端通过lua脚本把3个命令一次性发送给redis服务器那么这三个指令就不会被其他客户端指令打断。Redis 也保证脚本会以原子性的方式执行 当某个脚本正在运行的时候不会有其他脚本或 Redis 命令被执行。 这和使用 MULTI/ EXEC 包围的事务很类似。
2.4.2 lua介绍
2.5. 使用lua保证删除原子性