在交付PolarDB的过程中我们也遇到了只读事务造成的困扰,本文主要介绍Oracle只读事务和PolarDB只读事务的不同。
前言
Oracle提供了只读事务,同样的PolarDB也提供了只读事务,虽然名称一样,但是此非彼,两个概念存在差异。在交付PolarDB的过程中我们也遇到了只读事务造成的困扰,本文主要介绍Oracle只读事务和PolarDB只读事务的不同。
我们遇到的案例
客户从Oracle迁移到PolarDB Oracle,在进行功能测试过程中,手动开启事务执行DML操作,会报:can not execute update in a read-only transaction,而客户的应用在Oracle上运行正常,由此引发了后续的排查和定位。
Oracle只读事务
Oracle默认情况下保证了SQL语句级别的读一致性,即在一条SQL语句执行期间,它只会看到执行前点的数据状态,而不会看到执行期间数据被其他SQL改变的状态。如果你在一个事务里执行多次查询,想确保每次查询的数据都一致,也就是类似repeatable-read(可重复度),Oracle的只读查询则保证了事务级别的读一致性,即在该事务范围内执行的多条SQL都只会看到执行前点的数据状态,而不会看到事务期间的任何被其他 SQL改变的状态。除开查询一致性的特性,只读事务功能和PolarDB一致。
如何开启只读事务
#jdbc 该方式不生效
connection.setReadOnly(true);
#jdbc 生效
statement.execute("set transaction read only ");
我们先来看看JDBC 的协议说明:
/**
* Puts this connection in read-only mode as a hint to the driver to enable
* database optimizations.
*
* <P><B>Note:</B> This method cannot be called during a transaction.
*
* @param readOnly <code>true</code> enables read-only mode;
* <code>false</code> disables it
* @exception SQLException if a database access error occurs, this
* method is called on a closed connection or this
* method is called during a transaction
*/
void setReadOnly(boolean readOnly) throws SQLException;
在实际项目中我们遇到了客户开启只读事务的场景,客户通过setReadOnly的方式开启,但是实际上并没有生效,查看代码,Oracle jdbc驱动设置只读,只是对readOnly参数赋值,并没有执行:set transaction read only。
查看Oracle 官方文档:Read-only connections are supported by the Oracle server, but not by the Oracle JDBC drivers.。和我们的测试结果吻合,必须执行set transaction read only 才能开启只读事务,而客户侧是通过setReadOnly来开启只读事务,基本能确定客户的配置没有生效。下图是客户的事务配置:
PolarDB只读事务
PolarDB的只读事务和Oracle类似,开启了只读事务后,都不能执行DML,但是PolarDB不保证事务里可重复度的隔离级别。
在这个case里,为什么同样的配置到PolarDB表现不一样?为什么切换到PolarDB只读事务生效了?我们继续进行debug,在PolarDB上执行setReadOnly(true),可以看到jdbc驱动向server端发送了set session characteristics as transaction read only 的操作,和Oracle的实现完全不同。如图:
总结
通过测试我们能看到在JDBC层面Oracle和PolarDB对只读事务的开启方式是有差异的,实际上客户侧在Oracle的只读事务配置并没有生效,更换到PolarDB后,因为jdbc实现差异,开启了只读事务,所以导致客户在功能测试阶段大量出现只读事务的异常。如果客户侧真正开启了只读事务,PolarDB怎么去保证和Oracle只读事务一致性读特性一致,临时设置当前事务的隔离级别为repeatable-read是一个相对成本低的改造方案。