想要做读写分离,送你一些小经验[亲测有效]

想要做读写分离,送你一些小经验[亲测有效]场景二:假设你有一个 8 核 64G 的主库,8 核 64G 的从库,4 核 32G 的从库,从配置上来看,4 核 32G 的从库处理能力肯定是

读写分离是应用中提升数据访问性能最常见的一种技术,当用户量越来越多,访问量越来越大,单节点数据库难免会遇到性能瓶颈。很多场景基本上都是读多写少,所以增加多个从节点来分担主节点的压力自然是水到渠成的事情。

在应用接入读写分离后,难免会有一些我们意料之外的问题,这篇文章主要给大家介绍下一些经常会遇到的问题,有其他的问题欢迎留言补充。

实现方式

对于读写分离的使用,主要分为两种方式,客户端方式和代理方式。

客户端方式可以自己用 Spring 自带的 AbstractRoutingDataSource 来实现,也可以用开源的框架来实现,比如 Sharding-JDBC。

想要做读写分离,送你一些小经验[亲测有效]

代理方式需要编写代理服务来对所有节点进行管理,应用不需要关注多个数据库节点信息。可以自己实现,也可以用开源的框架,也可以用商业的云服务。

想要做读写分离,送你一些小经验[亲测有效]

数据延迟

谈到数据延迟,你先得理解主从架构的原理。对数据的增删改操作在主库上执行,查询在从库上执行,当数据刚插入到主库,然后马上去查询的时候,很有可能数据还没同步到从库上,就会出现查询不到的情况。

像我之前在某些网站发表文章,发表之后跳转到列表页面,发现没有新发表的文章,重新刷新下页面又有了,这一看这就是读写分离后的数据延迟导致的现象。

强制路由

数据延迟要不要解决,一般取决于业务场景。对于实时性要求没有那么高的业务场景,允许一定的延迟,对于实时性要求高的场景,唯一的方式就是直接从主库进行查询,这样才能及时读到刚插入或者修改后最新的数据。

强制路由就是一种解决方案,也就是将读请求强制分发到主库进行查询。大部分中间件都支持 Hint 语法/FORCE_MASTER/和/FORCE_SLAVE/。

以 Sharding-JDBC 举例,框架提供了 HintManager 来强制路由,使用方式如下:

HintManager hintManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();

为了方便使用,建议封装一个注解,在需要实时查询的业务方法上加上注解,通过切面进行强制路由的设置。

注解使用:

@MasterRoute
@Override
public UserBO getUser(Long id) {
    log.info("查询用户 [{}]", id);
    if (id == null) {
        throw new BizException(ResponseCode.PARAM_ERROR_CODE, "id不能为空");
    }
    UserDO userDO = userDao.getById(id);
    if (userDO == null) {
        throw new BizException(ResponseCode.NOT_FOUND_CODE);
    }
    return userBoConvert.convert(userDO);
}

切面设置:

@Aspect
public class MasterRouteAspect {
    @Around("@annotation(masterRoute)")
    public Object aroundGetConnection(final ProceedingJoinPoint pjp, MasterRoute masterRoute) throws Throwable {
        HintManager hintManager = HintManager.getInstance();
        hintManager.setMasterRouteOnly();
        try {
            return pjp.proceed();
        } finally {
            hintManager.close();
        }
    }
}

事务操作

在事务中的读请求,走主库还是从库呢?对于这个问题,最简单的方式就是所有事务中的操作都走主库,在事务中经常会存在插入,然后再重新查询的场景,此时事务没提交,就算同步很快,从库也是没有数据的,所以只能走主库。

但还有一些请求,只需要查询从库就行了,如果针对所有事务中的操作都强制路由,也不是很好。在 Sharding-JDBC 中的做法挺好的,对于同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。如果我们在数据写入之前有查询请求,还是走的从库,减轻主库压力。

想要做读写分离,送你一些小经验[亲测有效]

动态强制路由

在功能开发的时候就决定了哪些接口要强制走主库,这个时候我们会在代码上进行路由的控制,也就是前面讲的自定义注解。如果有些是没有加的,但是在线上运行的时候发现还是要走主库才可以,这个时候就需要改代码重新发布了。

动态强制路由可以结合配置中心来实现,通过配置的方式来决定哪些接口要强制路由,然后在 Filter 中通过 HintManager 来设置,避免代码重启。

也可以通过切面精确到业务方法级别的动态路由配置。

流量分发

场景一:

假设你有一个主节点,两个从节点,读请求较多,两个从节点压力有点大。这个时候只能增加第三个从节点来分担压力。现象是主库的压力并不大,写入较少,从成本来考虑,是否可以不增加第三个从节点呢?

场景二:

假设你有一个 8 核 64G 的主库,8 核 64G 的从库,4 核 32G 的从库,从配置上来看,4 核 32G 的从库处理能力肯定是要低于其他两个的,这个时候如果我们没有定制流量分发的比例,就会出现低配数据库压力过高而导致的问题。当然这个也能避免使用不同规则的从库。

