proxysql导致Mysql8 事务传播和事务隔离问题处理
1.问题:解决mysql8调度无法就绪问题
2.前提知识点
- 定位所涉及点
1、事务隔离失效(在事物中开启隔离级别是无效-Isolation.READ_UNCOMMITTED)
2、mvcc版本控制,生成快照策略不同(proxysql)
3、mysql版本是否会产生影响 - 相关知识点说明
a.事务
▪读未提交(READ UNCOMITTED)
• 读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读
▪读提交(READ COMMITTED)
•读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
•官方:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
▪可重复读(REPEATABLE READ)
•可重复读,MySQL 默认的事务隔离级别。可重复读可以解决“不可重复读”的问题,但还存在幻读的问题。所谓的幻读指的是,在同一事务的不同时间使用相同 SQL 查询时,会产生不同的结果。例如,一个 SELECT 被执行了两次,但是第二次返回了第一次没有返回的一行,那么这一行就是一个“幻像”行。
▪串行化(SERIALIZABLE)
•序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。
b.隔离
▪PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择
▪PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行
▪PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常
▪PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起
▪PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
▪PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
▪PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作
c.mvcc版本控制
i.Multi-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。
ii.当前读和快照读
1.当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录(加锁)
select … lock in share mode(共享锁)
select …for update、update、insert、delete(排他锁)
2.快照读
简单的select(不加锁)就是快照读,读取的是记录数据的可见版本,有可能是旧数据
RC隔离级别:每次执行select,都生成一个新的Read View,每次读取的数据都是最新数据
RR隔离级别:开启事务后第一个执行的select语句生成Read View,后面执行select都是使用第一次的Read View,读取的数据可能不是最新数据,实现了可重复读
串行化隔离界别:快照读会退化为当前读(不存在多事务并发执行,所有读写都加表锁)
iii.proxysql 简介https://www.jianshu.com/p/b77139fc5392
3. 解答
3.1 问题1: 事务隔离失效(在事物中开启隔离级别是无效-Isolation.READ_UNCOMMITTED)?
3.1.1 结论:在相同事务中开启隔离级别无效
3.1.2 文档验证论点,(官方看到文档 对设置transaction的范围)
3.1.2.1 地址:Transaction Characteristic Scope (官方地址:5或8文档解释
https://dev.mysql.com/doc/refman/8.0/en/set-transaction.html
https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html)
3.1.2.2 记录信息
You can set transaction characteristics globally, for the current session, or for the next transaction only:
○With the GLOBAL keyword:
▪The statement applies globally for all subsequent sessions.
▪Existing sessions are unaffected.
#session
○With the SESSION keyword:
▪The statement applies to all subsequent transactions performed within the current session.
(该语句适用于当前会话中执行的所有后续事务)
▪The statement is permitted within transactions, but does not affect the current ongoing transaction. (该语句在事务中是允许的,但不影响当前正在进行的事务。)
▪If executed between transactions, the statement overrides any preceding statement that sets the next-transaction value of the named characteristics.(如果在事务之间执行,该语句将覆盖任何前面设置命名特征的下一个事务值的语句。)
○Without any SESSION or GLOBAL keyword:
▪The statement applies only to the next single transaction performed within the session.
▪Subsequent transactions revert to using the session value of the named characteristics.
▪The statement is not permitted within transactions:
中文
a.global
▪在全局范围内产生影响
▪只对执行完该语句后的事务生效,当前已创建的事物无效
b.session
set session transaction isolation Level serializable;
i.对当前会话所有的后续事务有效
ii.语句执行不影响正在执行的事务,仅对后续事务有效
set transaction isolation Level serializable;
i.仅对当前会话下一个开启的事务生效,下一个事务执行完毕后,恢复到之前的隔离级别
ii.该语句不能在正在运行的事务中执行,否则会报错
(文档:https://blog.youkuaiyun.com/cainiao1412/article/details/124124453)
3.1.3 通过demo验证
3.1.3.1 sql语句验证
a.事务中隔离级别-当前会话开启的事务当前事务不生效
i.sql语句:
SQL
start transaction; #步骤1
SELECT * FROM test1
where id=‘1’; #步骤2
– set session transaction isolation level read committed;
set session transaction isolation level READ UNCOMMITTED; #步骤6 结果:修改为version_2
SELECT * FROM test2
where id=‘1’; #步骤7 结果:修改为version_1
select @@transaction_isolation;
commit;
事务2:
start transaction; #步骤3
– set session transaction isolation level read committed;
SELECT * FROM test2
where id=‘1’;
update test2 set name
= ‘version_2’ where id = 1; #步骤4
SELECT * FROM test2
where id=‘1’; #步骤5 结果:修改为version_2
select @@transaction_isolation;
3.1.5 结论:事物隔离尤其在spring 事物拦截器TransactionInterceptor里面是不支持,设置隔离级别
3.2 问题2和3:mvcc版本控制和数据库,生成快照策略不同(中间:proxysql)导致
开启事务1
查询user表中select语句
New 事务2
新事务里面修改为1
status=1 修改 event_map表状态为1 新事务里面修改为1
status=1
事务2提交
status=0 事务1查询event_map表状态 status=1
3.2.3.2 Mysql 5.7.38-log( proxysql)
总结:通过日志发现,ReadView (事务进行快照读操作的时候生产的读视图),select当行时候会生成sql
3.2.3.3 Mysql 8.0.31
总结:通过日志发现,ReadView (事务进行快照读操作的时候生产的读视图),在执行select的时候会出现快照
3.2.3.4 new事物 (事物隔离级别有效)
3.2.4 观点:proxy改变事务开启和缓存
3.2.5 官方文档说明
配置参数mysql-forward_autocommit 配置autocommit 是在ddl中才可以生效
官方网:https://proxysql.com/documentation/global-variables/mysql-variables/#mysql-query_digests_normalize_digest_text
mysql-forward_autocommit
When mysql-forward_autocommit=false (the default), ProxySQL will track (and remember) the autocommit value that the client wants and change autocommit on a backend connection as needed. For example, if a client sends set autocommit=0, ProxySQL will just reply OK. When the client sends a DDL, ProxySQL will get a connection to target hostgroup, and change autocommit before running the DDL.
If mysql-forward_autocommit=true, SET autocommit=0 is forwarded to the backend. SET autocommit=0 doesn’t start any transaction, the connection is set in the connection pool, and queries may execute on a different connection. If you set mysql-forward_autocommit=true, you should also set mysql-autocommit_false_not_reusable=true to prevent the connection to be returned to the connection pool. In other words, setting mysql-forward_autocommit=false will prevent this behaviour since the autocommit state is tracked.
翻译:
当mysql-forward_autocommit=false(默认)时,ProxySQL 将跟踪(并记住)客户端想要的自动提交值并根据autocommit需要在后端连接上更改。例如,如果客户端发送set autocommit=0,ProxySQL 只会回复 OK。当客户端发送 DDL 时,ProxySQL 将获得到目标主机组的连接,并autocommit在运行 DDL 之前更改。
如果mysql-forward_autocommit=true,SET autocommit=0则转发到后端。SET autocommit=0不启动任何事务,连接设置在连接池中,查询可能在不同的连接上执行。如果设置了mysql-forward_autocommit=true,还应该设置mysql-autocommit_false_not_reusable=true防止连接返回连接池。换句话说,设置mysql-forward_autocommit=false将阻止此行为,因为跟踪了自动提交状态。
查询方法:
SQL
mysql> select * from global_variables where variable_name like ‘%transaction%’;
±-------------------------------------------------------------±---------------+
| variable_name | variable_value |
±-------------------------------------------------------------±---------------+ |
| mysql-autocommit_false_is_transaction | false |
±-------------------------------------------------------------±---------------+
4 rows in set (0.00 sec)
mysql> select * from global_variables where variable_name='mysql-forward_autocommit
±-------------------------±---------------+
| variable_name | variable_value |
±-------------------------±---------------+
| mysql-forward_autocommit | false |
±-------------------------±---------------+
mysql> update global_variables set variable_value=‘true’ where variable_name=‘mysql-forward_autocommit’ ;
Query OK, 1 row affected (0.00 sec)
mysql> update global_variables set variable_value=‘true’ where variable_name=‘mysql-autocommit_false_not_reusable’ ;
mysql> load mysql variables to runtime;
Query OK, 0 rows affected (0.00 sec)
mysql> save mysql variables to disk;
Query OK, 137 rows affected (0.01 sec)
3.2.6 解读proxysql简介https://www.jianshu.com/p/b77139fc5392
3.2.8 解释观点:mysql日志信息(通过mysql日志进行打印)
3.2.8.1 proxySQL
3.2.9 事务验证:通过Proxysql配置验证mysql-forward_autocommit等参数
•设置为true
•设置为false
3.2.10 其他疑点:如果没有事务查询过程中如何进行mvcc
3.2.10.1 文档解释(https://www.cnblogs.com/hahaha111122222/p/16305922.html)
查询过程中会命中缓存
3.2.10.2 案例证明
JavaScript
List list=instantiationService.instantiate(flowInstance);
List list2=instantiationService.instantiate(flowInstance);
日志中:可以 查看出只是查询1条,为缓存策略
事务流程图源码