【发布时间】:2017-02-07 16:40:09
【问题描述】:
在我的测试中,我发现当使用 MariaDB 时,在 REPETEABLE_READ 隔离中执行相同的查询不会产生幻读,而它应该产生幻读。
例如:
bank_account 表中有两行:
ID | OWNER | MONEY
------------------------
1 | John | 1000
2 | Louis | 2000
预期的流程应该如下所示:
THREAD 1 (REPETEABLE_READ) THREAD 2 (READ_UNCOMMITED)
| |
findAll()->[1|John|1000,2|Louis|2000] |
| |
| updateAccount(1, +100)
| createAccount("Charles", 3000)
| flush()
| |
| commitTx()
| |_
|
findAll()->[1|John|1000,2|Louis|2000,
| 3|Charles|3000]
|
|
commitTx()
|_
综上所述,在Thread2.createAccount("Charles", 3000); 及其刷新之后,Thread1 将搜索所有行并得到
ID | OWNER | MONEY
------------------------
1 | John | 1000
2 | Louis | 2000
3 | Charles | 3000
Thread1 可以防止看到 [1, John, 1000] 而不是 [1, John, 1100] 的未提交更改,但它应该会看到新插入的行。
但是,Thread1 在第二个 findAll 中检索到的结果与第一个 findAll() 中的结果完全相同:
ID | OWNER | MONEY
------------------------
1 | John | 1000
3 | Charles | 3000
它没有幻读。为什么??????
这是Thread1执行的代码:
@Transactional(readOnly=true, isolation=Isolation.REPEATABLE_READ)
@Override
public Iterable<BankAccount> findAllTwiceRepeteableRead(){
printIsolationLevel();
Iterable<BankAccount> accounts = baDao.findAll();
logger.info("findAllTwiceRepeteableRead() 1 -> {}", accounts);
//PAUSE HERE
...
}
我在 //PAUSE HERE 处暂停执行。
然后线程2执行:
bankAccountService.addMoneyReadUncommited(ba.getId(), 200);
bankAccountService.createAccount("Carlos", 3000);
然后 Thread1 恢复:
//PAUSE HERE
...
Iterable<BankAccount> accounts = baDao.findAll();
logger.info("findAllTwiceRepeteableRead() 2 -> {}", accounts);
更新: 我已经用我真正在做的事情更新了线程事务流(我在新行插入之后提交了第二个事务)。
根据维基百科,这与幻读相匹配,我认为这是相同的场景。所以我还是不明白为什么我没有读到幻影[3|Charles,3000]
当在事务过程中,两个 执行相同的查询,并返回由 第二个查询与第一个不同。
当执行一个范围锁时,可能会发生这种情况 SELECT ... WHERE 操作。幻读异常是一个特殊的 事务 1 重复一个范围内的不可重复读取的情况 SELECT ... WHERE 查询以及在两个操作之间的事务 2 创建(即插入)新行(在目标表中)满足 那个 WHERE 子句。
Transaction 1 Transaction 2
/* Query 1 */
SELECT * FROM users
WHERE age BETWEEN 10 AND 30;
/* Query 2 */
INSERT INTO users(id,name,age) VALUES ( 3, 'Bob', 27 );
COMMIT;
/* Query 1 */
SELECT * FROM users
WHERE age BETWEEN 10 AND 30;
COMMIT;
【问题讨论】:
标签: mysql spring jdbc mariadb isolation-level