posts - 193,  comments - 520,  trackbacks - 0
测试在sqlserver2000上进行,对工作流操作的相关方法在单元测试里进行多线程并发。测试发现sqlserver出现死锁的情况相当多,一些典型的情况:

1、对同一张表先insert再update是很快会引起死锁的,不管操作的是否是同一记录
解决方法:对于同一记录,需要调整hibernate的映射策略,使得一次insert完成操作。对于不同的记录需要在代码中手动flush,使得update先于insert。

2、对两张表进行多次update操作时,两张表交替update也会很快引起死锁
解决方法:在代码中手动flush,保证对两张表的update不会出现交替的情况。

3、部分大范围扫描的select和update混合也会导致死锁
解决方法:优化sql,尽量减少sql语句,通过给po增加持久化字段的方式减少关联查询

经过优化,大部分情况下数据库死锁的情况得以避免,另外奇怪的是通过事件探查器在死锁时并未发现锁升级的事件。但是在一些特殊情况下(例如多个并发汇聚的直接联合),死锁依旧发生。最后不得不对方法进行synchronized关键字同步,这个通过synchronized flush完成。业务方法不必同步,最后批量操作数据库时进行同步。

换oracle进行测试,在未synchronized的情况下,未发生死锁情况。由此可见sqlserver与oracle锁实现机制存在很大的差别。对sqlserver鄙视之。另,同事说,sqlserver2005后性能和机制发生了很大的变化,未测试。

补充一下我的一个最简单情况下的测试用例:
PO:
public class TestPO {
    String id;
    String name;
    
int num;
    
    .
}

映射文件 hibernate3:
<hibernate-mapping default-access="field">
  
<class table="WFMS_TESTPO" name="com.eway.workflow.test.po.TestPO">

    
<id name="id" column="ID"><generator class="uuid" /></id>

    
<property name="name" column="NAME" type="string"/>

    
<property name="num" column="NUM" type="integer"/>

  
</class>
</hibernate-mapping>

被测试方法(都配置有事务):
    public void testSave(int num) {
        TestPO po 
= new TestPO();
        po.setName(
"ronghao");
        po.setNum(num);
        theadTestDao.save(po);
        po.setName(
"haorong");
    }

    
public void testSaveByJdbc(int num) {
        String sql 
= "insert into WFMS_TESTPO (ID,NAME,NUM) values (?,'RONGHAO',?)";
        Object[] params 
= new Object[]{num,num};
        jdbcTemplate.update(sql, params);
        sql
="update WFMS_TESTPO set name='haorong' where id=?"  ;
        params 
= new Object[]{num};
        jdbcTemplate.update(sql, params);
    }

测试用例:
     public void testSave() throws Exception {
        TheadtestTemplate template 
= new TheadtestTemplate();
        template.execute(
new TheadtestCallback() {
            
public void doInThead(int suquence) {
//               theadTestManager.testSave(suquence);
                theadTestManager.testSaveByJdbc(suquence);
            }
        }, 
10);
    }

测试结果:不论是hibernate还是jdbc,并发情况下都很快就会引起sqlserver2000的死锁,换用两种数据库驱动jtds和jturbo死锁的情况没有变化。

结论:sqlserver2000数据库的lock配置策略,不支持,或者数据库本身,就不支持对不同的行做同时操作(或者支持不完善),所谓的行锁支持很不完善,死锁情况非常容易发生。

补充:我对数据库的一些实现机制也并不是很了解,所以这里也只能列出现象而不能解释死锁的根本原因。另外感谢Alex的讨论。


http://www.blogjava.net/ronghao 荣浩原创,转载请注明出处:)
posted on 2008-06-19 13:34 ronghao 阅读(6265) 评论(22)  编辑  收藏 所属分类: 工作日志

FeedBack:
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-19 15:02 | Alex
xd ,你配置了事务了嘛?  回复  更多评论
  
