MySQL无法在READ ONLY中执行语句

问题描述 投票:2回答:1

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())

enter image description here

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;
mysql
1个回答
1
投票

您无法更改活动事务的特征,您只能对未来事务执行此操作,请参阅SET TRANSACTION

您可以在全局,当前会话或下一个事务中设置事务特征:

  • [...]
  • 使用SESSION关键字,该语句适用于在当前会话中执行的所有后续事务。

因此,在您的情况下,第一个和第二个set session transaction语句执行您期望的操作:没有活动事务,因此新特性适用于下一个查询。另一方面,第三个set session transaction语句在事务内部执行,因此在该事务结束之前不会变得相关,因此当您尝试第二次插入时,只读模式仍处于活动状态,从而给出错误。

这也解释了为什么它可以移除第一个commit:读写模式将在整个事务中保持活动状态,因为set session transaction read only在事务中执行,因此不会影响它。尝试在所谓的只读部分添加插入。

在设置读写模式之前添加commit显然也可以解决问题。

试试没有set transaction ...关键字的session。它不会做同样的事情(它只适用于下一个事务,而不是所有后续事务),但与使用sessionglobal相比,它不允许您在事务中执行它:

在有活跃交易时,不允许没有GLOBAL或SESSION的SET TRANSACTION

所以它会引发你的第三个set transaction语句本身的错误(不是下面的插入)。它可能会澄清发生了什么以及哪些陈述实际上做了你认为他们做的事情。

虽然允许,但出于某种原因,避免在事务内设置(全局或会话)事务特征通常是个好主意。

© www.soinside.com 2019 - 2024. All rights reserved.