问题
分布式系统的一致性模型包括:强一致性、弱一致性、最终一致性,以及一些最终一致性的变种,如因果一致性、读自己写一致性等。
有个项目,数据存放在主从同步的mysql数据库中,写操作统一落在主库上。由于主从数据库物理位置相距较远(分别在国内机房和国外机房),同步的网络延迟较大,所以有可能因为网络延迟的原因,在读写分离的情况下,读不到自己刚才所写的数据(读从库)。例如用户在国外机房的应用中写完数据后,在延迟时间窗口内立即读,此时写数据还未同步到从库,读从库失败。也就是通常的读写分离方案在这种场景下解决不了读自己写一致性问题。读自己写一致性中的‘自己’可以是进程或系统、也可以是用户。在我们的项目中,’自己‘指的是系统中的用户,用户在系统中发布消息后,要能在消息列表中看到刚才发布的消息。
方案
在通常的读写分离基础上考虑同步延迟窗口:用户在写操作(insert、update、delete)之后进行读(查询),如果是在写操作的同步延迟窗口之内读,则读取主库,其他情况下读从库。如果一个事务中有写操作,不管是否有读操作,肯定是操作主库。
Java实现
使用spring AbstractRoutingDataSource(用于读写分离),Java 注解(Annotation)和aop和thread local。
Spring提供了AbstractRoutingDataSource抽象类用于多数据源的访问也就是分库,可以继承该类覆盖来实现主从数据库的分库访问。
determineCurrentLookupKey()方法来实现读写分离。
RwResourceDesc注解如下,它用在service façade类(façade设计模式)的方法上,用于描述service方法涉及到哪些表或资源的读写。Controller类会调用service façade类的方法,façade类再调用service层的方法。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RwResourceDesc {
String[] readResources() default ""; //读哪些表
String[] writeResources() default "";//写哪些表
}
例如OrderFacede.java中的下单方法,读product表,写order表
@RwResourceDesc(readResources = { "product" }, writeResources = { "order"} )
public void placeOrder(…) throws Exception {…}
Spring数据访问的xml配置大致如下:
<!-- 前面介绍的aop advice -->
<bean id="contextAwareAdvice class="com.xxx.aop.advice.RWContextAwareAdvice"></bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="load*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="list*" read-only="true" propagation="SUPPORTS"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceFacadeOperation" expression="execution(* com.xxx.service.facade.*.*(..))"/>
<!—注意order属性的大小,决定advice的调用顺序 -->
<aop:advisor order="1" advice-ref="contextAwareAdvice" pointcut-ref="serviceFacadeOperation"/>
<aop:advisor order="2" advice-ref="txAdvice" pointcut-ref="serviceFacadeOperation"/>
</aop:config>
aop advice类:
public class RWContextAwareAdvice implements MethodInterceptor {
//注入该属性
private ContextAwareManager contextAwareManager;
public void setContextAwareManager(ContextAwareManager contextAwareManager) {
this.contextAwareManager = contextAwareManager;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
ContextAware context = contextAwareManager.getContext();
RwResourceDesc rwResourceDesc = invocation.getMethod().getAnnotation(RwResourceDesc.class);
if(null != rwResourceDesc){
//如果有写,则强制读主库
if(!rwResourceDesc.writeResources()[0].isEmpty()){
//设置操作主库标志
context.forceMaster();
}
else{
//没有写,只有读。判断是读主库还是读从库
if(!rwResourceDesc.readResources()[0].isEmpty()){
//以context id加readResources中的表名作为key,如果redis中存在该key, if(context.matchChangedResourceTypeForRead(
rwResourceDesc.readResources())){
//读操作在‘自己’的写操作时间窗口之内,读主库
context.forceMaster();
}
}
}
}
try{
Object result = invocation.proceed();
return result;
}
finally{
if(null != rwResourceDesc){
//如果有写,则记录'自己'写了哪些资源,将key、value放到redis中,过期时间同value。
if(!rwResourceDesc.writeResources()[0].isEmpty()){
context.recordChangedResourceType(rwResourceDesc.writeResources());
}
}
//复位thread local中的context id和读主库标志。
contextAware.resetContext();
}
}
}
使用RwResourceDesc注解来标示方法中读写的表有哪些,如果是使用myibatis作为dao框架,也可以写个myibatis拦截器捕获sql语句来判断读写的表。
相关推荐
在读多写少的业务场景下,读写分离可以将大量的读请求分发到多个只读从节点上,减轻主节点的压力,从而提升系统整体的响应速度。而写操作仍然由主节点处理,保证数据的一致性。 在C#中,我们可以使用StackExchange....
本文将深入探讨如何使用SpringMVC和MyBatis框架来实现读写分离,尤其是“一写多读”的模式。我们将讨论以下几个方面: 1. **读写分离的概念**:读写分离是数据库架构设计中的一个关键策略,它将数据库分为主库和多...
通常情况下,启用读写分离后,系统的读取性能将得到显著提升,同时也可以降低主数据库的压力,从而提高整体系统的稳定性和可扩展性。 通过上述详细的配置步骤和策略分析,我们可以看到,MyCAT读写分离不仅可以有效...
写服务访问写库,读服务访问读库,两个数据库之间通过主从同步等方式保持数据一致性。这种方式虽然提高了系统的灵活性和可扩展性,但也增加了系统的复杂度。 #### 四、反对服务读写分离的理由 尽管服务读写分离...
- 在进行读写分离的过程中,需要特别注意事务管理问题,确保跨库操作的一致性和完整性。 #### 六、参考文献 - [实现 MyBatis 的读写分离](http://blog.csdn.net/hupanfeng/article/details/21454847) - [深入理解 ...
其中“0”表示不开启读写分离机制,“1”表示全部的readHost与standbywriteHost参与select语句的负载均衡,“2”表示所有的readHost与writeHost都参与select语句的负载均衡,“3”表示全部的读节点参数读,写节点不...
3. 一致性问题:虽然大多数情况下主从同步延迟较小,但仍然可能存在短暂的数据不一致情况。 4. 成本:从库的维护和扩展成本相比缓存可能更高。 5. 不适用于一致性要求高的场景:如果业务对数据一致性有严格要求,...
主库上的所有写操作都会被记录到二进制日志(binlog),从库会定期或者实时地从主库获取这些日志并应用到自己的数据上,从而保持与主库数据的一致性。 **4. Spring配置** 在Spring中,可以使用多数据源配置来实现...
3. **负载均衡**:读写分离软件可以实现智能的负载均衡,根据从库的负载情况动态分配读请求,避免某个从库过载。 4. **故障切换**:在主库发生故障时,读写分离软件能实现快速切换,将写操作指向新的主库,通常需要...
**读写分离**是一种数据库设计模式,旨在通过将数据库的读操作与写操作分开,从而优化系统的整体性能。这种模式下,通常会有两个或多个数据库实例:一个主数据库负责处理写操作(包括插入、更新和删除),而一个或多...
读写分离是指在一个数据库系统中,将读操作和写操作分配到不同的数据库服务器上,通常读操作远大于写操作,这样可以有效地减少主数据库的压力,提高系统性能。在大型分布式系统中,这种设计尤其常见。 Spring AOP是...
读写分离是指在分布式数据库系统中,将读操作和写操作分开处理,通常主数据库负责写操作,而从数据库负责读操作。这样可以减轻主库的压力,提高数据读取速度,并且在主库出现故障时,可以从库可以继续提供服务,增强...
在IT行业中,数据库读写分离是一种常见的优化策略,特别是在高并发和大数据量的场景下,以提高系统的性能和可用性。本教程将详细介绍如何利用SpringBoot框架实现MySQL的读写分离,以及涉及到的相关技术。 首先,...
默认情况下,所有写操作(INSERT、UPDATE、DELETE)都会路由到主库,读操作(SELECT)则会路由到从库。这可以通过在表定义中设置`readwrite-splitting-rule`来实现。 4. SQL拦截与改写:MyCat内置的SQL解析器可以...
`amoeba.xml`很可能就是Amoeba的配置文件,Amoeba是一个分布式数据库中间件,它能将MySQL集群中的读操作分散到各个从服务器,同时透明地处理数据一致性问题。在Amoeba配置中,可能包含了服务器列表、权重分配、故障...
通过这种方式,我们可以在不改动大量业务代码的情况下,利用Spring AOP实现MySQL的读写分离,有效地提升了系统的读取能力和并发处理能力。在实践中,还可以结合其他优化策略,如缓存、分页、负载均衡等,进一步优化...
在大型应用系统中,由于读操作通常远多于写操作,读写分离可以显著降低主数据库的压力,确保数据的一致性和稳定性。本示例是基于Java和MySQL,利用MyBatis作为数据库访问层来实现读写分离的解决方案。 首先,我们...
- **提高数据库并发**:读写分离可以显著提高系统的并发处理能力,尤其是在读操作远多于写操作的场景下。 - **减轻服务器压力**:将读操作从主服务器转移到从服务器,可以显著减轻主服务器的压力,提高系统的整体...
此外,对于事务一致性有严格要求的场景,可能需要避免使用读写分离,或者采用其他方案如两阶段提交来保证数据的一致性。 通过这个demo,开发者可以快速了解和实践Sharding-JDBC的读写分离功能,为自己的项目带来更...
读写分离通过将读操作和写操作分配到不同的数据库服务器,可以显著提高系统处理能力,减轻主库的压力,确保数据的一致性。 MySQL Proxy是实现这一策略的一个工具,它是一个轻量级的中间件,位于客户端应用程序和...