MySQL如何允许READ WRITE
交易设置为READ ONLY
但不能再次返回READ WRITE
?
以下@test
会导致SQLException
显示并传递assertThrow
。
我已经在https://dev.mysql.com/doc/refman/5.6/en/innodb-performance-ro-txn.html阅读了相同的手册,但这看起来并不真实。
将commit
移到最后会避免抛出Exception
,但我很好奇为什么这是一个问题。虽然与此同时,我正试图考虑一个边缘情况,这将是一个问题:)
@Test
void testSeparatedCommitBroken() throws SQLException {
try (DataConnection connection = ds.getConnection()) {
connection.setAutoCommit(false);
// first transaction
Map<String, Object> vals1 = new HashMap<>();
vals1.put("name", UUID.randomUUID().toString());
Long id1 = connection.insertRow("products", vals1);
assertNotNull(id1);
connection.commit(); // placing this at the end solves the problem
// check first transaction
Map<String, Object> row1 = connection.selectRow("products", new Pair<>("id", id1));
assertEquals(vals1.get("name"), row1.get("name"));
// second transaction
Map<String, Object> vals2 = new HashMap<>();
vals2.put("name", UUID.randomUUID().toString());
// the connection was set to read only by SELECT which can't be swapped back whilst in autocommit=false
assertThrows(SQLException.class, () -> {
connection.insertRow("products", vals2);
});
connection.setAutoCommit(true);
}
}
常规日志快照
2017-12-31T13:43:37.290875Z 7556 Connect root@localhost on using TCP/IP
2017-12-31T13:43:37.291062Z 7556 Query set autocommit=1
2017-12-31T13:43:37.296937Z 7556 Query SET CHARACTER SET utf8
2017-12-31T13:43:37.297194Z 7556 Query SET NAMES utf8
2017-12-31T13:43:37.298778Z 7556 Query USE `prod_info_mngr`
2017-12-31T13:43:37.298993Z 7556 Query set autocommit=1
2017-12-31T13:43:37.301601Z 7556 Query SET autocommit=0
2017-12-31T13:43:37.303049Z 7556 Query set session transaction read write
2017-12-31T13:43:37.305546Z 7556 Query select @@session.tx_read_only
2017-12-31T13:43:37.315099Z 7556 Query INSERT INTO products (name) VALUES (UUID())
2017-12-31T13:43:37.315814Z 7556 Query commit
2017-12-31T13:43:37.318989Z 7556 Query set session transaction read only
2017-12-31T13:43:37.319842Z 7556 Query select @@session.tx_read_only
2017-12-31T13:43:37.327934Z 7556 Query SELECT * FROM products WHERE id = 162 LIMIT 1
2017-12-31T13:43:37.336647Z 7556 Query set session transaction read write
2017-12-31T13:43:37.337431Z 7556 Query select @@session.tx_read_only
2017-12-31T13:43:37.346483Z 7556 Query INSERT INTO products (name) VALUES (UUID())
SQL自己尝试(这个设计显然是愚蠢的 - 如果你提交每个INSERT,那么为什么要关闭AutoCommit):
CREATE DATABASE `prod_info_mngr` /*!40100 DEFAULT CHARACTER SET utf8 */;
use `prod_info_mngr`;
CREATE TABLE `products` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
set autocommit=0;
set session transaction read write;
insert into products (`name`) values (UUID());
commit;
set session transaction read only;
select * from products;
set session transaction read write;
insert into products (`name`) values (UUID());
commit;
set autocommit=1;
select * from products;
答案 0 :(得分:1)
您无法更改活动交易的特征,您只能对未来的交易进行更改,请参阅SET TRANSACTION
:
您可以全局,当前会话或下一个交易设置交易特征:
- [...]
- 使用SESSION关键字,该语句将应用于当前会话中执行的所有后续交易。
因此,在您的情况下,第一个和第二个set session transaction
语句执行您所期望的操作:没有活动事务,因此新特性适用于下一个查询。另一方面,第三个set session transaction
语句在事务内部执行,因此在该事务结束之前不会变得相关,因此当您尝试第二次插入时,只读模式仍然有效,错误。
这也解释了为什么在删除第一个commit
时它的工作原理:读写模式将在整个事务中保持活动状态,因为set session transaction read only
在事务中执行,因此不影响它。尝试在应该是只读的部分添加插入。
在设置读写模式之前添加commit
显然也可以解决问题。
在没有set transaction ...
关键字的情况下尝试session
。它不会做同样的事情(它只适用于下一个事务,而不是所有后续事务),但与使用session
或global
相反,它不允许您在事务中执行它:
当有活动交易时,不允许没有GLOBAL或SESSION的
SET TRANSACTION
。
因此,它将为您的第三个set transaction
- 语句本身(不是以下插入)引发错误。它可能会澄清发生了什么以及哪些陈述实际上按照您的想法行事。
虽然允许,但出于某种原因,避免在交易中设置(全局或会话)交易特征通常是个好主意。