我有一个带主键的简单表。大多数读取操作都按键的确切值获取一行。
每行中的数据与键顺序中前后的行保持某种关系。因此,当我插入一个新行时,我需要读取它要进入的两行,进行一些计算,然后插入。
显然,问题在于,同时另一个连接可能会在同一间隔中添加一个具有键值的行。如果它与第二个插入失败的键的值完全相同,我将被覆盖,但如果键值不同但在相同的时间间隔内关系可能会被破坏。
解决方案似乎是在我决定添加新行时锁定整个表以进行写入,或者(如果可能,我怀疑)锁定键值的间隔。但是我希望当时不会阻止只读事务。
我在客户端程序和IBM DB2免费版中使用带libodbc++ wrapper for C++的ODBC(尽管数据库选择可能仍会改变)。这就是我的想法:
这会完成这项工作吗?是否允许其他交易同时阅读?还有其他/更好的方法吗?
顺便说一下,我没有在libodbc ++ i / f中看到指定只读事务的方法。在odbc有可能吗?编辑:感谢非常有用的答案,我在选择一个时遇到了麻烦。
答案 0 :(得分: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!
答案 1 :(得分:1)
如果您的数据库和存储引擎允许,您应该为要插入的两个行发出SELECT FOR UPDATE
。
这将与任何并发的SELECT FOR UPDATE
冲突。
缺点是锁定行10
和12
(插入11
)也会阻止选择8
和10
(插入{ {1}})。
9
中的 InnoDB
也可以在索引上放置MySQL
锁定,即锁定索引记录和下一条记录之间的差距。
在这种情况下,您只需要在第一行发出next-key
,然后在此之前同时插入一行。
但是,这需要强制索引并在索引上提供SELECT FOR UPDATE
条件,这可能会也可能不会,具体取决于您的查询。
答案 2 :(得分:1)
您的一般方法是正确的。但是您应该使用SELECT语句来覆盖两行以及它们之间的所有可能行。例如:
SELECT * FROM MYTABLE WHERE PKCOL BETWEEN 6 AND 10
在具有悲观锁定和事务隔离级别可序列化的数据库系统中,此SELECT语句应该阻止插入将改变SELECT结果的新行。