基于spring的方式进行读写分离思考

6 篇文章 0 订阅

        读写分离 的字面意思,就是将数据库的读和写分开。在整个结果体系上的意思,就是写使用数据库的master节点,读使用slave或者master节点。

       这样,讲读的数据库压力从写库中分离,转移到slave节点上,减少写库的存储压力,并且也能够避免写库中数据锁导致查询的卡顿情况。


       在网上的资料中有很多的方式实现读写分离,比如中间件。也有部分代码实现的情况。

       在spring中,提供了实现读写分离的接口,然而在我的使用过程中发现,采用spring的方式进行读写分离,没有办法处理数据库事物的情况发生。

       我设想了两个场景进行读写分离;

            1 ,无事物状态,   使用读库查询数据。

            2, 有事物状态,采用写库查询,更新数据。

       在mysql的jdbctemplate的代码中,发现能够判断当前线程是否启用事物的状态,因此可以采用它在数据源上封装一层功能,获取数据库连接时,如果存在事物,返回写库的连接,如果不存在事物,返回读库的连接。


             代码: 


import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.List;
import java.util.logging.Logger;


import javax.sql.DataSource;


import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;


/**
 * 读写分离数据源
 * 
 * @date : 2015年11月23日
 * @comment :
 */
public class ReadAndWriteDataSource implements DataSource {


private static org.slf4j.Logger log = LoggerFactory.getLogger(ReadAndWriteDataSource.class);


private DataSource master;


private List<DataSource> slaves;


private volatile int index;


private int slaveSize;


public DataSource getMaster() {
return master;
}


public void setMaster(DataSource master) {
this.master = master;
}


public List<DataSource> getSlaves() {
return slaves;
}


public void setSlaves(List<DataSource> slaves) {
this.slaves = slaves;
}


@Override
public PrintWriter getLogWriter() throws SQLException {
return master.getLogWriter();
}


@Override
public void setLogWriter(PrintWriter out) throws SQLException {
master.setLogWriter(out);


}


@Override
public void setLoginTimeout(int seconds) throws SQLException {
master.setLoginTimeout(seconds);


}


@Override
public int getLoginTimeout() throws SQLException {


return master.getLoginTimeout();
}


@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return master.getParentLogger();
}


@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return master.unwrap(iface);
}


@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return master.isWrapperFor(iface);
}


private Connection getSlaveConnection() throws SQLException {
if (slaveSize == 0) {
return master.getConnection();
} else {
return slaves.get((index % slaveSize)).getConnection();
}


}


private Connection getSlaveConnection(String username, String password) throws SQLException {
if (slaveSize == 0) {
return master.getConnection(username, password);
} else {
return slaves.get((index % slaveSize)).getConnection(username, password);
}
}


private boolean isWrite() {
boolean read = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if (read) {
if (log.isDebugEnabled()) {
log.debug("*************** return slave dataSouce*****************");
}
return false;
}
if (log.isDebugEnabled()) {
log.debug("**************** return master dataSouce******************");
}
return true;
}


@Override
public Connection getConnection() throws SQLException {
if (isWrite()) {
return master.getConnection();
} else {
return getSlaveConnection();
}
}


@Override
public Connection getConnection(String username, String password) throws SQLException {
if (isWrite()) {
return master.getConnection(username, password);
} else {
return getSlaveConnection(username, password);
}
}


public void init() throws SQLException {
if (null == master) {
throw new SQLException("master can't be null");
}
if (null != slaves) {
slaveSize = slaves.size();
}
}
}


  修改spring的配置为:

   <bean id="baseDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName">
<value>${jdbc.connection.driver_class}</value>
</property>
<property name="maxWait">
<value>60000</value>
</property>
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="removeAbandoned">
<value>true</value>
</property>
<property name="removeAbandonedTimeout">
<value>1800</value>
</property>
<property name="filters" value="stat,counter,wall,config,mergeStat" />
<property name="useGlobalDataSourceStat" value="true" />
</bean>


<bean id="writer" parent="baseDataSource" init-method="init"
destroy-method="close">
<property name="url">
<value>${jdbc.connection.url01} </value>
</property>
<property name="username">
<value>${jdbc.connection.username01}</value>
</property>
<property name="password">
<value>${jdbc.connection.password01}</value>
</property>
<property name="maxActive">
<value>${jdbc.max.pool.size01}</value>
</property>
<property name="initialSize">
<value>${jdbc.min.pool.size01}</value>
</property>
<property name="minIdle">
<value>${jdbc.min.pool.size01}</value>
</property>
</bean>


<bean id="reader" parent="baseDataSource" init-method="init"
destroy-method="close">
<property name="url">
<value>${jdbc.connection.url02} </value>
</property>
<property name="username">
<value>${jdbc.connection.username02}</value>
</property>
<property name="password">
<value>${jdbc.connection.password02}</value>
</property>
<property name="maxActive">
<value>${jdbc.max.pool.size02}</value>
</property>
<property name="initialSize">
<value>${jdbc.min.pool.size02}</value>
</property>
<property name="minIdle">
<value>${jdbc.min.pool.size02}</value>
</property>
</bean>


<bean id="dataSource1" class="com.knet.datasource.ReadAndWriteDataSource"
init-method="init">
<property name="master" ref="writer" />
<property name="slaves">
<list>
<ref bean="reader" />
</list>
</property>
</bean>

    ---- 事物配置略

    运行代码,发现在spring事物管理中,开启事物时就获取了数据库连接,然后才会在无事物将 TransactionSynchronizationManager.isCurrentTransactionReadOnly()=true。采用LazyConnectionDataSourceProxy 来处理这种情况。

     加上配置  <bean id="dataSource"
class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<constructor-arg ref="dataSource1" />
</bean>


   使用代码,发现已经读写分离完成。

工程代码:

http://git.oschina.net/lkclkc88/RWS

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值