哪些事务会降低数据完整性?

时间:2013-07-17 18:19:26

标签: sql transactions acid

我已经学到了很多关于数据库完整性的知识,并知道如果我“需要将多个语句作为一个单元来执行以保持数据处于一致状态”,​​我应该使用事务。 Database development mistakes made by application developers(第16点,选择回答)

Wikipedia使用示例:

  1. 向杂货费用帐户借记100美元
  2. 向支票帐户贷记100美元
  3. 如果我尝试归功于不存在的帐户ID,并且我正在使用约束,则会抛出异常并且我可以捕获它并回滚。如果断电,这两个变化将保证是原子的。


    但是,如果我理解正确,在所有情况下,事务本身都无法帮助我:(使用PHP和MySQL的例子)

    1. MySQL:启动交易
    2. MySQL:从表格中选择数据
    3. PHP:使用所选数据计算状态
      • PHP:如果状态有效,请插入数据
      • PHP:否则,请勿插入数据
    4. MySQL:提交交易
    5. 这不起作用,因为查询可以原子方式一起执行而不会失败(它是PHP决定存在错误,而不是某些SQL约束)。

      其次,我刚刚测试过,事务是同步提交的,但可以异步启动。 如果我启动一个事务,并添加10秒的延迟,我可以启动慢速脚本,然后启动并提交另一个事务,演示并发事务。在看到其他实例的修改之前,两个实例可以选择相同的数据。只保证修改是原子的。


      那我该怎么办?我想锁定一个表是有效的,但这是一个好习惯吗?某些条件可以在单个语句中用SQL描述,但更复杂的条件不能。

1 个答案:

答案 0 :(得分:1)

这是一个很好的问题。表明你一直在考虑它。

您所描述的问题存在是因为数据库不了解您的数据依赖性。对于数据库,您的代码选择一些数据并写入一些数据。它不知道您只是根据所选数据编写该数据。通常,您需要告知数据库有关数据依赖性的信息。这在每个数据库中以不同方式完成。

你提到了MySQL。 InnoDB支持SELECT ... FOR UPDATE。这将为资源发出锁定,以便其他查询无法访问资源(取决于事务隔离级别)。这将使您的示例中的第二个事务在第一个事务提交之前无法执行,如果它们锁定相同的资源。它锁定的资源取决于数据库。

让我们看一个例子。要锁定行,首先要创建一个事务并使用以下内容查询数据库:

select * from tableA where value > 50 for update

此选择将锁定这些行,以便阻止不兼容的锁。然后你可以用PHP进行处理。准备好后,可以将行插入另一个表中:

insert into tableB values ('some value')

此时,在提交之前,所有这些行都将被锁定。这些行都不可用于其他客户端。因此,在整个事务中,除非他们读取未提交的内容,否则任何其他客户端都无法读取您触摸过的任何行。要在您的示例中使用此功能,您只需确保2中的所有select语句都使用select for update。

另一种方法是在update语句中告诉数据库。当您发出更新语句时,您还要指明您认为数据应该是什么。如果数据库确实更新了某些行,那么您可以确定没有其他任何内容更改了您的数据。如果不更新预期的行数,则可以知道其他人已更改了您的数据,您应该处理该异常。这是乐观的并发性,您猜测可能没有人会更新您的数据,因此您可以进行更改。之后,您可以检查是否有人确实这样做了。

查询将如下:

select value from table where id = '1'

然后:

update table set value = 'new value' where id = '1' and value = 'old value'

其他数据库为您提供了这两个基本想法的其他选择。例如,在乐观模型上,您可以验证时间戳(或自动增量)值而不是实际值。