分布式锁

1. 分布式锁要解决的问题

随着业务的发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程,多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的javaAPI并不能提供分布式锁的能力,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源访问,这就是分布式锁要解决的问题

2. 解决方案

  1. 基于数据库实现分布式锁
  2. 基于缓存(redis等)
  3. 基于zookeeper

本章我们基于性能最高redis来学习

3. 使用redis实现分布式锁

  1. 使用setnx上锁,通过del可以释放锁
  2. 锁一直没有释放,可以设置key的过期时间,自动释放
  3. 如果在上锁的时候,服务器突然断电,就设置不了过期时间了。其实我们可以边上锁边设置过期时间
set user 10 nx ex 20
nx 表示上锁
ex 表示设置过期时间 20

UUID防止误删锁

问题的出现
image
解决的思路

我们给锁一个标识(UUID),当Redis需要删除锁时,需要先和uuid对比,相同才能删

java代码实现

package com.google.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class RedisTestController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("/redisTest")
public void testLock() {
//获取uuid作为锁的标识符
String uuid = UUID.randomUUID().toString();
//1.获取锁,setnx
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);
if (lock) {
//2.如果获取到了锁,将num加1
Object value = redisTemplate.opsForValue().get("num");
if(value.equals(null)){
return;
}
int num = Integer.parseInt(value + "");
//3.将num值加1
redisTemplate.opsForValue().set("num", ++num);
String lockUuid = (String)redisTemplate.opsForValue().get("lock");
if(lockUuid.equals(uuid)){ //如果锁的标识相同,则释放锁
//4.释放锁
redisTemplate.delete("lock");
}
System.out.println("不为空");
} else {
try {
//如果没有获取到锁,就等0.1秒再尝试获取锁
Thread.sleep(100);
testLock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

lua脚本保证锁的原子性

锁的实现需要同时满足一下四个条件:

  1. 互斥性。在任意时刻,只有一个客户能持有锁
  2. 不会发生死锁,即使有一个客户端在持有锁的期间崩溃,而没有主动释放锁,也能保证后续其他客户能加锁。
  3. 加锁和解锁必须是同一个客户端,客户端自己不能把别人的锁给解了
  4. 加锁解锁必须具有原子性
posted @   小罗要有出息  阅读(30)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
点击右上角即可分享
微信分享提示