IBM Informix: getting 245, 144 error doing a select while another transaction has done an insert – possible bug?

huangapple go评论55阅读模式
英文:

IBM Informix: getting 245, 144 error doing a select while another transaction has done an insert - possible bug?

问题

在生产环境中遇到了一个死锁问题。我发现,如果一个事务在我的表中插入一行,而我想要从该表中选择一行完全不同的行,我会收到以下错误:

  245: 无法通过索引在文件内定位。

  144: ISAM 错误:键值被锁定
第1行错误
接近字符位置70

我的选择语句的形式为select * from table where bar = 3 and foo = "CCCC";,其中"foo"是指向包含18行的表的外键,而"bar"是第一个表的主键。我的插入语句也插入了一个具有foo = "CCCC"的行。有趣的是,选择查询在输出错误之前还返回了所需的行。

我在隔离级别设置为可重复读的情况下在Informix 12.10上尝试了所有这些操作。我在生产环境中尝试了它,并在一个只包含两个提到的表的新数据库中进行了设置。两个表的锁定模式都是"row"。

我通过修改选择语句进行了调查:select * from table where bar = 3;不会失败。另外,select * from table where bar = 3 and foo = "CCCC" order by ber;也不会失败(其中"ber"是表中的随机字段,"ber"没有索引)。

我期望我尝试的所有选择语句要么都返回所需的行而没有错误,要么都失败。在生产环境中,我的解决方案是按表中的随机字段进行排序,以解决死锁问题。

有人知道为什么会发生这个问题吗?我怀疑与表上的索引有关,这些索引在将主键和外键添加到表时都是自动创建的。但我对索引了解不够,无法理解发生了什么。这可能是一个bug吗?

表的架构:

create table options (
        foo     char(4)                 not null,
        fee     int                     not null)
        extent size 16 next size 16
        lock mode row;

alter table options add constraint (
        primary key (foo)
                constraint cons1 );

create table decisions (
        bar     char(3)                 not null,
        foo     char(4)                 not null,
        ber     int                     not null)
        extent size 131072 next size 65536
        lock mode row;

alter table decisions add constraint (
        primary key (bar)
                constraint cons2 );

alter table decisions add constraint (
        foreign key (foo) references options(foo)
                constraint cons3 );

我插入到"options"表中的数据:

AAAA|0|
BBBB|0|
CCCC|1|
DDDD|4|
EEEE|1|
FFFF|8|

我插入到"decisions"表中的数据:

QWE|AAAA|0|
WER|AAAA|9|
ERT|CCCC|2|
RTY|AAAA|32|
TYU|CCCC|1234|
YUI|CCCC|42398|
UIO|AAAA|23178|
IOP|CCCC|1233|
OPA|CCCC|11|
PAS|AAAA|890|
ASD|AAAA|90|
SDF|CCCC|2|
DFG|AAAA|4|
FGH|CCCC|7|

编辑:我使用了set explain on;来进行查询。对于select * from decisions where foo = "CCCC" and bar = "QWE" order by foo;,返回的索引是foo="CCCC"。然而,对于select * from decisions where foo = "CCCC" and bar = "QWE" order by ber;,它使用的是索引bar="QWE"。

英文:

Encountered this problem in production in the form of a deadlock. Figured out that if a transaction was inserting a row on my table, and I wanted to select a totally different row from that table, I would get the following error:

  245: Could not position within a file via an index.

  144: ISAM error: key value locked
Error in line 1
Near character position 70

My select statement was of the form select * from table where bar = 3 and foo = "CCCC";, where "foo" is a foreign key to a table with 18 rows, and "bar" is the first table's primary key. My insert statement was also inserting a row with foo = "CCCC". Curiously, the select query also returned the desired row before outputting the error.

I tried all this on informix 12.10 with isolation level set to repeatable read. I tried it on production, and in a fresh DB I set up with only the two tables mentioned. The lock mode of both tables is "row".

