如何让两个相同的读+写并发事务相互等待?
How can I make two identical read+write concurrent transactions wait for each other?
我有一个从数据库中读取的 Spring 事务,如果要查找的内容不存在,它会创建它。示例:
@Transactional
public int getUserIdCreateIfNotExistent(String email) throws Exception {
try {
return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email);
} catch (IncorrectResultSizeDataAccessException e) {
Thread.sleep(10000);
return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email);
}
}
当同时调用此方法两次时,将导致两个不同的用户在数据库中使用相同的电子邮件(对唯一电子邮件没有限制,这只是为了说明目的)。
我希望第二笔交易等待第一笔交易,以便只创建一个用户。我已经尝试将事务隔离级别更改为 Isolation.SERIALIZABLE
但它所做的只是让最后提交的事务回滚。
编辑:
在任何人建议使用 synchronized
或类似的东西在 java 中锁定解决方案之前,了解此方法是 Web 应用程序的一部分并在特定端点被命中时调用是很重要的。 Web 应用程序在几台不同的机器上运行并且是负载平衡的,当同时调用端点两次时会出现问题。这意味着 代码可能会在两台不同的机器上并行执行,并与另一台机器上的同一个数据库对话。
您可以使用 "locks" table。它将包含您需要锁定的所有内容,即 table 名称(例如 "users")和行键(例如 "email")。
如果您需要更快的速度,那么您可以使用 memcached 进行跨服务器复制。如果您的插入内容不多,那么数据库解决方案可能会很好。
已通过咨询锁解决:
@Transactional
public int getUserIdCreateIfNotExistent(String email) throws Exception {
try {
jdbcTemplate.queryForRowSet("SELECT pg_advisory_xact_lock(hashtext('users'), hashtext(?))", email);
return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email);
} catch (IncorrectResultSizeDataAccessException e) {
Thread.sleep(10000);
return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email);
}
}
我有一个从数据库中读取的 Spring 事务,如果要查找的内容不存在,它会创建它。示例:
@Transactional
public int getUserIdCreateIfNotExistent(String email) throws Exception {
try {
return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email);
} catch (IncorrectResultSizeDataAccessException e) {
Thread.sleep(10000);
return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email);
}
}
当同时调用此方法两次时,将导致两个不同的用户在数据库中使用相同的电子邮件(对唯一电子邮件没有限制,这只是为了说明目的)。
我希望第二笔交易等待第一笔交易,以便只创建一个用户。我已经尝试将事务隔离级别更改为 Isolation.SERIALIZABLE
但它所做的只是让最后提交的事务回滚。
编辑:
在任何人建议使用 synchronized
或类似的东西在 java 中锁定解决方案之前,了解此方法是 Web 应用程序的一部分并在特定端点被命中时调用是很重要的。 Web 应用程序在几台不同的机器上运行并且是负载平衡的,当同时调用端点两次时会出现问题。这意味着 代码可能会在两台不同的机器上并行执行,并与另一台机器上的同一个数据库对话。
您可以使用 "locks" table。它将包含您需要锁定的所有内容,即 table 名称(例如 "users")和行键(例如 "email")。
如果您需要更快的速度,那么您可以使用 memcached 进行跨服务器复制。如果您的插入内容不多,那么数据库解决方案可能会很好。
已通过咨询锁解决:
@Transactional
public int getUserIdCreateIfNotExistent(String email) throws Exception {
try {
jdbcTemplate.queryForRowSet("SELECT pg_advisory_xact_lock(hashtext('users'), hashtext(?))", email);
return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email);
} catch (IncorrectResultSizeDataAccessException e) {
Thread.sleep(10000);
return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email);
}
}