Redis分布式锁 (jedis)
1.针对场景
分布式集群,例如MR 多个节点上的不同jvm。 传统的多线程仅针对同一jvm下的操作。如果跨节点的数据锁问题需要另外的支持分布式锁的库来处理。例如zookeeper 或 redis 天生对分布式场景有良好的适性。
2.业务需求
一般用于一些统计业务,需要求取每个节点上的数据总和。分布式场景带来的并发问题需要利用redis分布式锁处理。
3.核心代码
private static String key="monitorESErrorCount";
private static int count=0;
/**
*
* 功能: 利用redis分布式锁 做数据统计
* @param lockName 可以为共享变量名,也可以为方法名,主要是用于模拟锁信息
* @return
*/
public boolean lock(String lockName) {
Long result = redisDao.setNx(lockName, String.valueOf(System.currentTimeMillis() + 5000));
// jedisCluster.setnx(key, value)
// 使用jedis接口 获取值 当且仅当key存在 赋予值 并返回1 否则不错任何操作 返回0
if (result != null && result.intValue() == 1) {
// 运行至此 说明已经有人抢到了这把锁 而其他人不会进入此处
System.out.println(Thread.currentThread() + "加锁成功!");
// 设置该锁过期时间 5s 防止redis崩坏造成死锁 只需要比业务逻辑代码执行时间稍长一点即可
redisDao.expire(lockName, 5);
// 此处开始处理业务逻辑 示例为获取随机数 累和
if (redisDao.get(key) != null) {
// 获取redis中的缓存值
Long errorCount = Long.parseLong(redisDao.get(key));
int n = (int) (Math.random() * 1000);
errorCount += n;
count += n;
// 删除之前的key
redisDao.del(key);
// 重新赋予key的值
redisDao.setNx(key, "" + errorCount);
} else {
// 当redis中还没有开始计数时
int n = (int) (Math.random() * 1000);
count += n;
redisDao.setNx(key, "" + n);
}
// 全部执行完毕后 删除锁
redisDao.del(lockName);
return true;
} else {
return false;
}
}
其中redisDao为自己封装的redis连接池 用于控制连接数等其他 实现了最基础的 expire setNx get 等方法 (即直接调用jedis内api) 在此就不贴出连接池相关代码。
4.原理解释
在每次做合并的时候将值根据redis内的缓存值进行累加,其内置的分布式锁可确保每次只有一个人获取到这把锁,其余人等待。 只要redis中还存在lock对应key value 则其他人无法处理业务涉及的值。删除lock 后其他人才能够继续获取。 对于MR来说,只需要在reduce最后阶段 while(lock(“DistributionLock”)) 即可,将全局变量count累和存入redis中并避免了分布式造成的并发问题。
5.缺点
每次锁的确认相当于请求了一次redis,需要对请求控制,例如100ms内只做一次request 避免高频访问给redis带来压力。