这几天对SQL中的事务进行复习,来自于对客户项目维护引起的思考!先说说项目维护中遇到的问题:
我们公司开发的软件项目,它建立在第三方公司开发项目(你见到过垄断主义的软件吗?)之上,数据库是直接的依赖关系,它偶尔出现死机的现象?
问题描述:
客户在使用我们的公司开发的软件系统的过程中偶尔出现死机的现象,然而又不能找到死机的操作流程是怎样的,而且软件死机后通过重启电脑后又能正常的运行?
重现死机的过程:
针对客户提出的这种现象我到客户现场去查找具体的原因,因为我们开发的软件系统是建立在第三方的系统之上,因此这给查找死机的原因带来了难度!而且客户提出的死机不是必然,这也给查找问题带来了困难。唉,不知是什么原因,我每次去客户那边都发现我们的软件系统都能正常的工作,不会出现死机的现象?可为什么客户在使用的过程中出现呢?最后只有告诉客户说:如果再出现死机的现象,请不要操作,看哈是不是系统反应慢,软件没有反应过来呢?最后由于这个死机问题没有重现,因此我这边也没有及时的去解决。(虽然客户多次发现,但是客户给我的反馈很不让我理解,当引起死机的时候,客户也不知道怎么操作流程导致的,也许客户自己通过重启电脑能解决暂时忽略了死机的重要性)。
记得后来有一次,我正在客户现场负责其他的事情,忽然客户在使用我们的软件工作中终于又一次出现死机了,我当时很惊讶,的确当时整个屏幕快变白了,更本无法动弹(死机了),我试着重新启动软件看能不能解决问题,最后还是不行,难道非要重启不可吗?我心里这样怀疑着。这个时候大概过了几分钟,软件终于反应过来了,给了我一个很吃惊的提示:
这个时候我看到客户正在使用第三方的软件系统(前面我提到我们软件的数据来源都基于这个系统),我开始怀疑是不是第三方的软件系统对我们的系统造成的呢?接着我把第三方的软件系统完全关闭了,然后再一次运行我们的系统,结果发现可以运行了。唉,终于可以不用重启解决死机现象了,接着我有试了几次,发现如果使用第三方软件系统指定的功能模块的条件下才会让我们的软件系统出现死机的现象,这也证明了为什么这种死机的现象不容易发现。唉,终于叹了口气,把我们死机的现象给重现了!
查找死机的原因:
功夫不负有心人,重现死机的过程找到了,也知道了用户是怎么操作导致我们的软件系统出现死机的过程,但是要解决这个问题的话,首先弄清楚出现死机的真正原因:究竟是由于什么引起的?然后我借助于SQL里面的事件探查器(SQL事件探查器,你使用了吗? ),最后终于找到了引起死机的真正原因:在使用第三方系统指定的功能模块后,对某一个张表的更新和查询作为一个事务操作并没有做提交(Commited)的动作,这种表上就建立了一个排他锁(X),这个时候我们公司开发的的软件系统去读取那张表后,会有一个共享锁(S)。然而排他锁(X)和共享锁(S)是不兼容的,故只有等那事务(排他锁)释放了资源后,才能获取到数据,因此导致的结果是我们的软件系统去获取那张表的数据时候就一直处于等待状态(由于直接用UI线程去获取数据,故在UI主线程上就死机了),当等待一段时间后,系统连接数据库超时,故程序抛出异常,弹出上面提示的对话框来。为了进一步验证,在第三方系统事务未提交(UnCommited)下,我又在SQL查询分析器中运行读取那张表的SQL语句,结果出现下面的情况(如下图所示),然而当第三方的软件系统关闭后,会有Commited动作,故我们的软件系统又能正常运行了,这样也进一步验证了我的判断结果。
解决问题:
找到了问题的原因,剩下的就是怎么解决了,因为我们没有办法控制第三方的软件,故只能在我们自己的软件系统修改。首先容易想到的是我们在获取那张表的时候不发生共享锁,采用with nolock关键字或者采用Read uncommited就可以解决了。最后通过试验,我们的软件系统在SELECT数据的时候,采用SELECT Field FROM TABLE WITH NOLOCK语法就能把死机的解决了。后来我又仔细分析了,针对当前系统的业务逻辑,对数据的准确性并不那样严格,可以读取还没有Commited的记录,故完全可以采用这种方式从本质上解决,而且这样也提高了查询效率,真是一举两得啊!
引起思考:
问题就这样很快的解决了,我们的软件系统也能正常的使用了,而且到目前为止还未发生死机的现象。这同时也说明了在软件测试的过程中,只怕找不到问题,没有解决不了问题的。因本人有做笔记的习惯,故针对这次项目的维护对事务进行了巩固和复习,方面以后查找和学习,也同时和朋友们一起来分享分享:
下面是事务的一些基础知识,记录下来和朋友们分享一下:
一、事务定义:
事务是有一系列操作序列构成的程序执行单元,这些操作要么都做,要么都不做,是一个不可分割的工作单元。例如:
银行转帐:事务T从A帐号转帐50元到B帐号。(其中Read(X):从数据库传送数据项X到事务的工作区,Write(X):从事务的工作区中将数据项X写入到数据库)
Read(A)
A=A-50;
Write(A);
Read(B);
B=B+50;
Write(B);
二、SQL中事务的定义:
事务以Begin transaction开始,以Commit transaction或Rollback transaction结束。
Commit transaction表示提交,事务正常结束。
Rollback transaction表示事务非正常结束,撤销事务已做的操作,回滚到事务开始时状态。
CommitWork(Rollback work)后不跟事务名称。
三、事务特性(ACID)
原子性:事务中包含的所有操作要么都做,要么都不做,原子性由数据库中的恢复机制实现的。
一致性:事务的隔离执行必须保证数据库的一致性,事务开始前,数据库处于一致性的状态,,事务结束后,数据库必须仍然处于一致性的状态。数据库的一致性状态由用户来负责的,也是由数据库的恢复机制和并发控制机制实现的。
隔离性:系统必须保证事务不受其他并发执行事务的影响。隔离性通过并发控制机制实现的。
永久性:一个事务一旦提交后,它对数据库的影响必须是永久的。系统发生故障不能改变事务的持久性。持久性通过恢复机制实现的。
四、事务生命周期图
五、事务执行模式:
显示事务:以Begin Transaction开始,以Commit或Rollback结束。
隐含事务:事务自动开始,知道遇到Commit或者Rollback对结束。语法:SET IMPLICIT_TRANSACTIONS ON
自动事务:每个事务操作语句作为一个事务。Update Table Set Score=Score+101
六、事务模型:
平面事务:一层结构Begin Transaction Commit,缺点是不能部分回滚。
嵌套事务:内部事务的提交并不释放资源或使其修改成永久修改,只有在提交了外部事务,数据的修改才具有永久性,而且资源才会释放。
保存点:批量工作可以设置一个保存点,一旦碰到意外的情况,可以回滚到最近的保存点,下次启动就可以在这个保存点接着往下执行,可以最大限制保存已经完成的工作。
分布式事务:牵涉到网络上不同的计算机节点的数据操作。
七、事务调度:
事务的执行顺序成为一个调度,表示事务的指令在系统中执行的时间顺序。
一组事务的调度必须保证:1.包含了所有事务的操作指令2.一个事务中指令的顺序必须保持不变。
并行VS串行:
基本比较:并行事务可能会破坏数据库的一致性,串行事务效率低。
并行的优点:一个事务由不同的步骤组成,所涉及的系统资源也不同,这些步骤可以并行执行,以提高系统的吞吐量。(目前的数据库都是采用并行的调度)。
八、事务隔离性级别:
当用户对数据库并发访问时,为了确保事务的完整性和数据库的一致性,需要使用锁定,这是实现数据库并发控制的主要手段,锁定可以防止用户读取正在由其他用户更改的数据,并可以防止多个用户同时更改相同数据。如果不使用锁定,则数据库中的数据可能在逻辑上不正确,并且对数据库的查询可能会产生意想不到的结果,具体的来说,锁可以防止一下情况:
丢失修改:两个事务T1和T2读入同一数据并修改,T1提交的结果破坏了T2提交的结果,导致T1的修改丢失。
脏读:事务T1修改某一个数据,并将其写回,事务T2读取同一数据后,T2由于某种原因被撤销,这是T1已经修改的数据恢复原值,T2读到的数据与数据库中的数据不一致,则T2读到的数据就是脏数据。
不可重复读:事务T2读取某一个数据后,事务T1对其做了修改,当T2再次读取该数据时候,得到的与前次不同的值。
幻想读:事务T2按一定条件读取了某些数据后,事务T1插入了一些满足这些条件的数据,当T2再次按相同条件读取数据时候,发现多了一些记录。
SQL中隔离性级别的定义:
Serializable:一个调度的执行必须等价于一个串行调度的结果。
Repeatable read:只允许读取已提交的记录,并要求一个事务对同一记录的两次读取之间,其他的事务不能对该记录进行更新。
Read committed:只允许读取已经提交的记录,但不要求重复读。
Read uncommitted:允许读取未提交的记录。
级别从高到低,发生数据不一致的几率低,但是封锁的强度越大,导致系统并发度越小。
不同的隔离性的级别下发生不一致的现象:
隔离性级别 |
不一致的现象 |
Read uncommitted |
读脏数据,不能重复读,幻想读 |
Read committed |
不能重复读,幻想读 |
Repeatable read: |
幻想读 |
Serializable |
|
实际的数据库中都是通过封锁机制来实现的:
Read uncommitted:不发出任何锁。
Read committed:发出一个共享锁
Repeatable read:两次读取之间锁是一致保持的。
Serializable:不但对现有数据的锁封锁,而且是根据事务的查询条件封锁。