如何锁定数据库表或一系列行进行写入?

时间:2010-11-11 14:15:09

标签: database odbc read-write transaction-isolation table-locking

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

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

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

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

我在客户端程序和IBM DB2免费版中使用带libodbc++ wrapper for C++的ODBC(尽管数据库选择可能仍会改变)。这就是我的想法:

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

这会完成这项工作吗?是否允许其他交易同时阅读?还有其他/更好的方法吗?

顺便说一下,我没有在libodbc ++ i / f中看到指定只读事务的方法。在odbc有可能吗?

编辑:感谢非常有用的答案,我在选择一个时遇到了麻烦。

3 个答案:

答案 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冲突。

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

9中的

InnoDB也可以在索引上放置MySQL锁定,即锁定索引记录和下一条记录之间的差距。

在这种情况下,您只需要在第一行发出next-key,然后在此之前同时插入一行。

但是,这需要强制索引并在索引上提供SELECT FOR UPDATE条件,这可能会也可能不会,具体取决于您的查询。

答案 2 :(得分:1)

您的一般方法是正确的。但是您应该使用SELECT语句来覆盖两行以及它们之间的所有可能行。例如:

SELECT * FROM MYTABLE WHERE PKCOL BETWEEN 6 AND 10

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