在互联网项目中,随着业务的增加,访问的增长,对系统性能,扩展性,伸缩性提出更高的同时,数据库的压力也陡然增加。越来越多的系统采用分布式系统架构,在数据方面,也采用数据库集群,与此同时,基于系统访问,数据的查询较多,增删改较少,为了减少数据压力,提高系统响效率,采用数据库读写分离也是一个较好的选择。采用此种策略,不仅使提高系统的响应时效,而且利于数据库的扩展。
java的形式的读写分离有两种:
方案一: 增加,删除,修改操作访问对应数据,查询访问对应数据库,不同数据库做好的数据一致性的处理。由于此方法相对易懂,简单,不做过多介绍。
问的数据库,方方案二:通过Spring的AOP,AspactJ来实现动态织入,通过编程继承实现Spring中的AbstractRoutingDataSource,来实现数据库访问的动态切换,不仅可以方便扩展,不影响现有程序,而且对于此功能的增删也比较容易。下面做详细介绍。
DataSource有如下两个方法:下述方法获取数据库连接。
//获取数据库连接
Connection getConnection() throws SQLException;
//需通过安全认证,获取数据库连接
Connection getConnection(String username, String password) throws SQLException;
AbstractDataSource实现上述方法如下:
//通过determineTargetDataSource()方法获取的实例获取数据库连接。
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
//通过determineTargetDataSource()方法获取的实例获取需要安全认证的数据库连接。
public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); }
//获取数据源
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
//获取对应数据源的属性键(因resolvedDataSources是map数据结构)
Object lookupKey = determineCurrentLookupKey();
//根据该参数获取数据源(因resolvedDataSources是map数据结构)
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
public class DataSourceTool extends AbstractRoutingDataSource {
//多个副本,实现多线程的资源共享
public static final ThreadLocal
@Override
protected Object determineCurrentLookupKey() {
return DataSourceTool .getDataSouce();
}
public static void putDataSource(String name) {
holder.set(name);
}
public static String getDataSouce() {
return holder.get();
}
}
public class DataSourceAspectJ {
//在方法调用之前进行切面操作
public void before(JoinPoint point)
{ //获取目标对象
Object target = point.getTarget();
//获取方法签名信息:然后获取于方法名
String method = point.getSignature().getName();
//获取目标对象类实现的接口
Class>[] classz = target.getClass().getInterfaces();
//获取该方法签名对应方法的参数类型
Class>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
//获取目标对象的方法
Method m = classz[0].getMethod(method, parameterTypes);
//若方法不为空,且方法上的注解是DataSource
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m
.getAnnotation(DataSource.class);
DynamicDataSourceHolder.putDataSource(data.value());
System.out.println(data.value());
}
} catch (Exception e) {
e.printStrace();
}
}
}
<!--数据源配置,主从库配置 -->
<!--AspactJ配置 -->
<!--AspactJ动态代理 -->
public interface UserMapper {
@DataSource("master")
public void add(User user);
@DataSource("master")
public void update(User user);
@DataSource("master")
public void delete(int id);
@DataSource("slave")
public User loadbyid(int id);
@DataSource("master")
public User loadbyname(String name);
@DataSource("slave")
public List
}
总的来说,AspectJ实现数据源的动态代理是比较方便,在不需要切换数据源的情况,去除切换代码不影响其他功能,同时易于程序扩展。