那一天,我们做了一个尖尖的堡垒,今天却爆了自己的菊
2017-06-22 16:52
183 查看
摘要: 关于java,关于sqlserver,关于事务隔离级别的问题,问题初级,高手绕道
1、他们一起来到了自助机前,当时医院数据库Sqlserver2008出现卡顿死锁情况
2、用身份证在自助机办了一张卡
3、他们给自己诊疗卡充值500元
4、顺带在自助机上挂了一个号,准备去找医生看病
5、去找医生期间,医生由于没有找到该患者挂号信息,也未找到该诊疗卡信息,于是联系信息科
6、信息科工程师查询,该患者卡号caoliu1024信息不存在,也不存在缴费信息,也不存在挂号信息
乍一看,哇,这个非常像事务回滚了,因为当时确实死锁,当时工程师给我反馈说他没有去 kill,数据库自动恢复了(他笑了笑,我觉得不像,这个不理会了)
暂且假设是事务被回滚,首先检查webservice服务端(已经通过日志确定,服务端返回给自助机都是成功,患者信息很明确,和没有发生什么一样)是否用了事务代码,代码虽然不是我开发,但是看java web项目还是比较轻车熟路,用了spring ,但是检查配置文件里面并没有配置事务
于是检查代码,我们都是使用webservice 底层去调用存储过程,代码如下(所有代码都并非我写,由乙方承包):
值得注意的是:他们在doInCallableStatement中做了检查数据是否真的写进去的校验操作,看似确实很保险,哇,安全性很高,很流逼
5. 先抛开有没有开启事务,我假设他开启了,但是你写数据用的cs.executeQuery,校验数据是从 jdbcTemplate重新getConnection,那么这两个Connection有可能是同一个连接吗 ?如果是同一个connection,并且事务隔离级别如果是 read committed,那这个check是没有用的,因为他们可能就是在同一个事务中.check肯定可以查询到他刚刚自己插入的数据,即使未提交
6. 单独写了一个junit Test看看connection是不是同一个connection,代码如下:
结果打印出来的两个 false,表示每次从jdbcTemplate获取到的connection都是新拿的,并没有做一些线程级别共享connection的控制,并且 getTransationIsolation=4 也就是 TRANSACTION_REPEATABLE_READ,可重复读,很正常。我同时把dbcp和c3p0都试了一下,效果还是一样
结论:connection没有公用,不存在共用同一个事务,并且我看了connection的getAutoCommit属性是为true,更加确定webservice服务端并没有使用事务(因为他们把事务全部交给了存储过程)
7. 检查存储过程(sqlserver过程一直没接触过,不过还是拿过来看了一下),随意挑选了一个比如充 值的过程,代码如下:
大概意思是先查一下这个患者信息有没建档,诊疗卡信息是否存在(后来我排查确实没有数据),当有患者信息的时候就执行了下面的操作:
嗯,看名字是在写日志,很谨慎,然后接下来就是写充值流水记录了
多余代码不看,根据自助机的日志看,这边其实都已经走成功了,数据也插了,日志也写了。
我们先考虑下这个患者的心情:“哇,他们窗口排那么长的队,还听说医院系统卡了,我在自助机上操作很顺畅,一点问题都没有,1分钟就操作完了~!我去看医生去咯!!”嗯,差不多应该是这样子
当他去看医生,他遇到了这些问题:
医生看不到他刚刚挂号的名字
跑去找导诊护士在系统里面查,导诊系统未查到患者信息
跑去ic卡中心咨询,没有您的卡信息(也就是废卡)
跑去收费处查询,没有您的缴费信息
对,他们当时两个就懵逼!谁可曾知道,他拿着诊疗卡、拿着缴费发票、拿着挂号凭条,然后他在我们医院系统的数据却 消失得无影无踪! 当然,就如新闻里一样患者要大吵大闹了
当然,此时我们乙方工程师立马出场,因为比较诡异,查询时间可能耗时比较长,二话不说先把患者信息补上、卡信息补上、费用补上!OK,完事
查到的是read committed,没问题!不同事务间commit之后,才会被别的事务看到,这个很正常不过了。
当我在网上漫无目的的查询sqlserver数据无故丢失数据时,我看到了这么一句话,这句话也是问题的最关键原因:
没有错,经过测试确实发现了这么一个问题,使用nolock之后,整体sql性能有所提升,而且还不锁表,多好(当初由于我们医院经常死锁,所以他们写sql都要求带上nolock关键字)
我们为了解决死锁频繁问题,我们加上了nolock
我们加了nolock,nolock给我带来了什么?
经过神秘微笑的工程师kill之后,各个进程得到释放,该回滚的回滚了,回滚了,回滚了(那两个患者信息回滚了!!)
那两位患者就像一张动态图一样,站在那里..
知其然不知其所以然,存在风险
响应前言,作者会用上面的内容告诉你,内容很扯淡
前言
你可能觉得这是一遍很扯淡的文章,但是作者会用下面的内容告诉你..问题描述
我院(医院)已经开通了自助机挂号缴费业务,自助机和我院核心系统通信方式采用Webservice方式。今天(流水账日记形式),两位患者怀揣着轻松的心情来看病,他们在自助机做了如下操作(话说图片会变形)1、他们一起来到了自助机前,当时医院数据库Sqlserver2008出现卡顿死锁情况
2、用身份证在自助机办了一张卡
3、他们给自己诊疗卡充值500元
4、顺带在自助机上挂了一个号,准备去找医生看病
5、去找医生期间,医生由于没有找到该患者挂号信息,也未找到该诊疗卡信息,于是联系信息科
6、信息科工程师查询,该患者卡号caoliu1024信息不存在,也不存在缴费信息,也不存在挂号信息
问题分析
这两名患者确实做了办卡、充值、挂号操作,并且手持凭条,证明他们没有骗人乍一看,哇,这个非常像事务回滚了,因为当时确实死锁,当时工程师给我反馈说他没有去 kill,数据库自动恢复了(他笑了笑,我觉得不像,这个不理会了)
暂且假设是事务被回滚,首先检查webservice服务端(已经通过日志确定,服务端返回给自助机都是成功,患者信息很明确,和没有发生什么一样)是否用了事务代码,代码虽然不是我开发,但是看java web项目还是比较轻车熟路,用了spring ,但是检查配置文件里面并没有配置事务
于是检查代码,我们都是使用webservice 底层去调用存储过程,代码如下(所有代码都并非我写,由乙方承包):
// resDataStr是返回成功与否的标记 String resDataStr = (String) getJdbcTemplate().execute(sql, new CallableStatementCallback() { public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { //他们比较聪明,想拿一个连接,在执行完过程之后,再去查一下,确保数据没问题 Connection conn = jdbcTemplate.getDataSource().getConnection(); //.. //.. cs.setQueryTimeout(5); rs = cs.executeQuery(); .. if (lstNeedChk.contains(procName)) { //.. //此处为检查刚刚写进去的数据 //如果没数据就返回失败 } // .... });
值得注意的是:他们在doInCallableStatement中做了检查数据是否真的写进去的校验操作,看似确实很保险,哇,安全性很高,很流逼
5. 先抛开有没有开启事务,我假设他开启了,但是你写数据用的cs.executeQuery,校验数据是从 jdbcTemplate重新getConnection,那么这两个Connection有可能是同一个连接吗 ?如果是同一个connection,并且事务隔离级别如果是 read committed,那这个check是没有用的,因为他们可能就是在同一个事务中.check肯定可以查询到他刚刚自己插入的数据,即使未提交
6. 单独写了一个junit Test看看connection是不是同一个connection,代码如下:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath*:spring-application.xml"}) public class JdbcTemplateTest { @Resource private JdbcTemplate jdbcTemplate; @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testGetUser() throws Exception { jdbcTemplate.execute("call my_function()",new CallableStatementCallback() { @Override public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { Connection conn = jdbcTemplate.getDataSource().getConnection(); Connection conn2 = jdbcTemplate.getDataSource().getConnection(); System.out.println(conn.getTransactionIsolation()); System.out.println(conn == conn2); System.out.println(conn == cs.getConnection()); return null; } }); } }
结果打印出来的两个 false,表示每次从jdbcTemplate获取到的connection都是新拿的,并没有做一些线程级别共享connection的控制,并且 getTransationIsolation=4 也就是 TRANSACTION_REPEATABLE_READ,可重复读,很正常。我同时把dbcp和c3p0都试了一下,效果还是一样
结论:connection没有公用,不存在共用同一个事务,并且我看了connection的getAutoCommit属性是为true,更加确定webservice服务端并没有使用事务(因为他们把事务全部交给了存储过程)
7. 检查存储过程(sqlserver过程一直没接触过,不过还是拿过来看了一下),随意挑选了一个比如充 值的过程,代码如下:
大概意思是先查一下这个患者信息有没建档,诊疗卡信息是否存在(后来我排查确实没有数据),当有患者信息的时候就执行了下面的操作:
嗯,看名字是在写日志,很谨慎,然后接下来就是写充值流水记录了
多余代码不看,根据自助机的日志看,这边其实都已经走成功了,数据也插了,日志也写了。
但是,当我去回头查这些数据
但是,当我根据患者诊疗卡号来查这些数据的时候,这些数据根本就不存在!其中包括,诊疗卡信息(ic_register)、患者基本信息(mz_patient_mi)、(ic流水)ic_account_record,日志表,没有!全部都没有!关于这个人的信息一条都没有!没有错,就是这么诡异!我们先考虑下这个患者的心情:“哇,他们窗口排那么长的队,还听说医院系统卡了,我在自助机上操作很顺畅,一点问题都没有,1分钟就操作完了~!我去看医生去咯!!”嗯,差不多应该是这样子
当他去看医生,他遇到了这些问题:
医生看不到他刚刚挂号的名字
跑去找导诊护士在系统里面查,导诊系统未查到患者信息
跑去ic卡中心咨询,没有您的卡信息(也就是废卡)
跑去收费处查询,没有您的缴费信息
对,他们当时两个就懵逼!谁可曾知道,他拿着诊疗卡、拿着缴费发票、拿着挂号凭条,然后他在我们医院系统的数据却 消失得无影无踪! 当然,就如新闻里一样患者要大吵大闹了
当然,此时我们乙方工程师立马出场,因为比较诡异,查询时间可能耗时比较长,二话不说先把患者信息补上、卡信息补上、费用补上!OK,完事
我很震惊
对,这只是一个UC标题通用模板,我借鉴一下!我检查了一下sqlserver事务隔离级别dbcc useroptions
查到的是read committed,没问题!不同事务间commit之后,才会被别的事务看到,这个很正常不过了。
当我在网上漫无目的的查询sqlserver数据无故丢失数据时,我看到了这么一句话,这句话也是问题的最关键原因:
nolock means READ UNCOMMITTED
没有错,经过测试确实发现了这么一个问题,使用nolock之后,整体sql性能有所提升,而且还不锁表,多好(当初由于我们医院经常死锁,所以他们写sql都要求带上nolock关键字)
归根结底这个NOLOCK爆了一下我们的菊花
我们为了追求业务系统正常运行,我们加上了nolock我们为了解决死锁频繁问题,我们加上了nolock
我们加了nolock,nolock给我带来了什么?
没有错,又是黑体字,NOLOCK将我原来事务隔离级别是可重复读变成了 read uncommitted,也就是说,这些进程都还没提交的数据,其他进程都可以查到了
而当时由于系统卡死导致数据库事务commit卡主了,很多数据虽然已经写表,但是并未提交成功,而是卡在那里,然而正巧,患者操作速度快,患者在充值的时候会反查患者信息表有数据,挂号的时候反查ic卡和患者信息数据(他的充值和患者信息都已经写表,只是并未提交。由于nolock原因,不同的进程还是把这个未提交的数据给查出来了),发现所有数据一起都他妈的正常得666经过神秘微笑的工程师kill之后,各个进程得到释放,该回滚的回滚了,回滚了,回滚了(那两个患者信息回滚了!!)
那两位患者就像一张动态图一样,站在那里..
结论
很多特性(如nolock)虽然看起来很酷,但是却不知道其真正的风险知其然不知其所以然,存在风险
响应前言,作者会用上面的内容告诉你,内容很扯淡
相关文章推荐
- 今天真是不在状态的一天,又是一个书写的错误(本文仅为提醒自己,你可以选择跳过)
- 时间没有动,我们在流逝。今天又是崭新的一天了,无论如何告诉自己,我太喜欢上班了,我太喜欢上学了。为了最爱的人最爱的事奋斗吧么么哒!!!
- 今天在CSDN发表了一水贴."自己离一个称职的程序员差得太远了"
- 今天老大要我们写一个关于个人绩效的报告,呵呵,觉的下面老兄的写的不错。
- 我们期待自己成为一个优秀的软件模型设计者
- 今天没事,看到一个用C#开发OutLook插件的例子,顺便自己做了一个
- 我们期待自己成为一个优秀的软件模型设计者,但是,要怎样做,又从哪里开始呢?
- 今天看到一个blog上讨论字符串分割函数,一时兴起,自己写了一个
- 今天是端午节日,也是我的生日。同时还是我在csdn blog的生日。还是我们一个项目的付款日期哦 。^_^
- (转贴)我们期待自己成为一个优秀的软件模型设计者,但是,要怎样做,又从哪里开始呢?
- 我们期待自己成为一个优秀的软件模型设计者
- 我们期待自己成为一个优秀的软件模型设计者
- 今天终于自己作了一个Ajax程序,陶醉中
- 今天要为自己的网站添加一个动态检索信息的下拉提示框,这个是在网上找的例子。
- 今天听说别的部门的一个manager是在公司的最后一天了
- 我们期待自己成为一个优秀的软件模型设计者(转载)
- 今天给加上一个计数器,看到自己的Blog都生锈了啊!以后要好好维护!
- 今天我们宿舍又要走一个朋友了
- 今天是一个倒霉的一天
- [导入](转贴)我们期待自己成为一个优秀的软件模型设计者,但是,要怎样做,又从哪里开始呢?