问题描述
基于Oracle12C的Web系统发生行锁问题,而且是行锁不同的表。
原因分析过程
1.怀疑框架的事务处理图元有问题,添加单独事务处理,但是事务处理的代码不正确;
tx=beginTx
try{
。。。
tx.commit
}catch{
。。。
tx.rollback
}
2.Oracle12C JDBC的版本不正确 & 驱动类写法不正确
JDK8 & Oracle 12C 官网的推荐是ojdbc8,之前用ojdbc6不合理,有隐坑风险。改之,但问题依旧。
3.Druid连接池没有及时升级
Druid1.1.9 一直没升级(最新Druid1.1.20),查Druid源码和升级ISSUE,发现修复的问题和升级源码处理的场景,跟出现的情况很相似。升级之,问题依旧。
开源软件的安全问题和版本管理,一直是需要留心的点。
4.异常场景处理不正确
偶尔修改了一组压测的测试数据,发现问题被重现了,原来的问题是在于有一个特殊情况的处理,跳过了正常的事务处理代码,导致事务泄露。
tx=beginTx
try{
。。。
if(condition is null) continue;
tx.commit
}catch{
。。。
tx.rollback
}
问题反思
这个问题的发生和处理,突现了自己的代码基础不扎实,测试数据组合和覆盖考虑不充分,能力和水平都不行┭┮﹏┭┮
- 对于事务处理的代码,codeReview没有做好。
反思:静态代码扫描的时候,需要通过PMD写规则的方式,增加应用特色的检查;
要做好核心模块和批量内容的CodeReview;同时关注git变更状态,把变更频繁,变更时间晚,变更核心代码的行为识别出来(通过jgit实现原型)。 - 手工测试和自动化测试的数据组合需要思考和细化;
反思:通过测试覆盖率来辅助验证是否充分;同时把覆盖率为零的代码牵出来,重新分析设计的合理性。 - 软件版本的对应和开源软件的升级管理,值得再做得仔细。
问题学习的扩展笔记
网络延时
延时和抖动是相互关联的两个东西,但是它们并不相同。延时是网络中的一个重要指标,它由四个关键部分组成:处理延时(processing delay),排队延时(queueing delay),传输延时(transmission delay)和传播延时(propagation delay)。它会影响用户体验,并可能因多种因素而发生变化。抖动是基于延时产生的—具体而言,就是前后延时的值不一致。抖动是两个数据包延时值之间的差异。它通常会导致丢包和网络拥塞。虽然延时和抖动有很多共同点和关联,但是它们并不相同。
什么是延时(delay)?
延时是网络中的一项重要指标,可衡量数据从一个端点移动到另一个端点所需的时间。网络延时通常在几秒钟的时间范围内,并且可以更具许多因素进行更改,包括端点的位置,数据包的大小以及流量大小。
延时(delay)与延迟(latency)有何不同
延迟和延时相互联系紧密,并且很多时候可以混用。但是,他们并不总是相同的。延时是数据从一个端点传输到另一个端点所花费的时间。然而,延迟可以表示两个量。
延迟有时被认为是数据包从一个端点传输到另一个端点所用的时间,这与单向延时是一样的。
但更多的情况,延迟表示的是往返时间。往返时间包括发送数据包所需的时间加上它返回所需的时间。这不包括在目的地处理数据包所需的时间。
网络监控工具可以确定给定网络上的精确往返时间。可以从发送处计算往返时间,因为它跟踪数据包发送的时间,并在确认返回时计算差值。但是,两个端点之间的延时可能难以确定,因为发送端没有到达接收端的时间信息。
延时的组成
延时可以理解为四个关键延时部分的组合:处理延时,排队延时,传输延时和传播延时。
处理延时:处理延时是系统分析数据包报头并确定数据包必须发送到何处的时间。这很大程度上取决于路由表中的条目,系统中数据结构的执行以及硬件实现。
排队延时:排队延时是数据包排队和发送之间的时间。这取决于数据流量的大小,流量类型以及实现哪些路由器队列算法。不同的算法可以调整系统偏好的延时,或者对所有流量要求相同的延时。
传输延时:传输延时是将数据包的数据推入线路所需的时间。这会根据数据包的不同大小和带宽大小而不同。这并不取决于传输线的距离,因为它仅仅是将包中数据推入传输线的时间,而不是沿着传输线到达接收端的时间。
传播延时:传播延时是与从发送端传输到接收端的数据包的第一个比特相关的时间。这通常被称为距离延时,并且因此数据比特受到传播距离和传播速度的影响。
这些延时组合在一起构成网络中的总延时。往返时间由这些延时和接收端到发送端之间的时间组成。
延时的影响
延时主要会影响用户体验。在严格的音频通话中,150毫秒的延时是非常明显的并且会影响用户。在严格的视频通话中,认为400毫秒是可辨识的。将这两种呼叫功能集中在一起后,联合的音频和视频呼叫应该保持同步,并且延时要少于150毫秒以不影响用户。但是,一般来说,延时尽可能低是非常重要的。无论如何,ITU建议将网络延时保持在100毫秒以下。
什么是抖动?
在网络上连续传输的数据包即便使用相同的路径,也会有不同的延时。这是由于分组交换网络固有的两个关键原因造成的。第一,数据包被单独路由。第二,网络设备接收队列中的数据包,因此无法保证延时调度不变。
每个数据包之间的这种延时不一致称为抖动。对于实时通信而言,这可能是一个相当大的问题,包括IP电话,视频会议和虚拟桌面基础架构。抖动可能由网络上的许多因素引起,并且每个网络都有延时时间变化。
抖动会导致什么后果?
丢包:当数据包不是均匀的到达接收端时,接收端必须进行弥补并尝试更正。在某些情况下,接收端无法进行适当的更正,并丢失数据包。就最终用户体验而言,这可以有多种呈现出的形式。比如,如果用户正在观看视频并且画面变成像素化,这就是潜在抖动的指示。
网络拥塞:网络设备无法发送相同数据的流量,因此他们的数据包缓冲区已满并开始丢弃数据包。如果端点上的网络没有干扰,则每个数据包都会到达。但是,如果端点缓冲区满了,会使数据包到达的越来越晚,导致抖动。这被称为初期拥塞(incipient congestion)。通过监视抖动,可以观察到初期拥塞。同样,如果出现初期网络拥塞,则说明抖动正在迅速变化。
当网络设备开始丢弃数据包,并且端点没有收到数据包时就会发生拥塞。终端可能会要求重发丢失的数据包,这会导致拥塞崩溃。
需要注意的是接收端不会直接导致拥塞,也不会丢弃数据包。请想象一条高速公路,其中有旅店A和旅店B。旅店B拥挤不是由于B没有足够的停车位而造成的。拥挤是由A引起的,所以它会不断地将公路上的骑车送到B旅店。
我该如何补偿抖动?
为了弥补抖动,在连接的接收端使用抖动缓冲区。抖动缓冲区收集并存储传入数据包,以便它可以确定如何以一致的间隔发送它们。
静态抖动缓冲—其在系统的硬件中实现,并且通常由制造商配置。
动态抖动缓冲—其在系统软件中实现,并由管理员进行配置。他们可对缓冲进行调整以适应网络变化。
播放延时
播放延时是数据包到达时和播放时间之间的延时。当抖动缓冲区存储传入数据包并等待以均匀间隔分配它们时,这会增加数据包到达时间与播放时间之间的时间,也被称为播放延时。这个延时是由抖动缓冲区引入的,因为它负责规定传入数据包何时分发。
总结
延时和抖动天生就紧密相连,但它们其实并不同。延时是数据从网络上的一个端点移动到另一个端点所花费的时间。这是一个受多种因素影响的复杂的值。另一方面,抖动是两个数据包之间的延时差异。同样,它也可能是由网络上的几个因素造成的。尽管抖动和延时有相似之处,但是抖动仅仅是基于延时产生的而已,但不等于它。
Oracle 连接时间的控制
什么是JDBC
JDBC是Java应用中用来连接关系型数据库的标准API。Sun公司一共定义了4种类型的JDBC,我们主要使用的是第4种,该类型的Driver完全由Java代码实现,通过使用socket与数据库进行通信。
第4种类型的JDBC通过socket对字节流进行处理,因此也会有一些基本网络操作,类似于HttpClient
这种用于网络操作的代码库。当在网络操作中遇到问题的时候,将会消耗大量的cpu资源,并且失去响应超时。如果你之前用过HttpClient,那么你一定遇到过未设置timeout造成的错误。同样,第4种类型的JDBC,若没有合理地设置socket timeout,也会有相同的错误——连接被阻塞。
接下来,如何正确地设置socket timeout,以及需要考虑的问题。
**应用与数据库间的timeout层级 **
上图展示了简化后应用与数据库间的timeout层级。(译者注:WAS/BLOC是作者公司的具体应用名称,无需深究) 高级别的timeout依赖于低级别的timeout,只有当低级别的timeout无误时,高级别的timeout才能确保正常。例如,当socket timeout出现问题时,高级别的statement timeout和transaction timeout都将失效。
我们收到的很多评论中提到:
即使设置了statement timeout,当网络出错时,应用也无法从错误中恢复。
statement timeout无法处理网络连接失败时的超时,它能做的仅仅是限制statement的操作时间。网络连接失败时的timeout必须交由JDBC来处理。
JDBC的socket timeout会受到操作系统socket timeout设置的影响,这就解释了为什么在之前的案例中,JDBC连接会在网络出错后阻塞30分钟,然后又奇迹般恢复,即使我们并没有对JDBC的socket timeout进行设置。
Oracle JDBC Statement的QueryTimeout处理过程
- 通过调用Connection的createStatement()方法创建statement
- 调用Statement的executeQuery()方法
- statement通过自身connection将query发送给Oracle数据库
- statement在OracleTimeoutPollingThread(每个classloader一个)上进行注册
- 达到超时时间
- OracleTimeoutPollingThread调用OracleStatement的cancel()方法
- 通过connection向正在执行的query发送cancel消息
Arthas--Java在线诊断
https://github.com/alibaba/arthas
Locust压力测试
https://github.com/locustio/locust]
读源码,分析运行脚本的一些报错是否正常,其中运行的核心实现类 runners.py,值得通读几次。