在并发初学者指南中 ,我提到了咨询锁。 这些不是通常的表锁-它们是与表无关的,特定于数据库的方法,可从您的应用程序中获取命名锁。 基本上,您将数据库实例用于集中式应用程序级锁定。
它有什么用? 如果要进行串行操作,这是一种相当简单的方法–无需消息队列或应用程序层中的分布式锁定库。 只需让您的应用程序从数据库请求锁,其他任何请求(与应用程序节点无关,如果有多个请求)都无法获得相同的锁。
您可以使用多种功能来获得这种锁定-在PostgreSQL中 ,在MySQL中 。 实现略有不同-在MySQL中,您需要显式释放锁;在PostgreSQL中,可以在当前事务结束时释放锁。
如何在Java应用程序中使用它,例如spring。 您可以提供锁定方面和自定义注释来触发锁定。 假设我们要对给定实体进行顺序更新。 在一般用例中,这很奇怪,但是有时候我们可能想要执行一些依赖于顺序更新的特定于应用程序的逻辑。
@Before("execution(* *.*(..)) && @annotation(updateLock)")
public void applyUpdateLocking(JoinPoint joinPoint, UpdateLock updateLock) {
int entityTypeId = entityTypeIds.get(updateLock.entity());
// note: letting the long id overflow when fitting into an int, because the postgres lock function takes only ints
// lock collisions are pretty unlikely and their effect will be unnoticeable
int entityId = (int) getEntityId(joinPoint.getStaticPart().getSignature(), joinPoint.getArgs(),
updateLock.idParameter());
if (entityId != 0) {
logger.debug("Locking on " + updateLock.entity() + " with id " + entityId);
// using transaction-level lock, which is released automatically at the end of the transaction
final String query = "SELECT pg_advisory_xact_lock(" + entityTypeId + "," + entityId + ")";
em.unwrap(Session.class).doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
connection.createStatement().executeQuery(query);
}
});
}
}
它有什么作用:
- 它查找使用
@UpdateLock
注释的方法并应用方面 - UpdateLock批注具有两个属性-实体类型和方法参数的名称,该参数包含我们要锁定更新的ID
-
entityTypeIds
基本上在实体的字符串名称和任意数字之间具有映射(因为postgres函数需要数字而不是字符串)
在一般用例中,这听起来不是很有用,但是如果出于任何原因需要确保在另外一个并发的多线程应用程序中按顺序执行某项功能,则这是一个好方法。
但是,使用这种特定于数据库的方式很少获取应用程序级锁。 如果您需要经常这样做,那么您可能会遇到更大的问题-通常不建议使用锁定。 在上述情况下,它将仅锁定单个实体ID,这意味着它将很少意味着有两个以上的请求正在等待该锁定(或无法获得该请求)。 好处是,分片将不会变得更加复杂-如果您锁定特定的ID,并且它依赖于单个分片,那么即使您可能有多个数据库实例(不共享锁),您也可以不必从其他分片获取锁。
总体而言,当遇到并发问题时,这是一个有用的工具。 但是,在使用锁之前,请考虑是否没有更大的问题。
翻译自: https://www.javacodegeeks.com/2016/11/using-named-database-locks.html