资源预留模式的锁定和隔离
Lock and Isolation for resource reservation pattern
我需要用 Spring 和 MariaDB 解决资源预留模式。
问题很简单,我有一个客人 table,我在其中存储活动的客人姓名,我必须确保活动的客人人数必须小于或等于最大容量。
这是table:
create table guest(
event int,
name varchar(50)
)
create index event on guest (event);
DB 的正确锁定过程和隔离级别是什么?
请考虑此代码将 运行 在多线程容器中。
我选择用 "SELECT...FOR UPDATE" 锁定 table 以将锁定限制在一个事件行中。
// START TRANSACTION
@Transactional
public void reserve(int event, String name){
getJdbc().query("SELECT * FROM guest WHERE id=? FOR UPDATE",event);
Integer count=getJdbc().queryForObject("SELECT COUNT(*) FROM guest WHERE id=?",Integer.class,event);
if(count>=MAX_CAPACITY)
throw new ApplicationException("No room left");
getJdbc().query("INSERT INTO guest VALUES (?,?)",event,name);
}
// COMMIT
我做了一些测试,似乎我需要 READ_COMMITTED 隔离级别,对吗?
这是我发现的:
这是我第一次必须更改隔离级别,我对这种需求感到有点惊讶,您能否确认标准 MariaDB 隔离级别 REPETABLE_READ 无法使用此模式?
问题是,在线程 2 中的事务期间,repeatable_read 保证您看到的数据库处于事务开始时的状态。因此,此时尚未完成的交易 1 的影响将被隐藏。
因此,您将始终看到相同数量的记录,而与同时进行的其他交易无关。所以两个事务都会插入一条记录。
READ_COMMITTED 表示根据文档:"Each consistent read, even within the same transaction, sets and reads its own fresh snapshot"。新快照意味着,提交的并发事务的结果将被包括在内。
解决问题的建议。这涉及保留一个计数器而不是 COUNT(*)
。 (是的,这违反了信息不冗余的原则。)
CREATE TABLE EventCount ( event ..., ct INT ..., PRIMARY KEY(event) ) ENGINE=InnoDB;
START TRANSACTION;
INSERT ...;
UPDATE EventCount
SET ct = ct + 1
WHERE event = ?;
ct = SELECT ct FROM EventCount WHERE event = ?;
if (ct > max)
{
ROLLBACK;
exit;
}
COMMIT;
(注意:我尚未验证这是否适用于您的情况。)
我需要用 Spring 和 MariaDB 解决资源预留模式。 问题很简单,我有一个客人 table,我在其中存储活动的客人姓名,我必须确保活动的客人人数必须小于或等于最大容量。
这是table:
create table guest(
event int,
name varchar(50)
)
create index event on guest (event);
DB 的正确锁定过程和隔离级别是什么? 请考虑此代码将 运行 在多线程容器中。 我选择用 "SELECT...FOR UPDATE" 锁定 table 以将锁定限制在一个事件行中。
// START TRANSACTION
@Transactional
public void reserve(int event, String name){
getJdbc().query("SELECT * FROM guest WHERE id=? FOR UPDATE",event);
Integer count=getJdbc().queryForObject("SELECT COUNT(*) FROM guest WHERE id=?",Integer.class,event);
if(count>=MAX_CAPACITY)
throw new ApplicationException("No room left");
getJdbc().query("INSERT INTO guest VALUES (?,?)",event,name);
}
// COMMIT
我做了一些测试,似乎我需要 READ_COMMITTED 隔离级别,对吗?
这是我发现的:
这是我第一次必须更改隔离级别,我对这种需求感到有点惊讶,您能否确认标准 MariaDB 隔离级别 REPETABLE_READ 无法使用此模式?
问题是,在线程 2 中的事务期间,repeatable_read 保证您看到的数据库处于事务开始时的状态。因此,此时尚未完成的交易 1 的影响将被隐藏。 因此,您将始终看到相同数量的记录,而与同时进行的其他交易无关。所以两个事务都会插入一条记录。
READ_COMMITTED 表示根据文档:"Each consistent read, even within the same transaction, sets and reads its own fresh snapshot"。新快照意味着,提交的并发事务的结果将被包括在内。
解决问题的建议。这涉及保留一个计数器而不是 COUNT(*)
。 (是的,这违反了信息不冗余的原则。)
CREATE TABLE EventCount ( event ..., ct INT ..., PRIMARY KEY(event) ) ENGINE=InnoDB;
START TRANSACTION;
INSERT ...;
UPDATE EventCount
SET ct = ct + 1
WHERE event = ?;
ct = SELECT ct FROM EventCount WHERE event = ?;
if (ct > max)
{
ROLLBACK;
exit;
}
COMMIT;
(注意:我尚未验证这是否适用于您的情况。)