# re: 高并发测试下的一些问题及解决
2008-06-19 17:55 | flybean
1、死锁,还是锁阻塞,这是个问题。
2、悲观并发、乐观并发生来以久,各有优缺点,搞清楚再来鄙视。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 09:14 | Alex
1、对同一张表先insert再update是很快会引起死锁的,不管操作的是否是同一记录
2、对两张表进行多次update操作时,两张表交替update也会很快引起死锁
3、部分大范围扫描的select和update混合也会导致死锁

如果连这些问题都解决不了,你觉得是数据库问题,还是你的问题呢?

别拿那么多名词出来吓人,这个招数我上小学的时候用来吓唬老师的,现在已经不用了。

就捡最后一个说吧,乐观锁,如果你控制不了,还是就不要用了,您还就是老老实实的本分点,使用数据库本地提供的锁功能吧,牺牲点数据库的性能,也总比你数据更新失败要好,如果你非要使用hibernate的乐观锁,请控制好,内存中的数据和数据库中的数据一致性。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 09:28 | ronghao
@Alex
我想你并没有弄明白问题的关键。乐观锁和悲观锁的作用是防止多个事务对同一数据操作产生冲突的两种策略。而我的问题是多个线程(即多个事务)并发操作不同的数据。不知道明不明白意思。
ps:以前我也把这两者的概念经常混淆。
  回复  更多评论
  
# re: 高并发测试下的一些问题及解决
2008-06-20 09:29 | BeanSoft
说不定是滥用 Hibernate 导致的后果  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 09:34 | ronghao
@BeanSoft
我倒真的认为是数据库的原因。
一个简单的例子:很简单的po:
String id;
String name;
两个字段,执行操作:
TestPO po=new TestPO();
po.setName("ronghao");
dao.save(po);
dao.flush();
po.setName("haorong");
配置事务,单元测试两个线程并发,sqlserver2000下马上死锁。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 09:39 | ronghao
@BeanSoft
当然,我并没有用jdbc直接测试  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:12 | Alex
@ronghao
如果不是多线程,那么配置事务还有何用,如果所有操作都在同一个流水线上,那么配置事务不是浪费人民的感情嘛?

  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:18 | Alex
使用hibernate乐观锁 如果导致你保存失败,简单点说,就是你的两个线程或者多个线程所控制的内存中的数据和数据库中数据集已经不一致了。

如果使用悲观锁,导致你保存失败,那么说明你事务配置的问题,或者至少说明你测试用例的事务配置有问题,要不你直接试试 jdbc 并且硬编码事务看看。

否则 sqlserver2000 真的不要玩了,两个线程都是死锁。。。

  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:20 | Alex
同时操作同的记录集合,一方面要看你数据库使用什么lock策略,一方面还要看你代码的控制。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:21 | Alex
同时操作不同的记录集合,一方面要看你数据库使用什么lock策略,一方面还要看你代码的控制.  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:21 | ronghao
@Alex
你的说法没有错。我想问题的原因在于死锁的原因你我的理解有差异。
你理解的是对数据库表同一记录并发操作引起数据库死锁,这个显然是应用程序应该控制和避免的。
而我的意思是并发操作某一动作,例如同时启动100个工作流流程,这个尽管操作的是同一张表但是显然是针对不同的记录,此时,乐观锁、悲观锁都是没有意义的。
当然,我对数据库的一些实现机制也并不是很了解,所以这里也只能列出现象而不能解释死锁的根本原因。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:26 | ronghao
@Alex
补充说明一下,在我的测试中,不会出现两个线程互相修改冲突的情况,甚至select也不会冲突,因为流程实例ID一开始就将所有的数据区分开来。我是并发启动多个流程,然后并发提交这些流程。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:31 | Alex
上面我也说了,如果你直接使用hibernate的乐观锁,那么最细粒度的数据其实是由你自己来控制的,只要保持内存中的当前的数据在需要保存时和数据库中的保存相同的version 就可以了,然后不管 有多少个线程insert 或者 update ,只要每个线程都满足这个要求,就能保存成功了,当然,如果你的数据库的lock配置策略,不支持,或者数据库本身,就不支持对不同的行做同时操作(或者支持不完善),所谓的行锁,那么肯定会像你所说的那样发生死锁。
  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:37 | Alex