上面的场景需要能够对请求进行管理,在 Sharding-JDBC 中提供了读写分离的路由算法,我们可以自定义算法来进行流量的分发管理。

实现算法类:

public class KittyMasterSlaveLoadBalanceAlgorithm implements MasterSlaveLoadBalanceAlgorithm {
    private RoundRobinMasterSlaveLoadBalanceAlgorithm roundRobin = new RoundRobinMasterSlaveLoadBalanceAlgorithm();
    @Override
    public String getDataSource(String name, String masterDataSourceName, List<String> slaveDataSourceNames) {
        String dataSource = roundRobin.getDataSource(name, masterDataSourceName, slaveDataSourceNames);
        // 控制逻辑,比如不同的从节点(配置不同)可以有不同的比例
        return dataSource;
    }
    @Override
    public String getType() {
        return "KITTY_ROUND_ROBIN";
    }
    @Override
    public Properties getProperties() {
        return roundRobin.getProperties();
    }
    @Override
    public void setProperties(Properties properties) {
        roundRobin.setProperties(properties);
    }
}

基于 SPI 机制的配置:

org.apache.shardingsphere.core.strategy.masterslave.RoundRobinMasterSlaveLoadBalanceAlgorithm
org.apache.shardingsphere.core.strategy.masterslave.RandomMasterSlaveLoadBalanceAlgorithm
com.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithm

读写分离的配置:

spring.shardingsphere.masterslave.load-balance-algorithm-class-name=com.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithm
spring.shardingsphere.masterslave.load-balance-algorithm-type=KITTY_ROUND_ROBIN

关于作者:尹吉欢,简单的技术爱好者,《Spring Cloud 微服务-全栈技术与案例解析》, 《Spring Cloud 微服务 入门 实战与进阶》作者, 公众号猿天地发起人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/11937.html

(0)

相关推荐

  • 如何强制实行 setTimeout 与 setInterval 的最佳实践「终于解决」

    如何强制实行 setTimeout 与 setInterval 的最佳实践「终于解决」在 JavaScript 异步编程中,作者经常会写出各种各样的 bug,其中最常见的错误,都是由于忘记清除 setTimeout 或者 setInterval 创建的定时器引起的。 组件挂载后,每隔一秒会将 sec 的值加 1,看起来一切正常,但是,当组件被卸载的时候,setI…

    2023-08-09
    126
  • 【从零开始 图文详解】IDEA整合SSM框架:Spring+SpringMVC+Mybatis[通俗易懂]

    【从零开始 图文详解】IDEA整合SSM框架:Spring+SpringMVC+Mybatis[通俗易懂]因为说的比较细,所以前面第一部分都了解可以跳过。 3.填写 Maven 配置。 1:首先你要先配置好 Maven,如果配置好了,去 CMD DOSS 界面下输入mvn version 命令,如果有显示,则代表已经配置好了,如果没,下载maven,配置环境变量。 2:选择我们…

    2023-07-19
    121
  • 利用SQL语句(命令方式)创建数据库(以及句子解释)「终于解决」

    利用SQL语句(命令方式)创建数据库(以及句子解释)「终于解决」create database 课程管理 //1:create database为SQL语句,用于创建数据库。执行完之后会创建一个新数据库及存储该数据库的文件,或从先前创建的数据库

    2023-03-17
    167
  • Python安装后CMD无法运行的解决方法

    Python安装后CMD无法运行的解决方法Python是一种高级编程语言,被广泛用于各种web应用程序和数据科学领域。但是,有时在安装Python后,无法在命令提示符(CMD)中运行python命令。本文将介绍如何解决这个问题。

    2024-04-22
    59
  • Python工程师:import os是什么意思

    Python工程师:import os是什么意思Python是一种非常流行的编程语言,被广泛应用于各种领域,包括数据科学、机器学习、Web应用程序开发和自动化任务等等。而import os语句则是Python编程中经常使用的一条命令,用于导入os模块。os模块提供了一系列与操作系统交互的功能,使得Python程序的编写更加方便。

    2024-06-02
    53
  • mysql 多表关联查询如何加快速度_多表关联查询还会走索引吗

    mysql 多表关联查询如何加快速度_多表关联查询还会走索引吗这个问题出现在多表关联时, 如一张商品表,其中的单位的字符串表示是在单位表中, 但这个单位的id之后进行了删除,并且不再奏效。 如下: select * from (select pg0.id,pg0

    2023-05-16
    144
  • Material Design控件之FloatingActionButton「终于解决」

    Material Design控件之FloatingActionButton「终于解决」这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战 在Material Design设计规范中,提出了一些控件的基本准则规范,我们来学习下Material Design中的控件

    2023-08-03
    132
  • [20191218]降序索引疑问4.txt

    [20191218]降序索引疑问4.txt[20191218]降序索引疑问4.txt–//前几天优化一个项目,我发现许多表里面有有隐含字段,一般开发很少建立函数索引.我自己检查发现里面存在大量的降序索引.–//我感觉有点奇怪,为什么开发要

    2022-12-27
    151

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注