shardingsphere之sharding-proxy讀寫分離學習筆記

引言

讀寫分離在業務中應該是比較常見的,當滿足以下幾點就可以考慮將數據進行讀寫分離,減輕數據庫的壓力和提高響應速度。

(1)業務對數據庫是讀多寫少。
(2)單臺服務器或者單個數據庫的性能已經不能滿足當前業務對數據庫大量讀取請求。
(3)數據量不是特別巨大,單表還沒超過五百萬行記錄,還不需要分庫分表。

重要提示

演示環境

  • 阿里雲ECS服務器,CentOS Linux release 7.7.1908 (Core)
  • docker版本:Docker version 19.03.8, build afacb8b
  • 本地系統:windows7

版本信息

  • spring-boot 2.1.6.RELEASE
  • mybatis-plus-generator 3.1.0
  • mybatis-plus-boot-starter 3.1.0
  • druid-spring-boot-starter 1.1.18
  • sharding-proxy 4.0.1
  • mysql 8.0.20

代碼示例和參考

用於演示的代碼和相關的參考鏈接已經放到文章的末尾。sql文件放置在sql文件夾,請求信息放置在postMan文件夾,導入postman執行即可。

讀寫分離簡介

讀寫分離主要分爲兩步,一是設置數據庫實現主從複製,二是在代碼層面實現增刪改操作到主數據庫,讀的操作到從數據庫,提高數據庫的響應能力。

主從複製

太多的理論知識就不在這裏贅述了,可以參考MYSQL主從複製原理,有興趣的可以進行深入研究,選擇適合自己的解決方案。

主從複製的原理和流程

在這裏插入圖片描述

(1)Master主庫將數據變更DataChanges記錄 binlog日誌中。
(2)Slave起一個I/O線程連接到Master,dump讀取Master的binlog日誌並寫入到Slave的中繼日誌Relaylog中
(3)Slave中的SQL線程讀取中繼日誌Relaylog進行SQL回放執行操作,完成主從複製,保證主從最終一致性。

sharding-proxy簡介

概念

太多的理論知識我就不贅述了,麻煩自己到官網去看。
在這裏插入圖片描述
sharding-jdbc 和 sharding-proxy對比

在這裏插入圖片描述

特點

配置好之後可作爲獨立的數據源使用,一個邏輯數據庫代理着幾個真實數據庫,可以用客戶端軟件比如Navicat Premium 直接去連接和操作。很好的幫助我們處理讀寫分離的問題,基本不需要對現有的業務代碼修改,減少時間成本。

使用情況

目前生產環境已使用的公司
在這裏插入圖片描述

基礎環境的搭建

本文是基於阿里雲ECS服務器上的docker搭建數據庫的,使用docker安裝會比傳統的方式效率高很多。
下面會把詳細的安裝步驟和注意的地方一一列出來。這裏演示的是一主三從,一個主庫master,三個從庫slave0,slave1,slave2。

小插曲

對於mysql的安裝,一開始我是選擇在windows7下面用docker安裝的。在安裝docker的時候出現一大堆問題,初始化也很麻煩,搞了一個下午也沒搞完。所以我轉到阿里雲上面安裝。當然也可以在windows下使用解壓的方式安裝mysql,這個就自行谷歌吧。

docker的安裝

(1)卸載舊的docker,可選。

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine

(2)安裝yum工具類

sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

(3)啓用Docker源

sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

(4)安裝Docker

sudo yum -y install docker-ce

(5)配置國內的Docker倉庫,加快Docker鏡像下載速度

vim /etc/docker/daemon.json

{
  "registry-mirrors" : [
    "https://registry.docker-cn.com"
  ]
}

(6)啓動docker

sudo systemctl start docker

(7) 檢查是否安裝成功,如果出現正常的信息則表示安裝成功。

sudo docker info

在這裏插入圖片描述

安裝mysql並配置主從複製

拉取mysql鏡像

