redis实现分布式锁踩坑记录

这里主要记录项目中使用基于redis的分布式锁所遇到的问题及解决方案;redis

业务场景

个人业务场景是这样的,咱们服务有库存模块,而个人服务又是多节点部署,要高峰期会存在库存差别,后面分析问题以后,打算采用redis实现分布式锁(主要的缘由是服务已经集成了redis,不须要作额外的配置) 算法

踩坑1. 数据库事务超时

不要感受奇怪,分布式锁怎么会致使数据库事务超时呢?
个人代码大概是这样的:数据库

伪代码
@Transaction(readOnly=false)
void update(){
    do{
        redis=JedisUtil.getJedis();
        flag = getLock(key,redis);
        if(flag){
            update();
        }
    }while(true)
}

当你的key长时间获取不到锁,而且数据库事务都有超时时间的限制,那么就会出现数据库事务超时问题;
解决方案并发

数据库事务改成手动提交事务;分布式

踩坑2. redis key过时,而业务没有执行完

个人key的过时时间设置的是30s,若是30秒业务尚未执行完毕,锁就会自动释放,锁释放以后,其它线程又会去占用锁,一样会致使问题的发生;
解决方案ide

最简单的解决方案就是使用redisson;
若是非要用redis来解决的话,只能使用定时器去检测key,若是说key还有2秒就快过时了,那么再为key从新设置30秒的过时时间;线程

踩坑3. redis链接池爆满

分布式锁刚加上以后,生产出现一个问题,就是:redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
解决办法
开始查代码,发现是开发人员没有对链接进行释放; code

修复bug以后,又在线上跑了一段时间,又出现了redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
解决办法事务

void update(){
    do{
        redis=JedisUtil.getJedis();
        flag = getLock(key,redis);
        if(flag){
            update();
        }else{
            // 释放当前redis链接
            // 因为咱们的业务场景属于比较耗时的业务型,因此在这里休眠1000毫秒
            redis.close();
            sleep(1000);
        }
    }while(true)
}

1.当前请求获取锁,若是获取不到,则释放当前链接,并休眠一会;
2.合理配置redis链接池大小,主要参考具体业务场景的并发量来设置;开发

踩坑4. 解铃还须系铃人

回顾一下加锁的参数:

set(key, vlue,"NX","PX", 30000);

其中:value,我使用它来表示加锁人,必须是一个惟一的标识

好比:
A线程 key=test value=01
B线程 key=test value=02

若是A线程执行业务耗时超过了锁的持有时间,锁会自动释放;锁自动释放以后,线程B又加锁成功,可是,此时A线程执行完业务逻辑以后,去释放锁,但A线程的锁已经自动释放了,若是没有value来标识的话,它可能就会去释放B线程的锁;

踩坑5. redis集群实现分布式锁

这种状况我没有遇到,由于公司的redis集群作了改进;

先说一下这种问题产生的缘由:
若是master节点因为某缘由发生了主从切换,那么就会出现锁丢失的状况;

  • 在master节点上拿到了锁;
  • 可是这个加锁的key尚未同步到slave节点;
  • master故障,发生故障转移,slave节点升级为master节点;
  • 故致使锁丢失;

解决办法

须要经过使用redlock算法;或使用redisson,它有对redlock算法作封装;

相关文章
相关标签/搜索