我想很多朋友都尝试过写读写分离插件,或者在项目中使用过。 首先读写分离的作用属于数据访问层而不是业务层,其次读写分离不应该侵入我们的代码层。 于是
在service-Dao-orm -数据库驱动的调用链中,插件要想不侵入我们的代码,只能写在orm层和数据库驱动层,写在orm层就是具体的ORM
当框架合并并写入数据库驱动程序层时,它将与特定数据库合并。
在
orm层是实现读写分离还是数据库驱动实现读写分离,主要看orm的交换
框架和数据库其成本高,易于实现。 这里不讨论那个更好。 今天介绍的读写分离插件是基于mybatis框架实现的一次读取很多内容。 基于spring引导
因此,集成到现有项目中很有用。 下载源代码,将其制成jar包并部署到项目中,然后在springboot中
将以下配置添加到配置文件时,将打开读写隔离:
该插件做了三件事:数据源代理、数据源路由和分布式事务。
该插件对现有代码的入侵为零,入侵为零得益于代理模式。
首先数据源代理,读写在一个业务中至少有两个数据源,读数据源、写数据源,但在一个事务中都有
因为sql的所有执行都在同一数据库连接上完成,所以必须实现DataSorce接口代理来读写数据源: DataSourceProxy。 数据源代理
类写入操作时返回用于写入数据源的连接,读取操作时返回用于读取数据源的连接
连接。 但是,在实际执行数据库操作之前,无法确定读/写操作;但是,由于在实际执行sql语句之前正在执行连接操作,因此正在检索该操作
对于Connection操作,返回代理的Connection,并在实际执行sql语句时根据当前环境返回真的
连接。
于是,我
数据源代理返回的连接是代理类。
它依赖于DataSourceRout接口,并在sql语句执行之前由Connection代理类处理。 重新运行
对于sql语句,数据源路由接口返回特定的连接并执行
sql语句,DataSourceRout接口中只有一个getTargetDataSource
方法。 具体的实现类根据当前环境确定目标数据源。 可以是读/写数据源,也可以是表化的特定目标数据源。
数据源路由
现在接口有两个实现类,AbstractRWDataSourceRout实现读写隔离,UserDataSourceRout
实现每个用户路由到不同的数据库组。 名为UserDataSourceRout的类依赖于组
AbstractRWDataSourceRout,实现读写分离。
具体的类结构如下
将
DataSourceProxy被注入org.my batis.spring.sqlsessiontemplate。 我的电池
实现读写分离。 此时对现有代码完全透明。 当然,您可以将其注入到hibernate框架中,但必须自己实现数据源路由
接口,DataSourceRout接口实现类AbstractRWDataSourceRout基于mybatis。
通过
org.my batis.spring.sqlsessiontemplate
这个类的源代码查询,在名为org.Apache.ibatis.mapping.mapped statement的类中的
名为org.Apache.ibatis.mapping.sqlcommand type的域定义了mybatis运行sql
句子类型。 在这个类中,可以确定当前操作是读取操作还是写入操作。
写一个
我的电池插件。 在sql运行时,名为SqlCommandType的类确定当前上下文是读还是写。 将读写标记存储在上下文中,然后在
AbstractRWDataSourceRout
在此类中,获取上下文中的读/写标记并返回相应的数据源。 为了简化事务,确保当前上下文最多只有一个写连接和一个读连接,并检查是否有与当前上下文对应的数据库连接。 如果没有合适的连接,则获取连接并将其存储在当前上下文中对下次很有用
执行sql语句和执行事务。
数据源从原始单个数据源变成一个读取数据源和一个写入数据源,事务也变成两个事务。 我的电池
集成spring后,mybatis事务由spring管理。 具体实现类如下:
org.my batis.spring.transaction.springmanagedtransaction与myabtis-spring
无缝集成,采用代理
模式,RWManagedTransaction 继承SpringManagedTransaction,把事务分别委托给读写事务,整个线程只有一个读事务和一个写事务,读事务比较弱。因此分布式事务采用 Best
Efforts 1PC 模式。
public
void commit() throws SQLException {
Map connectionMap =
ConnectionHold.CONNECTION_CONTEXT.get();
Connection writeCon = connectionMap.remove(ConnectionHold.WRITE);
if(writeCon != null){
writeCon.commit();
}
Connection readCon = connectionMap.remove(ConnectionHold.READ);
if(readCon != null){
try {
readCon.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
}
第一个事务成功后,第二个事务有可能因为网络原因或者服务器宕机,不能执行成功,这个网络通讯的危险期虽然概率很小,但是也是个不可靠因素之一。由于整个会话中,只有一个写数据连接和一个读数据连接,读的事务性比较弱,只要写事务成功了,读事务失败影响不大,当然也可以不考虑读事务。因此先处理写事务,再处理读事务。
作为一个初级程序员,想要扩展现有的开源框架,其实不是那么困难,只要实现相应的接口,参考现有实现类实现接口,如果现有的实现类太复杂看不懂逻辑,其实也很好实现接口,就是把自己的实现委托给现有实现类,自己在委托前后做一些自己的业务逻辑。
文章内容来源于网络,更多热门资讯,欢迎关注【一订app】