Redission分布式锁踩坑

踩坑1:数据库事务超时

先了解一下“锁互斥机制”

比如客户端1拿到锁之后,还未执行完代码,此时客户端2来尝试拿锁,

第一个判断:客户端2这时候会发现这个锁已经存在了

第二个判断:是否是客户端2加的锁,但是也发现不是,因为是客户端1加的锁

然后客户端2会获取到这个锁的剩余生存时间,此时客户端2会进入一个while循环,不停的尝试加锁。

伪代码:

@Transaction

   public void lock() {

        while (true) {

            boolean flag = this.getLock(key);

            if (flag) {

                insert();

            }

        }

    }

这里有个@Transaction事务注解,比如

boolean flag = this.getLock(key);

这块代码迟迟拿不到锁,超过了事务的时间限制,程序就会报数据库事务超时的异常;或者我们在执行insert();这块代码时,执行时间太长可能也会超过事务的时间限制,从而也导致程序报数据库超时的异常。一般解决这种问题就要:将数据库事务改为手动提交、回滚事务。

@Autowired

    DataSourceTransactionManager dataSourceTransactionManager;


    @Transaction

    public void lock() {

//手动开启事务

        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);

        try {

            while (true) {

                boolean flag = this.getLock(key);

                if (flag) {

                    insert();

//手动提交事务

                    dataSourceTransactionManager.commit(transactionStatus);

                }

            }

        } catch (Exception e) {

//手动回滚事务

            dataSourceTransactionManager.rollback(transactionStatus);

        }

    }


踩坑2:redis的锁未被释放,连接池爆满

这种情况是一种低级错误,由于当前线程获取到redis锁,处理完业务后未及时释放锁,导致其它线程会一直尝试获取锁阻塞,例如:用Jedis客户端会报如下的错误信息

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

redis线程池已经没有空闲线程来处理客户端命令。


解决办法


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连接池大小,主要参考具体业务场景的并发量来设置;

3.如果是重入锁未拿到锁后,线程可以释放当前连接并且sleep一段时间。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。