(1)查看mysql鏡像版本

可以直接訪問 MySQL鏡像庫地址,直接複製對應的命令即可拉取鏡像。
在這裏插入圖片描述

或者使用命令查看

docker search mysql

在這裏插入圖片描述(2)拉取鏡像

docker pull mysql:8.0.20

(3)查看已拉取的鏡像

docker images

在這裏插入圖片描述

安裝配置master庫

(1)運行mysql容器

docker run -itd --name master_3307 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root mysql:8.0.20

參數介紹

  • –name master_3307 表示容器的名稱爲master_3307
  • -p 3307:3306 表示將宿主機的3307端口映射到docker的3306端口
  • -e MYSQL_ROOT_PASSWORD=root 表示設置用戶root的密碼爲root
  • mysql:8.0.20 表示使用的鏡像版本

小插曲

其實一個鏡像可以複用安裝成多個容器的,一開始我還以爲一個鏡像只能對應一個容器呢。

(2)檢查是否安裝成功

docker ps 

下圖是成功安裝了master,slave0,slave1,slave2結果。
在這裏插入圖片描述簡單備註

docker pull mysql:8.0.20 和docker pull mysql:8.0其實拉取的是同一版本的鏡像,這個看鏡像ID一致即可知道。

(3)修改配置信息

進入容器內部

docker exec -it master_3307 bash

更新基礎軟件和安裝vim

apt-get update
apt-get install vim

編輯配置文件

vi /etc/mysql/my.cnf

新增信息

[mysqld]
## 同一局域網內注意要唯一
server-id=100  
## 開啓二進制日誌功能,可以隨便取(關鍵)
log-bin=mysql-bin
## 永久設置時區時間
default-time_zone = '+8:00'

修改後保存退出編輯

:wq

備註

  • 按照上面順序操作可以避免下方兩個提示。
bash: vi: command not found
Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package vim
  • 配置default-time_zone = ‘+8:00’,不然當datetime類型的默認值爲CURRENT_TIMESTAMP時,插入的時間會比系統時間少了8個小時

(4)創建用戶並授權,用於在主從庫之間同步數據。

進入mysql內部

mysql -uroot -proot

創建同步用戶

CREATE USER 'slave'@'%' IDENTIFIED WITH mysql_native_password BY 'slave';

授予權限

GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';

備註

如果本地要用客戶端比如Navicat Premium連接,最好設置root的認證方式

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';

否則連接可能會報錯

2059 - authentication plugin 'caching_sha2_password' cannot be loaded...

(5)查看關鍵信息

查看兩個關鍵信息,File和Position字段的值後面將會用到,在後面的操作完成之前,需要保證Master庫不能做任何操作,否則將會引起狀態變化,File和Position字段的值變化。

show master status;

在這裏插入圖片描述(6)重啓容器

退出mysql

exit

退出容器

exit

重啓容器

docker restart master_3307

至此,master數據庫安裝配置完成。

slave數據庫安裝配置

(1)查看master的容器ip,後面設置會用到

docker inspect --format='{{.NetworkSettings.IPAddress}}' master_3307

在這裏插入圖片描述(2)運行容器

docker run -itd --name slave0_3308 -p 3308:3306 -e MYSQL_ROOT_PASSWORD=root mysql:8.0.20

(3)進入容器內部

docker exec -it slave0_3308 bash

(4)安裝vim

apt-get update
apt-get install vim

(5)修改配置文件

vi /etc/mysql/my.cnf

(6)新增信息

[mysqld]
## 設置server_id,注意要唯一
server-id=1001  
## 開啓二進制日誌功能,以備Slave作爲其它Slave的Master時使用
log-bin=mysql-slave-bin   
## relay_log配置中繼日誌
relay_log=edu-mysql-relay-bin
## 永久設置時區時間
default-time_zone = '+8:00' 

(7)設置主從信息

進入mysql內部

