1

我有一个带有主键的简单表。大多数读取操作通过键的确切值获取一行。

每一行中的数据在键顺序中与它之前和之后的行保持某种关系。所以当我插入一个新行时,我需要读取它要进入的 2 行,进行一些计算然后插入。

显然,问题在于,同时另一个连接可能会在同一时间间隔内添加具有键值的行。如果它与第二次插入失败的键值完全相同,我会被覆盖,但如果键值不同但在相同的时间间隔内,则关系可能会被破坏。

解决方案似乎是在我决定添加新行时锁定整个表以进行写入,或者(如果可能的话,我怀疑)锁定键值的间隔。然而,我更希望那时只读事务不会被阻止。

我在客户端程序和 IBM DB2 免费版中使用带有libodbc++ 包装器的ODBC(尽管 DB 选择可能仍会改变)。这就是我想做的事情:

  • 以自动提交和默认隔离模式启动连接
  • 当需要添加新行时,将 auto-commit 设置为 false 并将隔离模式设置为 serialized
  • 读取新键值前后的行
  • 计算并插入新行
  • 犯罪
  • 返回到自动提交和默认隔离模式

这会做这项工作吗?是否允许其他事务同时读取?还有其他/更好的方法吗?

顺便说一句,我没有在 libodbc++ i/fa 中看到指定只读事务的方式。在odbc中可以吗?

编辑:感谢非常有用的答案,我很难选择一个。

4

3 回答 3

2

如果您的数据库处于 SERIALIZABLE 模式,则根本不会有任何问题。给定一个键 K,要获取上一个和下一个键,您必须运行以下查询:

select key from keys where key > K order by key limit 1;      # M?
select key from keys where key < K order by key desc limit 1; # I?

以上适用于MySQL。这个等效的查询在 DB2 中有效(来自评论):

select key from keys where key = (select min(key) from keys where key > K);
select key from keys where key = (select max(key) from keys where key < K);

第一个查询设置一个范围锁,防止其他事务插入大于 K 且小于或等于 M 的键。

第二个查询设置了一个范围锁,防止其他事务插入小于 K 且大于或等于 I 的键。

主键上的唯一索引可防止 K 被插入两次。所以你完全被覆盖了。

这就是交易的意义所在;这样您就可以编写代码,就好像整个数据库都被锁定了一样。

注意:这需要一个支持真正可序列化的数据库。幸运的是,DB2 做到了。其他支持真正可串行化的 DBMS:SQLServer 和 MySQL/InnoDB。不支持的 DBMS:Oracle、PostgreSQL!

于 2010-11-11T20:50:06.187 回答
1

如果您的数据库和存储引擎允许这样做,您应该SELECT FOR UPDATE为您尝试插入的两行发出问题。

这将与任何并发冲突SELECT FOR UPDATE

缺点是行1012(插入11)的锁定也会阻止选择810(插入9)。

InnoDBinMySQL还可以在索引上加一个next-key锁,即锁定索引记录和下一条记录之间的间隙。

在这种情况下,您只需要SELECT FOR UPDATE在第一行上发出 a 并在此之前同时插入一行。

但是,这需要强制索引并在索引上提供一个range条件,这取决于您的查询可能会或可能不会。

于 2010-11-11T14:21:11.093 回答
1

您的一般方法是正确的。但是您应该使用一个覆盖两行和其间所有可能行的 SELECT 语句。例如:

SELECT * FROM MYTABLE WHERE PKCOL BETWEEN 6 AND 10

在具有悲观锁定和可序列化事务隔离级别的数据库系统中,此 SELECT 语句应防止插入会更改 SELECT 结果的新行。

于 2010-11-11T14:39:40.883 回答