I investigated by modifying the select statement: select * from table where bar = 3; would not fail. Also, select * from table where bar = 3 and foo = "CCCC" order by ber; would not fail (ber being a random field from the table, ber is not indexed).

I would expect all the select statements I tried to return the desired row without error, OR all of them to fail. My solution in production was to order by a random field in the table, which fixed the deadlock issue

Does anyone know why this issue could have happened ? I suspect it is linked to the indexes on the table, which were all created automatically when adding the primary and foreign keys to the table. But I do not know enough about indexes to understand what happened. Could this be a bug ?

Schema of the tables:

create table options (
        foo     char(4)                 not null,
        fee     int                     not null)
        extent size 16 next size 16
        lock mode row;

alter table options add constraint (
        primary key (foo)
                constraint cons1 );

create table decisions (
        bar     char(3)                 not null,
        foo     char(4)                 not null,
        ber     int                     not null)
        extent size 131072 next size 65536
        lock mode row;

alter table decisions add constraint (
        primary key (bar)
                constraint cons2 );

alter table decisions add constraint (
        foreign key (foo) references options(foo)
                constraint cons3 );

Data I inserted into the "options" table:

AAAA|0|
BBBB|0|
CCCC|1|
DDDD|4|
EEEE|1|
FFFF|8|

Data I inserted into the "decisions" table:

QWE|AAAA|0|
WER|AAAA|9|
ERT|CCCC|2|
RTY|AAAA|32|
TYU|CCCC|1234|
YUI|CCCC|42398|
UIO|AAAA|23178|
IOP|CCCC|1233|
OPA|CCCC|11|
PAS|AAAA|890|
ASD|AAAA|90|
SDF|CCCC|2|
DFG|AAAA|4|
FGH|CCCC|7|

Edit: I used set explain on; for the queries.
select * from decisions where foo = "CCCC" and bar = "QWE" order by foo; returned that the index used was on foo="CCCC". However, for select * from decisions where foo = "CCCC" and bar = "QWE" order by ber;, it's indexed on bar="QWE".

答案1

得分: 2

我把我的问题咨询了更有经验的同事,以下是答案:

当某些操作在事务中更新表中的行时(例如插入一行),插入的值对应的索引键会被锁定。因为我插入了一个 foo="CCCC" 的行,这个键值在 foo 索引中被锁定。然后,当我执行一个选择语句,其中 foo="CCCC" 时,如果该语句使用了 foo 索引,就会出错;而如果使用了其他索引,就会正常工作。

通过使用优化指令,你可以在查询中指定要使用的索引或要避免使用的索引。

我选择的解决方法是首先为我的表定义明确的索引。然后,我使用 AVOID_INDEX 指令来避免 "foo" 索引。

其他解决方法可能包括指定要使用的索引(但我担心选择可能会使用两个索引并仍然遇到 foo 索引的问题);将锁定模式设置为 WAIT X(但由于我是通过死锁来发现问题的,这不会有帮助);或者使用脏读隔离(但这不适用于特定选择的目的)。

在修复后,我再也没有遇到死锁问题。

英文:

I asked my question to more experienced people working with me and here is the answer:

When something updates a row in the table (for example inserting a row) in a transaction the index keys for the values inserted get locked. Since I was inserting a row with foo="CCCC", that key value was locked in the foo index. Then, when I did a select statement where foo="CCCC", when that statement used the foo index, it errored, and when it used another index, it worked.

Using Optimizer Directives, you can specify an index to use in a query or an index to avoid.

The solution I chose was, first, to define explicit indexes for my table. Then, I used the AVOID_INDEX directive to avoid the "foo" index.

Other solutions would have been to specify the index to use instead (but I was worried the select could use two indexes and still encounter the foo index); to set the lock mode to WAIT X (but since I found the issue through deadlocks this wouldn't have helped); or to use isolation dirty read (but this was unwanted for the purpose of that specific select).

Following the fix, I haven't gotten a deadlock yet.

huangapple
  • 本文由 发表于 2023年2月16日 07:21:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/75466338.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定