数据库读写分离与事务纠缠
本篇文章讨论在数据库读写分离时使用事务的那些坑:
1. 在读写分离时会不会造成事务主从切换错误
一个线程在Serivcie时Select时选择的是从库,DynamicDataSourceHolder中ThreadLocal对应线程存储的是slave,然后调用Manager时进入事务,事务使用默认的transacatinManager关联的dataSource,而此时会不会获取到的是slave?

2. 事务隔离级别和传播特性会不会影响数据连接池死锁
一个线程在Service层Select数据会从数据库获取一个Connection,通常来讲,后续DB的操作在同一线线程会复用这个DB Connection,但是从Service进入Manager的事务后,Get Seq获取全局唯一标识,所以Get Seq一般都会开启新的事物从DB Pool里重新获取一个新连接进行操作,但是问题是如果两个事务关联的datasource是同一个,即DB Pool是同一个,那么如果DB Pool已经为空,是否会造成死锁?

为了减轻数据库的压力,一般会进行数据库的读写分离,实现方法一是通过分析sql语句是insert/select/update/delete中的哪一种,从而对应选择主从,二是通过拦截方法名称的方式来决定主从的,如:save*()、insert*() 形式的方法使用master库,select()开头的使用slave库。
通常在方法上标上自定义标签来选择主从。
@DataSource("slave")
int queryForCount(OrderQueryCondition queryCondition);
或者通过拦截器动态选择主从。
<property name="methodType">
<map key-type="java.lang.String">
<!-- read -->
<entry key="master" value="find,get,select,count,list,query,stat,show,mine,all,rank,fetch"/>
<!-- write -->
<entry key="slave" value="save,insert,add,create,update,delete,remove,gain"/>
</map>
</property>
读写动态库配置
<bean id="fwmarketDataSource" class="com.jd.fwmarket.datasource.DynamicDataSource" lazy-init="true">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="masterDB"/>
<entry key="slave" value-ref="slaveDB"/>
</map>
</property>
<!-- 设置默认的数据源,这里默认走写库 -->
<property name="defaultTargetDataSource" ref="masterDB"/>
</bean>
DynamicDataSource:
定义动态数据源,实现通过集成Spring提供的AbstractRoutingDataSource,只需要实现determineCurrentLookupKey方法即可,由于DynamicDataSource是单例的,线程不安全的,所以采用ThreadLocal保证线程安全,由DynamicDataSourceHolder完成。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 使用DynamicDataSourceHolder保证线程安全,并且得到当前线程中的数据源key
return DynamicDataSourceHolder.getDataSourceKey();
}
}
DynamicDataSourceHolder类:
public class DynamicDataSourceHolder {
// 写库对应的数据源key
private static final String MASTER= "master";
This chapter requires login to view full content. You are viewing a preview.
Login to View Full Content