3

ShardingSphere数据库读写分离 - 渣男小四

 1 year ago
source link: https://www.cnblogs.com/steakliu/p/16514796.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

码农在囧途

最近这段时间来经历了太多东西,无论是个人的压力还是个人和团队失误所带来的损失,都太多,被骂了很多,也被检讨,甚至一些不方便说的东西都经历了,不过还好,一切都得到了解决,无论好坏,这对于个人来说也是一种成长吧,事后自己也做了一些深刻的检讨,总结为一句话“挫败使你难受,使你睡不着觉,使你痛苦,不过最后一定会使你变得成熟,变得认真,变得负责”,每次面临挫败,我都会告诉自己,这不算什么,十年之后,你回过头来看待这件事的时候,你一定会觉得,这算什么屁事。

在现在这个数据量与日俱增的时代,传统的单表,单库已经无法满足我们的需求,可能早期数据量不是很大,CRUD都集中在一个库中,但是当数据量 到达一定的规模的时候,使用单库可能就无法满足需求了,在实际场景中,读的频率是远远大于写的,所以我们一般会做读写分离,主库一般用于写,而从库 用于读,而主从分离有好几种模式。

一主多从是只有一台主机用于写操作,多台从机用于读操作,一主多从是存在风险的,当主机宕机后,那么写服务就会瘫痪,本文我们主要说的是ShardingSphere读写分离, 而目前ShardingSphere只支持单主库,所以如果要保证业务的高可用,那么目前ShardingSphere不是很好的选择,不过希望ShardingSphere后面支持多主机模式。

从上面的一主多从我们看出了它的弊端,所以为了保证高可用,我们可能需要多个主机用于写操作,这样当某个主机宕机,其他主机还能继续工作,ShardingSphere只支持 单主机。

ShardingSphere只需要简单的配置就能实现数据库的读写的分离,我们甚至感知不到是在操作多个数据库,极大的简化了我们的开发,但是ShardingSphere 不支持多主库,也无法进行主从数据库的同步。

ShardingSphere整合SpringBoot项目进行主从分离

ShardingSphere和SpringBoot能够很简单的进行组合,只需要简单的配置,ShardingSphere能够和主流的ORM框架进行整合,ShardingSphere会 从ORM框架中解析出SQL语句,判断是读操作还是写操作,如果是读操作,则会落到主库上,如果是读操作,那么ShardingSphere会使用对应的负载均衡算法负载到 对应的从库上面。

maven引入ShardingSphere starter

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.1.2</version>
</dependency>

yml文件配置

names为数据库名称字符串,然后需要一个一个的进行配置JDBC连接,对于读写分离,我们需要关注rules下面的readwrite-splitting 通过load-balancers配置负载均衡策略,data-sources配置对应的读写库,目前ShardingSphere只支持单主库,多从库,如下我们写 库使用write-data-source-name,库为db1,读库使用read-data-source-names,库db2,db3,db4

spring:
  shardingsphere:
    datasource:
      names: db1,db2,db3,db4
      db1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: qwer123@
        type: com.zaxxer.hikari.HikariDataSource
        maximumPoolSize: 10
      db2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: qwer123@
        type: com.zaxxer.hikari.HikariDataSource
        maximumPoolSize: 10
      db3:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db3?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: qwer123@
        type: com.zaxxer.hikari.HikariDataSource
        maximumPoolSize: 10
      db4:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db4?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: qwer123@
        type: com.zaxxer.hikari.HikariDataSource
        maximumPoolSize: 10
    rules:
      sharding:
      readwrite-splitting:
        load-balancers:
          round_robin:
            type: ROUND_ROBIN
        data-sources:
          read_write_db:
            type: Static
            props:
              write-data-source-name: db1
              read-data-source-names: db2,db3,db4
            load-balancer-name: round_robin
    props:
      sql-show: true

测试写操作。

因为写操作配置的数据库是db1,所以所有写操作都应该进入db1,如下图所示,解析出来的ShardingSphere-SQL中显示的都是db1。

81f93a62a546449b951d22dfb64ec66d~tplv-k3u1fbpfcp-zoom-1.image

测试读操作

读操作配置的数据库是db2,db3,db4,配置的负载均衡算法是ROUND_ROBIN(轮询算法),所以查询请求会在三个库顺序查询。

4758feee1ad54b5cb080479044c0488a~tplv-k3u1fbpfcp-zoom-1.image

ShardingSphere负载均衡算法

因为从库有多个,所以我们需要根据一定的策略将请求分发到不同的数据库上,防止单节点的压力过大或者空闲,ShardingSphere内置了多种负载均衡算法,如果我们想实现自己的 算法,那么可以实现ReadQueryLoadBalanceAlgorithm接口,下面我们列举几种来看下。

ROUND_ROBIN 轮询算法

配置负载均衡算法为轮询算法,那么所有请求都会均匀的分发到对应的数据库,这样,每台数据库所承受的压力都是一样的,轮询算法对应的实现类是RoundRobinReplicaLoadBalanceAlgorithm