mysql -uroot -proot

重要提示:

如果本地要用客戶端比如Navicat Premium連接,最好設置root的認證方式

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';

設置主從信息

change master to master_host='172.17.0.2', master_user='slave', master_password='slave', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos= 3174, master_connect_retry=30,get_master_public_key=1;

參數介紹

  • master_host :Master的地址,指的是容器的獨立ip,可以通過下面命令來查看
docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名稱|容器id查詢容器的ip
  • master_port:Master的端口號,指的是容器的端口號
  • master_user:用於數據同步的用戶
  • master_password:用於同步的用戶的密碼
  • master_log_file:指定 Slave 從哪個日誌文件開始複製數據,即上文中提到的 File 字段的值
  • master_log_pos:從哪個 Position 開始讀,即上文中提到的 Position 字段的值
  • master_connect_retry:如果連接失敗,重試的時間間隔,單位是秒,默認是60秒

備註

之所以設置get_master_public_key=1,是因爲開啓主從複製的時候可能會報出下方的異常。原因是mysql8默認使用插件caching_sha2_password,有些client連接報這個錯誤,需要拿到server的public key來加密password。

 ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection. 

(7)查看主從同步狀態

show slave status \G;

正常情況下,SlaveIORunning 和 SlaveSQLRunning 都是No,因爲我們還沒有開啓主從複製過程。
在這裏插入圖片描述

(8)開啓主從複製

start slave;

(9)再次查詢主從同步狀態

show slave status \G;

在這裏插入圖片描述
SlaveIORunning 和 SlaveSQLRunning 都是Yes,說明主從複製已經開啓。

(10)重啓容器

退出mysql

exit

退出容器

exit

重啓容器

docker restart slave0_3308

剩餘的slave1_3309,slave2_3310的安裝配置方法和salve0_3308的方法是一樣的,區別就是cnf文件中的server-id不能相同罷了。

出現異常參考

(1)使用start slave開啓主從複製過程後,如果SlaveIORunning一直是Connecting,則說明主從複製一直處於連接狀態,這種情況一般是下面幾種原因造成的,我們可以根據 Last_IO_Error提示予以排除。

  • 網絡不通
    解決方法:檢查ip,端口
  • 密碼不對
    檢查是否創建用於同步的用戶和用戶密碼是否正確
  • pos不對
    檢查Master的 Position

(2)如果開啓主從複製失敗,需要修改和重新啓動,可參考下方提示

停止主從複製

stop slave;

重置之前的配置信息

reset slave;

修改信息

change master to ......

啓動主從複製

 start slave; 

備註

如果不重置修改後直接啓動可能會報出下方的異常

ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository

開放阿里雲安全組端口

到阿里雲安全組設置那裏開放3307,3308,3309,3310端口,無需開放防火牆firewall端口。

使用客戶端連接

在這裏插入圖片描述

小插曲

第一次安裝好之後開放了安全組端口,可是使用客戶端輸入公網ip和賬號密碼死活連接不上,這個也困擾了我很久。後面過了段時間發現設置的安全組規則不見了,直接重新設置就可以連接了,難以費解。

簡單測試

在master庫創建一個test數據庫,如果從庫也有則代表安裝成功。至此主從複製的工作已經完成,接下來就是代碼層面的讀寫分離了。

開始整合sharding-proxy

(1)其實大部分的工作都是在編寫conf目錄下的yml文件,配置好之後應用還是跟以前的方式一樣連接和操作,基本不需要對代碼進行修改。
(2)邏輯數據源可以配置多個,每個yml文件代表一個邏輯數據源,可手動創建yml文件和編寫規則實現多個邏輯數據源的使用
(3)本文演示的是單個邏輯數據源的配置和使用。

1、下載sharding-proxy二進制壓縮包

壓縮包下載地址
在這裏插入圖片描述

2、解壓zip包

注意事項:

(1)解壓工具最好選擇winrar,不然解壓出來的lib文件夾下的jar包由於命名太長被截斷,後面將會導致文件找不到,系統啓動失敗異常。

(2)解壓後的文件的路徑下最好不要有中文,否則也會導致啓動失敗。

3、下載mysql驅動包

MySQL Connector/J,下載之後放到lib文件夾下。

4、編寫yml文件

(1)首先要編寫的是server.yaml,配置全局信息

##治理中心
# orchestration:
#   name: orchestration_ds
#   overwrite: true
#   registry:
#     type: zookeeper
#     serverLists: localhost:2181
#     namespace: orchestration

#權限配置
authentication:
  users:
    root:               #用戶名
      password: root	#密碼
    sharding:
      password: sharding 
      authorizedSchemas: sharding_db	#只能訪問的邏輯數據庫

#Proxy屬性
props:
  max.connections.size.per.query: 1
  acceptor.size: 16  #用於設置接收客戶端請求的工作線程個數,默認爲CPU核數*2
  executor.size: 16  # Infinite by default.
  proxy.frontend.flush.threshold: 128  # The default value is 128.
    # LOCAL: Proxy will run with LOCAL transaction.
    # XA: Proxy will run with XA transaction.
    # BASE: Proxy will run with B.A.S.E transaction.
  proxy.transaction.type: LOCAL 	#默認爲LOCAL事務
  proxy.opentracing.enabled: false     #是否開啓鏈路追蹤功能,默認爲不開啓。
  query.with.cipher.column: true
  sql.show: true				#SQL打印
  check.table.metadata.enabled: true			#是否在啓動時檢查分表元數據一致性,默認值: false
# proxy.frontend.flush.threshold: 				# 對於單個大查詢,每多少個網絡包返回一次

注意事項:

(1)當你把註釋的示例內容複製粘貼,然後用快捷鍵去掉‘#’註釋符的時候,注意父子層的距離不要改變,比如下方父子層之間是兩個空格。

props:
  max.connections.size.per.query: 1

(2) 在編寫的時候不要用‘Tab’鍵拉開距離,應該使用空格鍵,否則啓動的時候會報錯。

小插曲

當你編寫yml文件的時候,裏面都會有這句話,一開始我以爲還要在哪裏引用這些要用的文件,後面才發現直接去掉註釋編寫就好,文件都是自動引用的。

If you want to configure orchestration, authorization and proxy properties, please refer to this file.

5、編寫config-master_slave.yaml配置文件

schemaName: master_slave_db

dataSources:
  master:
    url: jdbc:mysql://127.0.0.1:3307/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
  slave0:
    url: jdbc:mysql://127.0.0.1:3308/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
  slave1:
    url: jdbc:mysql://127.0.0.1:3309/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
  slave2:
    url: jdbc:mysql://127.0.0.1:3310/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50

masterSlaveRule:
  name: ms_ds
  masterDataSourceName: master
  slaveDataSourceNames:
    - slave0
    - slave1
    - slave2

6、編寫logback.xml日誌記錄文件

日誌路徑默認在啓動方式start.bat下創建個logs文件夾,所以只要填寫相對路徑就好。下面設置的是按級別按天輸出日誌,基本符合常規需求。

<?xml version="1.0"?>