@ronghao
如果每个线程只处理 某一个或一些独立 流程id相关的数据,而没有交叉数据,那么还会出现这种现象。

1.使用jdbc做这种测试,排除是否是hibernate的使用或者hibernate本身对2000支持的问题问题。(jdbc驱动也很关键。)
2.查阅sql2000 对行锁的支持程度。
  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:45 | ronghao
@Alex
哈哈,正如你说的,我想问题在这里:
当然,如果你的数据库的lock配置策略,不支持,或者数据库本身,就不支持对不同的行做同时操作(或者支持不完善),所谓的行锁,那么肯定会像你所说的那样发生死锁。
这也是我想表达的。我想有时间应该请个专业的DBA来,我也查过了SQLSERVER的锁机制,但是具体到这里理解还是存在问题。
另外你对乐观锁的理解也很到位。如果是多个事务同时提交一个流程,乐观锁就会起作用。
  回复  更多评论
  
# re: 高并发测试下的一些问题及解决[未登录]
2008-06-20 10:46 | ronghao
@Alex
好的,谢谢你的意见。我会用jdbc做这个测试。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决
2008-06-20 16:30 | BeanSoft
是呀 JDBC 先测试下比较好 排除法 如果 JDBC 也不行 那八九成是 SQL Server 问题 坊间的传言都认为 SQL Server 好多地方不如 Oracle  回复  更多评论
  
# re: 高并发测试下的一些问题及解决
2008-06-21 19:45 | 鬼狗
pk的这么热烈丫, 我想可能先要搞清楚的是行锁,页锁和表锁这些基本概念,搞清楚不同数据库的锁机制,然后再来谈乐观锁还是悲观锁会比较容易理解。

我估计楼主缺少在sybase,db2下的工作经验, 所以才会这么激动。不同数据库的设计本身是有相当大差异的,很难说好坏,我印象中,缺省情况下对锁的处理比较不伤脑筋的就是oracle了,其他的不管是sybase还是db2都不是缺省就可以很轻松做到高并发的,但是处理的好,至少db2 可以做到不输给oracle的高并发。 至于sql server ,tpc网站上去翻一下,可以发现sql server也是常客。

但是早期oracle的开发, 嗯, 我觉得在很多方面其实倒不如db2这样的数据库省事,比如大表和小表在sql中的位置,都会显著影响sql性能。再比如sql是简单好,还是复杂好,这2个数据库也是相反的。

这只能说明不同数据库的机制是有很大差异的,用好一个数据库需要点时间对他进行比较深入的理解。现代程序员往往被各种框架屏蔽了底层,有时候也不是什么好事,以前项目中就有专门组织DBA对程序员进行数据库方面的培训,目的也就是为了弥补这个问题。

对于锁的问题,在oracle9i&10g编程艺术那本书里,作者有很深入的介绍,也列举了其他数据库的处理情况。作者从其他数据库转向使用oracle的时候,对这个问题,貌似和楼主一样震惊,呵呵。当年我也有个项目,被db2的锁机制,搞的的死去活来,呵呵。

简单的说,摇身要是高可靠性数据库都只是知道写sql这么简单, 人家DBA还怎么活?  回复  更多评论
  
# re: 高并发测试下的一些问题及解决
2008-06-21 19:50 | 鬼狗
另外不知道lz用的jdbc是什么版本? sqlserver 2000 的官方版本问题多多,印象中诸多特性都不支持。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决
2008-06-22 15:24 | ronghao
@鬼狗
呵呵,你的意见非常中肯。  回复  更多评论
  
# re: 高并发测试下的一些问题及解决
2008-07-03 22:55 | leekiang
汗,我们公司做了很多项目了,可你们说的这些问题公司几十号人没有一个人清楚,那些项目能用吗?请问。  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
 
<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

关注工作流和企业业务流程改进。现就职于ThoughtWorks。新浪微博:http://weibo.com/ronghao100

常用链接

留言簿(38)

随笔分类

随笔档案

文章分类

文章档案

常去的网站

搜索

  •  

最新评论

阅读排行榜

评论排行榜