public final class RoundRobinReplicaLoadBalanceAlgorithm implements ReadQueryLoadBalanceAlgorithm {
    
    private final AtomicInteger count = new AtomicInteger(0);
    
    @Getter
    private Properties props;
    
    @Override
    public void init(final Properties props) {
        this.props = props;
    }
    
    @Override
    public String getDataSource(final String name, final String writeDataSourceName, final List<String> readDataSourceNames) {
        if (TransactionHolder.isTransaction()) {
            return writeDataSourceName;
        }
        return readDataSourceNames.get(Math.abs(count.getAndIncrement()) % readDataSourceNames.size());
    }
    
    @Override
    public String getType() {
        return "ROUND_ROBIN";
    }
    
    @Override
    public boolean isDefault() {
        return true;
    }
}

RANDOM 随机算法

如果使用随机算法,那么请求过来以后就会随机的分发到其中的一个数据库上面,使用随机算法可能会导致请求的分发不均匀,可能某一台 接受到了大量的请求,某一台接受到的请求相对来说较少。

WEIGHT 基于权重的算法

基于权重的算法需要做相应的配置,我们可以将某一台数据库的权重加大,某一台数据库的权重减小,这样,权重大的数据库 就会接收到更多的请求,权重小的接收到的请求就会比较少。

在ShardingSphere中自定义负载均衡算法

ShardingSphere中使用了大量的SPI,所以我们开发者可以自由的实现自己的规则,然后无缝的切换到自己的规则,我们可以实现自己的一套负载均衡算法,其实ShardingSphere内置的集中负载均衡算法完全能满足数据库负载均衡,只不过为了更加深入的学习ShardingSphere,所以我们很有必要自己简单的实现一下。

下面我们简单的实现一下,我们就不去实现一些复杂的了,为了演示,我们将所有请求全部都负载到db2

定义SPI

我们从ShardingSphere的读写分离模块shardingspere-readwrite-spliltting-core中的META-INF/services下面看到了负载均衡的SPI。

f91033147316406488e9715a17ef6e16~tplv-k3u1fbpfcp-zoom-1.image
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.RoundRobinReplicaLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.RandomReplicaLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.WeightReplicaLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.FixedPrimaryLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.FixedReplicaRandomLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.FixedReplicaRoundRobinLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.FixedReplicaWeightLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.TransactionRandomReplicaLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.TransactionRoundRobinReplicaLoadBalanceAlgorithm
org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.TransactionWeightReplicaLoadBalanceAlgorithm

为了实现自己的负载均衡算法,我们需要在自己的模块中定义SPI,如下,在自己项目的META-INF/services目录下编写负载均衡SPI接口,里面内容为我们自定义的负载均衡算法的类文件的位置。

8adb9676c9f24861a2b893ac1a072bc5~tplv-k3u1fbpfcp-zoom-1.image

编写负载均衡算法核心代码

自定义负载均衡算法需要实现ReadQueryLoadBalanceAlgorithm接口,里面核心的两个方法是getDataSourcegetTypegetDataSource是算法的逻辑实现部分,其目的是选出一个目标数据库,此方法会传入readDataSourceNames,它是读库的集合,我们此处直接返回db2,那么会一直读db2getType是返回负载均衡算法的名称。

/**
 * 功能说明: 自定义负载均衡算法
 * <p>
 * Original @Author: steakliu-刘牌, 2022-07-20  18:05
 * <p>
 * Copyright (C)2020-2022  steakliu All rights reserved.
 */
public class CustomReplicaLoadBalanceAlgorithm implements ReadQueryLoadBalanceAlgorithm {

    @Getter
    private Properties props;

    @Override
    public String getDataSource(final String name, final String writeDataSourceName, final List<String> readDataSourceNames) {
        return "db2";
    }

    @Override
    public String getType() {
        return "CUSTOM";
    }

    @Override
    public void init(Properties props) {
        this.props = props;
    }

    @Override
    public boolean isDefault() {
        return false;
    }
}

在yml中使用自己实现的负载均衡算法

rules:
  sharding:
  readwrite-splitting:
    load-balancers:
      custom:
        type: CUSTOM
    data-sources:
      read_write_db:
        type: Static
        props:
          write-data-source-name: db1
          read-data-source-names: db2,db3,db4
        load-balancer-name: custom

发起大量的查询操作

从日志输出来看,所有的请求全部落在了db2上面,于是证明我们自定义的负载均衡算法成功了。

f69fcc938d5848dc8ad2842f014055b0~tplv-k3u1fbpfcp-zoom-1.image

读写分离的中间件其实有很多,ShardingSphere旨在构建异构数据库上层的标准和生态,使用它我们基本上能解决数据库中的大部分问题,但是ShardingSphere也并不是万能的,还有一些东西没有实现,我们期待ShardingSphere能够实现更多强大,好用的功能。

关于ShardingSphere读写分离的分享,我们今天就先说到这里,后面我们会继续探索ShardingSphere的更多强大的功能,比如数据分片,高可用,数据加密,影子库等,今天的分享就到这里,感谢你的观看,我们下期见。

image

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK