一、采用读写分离技术的目标
随着网站的业务不断扩展,数据不断增加,用户越来越多,数据库的压力也就越来越大,采用传统的方式,比如:数据库或者SQL的优化基本已达不到要求,这个时候可以采用读写分离的策略来改变现状。采用读写分离技术能够有效减轻Master库的压力,又可以把用户查询数据的请求分发到不同的Slave库,从而保证系统的健壮性。
二、常用的两种方式
1、定义两个数据库链接,一个是masterDataSource,另个是slaveDataSource,更新数据时读取masterDataSource,查询是读取slaveDataSource。
2、动态数据源切换,在程序运行时,把数据源动态织如入程序中,从而选择主库还是从库。主要技术采用annotation,Spring AOP,反射,接下来详细介绍该种方法。
三、动态数据源切换实现读写分离
1,定义DataSource注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
2,继承抽象类AbstractRoutingDataSource来实现DynamicDataSource方法
public class DynamicDataSource extends AbstractRoutingDataSource{
public static final Logger logger = Logger.getLogger(DynamicDataSource.class.toString());
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSource();
}
}
3,定义DynamicDataSourceHolder方法
public class DynamicDataSourceHolder {
private static final ThreadLocal<String> holder = new ThreadLocal<String>();
public static void setDataSource(String name) {
holder.set(name);
}
public static String getDataSource() {
return holder.get();
}
}
4,定义DataSourceAspect类,在程序运行时动态切换数据源
public class DataSourceAspect {
public void before(JoinPoint point){
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature)point.getSignature()).getParameterTypes();
try{
Method m = classz[0].getMethod(method, parameterTypes);
if(m != null && m.isAnnotationPresent(DataSource.class)){
DataSource date = m.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(date.value());
}
else {
// 默认master
DynamicDataSourceHolder.setDataSource("master");
}
}catch(Exception e){
}
}
}
5,applicationContext-dataSource.xml配置
分别定义主从数据源后,再添加选择数据源的bean
<bean id="dataSource" class="****">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="masterDataSource"/>
<entry key="slave" value-ref="slaveDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="masterDataSource"/>
</bean>
增加aop配置
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="manyDataSourceAspect" class="****.DataSourceAspect" />
<aop:aspect id="c" ref="manyDataSourceAspect">
<aop:before method="before" pointcut="execution(* *****.data.dao.*.*(..))"></aop:before>
</aop:aspect>
</aop:config>
6,DAO层定义方法的数据源
@DataSource(value="slave")
public ImUser findById(int id);