这是我在使用Sybase ASE 15.7对应用程序进行压力测试时偶然发现的非常令人不安的事。
我们有下表:
CREATE TABLE foo
(
i INT NOT NULL,
blob IMAGE
);
ALTER TABLE foo ADD PRIMARY KEY (i);
即使在开始测试之前,该表还有一个单行,其中包含IMAGE列中的一些数据。在测试期间删除或插入否行。因此表总是包含一行。列 blob 仅更新(在事务 T1 中)到某个值(不是NULL)。
然后,我们有以下两个交易:
T1: UPDATE foo SET blob=<some not null value> WHERE i=1
T2: SELECT * FROM foo WHERE i=1
由于某种原因,上述事务可能会在负载下死锁(大约10个线程在循环中执行 T1 20次,另外10个线程在循环中执行 T2 20次)。
这已经很奇怪,但还有更多。始终选择 T1 作为死锁受害者。因此,应用程序逻辑在发生死锁(错误代码1205)时只重试 T1 。这应该有效,通常应该是故事的结尾。但是......
...有时 T2 将检索 blob 列的值为NULL的行!即使表已经以行开头,并且更新只是将先前的(非 NULL )值重置为其他(非 NULL )值。这在每次测试中都是100%可重复的。
使用READ COMMITTED序列化级别观察到这一点。
我确认上述行为也会出现在 TEXT 列类型中,但不会出现在 VARCHAR 中。
我还证实在事务 T1 中获得表 foo 上的独占锁定会使问题消失。
所以我想了解一下如何从根本上打破事务隔离的事情是否可能?事实上,我认为这比事务隔离更糟糕,因为 T1 从不将 blob 列的值设置为 NULL 。
<击> 测试代码是使用 jconn4.jar 驱动程序(类 com.sybase.jdbc4.jdbc.SybDriver )用Java编写的,所以我不排除这可能是JDBC驱动程序错误。 击>
这可以简单地使用 isql 重现,并且并行生成几个shell,在循环中连续执行 T1 。所以我删除了 Java 和 JDBC 标签,因为这肯定与服务器有关。
答案 0 :(得分:0)
默认情况下,您的示例创建表格代码会创建一个所有页锁定表,除非您的DBA更改了系统范围的锁定方案&#39;参数通过sp_configure到另一个值(您可以通过sp_configure&#39;锁定方案&#39;来自己检查这个值。
除非您有非常多的行,否则它们都将位于单个数据页上,因为int只有4个字节长,而blob数据存储在表的末尾(除非您使用in ASE15.7及更高版本中的LOB功能。这就是你遇到僵局的原因。根据定义,您创建了一个热点,其中所有数据都在页面级别进行访问。如果页面尺寸较大,则更有可能出现这种情况。使用2k,因为根据它们的性质,它们每页会有更多行,并且所有页面都会锁定,甚至更有可能发生争用。
将锁定方案更改为数据行(除非您计划具有非常高的行数),如上所述,您的问题应该消失。我将添加你的blob列看起来允许你的代码中的空值,所以你还应该考虑设置&#39; dealloc_first_txtpg&#39;如果图像列中有空值,则表的属性可以避免浪费空间。
答案 1 :(得分:0)
我们已经看到了隔离级别为1的各种奇怪的东西。我的印象是,当T2正在进行时,T1可以更改数据,而T2可能返回T1的中间结果。
尝试隔离级别2,看看是否有帮助(对我们有用)。