全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Spring AOP切面解决数据库读写分离实例详解

Spring AOP切面解决数据库读写分离实例详解

为了减轻数据库的压力,一般会使用数据库主从(master/slave)的方式,但是这种方式会给应用程序带来一定的麻烦,比如说,应用程序如何做到把数据写到master库,而读取数据的时候,从slave库读取。如果应用程序判断失误,把数据写入到slave库,会给系统造成致命的打击。

解决读写分离的方案很多,常用的有SQL解析、动态设置数据源。SQL解析主要是通过分析sql语句是insert/select/update/delete中的哪一种,从而对应选择主从。而动态设置数据源,则是通过拦截方法名称的方式来决定主从的,例如:save*(),insert*() 形式的方法使用master库,select()开头的,使用slave库。蛮多公司会使用在方法上标上自定义的@Master、@Slave之类的标签来选择主从,也有公司直接就调用setxxMaster,setxxSlave之类的代码进行主从选择。

下面我主要介绍一下基于Spring AOP动态设置数据源这种方式。注意这篇文章是基于自己项目的实际情况的,不是通用的方案,请知晓。

原理图


 

Spring AOP的切面主要的职责是拦截Mybatis的Mapper接口,通过判断Mapper接口中的方法名称来决定主从。

 Spring AOP 切面配置

<aop:config expose-proxy="true"> 
 
<aop:pointcut id="txPointcut" expression="execution(* com.test..persistence..*.*(..))" /> 
 
<aop:aspect ref="readWriteInterceptor" order="1"> 
 
<aop:around pointcut-ref="txPointcut" method="readOrWriteDB"/> 
 
</aop:aspect> 
 
</aop:config> 
 
  
 
<bean id="readWriteInterceptor" class="com.test.ReadWriteInterceptor"> 
 
  <property name="readMethodList"> 
 
   <list> 
 
    <value>query*</value> 
 
    <value>use*</value> 
 
    <value>get*</value> 
 
    <value>count*</value> 
 
    <value>find*</value> 
 
    <value>list*</value> 
 
    <value>search*</value> 
 
  </list> 
 
 </property> 
 
<property name="writeMethodList"> 
 
  <list> 
 
    <value>save*</value> 
 
    <value>add*</value> 
 
    <value>create*</value> 
 
    <value>insert*</value> 
 
    <value>update*</value> 
 
    <value>merge*</value> 
 
    <value>del*</value> 
 
    <value>remove*</value> 
 
    <value>put*</value> 
 
    <value>write*</value> 
 
  </list> 
 
</property> 
 
</bean> 

把所有Mybatis接口类都放置在persistence下。配置的切面类是ReadWriteInterceptor。这样当Mapper接口的方法被调用时,会先调用这个切面类的readOrWriteDB方法。在这里需要注意<aop:aspect>中的order="1" 配置,主要是为了解决切面于切面之间的优先级问题,因为整个系统中不太可能只有一个切面类。

Spring AOP 切面类实现

public class ReadWriteInterceptor { 
  private static final String DB_SERVICE = "dbService"; 
  private List<String> readMethodList = new ArrayList<String>(); 
  private List<String> writeMethodList = new ArrayList<String>(); 
  public Object readOrWriteDB(ProceedingJoinPoint pjp) throws Throwable { 
    String methodName = pjp.getSignature().getName(); 
    if (isChooseReadDB(methodName)) { 
      //选择slave数据源 
    } else if (isChooseWriteDB(methodName)) { 
      //选择master数据源 
    } else { 
     //选择master数据源 
    } 
    return pjp.proceed(); 
} 
 
 private boolean isChooseWriteDB(String methodName) { 
   for (String mappedName : this.writeMethodList) { 
     if (isMatch(methodName, mappedName)) { 
       return true; 
     } 
   } 
  return false; 
} 
 
 private boolean isChooseReadDB(String methodName) { 
  for (String mappedName : this.readMethodList) { 
    if (isMatch(methodName, mappedName)) { 
      return true; 
    } 
  } 
  return false; 
} 
 
 private boolean isMatch(String methodName, String mappedName) { 
  return PatternMatchUtils.simpleMatch(mappedName, methodName); 
} 
 
 public List<String> getReadMethodList() { 
  return readMethodList; 
 } 
 
 public void setReadMethodList(List<String> readMethodList) { 
  this.readMethodList = readMethodList; 
} 
 
 public List<String> getWriteMethodList() { 
  return writeMethodList; 
 } 
 