<configuration>
  
    <!-- 控制檯輸出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日誌消息,%n是換行符-->
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %highlight([%-5level] %logger{50} - %msg%n)</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
   
    <!-- 系統正常日誌文件 -->
    <appender name="SYSTEM_INFO"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 過濾器,只打印INFO級別的日誌 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日誌文件輸出的文件名-->
            <fileNamePattern>../logs/%d{yyyy-MM-dd}/info.%i.log</fileNamePattern>
            <!--日誌文件保留天數-->
            <!-- <MaxHistory>15</MaxHistory> -->
                <!-- 除按日誌記錄之外,還配置了日誌文件不能超過2M,若超過2M,日誌文件會以索引0開始,命名日誌文件,例如log-error-2013-12-21.0.log -->
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>10MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
          <!-- 追加方式記錄日誌 -->
            <append>true</append>
      <!-- 日誌文件的格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日誌消息,%n是換行符-->
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%-5level] %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    
     <!-- 系統警告日誌文件,記錄用戶輸入不符合規則的數據 -->
    <appender name="SYSTEM_WARN"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 過濾器,只打印warn級別的日誌 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日誌文件輸出的文件名-->
            <fileNamePattern>../logs/%d{yyyy-MM-dd}/warn.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>10MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
          <!-- 追加方式記錄日誌 -->
            <append>true</append>
      <!-- 日誌文件的格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日誌消息,%n是換行符-->
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%-5level] %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    
    <!-- 系統錯誤日誌文件 -->
    <appender name="SYSTEM_ERROR"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 過濾器,只打印ERROR級別的日誌 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日誌文件輸出的文件名-->
            <fileNamePattern>../logs/%d{yyyy-MM-dd}/error.%i.log</fileNamePattern>
                <!-- 除按日誌記錄之外,還配置了日誌文件不能超過2M,若超過2M,日誌文件會以索引0開始,命名日誌文件,例如log-error-2013-12-21.0.log -->
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <MaxFileSize>10MB</MaxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
          <!-- 追加方式記錄日誌 -->
            <append>true</append>
      <!-- 日誌文件的格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日誌消息,%n是換行符-->
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%-5level] %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    
  
     <logger name="system_error" additivity="false">
        <appender-ref ref="SYSTEM_ERROR"/>
    </logger>
    
     <!-- 默認紀錄級別 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="SYSTEM_INFO" />
        <appender-ref ref="SYSTEM_WARN" />
        <appender-ref ref="SYSTEM_ERROR" /> 
    </root>
  
</configuration> 

7、啓動sharding-proxy

在cmd命令窗口中cd到bin目錄下,然後直接運行start.bat,默認是3307端口;當然你也可以指定端口,在後面加上參數即可,比如start.bat 3309即可指定連接端口爲3309。然後觀察cmd窗口,如果沒報出異常的話表示啓動成功。

在這裏插入圖片描述

8、客戶端連接

這裏推薦使用Navicat Premium 11 去連接,使用Navicat Premium 12的話你會發現很多奇怪的問題,下面是官網的建議和成功連接的圖。至此,proxy服務端搭建好了,接下來是編寫代碼。

在這裏插入圖片描述在這裏插入圖片描述

9、初始化表格

在master_slave_db庫下新建一張area表用來測試,建好之後master,slave0,slave1,slave2的test庫下也會自動創建。

