`
leitelyaya
  • 浏览: 68892 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

实施MySQL ReplicationDriver支持读写分离

    博客分类:
  • Java
阅读更多

MySQL 提供支持读写分离的驱动类:

com.mysql.jdbc.ReplicationDriver

替代

com.mysql.jdbc.Driver

注意,所有参数主从统一:

jdbc:mysql:replication://<master>,<slave>.../...?...=... 

当然,用户名和密码也必须相同

 

触发Slave的情况

  1. 设置 auto_commit = false

  2. 设置 readOnly 为 true

 

综上特点,读写分离依赖于事务

 

常用使用场景:

第一种, 事务管理使用【注解】支持

通常,事务管理在Service层,只需要简单的操作即可支持读写分离:

1
2
@Transactional(propagation=Propagation.REQUIRED, readOnly = true)
public List<OrderBase> findOrderList(String orderCode);

事务开启后,查询自动切换到从库。

注意:@Transactional 默认的readOnly参数是false,更新操作不需要特别的改动。propagation是指的事务传播方式,默认设置是Require,指的是“本次操作需要事务支持,如果没有事务开启一个事务,如果有事务,加入到该事务中”

 

考虑复杂一点的情况,当Service中出现自我方法的调用时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Transactional(propagation=Propagation.REQUIRED, readOnly = true)
public OrderBase getOrder(String orderCode) {
    findSubOrderList(orderCode);
}
 
@Transactional(propagation=Propagation.REQUIRED, readOnly = true)
public List<OrderSub> findSubOrderList(String orderCode) {
}
 
@Transactional(propagation=Propagation.REQUIRED, readOnly = false)
public void updateOrder(OrderBase orderBase) {
    findSubOrderList(orderBase.getCode());
    ...
}

当外部调用getOrder时,getOrder方法的@Transaction注解生效,设置从库查询。

当外部调用updateOrder时,updateOrder方法的@Transaction注解生效,设置操作主库。

注意,这两个方法都调用了findSubOrderList方法,而调用的对象是this,不是被spring事务管理器替换过的service对象,所以findSubOrderList方法上的@Transaction注解无效,会根据上文环境来查主库和从库

这种特性对于业务来说是恰当好处的,生效的事务是在最外层的方法上,可以避免在一个事务内部出现读写库不统一的情况。

 

更复杂一点的情况,当service中调用了其它类的service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// OrderSerivceImpl:
@Transactional(propagation=Propagation.REQUIRED, readOnly = true)
public OrderBase getOrder(String orderCode) {
    orderCouponService.getById(couponId);
}
  
@Transactional(propagation=Propagation.REQUIRED, readOnly = false)
public OrderBase createOrder(OrderGeneratorDto dto) {
    orderCouponService.saveCoupon(coupon);
}
 
@Transactional(propagation=Propagation.REQUIRED, readOnly = false)
public OrderBase updateOrder(OrderBase orderBase) {
    orderCouponService.getById(couponId);
}
  
// OrderCouponServiceImpl:
@Transactional(propagation=Propagation.REQUIRED, readOnly = true)
public OrderCoupon getById(Integer couponId) {
}
  
@Transactional(propagation=Propagation.REQUIRED, readOnly = false)
public OrderCoupon saveCoupon(OrderCoupon coupon) {
}

1, 当外部调用OrderSerivce的getOrder时,getOrder方法的@Transaction注解生效,设置从库查询。

getOrder内部调用了OrderCouponService的getById方法,由于orderCouponService是spring提供的对象,经过了事务管理,所以getById方法上的@Transaction注解生效,

我们知道Require这个事务传播的特性,getById不会创建新的事务,所以依旧是由从库读取数据。

 

2, 当外部调用OrderSerivce的saveOrder时,saveOrder方法的@Transaction注解生效,设置操作主库。

saveOrder内部调用了OrderCouponService的saveCoupon方法,同样由于Require的特性,没有创建新事务,操作主库。

 

3, 当外部调用OrderSerivce的updateOrder时,updateOrder方法的@Transaction注解生效,设置操作主库。

updateOrder内部调用了OrderCouponService的getById方法,同样由于Require的特性,没有创建新事务,从主库读出数据。

这些特性也是很好的,我们只需要关心最外部调用的方法的注解内容,就可以确定走的哪个库。

 

 

更复杂点的情况是新开事务的情况,建议谨慎对待

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// OrderSerivceImpl:
@Transactional(propagation=Propagation.REQUIRED, readOnly = true)
public Price getOrderPrice(String orderCode) {
    // 不恰当的业务逻辑!此处只是演示
    otherService.updateById(id, xxx);
    foo = otherService.getById(id);
}
   
// OtherServiceImpl:
@Transactional(propagation=Propagation.REQUIRED, readOnly = true)
public ... getById(Integer id) {
}
   
@Transactional(propagation=Propagation.REQUIRED_NEW, readOnly = false)
public void updateById(id, ...) {
}

该想法是构想把OrderSerivce的getOrderPrice查询走从库,其中一个小逻辑更新库设置操作主库。在没有设置主从的情况下,这种方式是支持的,并不会出现问题。

但在设置了主从的情况下,这种业务逻辑操作就“不安全”了,因为,updateById走的是主库,它的更新操作是依赖于主从同步的,很有可能getById取到了“过期”的数据。

这种情况在业务上来说是应该要避免的,如果不能避免,最好的办法是让外部都走主库,保证数据来源的一致性。

 

综上,事务管理配置用注解的方式还是蛮方便的。

 

第二种, 事务管理使用【XML配置】支持

XML配置的事务是以判断指定名称开头的方法来实现的,跟注解配置事务是类似的。可以把select和get判定为readOnly,传播机制设定为Require。

 

 第三种,使用支持读事务的入口类

鉴于现有代码Service层被融合到web和admin中,在Service层的注入会影响多个系统,而单独写方法,不免繁琐,使用代理方法支持事务的读比较灵活。

流程:

 

这个模式好处在于可以根据业务的需要,合理安排开发和测试的工作,影响范围可控。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class ReadOnlyTransFactoryBean<T> implements MethodInterceptor, FactoryBean<T>, ApplicationContextAware {
 
    private Logger logger = Logger.getLogger(ReadOnlyTransFactoryBean.class);
 
    /**
     * 代理的Service类
     */
    Class<T> serviceInterface;
 
    /**
     * 代理的Service名
     */
    String delegateBeanName;
 
    ApplicationContext applicationContext;
 
    public Class<T> getServiceInterface() {
        return serviceInterface;
    }
 
    public void setServiceInterface(Class<T> serviceInterface) {
        this.serviceInterface = serviceInterface;
    }
 
    public String getDelegateBeanName() {
        return delegateBeanName;
    }
 
    public void setDelegateBeanName(String delegateBeanName) {
        this.delegateBeanName = delegateBeanName;
    }
 
    Enhancer enhancer = new Enhancer();
 
    @Override
    public T getObject() throws Exception {
        // 使用CGlib增强,提供代理功能
        enhancer.setSuperclass(serviceInterface);
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }
 
    @Override
    public Class<?> getObjectType() {
        return this.serviceInterface;
    }
 
    @Override
    public boolean isSingleton() {
        return true;
    }
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
 
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        T service = applicationContext.getBean(delegateBeanName, serviceInterface);
        DataSourceTransactionManager txManager = applicationContext.getBean(DataSourceTransactionManager.class);
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
        definition.setReadOnly(true);
        logger.info("Start ReadOnly Transactional!");
        TransactionStatus transaction = txManager.getTransaction(definition);
        Object result = method.invoke(service, args);
        if (!transaction.isCompleted()) {
            txManager.commit(transaction);
        }
        logger.info("End ReadOnly Transactional!");
        return result;
    }
}

按需配置:

1
2
3
4
5
<!-- ReadOnly Transaction Service -->
<bean id="readOnlyOrderPlatformService" class="com.qding.order.service.util.ReadOnlyTransFactoryBean">
        <property name="serviceInterface" value="com.qding.order.service.IOrderService" />
        <property name="delegateBeanName" value="orderPlatformService" />
</bean>

使用时请注意:原有@Autowired方式注入请改成@Resource指定名称的方式,以区别不同入口

1
2
3
4
5
@Resource(name="orderPlatformService")
protected IOrderService orderService;
 
@Resource(name="readOnlyOrderPlatformService")
protected IOrderService readOnlyOrderService;

 

 

 

 

分享到:
评论

相关推荐

    springboot+mysql读写分离

    springboot结合mysql主从来实现读写分离 一、实现的功能 1、基于springboot框架,application.yml配置多个数据源,使用AOP以及AbstractRootingDataSource、ThreadLocal来实现多数据源切换,以实现读写分离。mysql...

    mysql与mycat的读写分离

    Mycat是一款开源的数据库中间件,它可以实现对多个MySQL实例的连接管理和负载均衡,支持SQL路由等功能,进而实现读写分离。 ##### 启用Mycat读写分离 完成MySQL主从配置后,下一步是启用Mycat的读写分离机制。这...

    【27】基于MyCat的MySQL高可用读写分离集群实战课程下载【No215】基于MyCat的MySQL高可用读写分离集群实战课程下载 .txt

    通过对基于MyCat的MySQL高可用读写分离集群进行深入探讨,我们不仅了解了其基本原理和技术要点,还掌握了一套完整的部署实施流程。在实际应用过程中,还需要根据具体的业务场景和技术需求,灵活调整配置策略,以达到...

    MYSQL+MYCAT 读写分离

    MYSQL+MYCAT 读写分离详解 MYSQL+MYCAT 读写分离是指在数据库系统中,将数据库读写操作分离到不同的服务器上,以提高数据库的性能和可用性。在本文中,我们将详细介绍如何使用MYSQL和MYCAT实现读写分离架构。 ...

    使用mysql-proxy实现mysql读写分离

    ### 使用mysql-proxy实现MySQL读写分离 随着业务规模的不断扩大,数据库系统往往成为整个系统中的瓶颈之一。为了提高系统的响应速度以及数据处理能力,一种常用的技术手段就是采用**读写分离**的方式。读写分离的...

    springboot实现mysql的读写分离的案例源码

    读写分离就是对于一条SQL该选择哪一个数据库去执行,至于谁来做选择数据库这件事,有两个,要么使用中间件帮我们做,要么程序自己做。一般来说,读写分离有两种实现方式。第一种是依靠中间件MyCat,也就是说应用程序...

    YII2数据库MySQL复制和读写分离配置

    MySQL数据库复制和读写分离是数据库管理中的重要概念和实践,可以有效提升数据库的扩展性和可用性。特别是在高流量的Web应用中,读写分离可以减轻主服务器的压力,提高应用的性能。YII2是一个基于组件的高性能PHP...

    基于mycat的mysql高可用读写分离

    基于mycat的mysql高可用读写分离,适合mysql进阶。比较实用的技术。

    spring mysql 读写分离

    Spring框架提供了对MySQL数据库读写分离的良好支持,使得开发者能够轻松地在应用中实现这一功能。 **1. 什么是读写分离** 读写分离是指在数据库系统中,将读操作和写操作分配到不同的服务器上执行,读操作通常发生...

    MySQL主从复制 读写分离

    根据提供的文件信息,我们可以总结出以下关于MySQL主从复制与读写分离的相关知识点: ### MySQL主从复制原理 MySQL主从复制是一种数据同步机制,它允许将一个MySQL服务器(主服务器)上的数据自动复制到一个或多个...

    springboot+mybatis+mysql实现读写分离.zip

    springboot+mybatis+mysql实现读写分离 先在建好mysql主从数据库的配置,然后在代码中根据读写分离或强制读取master数据库中的数据 mysql数据库设置主从,参考: ...

    mysql_双主+keepalived读写分离.docx

    MySQL 双主 + Keepalived 读写分离解决方案 MySQL 双主 + Keepalived 读写分离是一种高可用性解决方案,旨在解决 MySQL 单点故障问题。该方案通过双主 MySQL 实现高可用性,并使用 Keepalived 实现虚拟 IP(VIP)...

    Mybatis读写分离 mysql数据库 Druid连接池

    Mybatis读写分离,支持n多的从库,简单的负载均衡。数据库是mysql,采用druid连接池。 读写分离采用插件的形式实现的,优点是不需要写源注解,不需要写分开的Mapper.xml。 如果只有主库的话,那么会创建两个地址相同...

    springboot 实现mysql读写分离

    本教程将详细介绍如何利用SpringBoot框架实现MySQL的读写分离,以及涉及到的相关技术。 首先,我们需要理解读写分离的基本概念。读写分离是指将数据库的读操作和写操作分配到不同的服务器上,通常主库负责写操作,...

    mysql读写分离的中间件

    在linux下用于部署读写分离的中间件maxscale。它是实现mysql数据库的读写分离的

    基于数据库中间件Mycat的MySQL主从与读写分离配置详解与示例

    本文将深入探讨基于Mycat的MySQL主从配置与读写分离的详细步骤,并结合【心跳问题heartbeat bug #393】和【bug407:修复主从状态监控和读写分离】的修复进行讲解。 首先,理解Mycat的工作原理至关重要。Mycat作为...

    基于SpringBoot,来实现MySQL读写分离技术.zip

    基于SpringBoot,来实现MySQL读写分离技术.zip基于SpringBoot,来实现MySQL读写分离技术.zip基于SpringBoot,来实现MySQL读写分离技术.zip基于SpringBoot,来实现MySQL读写分离技术.zip基于SpringBoot,来实现MySQL...

    实现mysql读写分离+使用视频

    总的来说,这个教程涵盖了从理论到实践的整个过程,旨在帮助你理解和实施MySQL的读写分离,提升系统的性能和稳定性。通过学习和实践,你将能够更好地应对高并发的数据库访问需求,优化数据库架构,提高应用的响应...

Global site tag (gtag.js) - Google Analytics