代码之家  ›  专栏  ›  技术社区  ›  Mike Q

“select for update”是否阻止在不存在行时插入其他连接

  •  14
  • Mike Q  · 技术社区  · 14 年前

    select for update

    例如

    表FooBar有两列,foo和bar,foo有一个唯一的索引

    • 问题查询 select bar from FooBar where foo = ? for update
    • 如果查询返回零行
      • insert into FooBar (foo, bar) values (?, ?)

    现在,插入是否可能导致索引冲突,或者 你能阻止吗?

    5 回复  |  直到 14 年前
        1
  •  9
  •   DCookie    14 年前

        2
  •  12
  •   Marcus Adams    5 年前

    选择。。。用UPDATE更新

    使用带有InnoDB(自动提交关闭)的事务 SELECT ... FOR UPDATE 允许一个会话临时锁定一个或多个特定记录,以便其他会话无法更新它。然后,在同一事务中,会话实际上可以执行 UPDATE

    这是通过锁定来实现的。InnoDB利用索引来锁定记录,因此锁定现有记录似乎很容易——只需锁定该记录的索引即可。

    选择。。。更新时插入

    但是,使用 选择。。。更新 具有 INSERT ,如何为尚不存在的记录锁定索引?如果使用的是默认隔离级别 REPEATABLE READ ,InnoDB也将利用 缺口 锁。只要你知道 id

    如果你的 身份证件 列是一个自动递增的列 具有 INSERT INTO 因为你不知道新的 您希望插入的内容, 具有 插入 会有用的。

    在默认隔离级别上, 选择。。。更新 在一个不存在的记录上 选择。。。更新 在同一个不存在的索引记录上,它们都将获得锁,并且两个事务都不能更新该记录。事实上,如果他们尝试,就会检测到死锁。

    因此,如果不想处理死锁,可以执行以下操作:

    插入到。。。

    启动事务,并执行 插入 插入 具有相同唯一索引的记录。如果第二个事务在第一个事务提交insert之后尝试插入具有相同索引的记录,那么它将得到一个“duplicate key”错误。相应地处理。

    选择。。。锁定共享模式

    如果您选择 LOCK IN SHARE MODE 插入 SELECT ... LOCK IN SHARE MODE

    因此,为了减少重复密钥错误的机会,特别是在提交或回滚锁之前执行业务逻辑时,如果您持有锁一段时间,请执行以下操作:

    1. SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
    2. 如果没有返回记录,则
    3. INSERT INTO FooBar (foo, bar) VALUES (?, ?)
        3
  •  2
  •   jva    14 年前

    第1课时

    create table t (id number);
    alter table t add constraint pk primary key(id);
    
    SELECT *
    FROM t
    WHERE id = 1
    FOR UPDATE;
    -- 0 rows returned
    -- this creates row level lock on table, preventing others from locking table in exclusive mode
    

    第2课时

    SELECT *
    FROM t 
    FOR UPDATE;
    -- 0 rows returned
    -- there are no problems with locking here
    
    rollback; -- releases lock
    
    
    INSERT INTO t
    VALUES (1);
    -- 1 row inserted without problems
    
        4
  •  1
  •   BenMorel Sonaten    10 年前

    我在SQL Server上详细分析了这个问题: Developing Modifications that Survive Concurrency

    无论如何,您需要使用可序列化的隔离级别,而且您确实需要进行压力测试。

        5
  •  0
  •   bobs    14 年前

    SQL Server只有 FOR UPDATE 作为光标的一部分。而且,它只适用于与游标中的当前行关联的UPDATE语句。

    更新 INSERT . 因此,我认为您的答案是它不适用于SQLServer。

    更新 具有事务和锁定策略的行为。但是,这可能不止是你想要的。