《软件开发性能优化系列》之阻塞

阻塞原因

      在默认事务隔离情况下,数据库事务越长,一方面独占锁被持有的时间越长,写操作阻塞读操作的机会就越多;另一方面,在默认的读提交隔离模式下,读操作使用共享锁与独占锁不兼容,读操作也会阻塞写操作。

 

      阻塞也是死锁产生的基本条件,改善了阻塞就能有效减少死锁。

 

      在软件开发后期,在对大数据量的集成测试工程中,通过活动查看器可以观察到阻塞情况,主要产生阻塞的原因就是读和写相互阻塞在对同一个大表的操作上。因此对于读写阻塞问题需要加以足够考虑。

 

减少阻塞一些指导原则

 

整体原则

      *归结起来也依赖于代码、sql的优化,一方面要使逻辑代码优化到最快。另一方面,一个耗时较长的sql语句将会阻塞全部用户等待几十秒甚至几分钟,针对查询sql语句的优化也是最重要的。

 

      *修改批量操作的需求,批量操作耗时和记录数量是成正比的。为此设计时要避免在同一个自动事务服务方法中做批量的循环操作,可以将循环操作放到UI控制端,这一就使一个长事务变成多个短小的事务,将减少阻塞的机会。

 

      *减少读写操作使用锁的数量,比如减少批更新操作中修改行的数量,保证行锁定少,同时减少锁升级至表锁的机会。

 

      *一些耗时大、锁定数据多的操作需要避免和正常业务操作冲突,可以使用调度计划在系统闲置的时候来运行,或者使用互斥机制来保证其它用户暂退出操作独立运行,视业务情况而定。

 

读写锁阻塞的处理原则

      *减少读操作需要的共享锁

1、将事务隔离级别由默认读提交(ReadCommited)修改成读未提交(ReadUnCommited),将不会有读写阻塞,但是会造成读取其它事务未提交的数据。

     方法一:如果服务方法为自动事务,则在服务方法特性SerivceMethod指定IsolationLevel属性为ReadUnCommited

     方法二:对于使用手工事务的情况,使用DBSession接口带隔离级别参数的方法session.beginTransaction(IsolationLevel), level值为  ReadUnCommited.

2、在select语句中带NoLock提示,事务内无锁提示也不会有读写阻塞,与上面一样也会有脏读。

 

    Select上加无锁控制,在执行select操作时加 with(nolock)。如:SELECT * FROM person WITH(NoLock)

   

上面两种方式需要平台和业务涉及人员根据情况使用。事务隔离级别控制粒度较粗,使用时需要考虑对多个select语句的影响,NoLock提示控制单个select语句,粒度更细。

     *在sqlserver2005中使用基于行版本的快照隔离模式

          在快照模式下,读取数据不再使用共享锁,阻塞的现象能大大减少。

          方法:在建立数据库后执行下面命令:

          ALTER DATABASE 替换的数据库名 SET READ_COMMITIED_SNAPSHOT ON;

          ALTER DATABASE 替换的数据库名 SET ALLOW_SNAPSHOT_ISOLATION ON;

      注意:sqlserver2005和with(no lock)语句,这两种方案都能够避免阻塞,但是这两种方式是有区别的。

      举个小例子说明一下:比如数据库表T1中有两个字段Col1且其默认值为1.此时恰好有个A事务通过Update语句修改表T1的Col字段值为2,但还未提交,如下:

A事务:

{

          Begion tran

          Update T1 set Col1=2

         //Commit;//注释掉此句,模拟A事务未提交。

}

这时如果另一个事务如果使用Sql server2005的快照模式获取T1表Col1字段的值则取到的值是之前默认的1;而使用with(no lock)的方式获取到Col1字段的值为2.

其实不管使用哪种方式,得到的数据都不确保是准确的,这要取决于A事务是否执行并提交成功。

posted @ 2010-01-16 11:00  JoneLee  阅读(1843)  评论(1编辑  收藏  举报
http://s.click.taobao.com/t_9?p=mm_33531378_0_0&l=http%3A%2F%2Fwww.tmall.com%2Fgo%2Fact%2Fsale%2Ftmmytkpd.php%E8%81%BD