<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot+ShardingSphereJDBC實(shí)現(xiàn)讀寫分離!

          共 18867字,需瀏覽 38分鐘

           ·

          2022-07-24 21:35

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來,我們一起精進(jìn)!你不來,我和你的競爭對手一起精進(jìn)!

          編輯:業(yè)余草

          segmentfault.com/a/1190000040242523

          推薦:https://www.xttblog.com/?p=5351

          1 概述

          本文講述了如何使用MyBatisPlus+ShardingSphereJDBC進(jìn)行讀寫分離,以及利用MySQL進(jìn)行一主一從的主從復(fù)制。

          具體步驟包括:

          • MySQL主從復(fù)制環(huán)境準(zhǔn)備(Docker
          • 搭建ShardingShpereJDBC+MyBatisPlus+Druid環(huán)境
          • 測試

          2 環(huán)境

          • OpenJDK 17.0.3
          • Spring Boot 2.7.0
          • MyBatis Plus 3.5.1
          • MyBatis Plus Generator 3.5.2
          • Druid 1.2.10
          • ShardingSphereJDBC 5.1.1
          • MySQL 8.0.29Docker

          3 一些基礎(chǔ)理論

          3.1 讀寫分離

          讀寫分離,顧名思義就是讀和寫分開,更具體來說,就是:

          • 寫操作在主數(shù)據(jù)庫進(jìn)行
          • 讀操作在從數(shù)據(jù)庫進(jìn)行

          使用讀寫分離的根本目的就是為了提高并發(fā)性能,如果讀寫都在同一臺MySQL上實(shí)現(xiàn),相信會不如一臺MySQL寫,另外兩臺MySQL讀這樣的配置性能高。另一方面,在很多時(shí)候都是讀操作的請求要遠(yuǎn)遠(yuǎn)高于寫操作,這樣就顯得讀寫分離非常有必要了。

          3.2 主從復(fù)制

          主從復(fù)制,顧名思義就是把主庫的數(shù)據(jù)復(fù)制到從庫中,因?yàn)樽x寫分離之后,寫操作都在主庫進(jìn)行,但是讀操作是在從庫進(jìn)行的,也就是說,主庫上的數(shù)據(jù)如果不能復(fù)制到從庫中,那么從庫就不會讀到主庫中的數(shù)據(jù)。嚴(yán)格意義上說,讀寫分離并不要求主從復(fù)制,只需要在主庫寫從庫讀即可,但是如果沒有了主從復(fù)制,讀寫分離將失去了它的意義。因此讀寫分離通常與主從復(fù)制配合使用。

          因?yàn)楸臼纠褂玫氖?code style="font-size: 14px;word-wrap: break-word;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #3594F7;background: RGBA(59, 170, 250, .1);padding: 0 2px;border-radius: 2px;height: 21px;line-height: 22px;">MySQL,這里就說一下MySQL主從復(fù)制的原理,如下圖所示:

          工作流程如下:

          • 主庫修改數(shù)據(jù)后,將修改日志寫入binlog
          • 從庫的I/O線程讀取主庫的binlog,并拷貝到從庫本地的binlog
          • 從庫本地的binlogSQL線程讀取,執(zhí)行其中的內(nèi)容并同步到從庫中

          3.3 數(shù)據(jù)庫中間件簡介

          數(shù)據(jù)庫中間件可以簡化對讀寫分離以及分庫分表的操作,并隱藏底層實(shí)現(xiàn)細(xì)節(jié),可以像操作單庫單表那樣操作多庫多表,主流的設(shè)計(jì)方案主要有兩種:

          • 服務(wù)端代理:需要獨(dú)立部署一個代理服務(wù),該代理服務(wù)后面管理多個數(shù)據(jù)庫實(shí)例,在應(yīng)用中通過一個數(shù)據(jù)源與該代理服務(wù)器建立連接,由該代理去操作底層數(shù)據(jù)庫,并返回相應(yīng)結(jié)果。優(yōu)點(diǎn)是支持多語言,對業(yè)務(wù)透明,缺點(diǎn)是實(shí)現(xiàn)復(fù)雜,實(shí)現(xiàn)難度大,同時(shí)代理需要確保自身高可用
          • 客戶端代理:在連接池或數(shù)據(jù)庫驅(qū)動上進(jìn)行一層封裝,內(nèi)部與不同的數(shù)據(jù)庫建立連接,并對SQL進(jìn)行必要的操作,比如讀寫分離選擇走主庫還是從庫,分庫分表select后如何聚合結(jié)果。優(yōu)點(diǎn)是實(shí)現(xiàn)簡單,天然去中心化,缺點(diǎn)是支持語言較少,版本升級困難

          一些常見的數(shù)據(jù)庫中間件如下:

          • Cobar:阿里開源的關(guān)系型數(shù)據(jù)庫分布式服務(wù)中間件,已停更
          • DRDS:脫胎于Cobar,全稱分布式關(guān)系型數(shù)據(jù)庫服務(wù)
          • MyCat:開源數(shù)據(jù)庫中間件,目前更新了MyCat2版本
          • AtlasQihoo 360公司Web平臺部基礎(chǔ)架構(gòu)團(tuán)隊(duì)開發(fā)維護(hù)的一個基于MySQL協(xié)議的數(shù)據(jù)中間層項(xiàng)目,同時(shí)還有一個NoSQL的版本,叫Pika
          • tddl:阿里巴巴自主研發(fā)的分布式數(shù)據(jù)庫服務(wù)
          • Sharding-JDBCShardingShpere的一個子產(chǎn)品,一個輕量級Java框架

          4 MySQL主從復(fù)制環(huán)境準(zhǔn)備

          看完了一些基礎(chǔ)理論就可以進(jìn)行動手了,本小節(jié)先準(zhǔn)備好MySQL主從復(fù)制的環(huán)境,基于Docker+MySQL官方文檔搭建。

          4.1 主庫操作

          4.1.1 拉取鏡像并創(chuàng)建容器運(yùn)行

          docker pull mysql
          docker run -itd -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name master mysql
          docker exec -it master /bin/bash

          在主庫中進(jìn)行更新鏡像源,安裝vim以及net-tools的操作:

          cd /etc/apt
          echo deb http://mirrors.aliyun.com/debian/ buster main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib deb http://mirrors.aliyun.com/debian-security buster/updates main deb-src http://mirrors.aliyun.com/debian-security buster/updates main deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib > sources.list
          apt update && apt upgrade
          apt install vim net-tools

          4.1.2 修改配置文件

          vim /etc/mysql/my.cnf

          添加下面兩行數(shù)據(jù):

          [mysqld]
          server-id=1                # 全局唯一,取值[1,2^32-1],默認(rèn)為1
          binlog-do-db=test          # 表示需要復(fù)制的是哪個庫

          修改完成后重啟。

          4.1.3 準(zhǔn)備數(shù)據(jù)源

          CREATE DATABASE test;
          USE test;
          CREATE TABLE user(
              id BIGINT PRIMARY KEY,
              name VARCHAR(30NOT NULL,
          );

          4.1.4 創(chuàng)建一個復(fù)制操作的用戶(可選但推薦)

          注意創(chuàng)建用戶需要加上mysql_native_password,否則會導(dǎo)致從庫一直處于連接狀態(tài):

          CREATE USER 'repl'@'172.17.0.3' IDENTIFIED WITH mysql_native_password BY '123456';
          GRANT REPLICATION slave ON *.* TO 'repl'@'172.17.0.3';

          具體的地址請根據(jù)從庫的地址修改,可以先看后面的從庫配置部分。

          4.1.5 數(shù)據(jù)備份(可選)

          如果原來的主庫中是有數(shù)據(jù)的,那么這部分?jǐn)?shù)據(jù)需要手動同步到從庫中:

          FLUSH TABLES WITH READ LOCK;

          開啟主庫的另一個終端,使用mysqldump導(dǎo)出:

          mysqldump -u root -p --all-databases --master-data > dbdump.db

          導(dǎo)出完成后,解除讀鎖:

          UNLOCK TABLES;

          4.1.6 查看主庫狀態(tài)

          SHOW MASTER STATUS;

          需要把File以及Position記錄下來,后面從庫的配置需要用到。

          4.2 從庫操作

          4.2.1 拉取鏡像并創(chuàng)建容器運(yùn)行

          docker pull mysql
          docker run -itd -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 --name slave mysql
          docker exec -it slave /bin/bash

          進(jìn)入容器后,像主庫一樣更新源然后安裝vimnet-tools

          cd /etc/apt
          echo deb http://mirrors.aliyun.com/debian/ buster main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib deb http://mirrors.aliyun.com/debian-security buster/updates main deb-src http://mirrors.aliyun.com/debian-security buster/updates main deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib > sources.list
          apt update && apt upgrade
          apt install vim net-tools

          4.2.2 修改配置文件

          vim /etc/mysql/my.cnf

          添加如下兩行:

          [mysqld]
          server-id=2            # 全局唯一,不能與主庫相同
          replicate-do-db=test   # 與主庫相同,表示對該庫進(jìn)行復(fù)制

          修改完成后重啟。

          4.2.3 查看ip地址

          查看從庫的ip地址,用于給主庫設(shè)置同步的用戶:

          ifconfig

          輸出:

          inet 172.17.0.3  netmask 255.255.0.0  broadcast 172.17.255.255

          那么主庫中用于復(fù)制的用戶就可以是[email protected]

          4.2.4 導(dǎo)入數(shù)據(jù)(可選)

          如果主庫有數(shù)據(jù)可以先導(dǎo)入到從庫:

          mysqldump -u root -p --all-databases < dbdump.db

          4.2.5 準(zhǔn)備數(shù)據(jù)源

          CREATE DATABASE test;
          USE test;
          CREATE TABLE user(
              id BIGINT PRIMARY KEY,
              name VARCHAR(30NOT NULL,
          );

          4.2.6 設(shè)置主庫

          可以使用change master to/change replication source to8.0.23+)命令:

          CHANGE REPLICATION SOURCE TO
          source_host='172.17.0.2',                   # 可以使用ifconfig查看主庫ip
          source_user='repl',                         # 之前主庫創(chuàng)建的用戶
          source_password='123456',                   # 密碼
          source_log_file='binlog.000003',            # 之前在主庫上使用show master status查看的日志文件
          source_log_pos=594;                         # 同樣使用show master status查看

          4.2.7 開啟從庫

          START SLAVE;
          SHOW SLAVE STATUS\G

          新版本(8.0.22+)可使用:

          START REPLICA;
          SHOW REPLICA STATUS\G

          需要IOSQL線程顯示Yes才算成功:

          4.3 測試

          主庫選擇插入一條數(shù)據(jù):

          INSERT INTO user VALUES(1,"name",3);

          然后從庫就能select到了:

          5 搭建Spring Boot環(huán)境

          5.1 新建項(xiàng)目并引入依賴

          新建Spring Boot項(xiàng)目,并引入如下依賴:

          implementation 'com.alibaba:druid:1.2.10'
          implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.1'
          implementation 'org.freemarker:freemarker:2.3.31'
          implementation 'com.baomidou:mybatis-plus-generator:3.5.2'
          implementation 'org.apache.shardingsphere:shardingsphere-jdbc-core-spring-boot-starter:5.1.1'

          Maven版本:

          <dependency>
              <groupId>com.baomidou</groupId>
              <artifactId>mybatis-plus-boot-starter</artifactId>
              <version>3.5.1</version>
          </dependency>
          <dependency>
              <groupId>com.baomidou</groupId>
              <artifactId>mybatis-plus-generator</artifactId>
              <version>3.5.2</version>
          </dependency>
          <dependency>
              <groupId>org.freemarker</groupId>
              <artifactId>freemarker</artifactId>
              <version>2.3.31</version>
          </dependency>
          <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>druid</artifactId>
              <version>1.2.10</version>
          </dependency>
          <dependency>
              <groupId>org.apache.shardingsphere</groupId>
              <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
              <version>5.1.1</version>
          </dependency>

          5.2 使用生成器

          import com.baomidou.mybatisplus.generator.FastAutoGenerator;
          import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

          public class Generator {
              public static void main(String[] args) {
                  FastAutoGenerator.create("jdbc:mysql://localhost:3306/test""root""123456")
                  .globalConfig(builder ->
                      builder.author("author").outputDir(System.getProperty("user.dir") + "/src/main/java").build())
                  .packageConfig(builder -> 
                      builder.parent("com.example.demo").moduleName("user").build())
                  .strategyConfig(builder -> 
                      builder.addInclude("user").entityBuilder().enableLombok().disableSerialVersionUID().build())
                  .templateEngine(new FreemarkerTemplateEngine())
                  .execute();
              }
          }

          直接運(yùn)行main方法即可生成代碼,配置請根據(jù)個人需要進(jìn)行更改。

          5.3 配置文件

          spring:
            shardingsphere:
              mode:
                type: Memory                                     # 內(nèi)存模式,元數(shù)據(jù)保存在當(dāng)前進(jìn)程中
              datasource:
                names: master,slave                              # 數(shù)據(jù)源名稱,這里有兩個
                master:                                          # 跟上面的數(shù)據(jù)源對應(yīng)
                  type: com.alibaba.druid.pool.DruidDataSource   # 連接池
                  url: jdbc:mysql://127.0.0.1:3306/test          # 連接url
                  username: root
                  password: 123456
                slave:                                           # 跟上面的數(shù)據(jù)源對應(yīng)
                  type: com.alibaba.druid.pool.DruidDataSource
                  url: jdbc:mysql://127.0.0.1:3306/test
                  username: root
                  password: 123456
              rules:
                readwrite-splitting:                             # 讀寫分離規(guī)則
                  data-sources:                                  # 數(shù)據(jù)源配置
                    random:                                      # 這個名字隨便起
                      type: Static                               # 靜態(tài)類型
                      load-balancer-name: round_robin            # 負(fù)載均衡算法名字
                      props:
                        write-data-source-name: master           # 寫數(shù)據(jù)源
                        read-data-source-names: slave            # 讀數(shù)據(jù)源
                  load-balancers:                                # 負(fù)載均衡配置
                    round_robin:                                 # 跟上面負(fù)載均衡算法的名字對應(yīng)
                      type: ROUND_ROBIN                          # 負(fù)載均衡算法
              props:
                sql-show: true                                   # 打印SQL

          因?yàn)榕渲梦募膬?nèi)容比較多,以下進(jìn)行分開說明。

          5.3.1 模式

          spring.shardingsphere.mode.type,模式有三種:

          • Memory:內(nèi)存模式,初始化配置或執(zhí)行SQL等操作均在當(dāng)前進(jìn)程生效
          • Standalone:單機(jī)模式,可以將數(shù)據(jù)源和規(guī)則等元數(shù)據(jù)信息持久化,但是這些元數(shù)據(jù)不會在集群中同步
          • Cluster:集群模式,提供了多個Apache ShardingSphere實(shí)例之間元數(shù)據(jù)共享以及分布式場景下的狀態(tài)協(xié)調(diào)的能力,也提供水平擴(kuò)展以及高可用的能力

          這里使用內(nèi)存模式,如果想將元數(shù)據(jù)等信息持久化,請使用單機(jī)模式,單機(jī)模式需要配置以下屬性:

          • spring.shardingsphere.mode.type=Standalone:設(shè)置單機(jī)模式
          • spring.shardingsphere.mode.repository.type=:持久化倉庫的類型,單機(jī)模式適用類型為File
          • spring.shardingsphere.mode.repository.props.path=:元數(shù)據(jù)存儲路徑,默認(rèn).shardingsphere
          • spring.shardingsphere.mode.overwrite=:是否覆蓋

          而采用集群模式,需要配置以下屬性:

          • spring.shardingsphere.mode.type=Cluster:設(shè)置集群模式
          • spring.shardingsphere.mode.repository.type=:持久化倉庫類型,集群模式支持ZooKeeper以及Etcd持久化
          • spring.shardingsphere.mode.repository.props.namespace=:注冊中心命名空間
          • spring.shardingsphere.mode.repository.props.server-lists=:注冊中心服務(wù)器列表
          • spring.shardingsphere.mode.overwrite=:是否覆蓋
          • spring.shardingsphere.mode.repository.props.<key>=:注冊中心的屬性配置,對于ZooKeeper,可以配置retryIntervalMilliseconds(重試間隔毫秒)、maxRetries(客戶端連接最大重試數(shù))、timeToLiveSeconds(臨時(shí)數(shù)據(jù)存活秒數(shù))、operationTimeoutMilliseconds(客戶端操作超時(shí)毫秒數(shù))、digest(登錄密碼),對于Etcd,可以配置timeToLiveSeconds(臨時(shí)數(shù)據(jù)存活秒數(shù))、connectionTimeout(連接超時(shí)秒數(shù))

          5.3.2 數(shù)據(jù)源配置

          spring.shardingsphere.datasource.names,后面接數(shù)據(jù)源的名稱,使用,分隔,比如此處有兩個數(shù)據(jù)源:

          • master
          • slave

          然后每個數(shù)據(jù)源可以配置:

          • type:數(shù)據(jù)庫連接池類型,這里使用的是Druid
          • username:用戶名
          • password:密碼
          • jdbc-url:連接url,注意,對于此處使用的Druid連接池,需要使用url而不是jdbc-url

          5.3.3 讀寫分離規(guī)則配置

          spring.shardingsphere.rules.readwrite-splitting,需要配置其中的數(shù)據(jù)源以及負(fù)載均衡類型:

          • spring.shardingsphere.rules.readwrite-splitting.data-sources
          • spring.shardingsphere.rules.readwrite-splitting.load-balancers
          5.3.3.1 數(shù)據(jù)源配置

          數(shù)據(jù)源配置首先需要添加一個數(shù)據(jù)源的名字,隨便起一個,比如這里是random,然后需要配置三個屬性:

          • spring.shardingsphere.rules.readwrite-splitting.data-sources.random.type:讀寫分離的類型,可選值為StaticDynamic,這里選擇Static,如果選擇Dynamic,也就是動態(tài)數(shù)據(jù)源,請配合dynamic-datasource-spring-boot-starter使用
          • spring.shardingsphere.rules.readwrite-splitting.data-sources.random.props.write-data-source-name:寫數(shù)據(jù)源
          • spring.shardingsphere.rules.readwrite-splitting.data-sources.random.props.read-data-source-name:讀數(shù)據(jù)源
          • spring.shardingsphere.rules.readwrite-splitting.data-sources.random.load-balancer-name:負(fù)載均衡算法的名稱,這里寫的是round_robin
          5.3.3.2 負(fù)載均衡配置

          負(fù)載均衡配置需要與上面的spring.shardingsphere.rules.readwrite-splitting.data-sources.random.load-balancer-name屬性對應(yīng),比如這里是round_robin,那么需要配置的就是spring.shardingsphere.rules.readwrite-splitting.load-balancers.round_robin。然后下一步就是配置具體的負(fù)載均衡算法。

          內(nèi)置的負(fù)載均衡算法有三個:

          • 輪詢算法:ROUND_ROBIN,配置type=ROUND_ROBIN即可,也就是spring.shardingsphere.rules.readwrite-splitting.load-balancers.round_robin.type=ROUND_ROBIN
          • 隨機(jī)訪問算法:RANDOM,配置type=RANDOM
          • 權(quán)重訪問算法:WEIGHT,配置type=WEIGHT,同時(shí)需要配置props,在其中配置各個讀節(jié)點(diǎn)的權(quán)重

          5.3.4 屬性配置

          屬性的話這里只配置了一個spring.shardingsphere.props.sql-show=true,也就是打印SQL,其他支持的屬性有:

          • spring.shardingsphere.props.sql-simple:是否打印簡單風(fēng)格的SQL,默認(rèn)為false
          • spring.shardingsphere.props.kernel-exector-size:設(shè)置任務(wù)處理線程池大小,默認(rèn)為infinite
          • spring.shardingsphere.props.max-connections-size-per-query:每次查詢所能使用的最多數(shù)據(jù)庫連接數(shù),默認(rèn)為1
          • spring.shardingsphere.props.check-table-metadata-enabled:啟動時(shí)是否檢查分片元數(shù)據(jù)的一致性,默認(rèn)為false
          • spring.shardingsphere.props.check-duplicate-table-enabled:啟動時(shí)是否檢查重復(fù)表,默認(rèn)為false
          • spring.shardingsphere.props.sql-federation-enabled:是否開啟聯(lián)邦查詢,默認(rèn)為false

          5.4 準(zhǔn)備Controller

          @RestController
          @RequestMapping("/user")
          @RequiredArgsConstructor(onConstructor = @__(@Autowired))
          public class UserController {
              private final UserServiceImpl userService;

              @GetMapping("/select")
              public User select() {
                  return userService.getById(1);
              }

              @GetMapping("/insert")
              public boolean insert() {
                  return userService.saveOrUpdate(User.builder().id(3L).name("name3").build());
              }
          }

          6 測試

          訪問http://localhost:8080/user/insert,可以看到寫操作在主庫進(jìn)行:

          訪問http://localhost:8080/user/select,可以看到讀操作在從庫進(jìn)行:

          這樣讀寫分離就算是完成了。

          瀏覽 70
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品一区二区三区东京热 | 婷婷视频在线观看 | 日本sm视频 | 91精品综合久久久久久五月天 | 日韩网站在线观看 |