【问题标题】:What transactions diminish data integrity?哪些事务会降低数据完整性?
【发布时间】:2013-07-17 18:19:26
【问题描述】:

我对数据库完整性有了相当的了解,并且知道如果我“需要将多个语句作为一个单元执行以使数据保持一致状态”,​​我应该使用事务。 Database development mistakes made by application developers(第 16 点,选择答案)

Wikipedia 使用示例:

  1. 从杂货费用帐户中借记 100 美元
  2. 将 100 美元存入支票账户

如果我尝试记入一个不存在的帐户 ID,并且我正确使用了约束,则会引发异常,我可以捕获它并回滚。如果停电,这两个更改保证是原子的。


但是,如果我理解正确,事务本身不会在所有情况下帮助我:(以 PHP 和 MySQL 为例)

  1. MySQL:开始事务
  2. MySQL:从表中选择数据
  3. PHP:使用所选数据计算状态
    • PHP:如果状态有效,插入数据
    • PHP:否则,不要插入数据
  4. MySQL:提交事务

这不起作用,因为查询可以原子地一起执行而不会失败(是 PHP 决定有错误,而不是一些 SQL 约束)。

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


那我该怎么办?我想锁定一张桌子是可行的,但这是一种好的做法吗?有些条件可以用 SQL 在一条语句中描述,但更复杂的条件则不能。

【问题讨论】:

    标签: sql transactions acid


    【解决方案1】:

    这是个好问题。说明你已经考虑了一点。

    您所描述的问题存在,因为数据库不知道您的数据依赖关系。对于数据库,您的代码选择一些数据并写入一些数据。它不知道您只是根据所选数据写入该数据。通常,您需要告诉数据库您的数据依赖关系。这在每个数据库中都是不同的。

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

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

    select * from tableA where value > 50 for update
    

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

    insert into tableB values ('some value')
    

    此时,在您提交之前,所有这些行都将被锁定。这些行中的任何一个都对其他客户端不可用。因此,在您的整个事务中,没有其他客户端能够读取您接触过的任何行,除非它们读取未提交。要在您的示例中使用此功能,您只需确保 2 中的所有选择语句都使用 select 进行更新。

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

    查询应该是这样的:

    select value from table where id = '1'
    

    然后:

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

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

    【讨论】:

    • 嗯,如果我理解这仅对简单的情况有用(这不是问题所要求的)。例如,SELECT ... FOR UPDATE 在很多时候都很重要,但它仅在您使用一行时才有效。与select... where value=old value 相同。很高兴知道这一点,但是如果我需要选择大量数据并计算结果以决定是否继续前进怎么办?在这些情况下是否可以接受表锁定(这似乎是我应该非常小心的事情)?
    • @Raekye 这两种技术都可以用于多行,但是选择更新会更容易使用,因为它会锁定所有选定的行。
    • 我的意思是如果我从多行中选择数据 - 甚至是表,这些数据没有被更新,但用于计算条件以查看是否应该插入另一行
    • @Raekye 这正是使用 SELECT ... FOR UPDATE 的情况。它从它读取的所有表中获取所有行的排他锁。没有其他客户端可以读取数据。我已经用一个例子更新了答案。希望这会让它更清楚一点。我还想澄清一下,这仅适用于多个客户。如果你只有一个客户端有多个事务,即嵌套事务,那么行为是不同的。
    • 啊我忘记了(它锁定了它读取的所有行)......这似乎是正确的......我需要时间来解决它:P 但它看起来像正确答案(暂时+1,让我考虑一下)
    猜你喜欢
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多