MySQL无法在READ ONLY中执行语句

时间:2017-12-31 13:36:14

标签: mysql

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;

1 个答案:

答案 0 :(得分: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 - 语句本身(不是以下插入)引发错误。它可能会澄清发生了什么以及哪些陈述实际上按照您的想法行事。

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