CREATE TABLE `area`  (
  `id` bigint(0) UNSIGNED NOT NULL COMMENT 'id',
  `area_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '地區編號',
  `area_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '地區名稱',
  `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
  `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '地區表(單庫單表)' ROW_FORMAT = Dynamic;

10、pom.xml加入依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.sharding-proxy.read-write</groupId>
	<artifactId>sharding-proxy-read-write</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>sharding-proxy-read-write</name>
	<description>shardingsphere之sharding-proxy讀寫分離</description>

	<properties>
		<java.version>1.8</java.version>
		<mybatis-plus-generator.version>3.1.0</mybatis-plus-generator.version>
        <mybatis-plus-extension.version>3.1.0</mybatis-plus-extension.version>
        <mybatis-plus-boot-starter.version>3.1.0</mybatis-plus-boot-starter.version>
        <mybatis-ehcache.version>1.0.0</mybatis-ehcache.version>
        <org.apache.velocity.version>2.1</org.apache.velocity.version>
        <druid-spring-boot-starter.version>1.1.18</druid-spring-boot-starter.version>
        <hutool-core.version>4.6.4</hutool-core.version>
		<guava.version>r05</guava.version>
	</properties>

	<dependencies>
		<!-- springboot基本依賴 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<!--mybatis-plus依賴 -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-generator</artifactId>
			<version>${mybatis-plus-generator.version}</version>
		</dependency>
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>${mybatis-plus-boot-starter.version}</version>
		</dependency>
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-extension</artifactId>
			<version>${mybatis-plus-extension.version}</version>
		</dependency>

		<!-- 集成echcache -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-ehcache</artifactId>
			<version>${mybatis-ehcache.version}</version>
		</dependency>
		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>
		
		<!-- 代碼生成器模板 -->
		<dependency>
			<groupId>org.apache.velocity</groupId>
			<artifactId>velocity-engine-core</artifactId>
			<version>${org.apache.velocity.version}</version>
			<scope>test</scope>
		</dependency>

		<!-- lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

		<!-- 熱部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional> <!-- 這個需要爲 true 熱部署纔有效 -->
		</dependency>

		 <!-- druid數據庫連接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid-spring-boot-starter.version}</version>
        </dependency>
        
        <!-- mysql驅動 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
            <scope>runtime</scope>
        </dependency>

		<!-- bean工具 -->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-core</artifactId>
			<version>${hutool-core.version}</version>
		</dependency>

		<!-- 使用@ConfigurationProperties註解 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		
		<!-- 谷歌工具 -->
		<dependency>
		    <groupId>com.google.guava</groupId>
		    <artifactId>guava</artifactId>
		    <version>${guava.version}</version>
		</dependency>
		
		
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

11、基礎CRUD代碼生成

通過示例代碼的工具類可以快速生成基礎的CRUD代碼

 src/test/java/com/project/generator/MybatisGenerator.java

12、編寫測試代碼

@RestController
@RequestMapping("/area")
public class AreaController {

    @Autowired
    private IAreaService areaService;

    /**
     * 模擬插入數據
     */
    List<Area> list = Lists.newArrayList();

    @PostConstruct
    private void getData() {
        list.add(new Area("110000", "新疆2"));
        list.add(new Area("110101", "烏魯木齊2"));
        list.add(new Area("110102", "西藏2"));
        list.add(new Area("110106", "天台2"));
    }

    @PostMapping("/save")
    public ResponseData<?> save() {
        areaService.saveBatch(list);
        return ResponseData.out(CodeEnum.SUCCESS, null);
    }

    @GetMapping("/list")
    public ResponseData<?> list() {
        List<Area> aList = areaService.list();
        return ResponseData.out(CodeEnum.SUCCESS, aList);
    }

    @GetMapping("/update")
    public ResponseData<?> update() {
        Area area = new Area("1180000", "天津市");
        area.setId(1253130139206709249l);
        areaService.updateById(area);
        return ResponseData.out(CodeEnum.SUCCESS, null);
    }

    @GetMapping("/delete")
    public ResponseData<?> delete() {
        Long id = 1253130139206709249l;
        areaService.removeById(id);
        return ResponseData.out(CodeEnum.SUCCESS, null);
    }
}

13、運行測試

通過發起請求同時觀察日誌文件你會發現:

(1)當執行插入操作時,打印如下SQL

