设置事务超时时间的问题及Oracle数据库update和锁
写在前面:2020年面试必备的Java后端进阶面试题总结了一份复习指南在Github上,内容详细,图文并茂,有需要学习的朋友可以Star一下! GitHub地址:https://github.com/abel-max/Java-Study-Note/tree/master
Oracle的update语句问题:
update config t set t.value =1 where t.key='DB_ KEY'
或者:
select * from config t where t.key='DB_KEY' for update
这个update会试图为where条件指定的那些数据加上行级锁,但如果此时这些数据已经被其他事务加锁了,则会一直等待,直到锁被释放,然后再去竞争这些数据的锁。
问题是,我不想让它一直等待,如果加不上锁就立即返回失败信息。对于select ... for update语句,可以用如下方式:
select ... for update nowait(加锁失败则立即报错)
select ... for update wait 3(最多等待3秒)
但仅限于select ... for update语句,update语句没有这个功能。而且,这是Oracle数据库所特有的功能。
我在设计多服务器、多线程数据库同步操作时,最开始是采用的如下方式:
// 如果更新成功了,则i=1,否则i=0
int i = "update config t set t.flag =1 where t.key='DB_KEY' and t.flag=0";
// 当i=1时才允许去做查询和更新数据
if(i==1) do query & update DATA;
// 查询完之后将DB_KEY的flag改回原来的值
update config t set t.flag =0 where t.key='DB_KEY' and t.flag=1
从中可见,t.key='DB_KEY'这条数据就好似一把锁,谁成功的将flag置为1就意味着谁打开了这把锁,只有打开了锁才能操作真正的数据,避免了多服务器、多线程查询出同样的数据。
但是啊,上面的方法有个致命的缺点:如果一个线程将锁打开了,而因为意外死亡(停机、重启等原因)未能将锁锁上,那么这意味着什么?意味着这把锁永远处于打开状态,其他线程都没有机会再次获取它了。所以我想到了利用事务来控制,起初开启事务,然后再update KEY,如果成功了,再query & update DATA,然后再update KEY归还钥匙。如果线程意外停止了,那么未提交的事务会立即回滚,锁回归未使用状态。
我是这样做的,设置事务的超时时间:开启事务——update——doSomething比如query——关闭事务。事务超时时间设置为5秒。如果update等待超过这个时间,则会抛出异常,报错终止。
为什么要设置一个超时时间呢,因为完整的这一套事务控制需要一定时间,比如4秒,如果DB_KEY已经被加锁,则其他update KEY将会处于等待状态,等待多久,这个时间是不可控的,所以我想要自己来控制这个等待的timeout时间。
但是我测试时发现的是,超时后,update处没有报错,后面的query依然执行,query时才报错(事务超时异常)。这是个比较难看透的问题,我想了很久才想通,原因是update时可能已经等待了4.99秒,然后update成功了,接着执行query,但是此时时间已经超过5秒了,所以query报事务超时异常(正所谓,好不容易等到update成功了,但是却没有足够的时间留给后面的query)。因此,update和query的时间差不能太大,否则的话有可能update成功了而query失败(从设计上讲,我不太希望有这种情况出现,当然,即使出现了,也只是浪费了一次事务罢了)。
分析清楚了这个问题之后,现在有两种合理的设计方案:
1)不用事务,直接走update KEY——query & update DATA——update KEY路线,但是在update KEY时要记录update date,并启动一个线程循环不断的去检查KEY是否处于flag=1且now - update date > 30秒的状态,如果处于这种状态,则占用KEY的时间过长,因而断定获取KEY的那个线程出现了异常,没有能力将KEY还原,此时这个Check线程就帮忙把KEY重置为未使用状态。
2)使用事务,为update KEY——query & update DATA——update KEY期间加上事务控制,如果线程挂掉,则事务回滚。也可以设置一个超时时间,但是有可能会因为timeout限制而误杀正常的流程。因此超时时间不能太短——越短,误杀正常流程的几率越大。
来源:https://blog.csdn.net/zollty/article/details/85165342
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PyTorch实现TPU版本CNN模型
- 使用NLP检测和对抗AI假新闻
- kallisto --genomebam报错解决(GTF文件的坑)
- linux查找文件
- TCP 协议面试灵魂 12 问,问到你怀疑人生!
- 方差分析简介(结合COVID-19案例)
- mysql计算两个时间字段的时间差
- 学生党学编程,有这个开源项目就够了!
- 【最强ResNet改进系列】Res2Net:一种新的多尺度网络结构,性能提升显著
- Java中的锁以及sychronized实现机制(十)
- Web 指纹识别之路
- RedisTemplate常用集合使用说明-opsForHash(四)
- Mybatis高级查询(二):多表联合查询
- Maven配置多仓库无效?来看看这篇文章
- Spring Boot启动slf4j提示找不到weblogic.xml日志异常