分布式锁一般有三种实现方式:
1. 数据库乐观锁;
2. 基于 ZooKeeper 的分布式锁;
3. 基于 Redis 的分布式锁;
这里大概说一下三种方式的优缺点, 数据库乐观锁优点是实现简单, 只需要 for update 关键词就可以实现, 缺点是无法满足高并发量以及数据库读写频繁的系统
ZooKeeper 分布式锁无论是从性能以及实现的功能来说都是非常优秀, 只是在开发起来需要一定的基础, 对新手可能不是很友好
而本文主要讲第三种利用 Redis 实现分布式锁, 优点是开发相对简单, 能满足一定并发量的系统, 缺点是存在线程争抢锁的问题, 当并发量到达一定级别, 多个线程去争抢同一个锁, 对性能的影响较大
事务以及原子性
虽然 Redis 是单线程运行, 但是在分布式的情况下对同一资源进行操作还是会出现问题, 下图是一个简单的例子
所以一定要保证 tomcat1 以及 tomcat2 读写的原子性, 既读与写要么都执行, 要么都不执行. 关于事务的原子性可以查询这里 https://www.jianshu.com/p/cf57726e77f2
那么如何保证呢, Redis 在 2.6 中加入了 lua 脚本功能可以轻松的解决这个问题, 下面是一个简单的例子实现了上述的加 100 操作
- Jedis jedis = jedisPool.getResource();
- String script = "local a = redis.call('get', KEYS[0]) a = a + 100 redis.call('set', a)";
- jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID,"1000");
分布式锁的具体实现
大概讲一下思路: 首先加锁的方式是向 Redis 里存入一个 KEY-VALUE,KEY 存入的加锁对象可以是方法, 类, 数据等等, VALUE 存入持有锁的节点 (例如 tomcat1)
大概整理了一下几个问题:
Q: 为什么 VALUE 存入持有锁的节点
A: 为的是防止 A 加的锁被 B 给解除, 保证只有持有锁的节点才能解锁
Q: 怎么存入持有锁的节点
A: 这里只是我的思路是在 tomcat 启的时候生成一个 uuid 作为该 tomcat 的 token 存入到 VALUE 中
Q: 怎么防止死锁
A: 利用 Redis 设置键的过期时间
下面贴出部分代码, 进攻参考
加锁
- JedisPool jedisPool = new JedisPool(new JedisPoolConfig(),RedisInstance.hostName,Integer.parseInt(RedisInstance.port),5000,password);
- Jedis jedis = jedisPool.getResource();
- // key1 : key 值 argv1 :value 值 argv2 : 过期时间
- String script = "if redis.call('EXISTS',KEYS[1]) ==0 then redis.call('set',KEYS[1],ARGV[1]) redis.call('EXPIRE',KEYS[1],ARGV[2]) return 1 else return 0 end";
- long result = (long) jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID,"1000");
- jedis.close();
- jedisPool.close();
解锁
- JedisPool jedisPool = new JedisPool(new JedisPoolConfig(),RedisInstance.hostName,Integer.parseInt(RedisInstance.port),5000,password);
- Jedis jedis = jedisPool.getResource();
- String script = "if redis.call('EXISTS',KEYS[1]) ==1 and redis.call('GET',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
- long result = (long) jedis.eval(script, 1, rname+"Lock",RedisCacheFactory.FactoryUUID);
- jedis.close();
- jedisPool.close();
以上仅个人意见, 如有错误的地方, 还请各位海涵.
后面可能会整合这次缓存改造的所有环节发出来给大家参考 一下
来源: https://www.cnblogs.com/buggeerWang/p/10433248.html