[2020-05-07 12:10:23.545] [ShardingSphere-Command-5] [INFO ] ShardingSphere-SQL - Rule Type: master-slave
[2020-05-07 12:10:23.545] [ShardingSphere-Command-5] [INFO ] ShardingSphere-SQL - SQL: INSERT INTO area  ( id,
area_code,
area_name )  VALUES  ( 1258247784025628674,
'110000',
'新疆' ) ::: DataSources: master
[2020-05-07 12:10:23.584] [ShardingSphere-Command-6] [INFO ] ShardingSphere-SQL - Rule Type: master-slave
[2020-05-07 12:10:23.588] [ShardingSphere-Command-6] [INFO ] ShardingSphere-SQL - SQL: INSERT INTO area  ( id,
area_code,
area_name )  VALUES  ( 1258247784176623618,
'110101',
'烏魯木齊' ) ::: DataSources: master
[2020-05-07 12:10:23.619] [ShardingSphere-Command-7] [INFO ] ShardingSphere-SQL - Rule Type: master-slave
[2020-05-07 12:10:23.619] [ShardingSphere-Command-7] [INFO ] ShardingSphere-SQL - SQL: INSERT INTO area  ( id,
area_code,
area_name )  VALUES  ( 1258247784180817921,
'110102',
'西藏' ) ::: DataSources: master
[2020-05-07 12:10:23.647] [ShardingSphere-Command-8] [INFO ] ShardingSphere-SQL - Rule Type: master-slave
[2020-05-07 12:10:23.647] [ShardingSphere-Command-8] [INFO ] ShardingSphere-SQL - SQL: INSERT INTO area  ( id,
area_code,
area_name )  VALUES  ( 1258247784189206529,
'110106',
'天台' ) ::: DataSources: master

(2)當執行查詢操作時

[2020-05-08 11:54:18.768] [ShardingSphere-Command-4] [INFO ] ShardingSphere-SQL - Rule Type: master-slave
[2020-05-08 11:54:18.768] [ShardingSphere-Command-4] [INFO ] ShardingSphere-SQL - SQL: SELECT  id,area_code,area_name,create_time,update_time  FROM area ::: DataSources: slave1

出現異常參考

在整合的過程中可能由於框架之間的版本導致啓動或者生成代碼時報錯,可參考下方提示,至於報錯原因大家有空可以自行研究下。

報錯1

報錯信息:

CLIENT_PLUGIN_AUTH is required

或者

java.sql.SQLException: Unknown system variable 'query_cache_size'
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:965)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3978)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3914)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2530)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2683)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2491)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2449)
	at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1381)
	at com.mysql.jdbc.ConnectionImpl.loadServerVariables(ConnectionImpl.java:3797)
	at com.mysql.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:3230)
	at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2243)
	at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2025)
	at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:778)
	at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:47)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)

解決方法:修改mysql的驅動版本,sirngboot2.1.6 對應的是mysql8.0.16版本,重寫爲5.1.6即可。

報錯2

報錯信息:

Caused by: java.sql.SQLFeatureNotSupportedException: null
	at com.alibaba.druid.proxy.jdbc.ResultSetProxyImpl.getObject(ResultSetProxyImpl.java:1556)
	at com.alibaba.druid.pool.DruidPooledResultSet.getObject(DruidPooledResultSet.java:1771)
	at org.apache.ibatis.type.LocalDateTimeTypeHandler.getNullableResult(LocalDateTimeTypeHandler.java:38)
	at org.apache.ibatis.type.LocalDateTimeTypeHandler.getNullableResult(LocalDateTimeTypeHandler.java:28)
	at org.apache.ibatis.type.BaseTypeHandler.getResult(BaseTypeHandler.java:81)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyAutomaticMappings(DefaultResultSetHandler.java:521)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:402)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap(DefaultResultSetHandler.java:354)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:328)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:301)

解決方法:修改mybatis-plus的版本爲3.1.0即可。

結語

官網的文檔比較詳細和社區都是很活躍的,這些可以減少我們的學習成本,快速用於項目。如果在學習的過程中遇到問題可以多看看官方文檔或者直接到github上面提issues,官方人員會很快給予答覆的。

演示代碼地址
Docker簡介
CentOS7 安裝Docker
CentOS系統Docker安裝
Docker 安裝 MySQL
基於Docker的Mysql主從複製搭建
MYSQL主從複製原理
連接 MySQL 報錯:2059
啓動slave時報錯Slave failed to initialize relay log info structure from the repository
shardingsphere官網地址
Spring boot CLIENT_PLUGIN_AUTH is required
java.sql.SQLException: Unknown system variable ‘query_cache_size’

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章