【问题标题】:Basic Locking Based on Select result基于选择结果的基本锁定
【发布时间】:2019-12-19 15:08:37
【问题描述】:

我有一个简单的程序正在修改一个数据库,它也可以由不同的用户同时运行。

在程序中,我有一些顺序操作读取/更新/插入数据库,这些操作相互依赖。

例如:

String selectQuery = "select order.[total] from order where id=?";
selectQuery.setString(1, "15679");

ResultSet queryResults = selectQuery.executeQuery();

if(queryResults.next() == false){

    //execute insert into the order table
    String insertQuery = "insert into order (id, total) values (?,?)";
    .......

}

现在在上面的例子中,不能保证两个程序不会同时尝试插入订单,因为它们都可能执行选择,得到一个空的结果集,然后尝试插入。

我知道在这个例子中这有点不切实际,因为我们可以在 id 列上强制唯一性,这将阻止重复插入。

我要做的是确保我们可以在不锁定整个表的情况下避免这种情况?所以我们可以保证在执行选择查询之前,我们可以知道行上不会有插入/更新/读取(可能存在也可能不存在)。之后我们可以释放该行的锁。

【问题讨论】:

    标签: java sql sql-server jdbc


    【解决方案1】:

    嗯,您似乎了解竞争条件以及您不想在应用程序中进行检查的原因。

    因此,请确保 idorders 中是唯一的:

    alter table orders add constraint unq_orders_id unique (id);
    

    您可以尝试任意数量的并发插入。由于数据库正在确保关系完整性,因此除了一个之外,其他所有数据库都会失败。

    【讨论】:

    • 我想我可以更好地措辞这个问题。我的问题是我希望程序基于 SELECT 语句执行代码,并保证 SELECT 语句的结果在程序代码执行期间不会改变。这不一定只是一个 INSERT 事情,它只是一个示例。
    【解决方案2】:

    在 SQL Server 中执行此操作的正确方法是使用事务,并将 (updlock,holdlock) 提示添加到选择中。 updlock 强制 SELECT 查询使用限制性更新 (U) 锁,而不是使用版本化快照和无锁或许可共享 (S) 锁。并且updlock 启用范围锁定,就像在 SERIALIZABLE 隔离级别中一样,因此查询所针对的键范围被锁定,即使该范围内当前没有行。

    这将在 id 上设置 U 范围锁,防止另一个会话在键值上设置 U 或 X 锁,并且范围锁将锁定空键范围,因此无论有没有带有该 @ 的行都有效987654324@ 存在。例如:

    con.setAutoCommit(false);
    String selectQuery = "select order.[total] from order with (updlock,holdlock) where id=?";
    selectQuery.setString(1, "15679");
    
    ResultSet queryResults = selectQuery.executeQuery();
    
    if(queryResults.next() == false){
    
        //execute insert into the order table
        String insertQuery = "insert into order (id, total) values (?,?)";
        .......
    
    }
    con.commit();
    

    完成同样事情的一个次要方法是强制事务进入 SERIALIZABLE 隔离级别。但这不会在初始读取时强制使用 U 锁,因此当两个会话都运行第一个查询时,一个会话将通过 INSERT 成功,另一个将因死锁而失败。

    【讨论】:

    • 完美!这也适用于不基于键值的 SELECT 语句吗?
    • 它会对 SELECT 查询读取的任何内容设置限制性锁,所以是的,但锁定不会那么精细。
    • 您能否详细说明锁定的粒度较小?
    • 如果 SELECT 生成一个扫描整个表的查询计划。整个表最终将被锁定。如果查询计划扫描索引的一部分,然后对聚集索引进行书签查找,则扫描的索引范围和所有匹配的行最终都会被锁定,等等。
    猜你喜欢
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-15
    • 1970-01-01
    • 2010-11-28
    • 2018-03-09
    相关资源
    最近更新 更多