 public void setWriteMethodList(List<String> writeMethodList) { 
  this.writeMethodList = writeMethodList; 
} 
 

覆盖DynamicDataSource类中的getConnection方法

ReadWriteInterceptor中的readOrWriteDB方法只是决定选择主还是从,我们还必须覆盖数据源的getConnection方法,以便获取正确的connection。一般来说,是一主多从,即一个master库,多个slave库的,所以还得解决多个slave库之间负载均衡、故障转移以及失败重连接等问题。

1、负载均衡问题,slave不多,系统并发读不高的话,直接使用随机数访问也是可以的。就是根据slave的台数,然后产生随机数,随机的访问slave。

2、故障转移,如果发现connection获取不到了,则把它从slave列表中移除,等其回复后,再加入到slave列表中

3、失败重连,第一次连接失败后,可以多尝试几次,如尝试10次。

处理业务方法中的@Transactional注解

我参与的这个项目,大部分业务代码是不需要事务的,只有极个别情况需要。那么按照上面提到的方案,如果不对业务方法中@Transactional注解进行特殊处理的话,主从的选择会出现问题。大家都知道,如果使用了Spring的事务,那么在同一个业务方法内,只会调用一次数据源的getConnection方法,如果该业务方法内,调用的mapper接口刚好以select开头的,就会选择slave库,那么接下来调用以insert开头的mapper接口方法时,会把数据写入到slave库。如何解决这个问题呢?必须在进入标有@Transactional注解的业务方法前,指定选择master主库。可以通过覆盖DataSourceTransactionManager类中的doBegin方法,如下:

public class MyTransactionManager extendsDataSourceTransactionManager{ 
 
@Override 
 
protected void doBegin(Object transaction, TransactionDefinitiondefinition) { 
 
//选择master数据库 
 
super.doBegin(transaction, definition); 
 
} 
 
} 

这样既可以避免,把数据写入到从库的问题。

总结

本人的解决方案是基于项目实际的,不一定合适你,我只是展示了解决方案而已。当然你可以选择开源的框架,像阿里的Cobar,360的Atlas。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# Spring  # AOP切面解决数据库读写分离  # AOP切面  # Spring AOP使用接口方式实现  # Springboot接口项目如何使用AOP记录日志  # Spring AOP实现Redis缓存数据库查询源码  # Spring+Mybatis 实现aop数据库读写分离与多数据库源配置操作  # 使用Spring AOP实现MySQL数据库读写分离案例分析(附demo)  # Spring AOP实现接口请求记录到数据库的示例代码  # 随机数  # 多个  # 应用程序  # 标上  # 会给  # 类中  # 负载均衡  # 就会  # 在这里  # 也有  # 你可以  # 大家都  # 不太  # 不需要  # 是基于  # 则是  # 列表中  # 不多  # 几次  # 是从 


相关文章: 网站制作中优化长尾关键字挖掘的技巧,建一个视频网站需要多少钱?  如何快速搭建自助建站会员专属系统?  公司网站建设制作费用,想建设一个属于自己的企业网站,该如何去做?  C#怎么创建控制台应用 C# Console App项目创建方法  建站之星安装失败:服务器环境不兼容?  网站制作模板下载什么软件,ppt模板免费下载网站?  制作电商网页,电商供应链怎么做?  如何设计高效校园网站?  广州顶尖建站服务:企业官网建设与SEO优化一体化方案  如何在Mac上搭建Golang开发环境_使用Homebrew安装和管理Go版本  如何在建站主机中优化服务器配置?  相册网站制作软件,图片上的网址怎么复制?  免费视频制作网站,更新又快又好的免费电影网站?  建站主机选哪家性价比最高?  电商平台网站制作流程,电商网站如何制作?  网站设计制作公司地址,网站建设比较好的公司都有哪些?  网站制作话术技巧,网站推广做的好怎么话术?  建站之星如何快速更换网站模板?  如何挑选高效建站主机与优质域名?  网页设计与网站制作内容,怎样注册网站?  洛阳网站制作公司有哪些,洛阳的招聘网站都有哪些?  建站VPS选购需注意哪些关键参数?  阿里云高弹*务器配置方案|支持分布式架构与多节点部署  北京制作网站的公司,北京铁路集团官方网站?  购物网站制作公司有哪些,哪个购物网站比较好?  如何快速配置高效服务器建站软件?  专业网站设计制作公司,如何制作一个企业网站,建设网站的基本步骤有哪些?  已有域名和空间如何快速搭建网站?  猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?  c++怎么使用类型萃取type_traits_c++ 模板元编程类型判断【方法】  php8.4新语法match怎么用_php8.4match表达式替代switch【方法】  如何选择适合PHP云建站的开源框架?  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  网站制作公司广州有几家,广州尚艺美发学校网站是多少?  小说建站VPS选用指南:性能对比、配置优化与建站方案解析  建站之星伪静态规则如何设置?  建站之星下载版如何获取与安装?  公司网站制作价格怎么算,公司办个官网需要多少钱?  网站制作难吗安全吗,做一个网站需要多久时间?  宁波免费建站如何选择可靠模板与平台?  建站之星展会模板:智能建站与自助搭建高效解决方案  潮流网站制作头像软件下载,适合母子的网名有哪些?  如何在建站之星绑定自定义域名?  唐山网站制作公司有哪些,唐山找工作哪个网站最靠谱?  建站之星如何一键生成手机站?  制作网站的网址是什么,请问后缀为.com和.com.cn还有.cn的这三种网站是分别是什么类型的网站?  ,网站推广常用方法?  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  沈阳制作网站公司排名,沈阳装饰协会官方网站?  建站主机如何选?高性价比方案全解析 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。