锁定和隔离资源预留模式

时间:2018-10-22 06:47:26

标签: locking mariadb spring-jdbc isolation-level

我需要使用Spring和MariaDB解决资源预留模式。 问题很简单,我有一个来宾表,用于存储事件的来宾名称,我必须确保该事件的来宾计数必须小于或等于最大容量。

这是表格:

create table guest(
    event int,
    name varchar(50)
)
create index event on guest (event);

DB的正确锁定过程和隔离级别是什么? 请考虑该代码将在多线程容器中运行。 我选择使用“ SELECT ... FOR UPDATE”锁定表,以仅将锁定限制在一个事件行中。

// 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隔离级别,对吗? 这是我发现的: enter image description here

这是我第一次必须更改隔离级别,对此感到有些惊讶,您是否可以确认标准MariaDB隔离级别REPETABLE_READ不能通过这种模式执行?

2 个答案:

答案 0 :(得分:1)

问题在于,在线程2中进行事务处理时,repeatable_read保证您看到的DB处于事务开始时的状态。因此,那时尚未完成的事务1的效果将被隐藏。 因此,无论其他事务同时执行什么操作,您将始终看到相同数量的记录。因此,两个事务都将插入一条记录。

READ_COMMITTED根据文档的意思是:“即使在同一事务中,每次一致性读取也会设置并读取其自己的新快照”。全新快照意味着将包括已提交的并发事务的结果。

答案 1 :(得分:0)

解决此问题的建议。这涉及保持计数器而不是执行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;

(注意:我尚未验证这是否适合您的情况。)