本博客使用第三方开源组件Jedis实现Redis客户端,且只考虑Redis服务端单机部署的场景。
为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。
数据库锁(包括数据库唯一约束/乐观锁),Redis 锁,ZK 锁
如下代码即为redis 加锁代码,使用jedis.set 方法将redis 的setNx 命令和 expire 命令绑定在一个原子操作中,避免在执行setNx 后系统宕机导致死锁。
clientId 意义为线程的标识,避免在解锁的过程中A 线程解了B 线程的锁具体过程在下文会有解释
public class RedisLock { /** * redis setNx 命令 */ private final String REDIS_COMMAND_NX = "NX"; private final String REDIS_COMMAND_EXPIRE = "PX"; /** * 获取redis 分布式锁 * * @param [jedis, lockKey, clientId, expireTime] * @return boolean * @author longcheng * @date 2020/10/21 23:13 */ public boolean tryLock(Jedis jedis, String lockKey, String clientId, int expireTime) { String result = jedis.set(lockKey, clientId, REDIS_COMMAND_NX, REDIS_COMMAND_EXPIRE, expireTime); if(result.equals("OK")){ return true; }else { return false; } } }不可靠示例1
如下代码假如执行完整个减库存方法需要15s(在系统压力较大的情况下,正常情况只需要8秒) ,现有三个线程 A,B,C, 当线程A 执行到减库存方法时花费了10秒,此时锁被释放。B 进入加锁,A执行释放锁。此时 是不是就把B 的锁释放掉了? 所以上述加锁代码中的clientId 是否就好理解了
/** * 减库存方法 * * @return */ public Integer reduceStock(Integer num) { String goodsId = "001"; Integer row = 0; try { //加锁 Long res = jedis.setnx("goodsId", "ok"); jedis.expire("goodsId", 10); if (res == 0) { //代表已经被锁住 throw new Exception("已被锁住"); } //减库存 row = goodsDAO.reduceStock(num); } finally { //释放锁 jedis.del(goodsId); } return row; }