代码之家  ›  专栏  ›  技术社区  ›  Tobia

资源保留模式的锁定和隔离

  •  3
  • Tobia  · 技术社区  · 6 年前

    我需要用Spring和Mariadb解决资源保留模式。 问题很简单,我有一张来宾表,其中存储了活动的来宾名称,我必须确保活动的来宾计数必须小于或等于最大容量。

    这是桌子:

    create table guest(
    事件INT,
    名称varchar(50)
    )
    为客人创建索引事件(事件);
    < /代码> 
    
    

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

    //启动事务
    交易的
    公共void reserve(int event,string name){
    getjdbc().query(“select*from guest where id=?”更新”,事件);
    integer count=getjdbc().queryforobject(“select count(*)from guest where id=?”,integer.class,事件);
    if(count>=最大容量)
    引发新的应用程序异常(“没有剩余空间”);
    getjdbc().query(“insert into guest values(?)?);事件,名称;
    }
    //提交
    < /代码> 
    
    

    我做了一些测试,似乎我需要阅读承诺的隔离级别,对吗? 这就是我发现的:

    这是我第一次必须更改隔离级别,我对此需求有点惊讶,您能确认标准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
    

    我做了一些测试,似乎我需要阅读承诺的隔离级别,对吗? 这就是我发现的: enter image description here

    这是我第一次必须更改隔离级别,我对此需求有点惊讶,您能确认标准Mariadb隔离级别可报告读取失败与此模式吗?

    2 回复  |  直到 6 年前
        1
  •  1
  •   aschoerk    6 年前

    问题是,在线程2中的事务期间,可重复读保证您看到DB处于事务开始时的状态。因此,此时尚未完成的事务1的效果将被隐藏。 因此,您将始终看到相同数量的记录,这些记录独立于同时进行的其他事务。因此两个事务都将插入一个记录。

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

        2
  •  0
  •   Rick James diyism    6 年前

    解决问题的建议。这需要保持一个计数器而不是 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;
    

    (警告:我还没有证实这对您的情况有效。)