Java 基于选择结果的基本锁定
我有一个简单的程序,它正在修改一个数据库,这个数据库也可以由不同的用户并发运行 在这个程序中,我有几个相互依赖的顺序操作读取/更新/插入数据库 例如:Java 基于选择结果的基本锁定,java,sql,sql-server,jdbc,Java,Sql,Sql Server,Jdbc,我有一个简单的程序,它正在修改一个数据库,这个数据库也可以由不同的用户并发运行 在这个程序中,我有几个相互依赖的顺序操作读取/更新/插入数据库 例如: String selectQuery = "select order.[total] from order where id=?"; selectQuery.setString(1, "15679"); ResultSet queryResults = selectQuery.executeQuery(); if(queryResults.n
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 (?,?)";
.......
}
在上面的示例中,无法保证两个程序不会同时尝试插入订单,因为它们都可能执行select,获得一个空结果集,然后尝试插入
我理解在这个示例中这有点不现实,因为我们可以在id
列上强制唯一性,这将阻止重复项被插入
我想做的是确保我们可以避免这种情况而不锁定整个表?因此,我们可以保证在执行select查询之前,我们可以知道行上不会有插入/更新/读取(可能存在也可能不存在)。之后,我们可以释放该行上的锁。好吧,您似乎了解竞争条件以及不想在应用程序中执行检查的原因 因此,请确保
id
在订单中是唯一的:
alter table orders add constraint unq_orders_id unique (id);
您可以尝试任意多的并发插入。因为数据库正在确保关系完整性,所以除一个之外,所有操作都将失败。在SQL Server中执行此操作的正确方法是使用事务,并将(updlock,holdlock)
提示添加到selectupdlock
强制SELECT查询使用限制性更新(U)锁,而不是使用版本化快照和无锁或许可共享(S)锁。并且updlock
像在可序列化隔离级别中一样启用范围锁定,这样即使范围中当前没有行,查询所针对的键范围也会被锁定
这将在id上放置U范围锁,防止另一个会话在键值上放置U或X锁,范围锁将锁定空的键值范围,因此无论是否存在具有该id的任何行
。例如:
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();
完成同样事情的一种低级方法是强制事务进入可序列化隔离级别。但这不会强制在初始读取时使用U锁,因此当两个会话都运行第一个查询时,一个将成功插入,另一个将失败并出现死锁 我想我本可以把这个问题写得更好。我的问题是,我希望程序根据SELECT语句执行代码,同时保证SELECT语句的结果在程序代码执行期间不会更改。这不一定只是一个插入的东西,它只是一个例子。完美!这也适用于不基于键值的SELECT语句吗?它会对SELECT查询读取的任何内容设置限制性锁,因此是的,但锁定不会那么精细。您能否详细说明一下锁定是否不那么精细?如果SELECT生成扫描整个表的查询计划。整张桌子都会锁上的。如果查询计划扫描索引的一部分,然后对聚集索引执行书签查找,则扫描的索引范围和所有匹配的行将最终锁定,以此类推。