踩坑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一段时间。
