基于Spring AOP方式的读写分离

本文介绍了一种通过定义注解和采用Spring AOP等技术实现动态数据源切换的方法,以达到数据库读写分离的目的,有效减轻主库压力并提高系统性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

一、为什么需要读写分离

      随着网站的业务不断扩展,数据不断增加,用户越来越多,数据库的压力也就越来越大,采用传统的方式,比如:数据库或者SQL的优化基本已达不到要求,这个时候可以采用读写分离的策略来改变现状。采用读写分离技术能够有效减轻Master库的压力,又可以把用户查询数据的请求分发到不同的Slave库,从而保证系统的健壮性。

二、常用的两种方式

   1、定义两个数据库链接。

一个是masterDataSource,另个是slaveDataSource,更新数据时读取masterDataSource,查询是读取slaveDataSource。

    2、动态数据源切换。

在程序运行时,把数据源动态织如入程序中,从而选择主库还是从库。主要技术采用annotation,Spring AOP,反射,接下来详细介绍该种方法。

三、动态数据源切换实现读写分离

1、mybatis查询和更新的注解区分读写

 

 

package org.apache.ibatis.annotations;
 
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target({ java.lang.annotation.ElementType.METHOD })
public @interface Select {
	public abstract String[] value();
}

 

package org.apache.ibatis.annotations;
 
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target({ java.lang.annotation.ElementType.METHOD })
public @interface Update {
	public abstract String[] value();
}


也可自定义注解识别所需读写数据源,如下:

 

 

package com.javagaozhi.db;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * 自定义识别读写数据源注解
 *
 * @author Administrator
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
	String value();
}

 

2、继承抽象类AbstractRoutingDataSource来实现DynamicDataSource方法

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;
 
/**
 * 多数据源管理
 *
 * @author Administrator
 *
 */
@Component
public class MultipleDataSource extends AbstractRoutingDataSource {
	private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();
 
	/**
	 * 设置数据源
	 *
	 * @param dataSource
	 */
	public static void setDataSourceKey(String dataSource) {
    	dataSourceKey.set(dataSource);
	}
 
	/**
	 * 返回当前数据源
	 */
	@Override
	protected Object determineCurrentLookupKey() {
    	return dataSourceKey.get();
	}
}

3、定义切面DataSourceAspect,切换读写数据源

import java.lang.reflect.Method;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
 
/**
 * 定义数据源切面,程序运行时动态切换数据源
 *
 * @author Administrator
 *
 */
@Aspect
@Component
public class DataSourceAspect {
	@Around("execution(* com.javagaozhi.dao.*.*(..))")
	public Object before(ProceedingJoinPoint point) throws Throwable {
    	Object target = point.getTarget();
    	String method = point.getSignature().getName();
    	Class<?>[] classz = target.getClass().getInterfaces();
    	Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
    	Method m = classz[0].getMethod(method, parameterTypes);
        // 根据注解类型选择对应数据源,Select选择读数据源,Update选择写数据源
    	if (null != m && m.isAnnotationPresent(Select.class)) {
        	MultipleDataSource.setDataSourceKey("dataSource-slave");
    	} else if (null != m && m.isAnnotationPresent(Update.class)) {
        	MultipleDataSource.setDataSourceKey("dataSource-master");
    	} else {
        	return null;
    	}
    	return point.proceed();
	}
}

4、applicationContext-dataSource.xml配置
<!--数据源1-->       
<bean id="dataSource-master" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">  
	<property name="poolProperties">
    	<bean class="org.apache.tomcat.jdbc.pool.PoolProperties">  
        	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        	<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true"/>  
        	<property name="username" value="root"/>  
        	<property name="password" value="root"/>
        	<property name="jmxEnabled" value="true"/>
        	<property name="testWhileIdle" value="true"/>
        	<property name="testOnBorrow" value="true"/>  
        	<property name="testOnReturn" value="false"/>  
        	<property name="validationInterval" value="30000"/>  
        	<property name="validationQuery" value="SELECT 1"/>  
        	<property name="timeBetweenEvictionRunsMillis" value="30000"/>  
        	<property name="maxActive" value="100"/>  
        	<property name="initialSize" value="1"/>  
        	<property name="maxWait" value="10000"/>  
        	<property name="minEvictableIdleTimeMillis" value="30000"/>  
        	<property name="minIdle" value="10"/>  
        	<property name="logAbandoned" value="false"/>  
        	<property name="removeAbandoned" value="true"/>  
        	<property name="removeAbandonedTimeout" value="60"/>
        	<property name="jdbcInterceptors" value="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"/>  
    	</bean>  
	</property>  
</bean>
 
<!--数据源2-->       
<bean id="dataSource-slave" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">  
	<property name="poolProperties">
    	<bean class="org.apache.tomcat.jdbc.pool.PoolProperties">  
        	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        	<property name="url" value="jdbc:mysql://localhost:3306/test_read?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true"/>  
        	<property name="username" value="root"/>  
        	<property name="password" value="root"/>
        	<property name="jmxEnabled" value="true"/>
        	<property name="testWhileIdle" value="true"/>
        	<property name="testOnBorrow" value="true"/>  
        	<property name="testOnReturn" value="false"/>  
        	<property name="validationInterval" value="30000"/>  
        	<property name="validationQuery" value="SELECT 1"/>  
        	<property name="timeBetweenEvictionRunsMillis" value="30000"/>  
        	<property name="maxActive" value="100"/>  
        	<property name="initialSize" value="1"/>  
        	<property name="maxWait" value="10000"/>  
        	<property name="minEvictableIdleTimeMillis" value="30000"/>  
        	<property name="minIdle" value="10"/>  
        	<property name="logAbandoned" value="false"/>  
        	<property name="removeAbandoned" value="true"/>  
        	<property name="removeAbandonedTimeout" value="60"/>
        	<property name="jdbcInterceptors" value="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"/>  
    	</bean>  
	</property>  
</bean>
 
<bean id="multipleDataSource" class="com.alifi.uums.db.MultipleDataSource"> 
	<property name="defaultTargetDataSource" ref="dataSource-master"/> 
	<property name="targetDataSources"> 
    	<map> 
        	<entry key="dataSource-master" value-ref="dataSource-master"/> 
        	<entry key="dataSource-slave" value-ref="dataSource-slave"/> 
    	</map> 
	</property> 
</bean>
 
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
	<property name="dataSource" ref="multipleDataSource"/>
	<property value="classpath:system-mybatis-conf.xml" name="configLocation" />
</bean>

5、DAO层直接使用注解Select、Update操作数据库

/**
 * 根据id查找用户
 *
 * @param user
 * @return user
 */
@Select("select * from piao_user where id= #{id}")
User getUser(String id);
 
/**
 * 更新用户信息
 *
 * @param user
 * @return user
 */
@Update("update  from piao_user set nickname=’张三’ where id= #{id}")
User getUser(String id);

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java高知社区

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值