1. 擴容方案剖析
1.1 擴容問題
在項目初期,我們部署了三個數(shù)據(jù)庫 A、B、C,此時數(shù)據(jù)庫的規(guī)??梢詽M足我們的業(yè)務需求。為了將數(shù)據(jù)做到平均分配,我們在 Service 服務層使用 uid%3 進行取模分片,從而將數(shù)據(jù)平均分配到三個數(shù)據(jù)庫中。
如圖所示:
后期隨著用戶量的增加,用戶產(chǎn)生的數(shù)據(jù)信息被源源不斷的添加到數(shù)據(jù)庫中,最終達到數(shù)據(jù)庫的最佳存儲容量。如果此時繼續(xù)向數(shù)據(jù)庫中新增數(shù)據(jù),會導致數(shù)據(jù)庫的 CRUD 等基本操作變慢,進而影響整個服務的響應速度。
這時,我們需要增加新的節(jié)點,對數(shù)據(jù)庫進行水平擴容,那么加入新的數(shù)據(jù)庫 D 后,數(shù)據(jù)庫的規(guī)模由原來的 3 個變?yōu)?4 個。
如圖所示:
此時由于分片規(guī)則發(fā)生了變化(uid%3 變?yōu)?uid%4),導致大部分的數(shù)據(jù),無法命中原有的數(shù)據(jù),需要重新進行分配,要做大量的數(shù)據(jù)遷移處理。
比如之前 uid 如果是 uid=3 取模 3%3=0, 是分配在 A 庫上,新加入 D 庫后, uid=3 取模 3%4=3,分配在 D 庫上;
新增一個節(jié)點, 大概會有 90% 的數(shù)據(jù)需要遷移, 這樣會面臨大量的數(shù)據(jù)壓力,并且對服務造成極大的不穩(wěn)定性。
1.2 停機方案
發(fā)布公告
為了進行數(shù)據(jù)的重新拆分,在停止服務之前,我們需要提前通知用戶,比如:我們的服務會在 yyyy-MM-dd 進行升級,給您帶來的不便敬請諒解。
停止服務
關閉 Service
離線數(shù)據(jù)遷移(拆分,重新分配數(shù)據(jù))
將舊庫中的數(shù)據(jù)按照 Service 層的算法,將數(shù)據(jù)拆分,重新分配數(shù)據(jù)
數(shù)據(jù)校驗
開發(fā)定制一個程序對舊庫和新庫中的數(shù)據(jù)進行校驗,比對
更改配置
修改 Service 層的配置算法,也就是將原來的 uid%3 變?yōu)?uid%4
恢復服務
重啟 Service 服務
回滾預案
針對上述的每個步驟都要有數(shù)據(jù)回滾預案,一旦某個環(huán)節(jié)(如:數(shù)據(jù)遷移,恢復服務等)執(zhí)行失敗,立刻進行回滾,重新再來
停止服務之后, 能夠保證遷移工作的正常進行, 但是服務停止,傷害用戶體驗, 并造成了時間壓力, 必須在指定的時間內完成遷移。
1.3 停寫方案
支持讀寫分離
數(shù)據(jù)庫支持讀寫分離,在擴容之前,每個數(shù)據(jù)庫都提供了讀寫功能,數(shù)據(jù)重新分配的過程中,將每個數(shù)據(jù)庫設置為只讀狀態(tài),關閉寫的功能
升級公告
為了進行數(shù)據(jù)的重新拆分,在停寫之前,我們需要提前通知用戶,比如:我們的服務會在 yyyy-MM-dd 進行升級,給您帶來的不便敬請諒解。
中斷寫操作,隔離寫數(shù)據(jù)源(或攔截返回統(tǒng)一提示)
在 Service 層對所有的寫請求進行攔截,統(tǒng)一返回提示信息,如:服務正在升級中,只對外提供讀服務
數(shù)據(jù)同步處理
將舊庫中的數(shù)據(jù)按照 Service 層的算法,將數(shù)據(jù)重新分配,遷移(復制數(shù)據(jù))
數(shù)據(jù)校驗
開發(fā)定制一個程序對舊庫中的數(shù)據(jù)進行備份,使用備份的數(shù)據(jù)和重新分配后的數(shù)據(jù)進行校驗,比對
更改配置
通過配置中心,修改 Service 層的配置算法,也就是將原來的 uid%3 變?yōu)?uid%4,這個過程不需要重啟服務
恢復寫操作
設置數(shù)據(jù)庫恢復讀寫功能,去除 Service 層的攔截提示
數(shù)據(jù)清理
使用 delete 語句對冗余數(shù)據(jù)進行刪除
回滾預案
針對上述的每個步驟都要有數(shù)據(jù)回滾預案,一旦某個環(huán)節(jié)(如:數(shù)據(jù)遷移等)執(zhí)行失敗,立刻進行回滾,重新再來
缺點:在數(shù)據(jù)的復制過程需要消耗大量的時間,停寫時間太長,數(shù)據(jù)需要先復制,再清理冗余數(shù)據(jù)
1.4 日志方案
核心是通過日志進行數(shù)據(jù)庫的同步遷移, 主要操作步驟如下:
數(shù)據(jù)遷移之前, 業(yè)務應用訪問舊的數(shù)據(jù)庫節(jié)點。
日志記錄
在升級之前, 記錄 “對舊數(shù)據(jù)庫上的數(shù)據(jù)修改” 的日志(這里修改包括增、刪、改),這個日志不需要記錄詳細的數(shù)據(jù)信息,主要記錄:
(1)修改的庫;
(2)修改的表;
(3)修改的唯一主鍵;
(4)修改操作類型。
日志記錄不用關注新增了哪些信息,修改的數(shù)據(jù)格式,只需要記錄以上數(shù)據(jù)信息,這樣日志格式是固定的, 這樣能保證方案的通用性。
服務升級日志記錄功能風險較?。?/p>
寫和修改接口是少數(shù), 改動點少;
升級只是增加了一些日志,采用異步方式實現(xiàn), 對業(yè)務功能沒有太多影響。
數(shù)據(jù)遷移:
研發(fā)定制數(shù)據(jù)遷移工具, 作用是把舊庫中的數(shù)據(jù)遷移至新庫中。
整個過程仍然采用舊庫進行對外服務。
數(shù)據(jù)同步工具實現(xiàn)復雜度不高。
只對舊庫進行讀取操作, 如果同步出現(xiàn)問題, 都可以對新庫進行回滾操作。
可以限速或分批遷移執(zhí)行, 不會有時間壓力。
數(shù)據(jù)遷移完成之后, 并不能切換至新庫提供服務。
因為舊庫依然對線上提供服務, 庫中的數(shù)據(jù)隨時會發(fā)生變化, 但這些變化的數(shù)據(jù)并沒有同步到新庫中, 舊庫和新庫數(shù)據(jù)不一致, 所以不能直接進行切換, 需要將數(shù)據(jù)同步完整。
日志增量遷移
研發(fā)一個日志遷移工具,把上面遷移數(shù)據(jù)過程中的差異數(shù)據(jù)追平,處理步驟:
讀取 log 日志,獲取具體是哪個庫、表和主鍵發(fā)生了變化修改;
把舊庫中的主鍵記錄讀取出來
根據(jù)主鍵 ID,把新庫中的記錄替換掉
這樣可以最大程度的保障數(shù)據(jù)的一致性。風險分析:
整個過程, 仍然是舊庫對線上提供服務;
日志遷移工具實現(xiàn)的復雜度較低;
任何時間發(fā)現(xiàn)問題, 可以重新再來,有充分的容錯空間;
可以限速重放處理日志, 處理過程不會因為對線上影響造成時間壓力。
但是, 日志增量同步完成之后, 還不能切換到新的數(shù)據(jù)庫。
因為日志增量同步過程中,舊庫中可能有數(shù)據(jù)發(fā)生變化, 導致數(shù)據(jù)不一致,所以需要進一步讀取日志, 追平數(shù)據(jù)記錄;日志增量同步過程隨時可能會產(chǎn)生新的數(shù)據(jù), 新庫與舊庫的數(shù)據(jù)追平也會是一個無限逼近的過程。
數(shù)據(jù)校驗
準備好數(shù)據(jù)校驗工具,將舊庫和新庫中的數(shù)據(jù)進行比對,直到數(shù)據(jù)完全一致。
切換新庫
數(shù)據(jù)比對完成之后, 將流量轉移切換至新庫, 至此新庫提供服務, 完成遷移。
但是在極限情況下, 即便通過上面的數(shù)據(jù)校驗處理, 也有可能出現(xiàn) 99.99% 數(shù)據(jù)一致, 不能保障完全一致,這個時候可以在舊庫做一個 readonly 只讀功能, 或者將流量屏蔽降級,等待日志增量同步工具完全追平后, 再進行新庫的切換。
至此,完成日志方案的遷移擴容處理, 整個過程能夠持續(xù)對線上提供服務, 只會短暫的影響服務的可用性。
這種方案的弊端,是操作繁瑣,需要適配多個同步處理工具,成本較高, 需要制定個性化業(yè)務的同步處理, 不具備普遍性,耗費的時間周期也較長。
1.5 雙寫方案(中小型數(shù)據(jù))
雙寫方案可通過 canal 或 mq 做實現(xiàn)。
增加新庫,按照現(xiàn)有節(jié)點, 增加對應的數(shù)量。
數(shù)據(jù)遷移:避免增量影響, 先斷開主從,再導入(耗時較長), 同步完成并做校驗
增量同步:開啟 Canal 同步服務, 監(jiān)聽從節(jié)點數(shù)據(jù)庫, 再開啟主從同步,從節(jié)點收到數(shù)據(jù)后會通過 Canal 服務, 傳遞至新的 DB 節(jié)點。
切換新庫:通過 Nginx,切換訪問流量至新的服務。
修復切換異常數(shù)據(jù):在切換過程中, 如果出現(xiàn),Canal 未同步,但已切換至新庫的請求(比如下單,修改了資金, 但還未同步 ), 可以通過定制程序, 讀取檢測異常日志,做自動修復或人工處理。
針對此種情況, 最好是在凌晨用戶量小的時候, 或專門停止外網(wǎng)訪問,進行切換,減少異常數(shù)據(jù)的產(chǎn)生。
數(shù)據(jù)校驗:為保障數(shù)據(jù)的完全一致, 有必要對數(shù)據(jù)的數(shù)量完整性做校驗。
1.6 平滑 2N 方案(大數(shù)據(jù)量)
線上數(shù)據(jù)庫,為了保障其高可用,一般每臺主庫會配置一臺從庫,主庫負責讀寫,從庫負責讀取。下圖所示,A,B 是主庫,A0 和 B0 是從庫。
當需要擴容的時候,我們把 A0 和 B0 升級為新的主庫節(jié)點,如此由 2 個分庫變?yōu)?4 個分庫。同時在上層的分片配置,做好映射,規(guī)則如下:
把 uid%4=0 和 uid%4=2 的數(shù)據(jù)分別分配到 A 和 A0 主庫中
把 uid%4=1 和 uid%4=3 的數(shù)據(jù)分配到 B 和 B0 主庫中
因為 A 和 A0 庫的數(shù)據(jù)相同,B 和 B0 數(shù)據(jù)相同,此時無需做數(shù)據(jù)遷移。只需調整變更一下分片配置即可,通過配置中心更新,不需要重啟。
由于之前 uid%2 的數(shù)據(jù)是分配在 2 個庫里面,擴容之后需要分布到 4 個庫中,但由于舊數(shù)據(jù)仍存在(uid%4=0 的節(jié)點,還有一半 uid%4=2 的數(shù)據(jù)),所以需要對冗余數(shù)據(jù)做一次清理。
這個清理,并不會影響線上數(shù)據(jù)的一致性,可以隨時隨地進行。
處理完成之后,為保證數(shù)據(jù)的高可用,以及將來下一步的擴容需求。
可以為現(xiàn)有的主庫再次分配一個從庫。
2. 平滑 2N 擴容方案實踐
2.1 實現(xiàn)應用服務級別的動態(tài)擴容
擴容前部署架構:
2.1.1 MariaDB 服務安裝
切換阿里云鏡像服務(YUM 安裝過慢可以切換)
yum -y install wget ## 備份CentOS-Base.repo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-7.repo yum clean all yum makecache
配置 YUM 源
vi /etc/yum.repos.d/mariadb-10.2.repo
增加以下內容:
[mariadb] name = MariaDB baseurl = https://mirrors.ustc.edu.cn/mariadb/yum/10.2/centos7-amd64 gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB gpgcheck=1
執(zhí)行安裝
yum -y install mariadb mariadb-server MariaDB-client MariaDB-common
如果之前已經(jīng)安裝, 需要先刪除(如果之前沒有安裝, 可以忽略此步驟)
停止 Mariadb 服務
[root@localhost yum.repos.d]# ps -ef | grep mysql root 1954 1 0 Oct04 ? 0043 /usr/sbin/mysqld --wsrep-new-cluster --user=root root 89521 81403 0 07:40 pts/0 0000 grep --color=auto mysql [root@localhost yum.repos.d]# kill 1954
卸載 Mariadb 服務
yum -y remove Maria*
刪除數(shù)據(jù)與配置:
rm -rf /var/lib/mysql/* rm -rf /etc/my.cnf.d/ rm -rf /etc/my.cnf
啟動 MariaDB 后,執(zhí)行安全配置向導命令,可根據(jù)安全配置向導提高數(shù)據(jù)庫的安全性
systemctl start mariadb mysql_secure_installation
開啟用戶遠程連接權限
將連接用戶 root 開啟遠程連接權限;
mysql -uroot -p654321
進入 MySQL 服務, 執(zhí)行以下操作:
use mysql; delete from user; ## 配置root用戶使用密碼654321從任何主機都可以連接到mysql服務器 GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '654321' WITH GRANT OPTION; FLUSH PRIVILEGES;
2.1.2 MariaDB 雙主同步
在 Server1 增加配置:
在 /etc/my.cnf 中添加以下配置:
[mysqld] server-id = 1 log-bin=mysql-bin relay-log = mysql-relay-bin ## 忽略mysql、information_schema庫下對表的操作 replicate-wild-ignore-table=mysql.% replicate-wild-ignore-table=information_schema.% ## 默認的情況下mysql是關閉的; log-slave-updates=on ## 復制過程中,有任何錯誤,直接跳過 slave-skip-errors=all auto-increment-offset=1 auto-increment-increment=2 ## binlog的格式:STATEMENT,ROW,MIXED binlog_format=mixed ## 自動過期清理binlog,默認0天,即不自動清理 expire_logs_days=10
注意, Server1 自增為奇數(shù)位:
auto-increment-offset=1 主鍵自增基數(shù),從 1 開始。
auto-increment-increment=2 主鍵自增偏移量,每次為 2。
在 Server2 增加配置:
修改 /etc/my.cnf:
[mysqld] server-id = 2 log-bin=mysql-bin relay-log = mysql-relay-bin replicate-wild-ignore-table=mysql.% replicate-wild-ignore-table=information_schema.% log-slave-updates=on slave-skip-errors=all auto-increment-offset=2 auto-increment-increment=2 binlog_format=mixed expire_logs_days=10
Server2 自增為偶數(shù)位:
auto-increment-offset=2 主鍵自增基數(shù),從 2 開始。
auto-increment-increment=2 主鍵自增偏移量,每次為 2。
配置修改完成后, 重啟數(shù)據(jù)庫。
同步授權配置
在 Server1 創(chuàng)建 replica 用于主從同步的用戶:
MariaDB [(none)]> grant replication slave, replication client on *.* to 'replica'@'%' identified by 'replica'; mysql> flush privileges;
查詢日志文件與偏移量,開啟同步時需使用:
MariaDB [(none)]> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000001 | 663 | | | +------------------+----------+--------------+------------------+
同樣, 在 Server2 創(chuàng)建 replica 用于主從同步的用戶:
MariaDB [(none)]> grant replication slave, replication client on *.* to 'replica'@'%' identified by 'replica'; mysql> flush privileges;
查詢日志文件與偏移量:
MariaDB [(none)]> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000001 | 663 | | | +------------------+----------+--------------+------------------+
配置主從同步信息
在 Server1 中執(zhí)行:
MariaDB [(none)]> change master to master_host='192.168.116.141',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000007', master_log_pos=374, master_connect_retry=30;
在 Server2 中執(zhí)行:
MariaDB [(none)]> change master to master_host='192.168.116.140',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000015', master_log_pos=374, master_connect_retry=30;
開啟雙主同步
在 Server1 和 Server2 中分別執(zhí)行:
MariaDB [(none)]> start slave; Query OK, 0 rows affected (0.00 sec)
在 Server1 查詢同步信息:
MariaDB [(none)]> show slave statusG; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.10.20.126 Master_User: replica Master_Port: 3306 Connect_Retry: 30 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 663 Relay_Log_File: mysql-relay-bin.000002 Relay_Log_Pos: 555 Relay_Master_Log_File: mysql-bin.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes ...
在 Server2 查詢同步信息:
MariaDB [(none)]> show slave statusG; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.10.20.125 Master_User: replica Master_Port: 3306 Connect_Retry: 30 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 663 Relay_Log_File: mysql-relay-bin.000002 Relay_Log_Pos: 555 Relay_Master_Log_File: mysql-bin.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes ...
Slave_IO_Running 和 Slave_SQL_Running 都是 Yes,說明雙主同步配置成功。
2.1.3 KeepAlived 安裝與高可用配置
在 Server1 與 Server2 兩臺節(jié)點安裝 keepalived:
yum -y install keepalived
關閉防火墻
systemctl stop firewalld systemctl disable firewalld
設置主機名稱:
Server1 節(jié)點:
hostnamectl set-hostname vip1
Server2 節(jié)點:
hostnamectl set-hostname vip2
Server1 節(jié)點配置
/etc/keepalived/keepalived.conf:
global_defs { router_id vip1 # 機器標識,和主機名保持一致,運行keepalived服務器的一個標識 } vrrp_instance VI_1 { #vrrp實例定義 state BACKUP #lvs的狀態(tài)模式,MASTER代表主, BACKUP代表備份節(jié)點 interface ens33 #綁定對外訪問的網(wǎng)卡,vrrp實例綁定的網(wǎng)卡 virtual_router_id 111 #虛擬路由標示,同一個vrrp實例采用唯一標示 priority 100 #優(yōu)先級,100代表最大優(yōu)先級, 數(shù)字越大優(yōu)先級越高 advert_int 1 #master與backup節(jié)點同步檢查的時間間隔,單位是秒 authentication { #設置驗證信息 auth_type PASS #有PASS和AH兩種 auth_pass 6666 #驗證密碼,BACKUP密碼須相同 } virtual_ipaddress { #KeepAlived虛擬的IP地址 192.168.116.150 } } virtual_server 192.168.116.150 3306 { #配置虛擬服務器IP與訪問端口 delay_loop 6 #健康檢查時間 lb_algo rr #負載均衡調度算法, rr代表輪詢 lb_kind DR #負載均衡轉發(fā)規(guī)則 DR/NAT/ persistence_timeout 0 #會話保持時間,這里要做測試, 所以設為0, 實際可根據(jù)session有效時間配置 protocol TCP #轉發(fā)協(xié)議類型,支持TCP和UDP real_server 192.168.116.140 3306 { #配置服務器節(jié)點VIP1 notify_down /usr/local/shell/mariadb.sh #當服務掛掉時, 會執(zhí)行此腳本,結束keepalived進程 weight 1 #設置權重,越大權重越高 TCP_CHECK { #狀態(tài)監(jiān)測設置 connect_timeout 10 #超時配置, 單位秒 retry 3 #重試次數(shù) delay_before_retry 3 #重試間隔 connect_port 3306 #連接端口, 和上面保持一致 } } }
創(chuàng)建關閉腳本 mariadb.sh
/usr/local/shell/mariadb.sh:
pkill keepalived
加入執(zhí)行權限:
chmod a+x mariadb.sh
Server2 節(jié)點配置:
global_defs { router_id vip2 # 機器標識,和主機名保持一致,運行keepalived服務器的一個標識 } vrrp_instance VI_1 { #vrrp實例定義 state BACKUP #lvs的狀態(tài)模式,MASTER代表主, BACKUP代表備份節(jié)點 interface ens33 #綁定對外訪問的網(wǎng)卡 virtual_router_id 111 #虛擬路由標示,同一個vrrp實例采用唯一標示 priority 98 #優(yōu)先級,100代表最大優(yōu)先級, 數(shù)字越大優(yōu)先級越高 advert_int 1 #master與backup節(jié)點同步檢查的時間間隔,單位是秒 authentication { #設置驗證信息 auth_type PASS #有PASS和AH兩種 auth_pass 6666 #驗證密碼,BACKUP密碼須相同 } virtual_ipaddress { #KeepAlived虛擬的IP地址 192.168.116.150 } } virtual_server 192.168.116.150 3306 { #配置虛擬服務器IP與訪問端口 delay_loop 6 #健康檢查時間 lb_algo rr #負載均衡調度算法, rr代表輪詢, 可以關閉 lb_kind DR #負載均衡轉發(fā)規(guī)則, 可以關閉 persistence_timeout 0 #會話保持時間,這里要做測試, 所以設為0, 實際可根據(jù)session有效時間配置 protocol TCP #轉發(fā)協(xié)議類型,支持TCP和UDP real_server 192.168.116.141 3306{ #配置服務器節(jié)點VIP2 notify_down /usr/local/shell/mariadb.sh #當服務掛掉時, 會執(zhí)行此腳本,結束keepalived進程 weight 1 #設置權重,越大權重越高 TCP_CHECK { #r狀態(tài)監(jiān)測設置 connect_timeout 10 #超時配置, 單位秒 retry 3 #重試次數(shù) delay_before_retry 3 #重試間隔 connect_port 3306 #連接端口, 和上面保持一致 } } }
和 Server1 的差異項:
router_id vip2 # 機器標識,和主機名保持一致 priority 98 #優(yōu)先級,100代表最大優(yōu)先級, 數(shù)字越大優(yōu)先級越高 real_server 10.10.20.126 3306 #配置服務器節(jié)點VIP2
注意, 兩臺節(jié)點都設為 BACKUP
virtual_router_id 111 #同一個vrrp實例采用唯一標示 state BACKUP
如果不想重啟后, 爭奪備用節(jié)點的 VIP, 可以設置此項
nopreempt #不主動搶占資源
注意:這個配置只能設置在 backup 主機上,而且這個主機優(yōu)先級要比另外一臺高
驗證高可用
停止主節(jié)點 MariaDB 服務, 驗證是否自動切換。
2.1.4 搭建應用服務工程
ShardingJDBC 的介紹
是 ShardingSphere 下的一個產(chǎn)品
定位為輕量級 Java 框架,在 Java 的 JDBC 層提供的額外服務。它使用客戶端直連數(shù)據(jù)庫,以 jar 包形式提供服務,無需額外部署和依賴,可理解為增強版的 JDBC 驅動,完全兼容 JDBC 和各種 ORM 框架。
適用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。
支持任何第三方的數(shù)據(jù)庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
支持任意實現(xiàn) JDBC 規(guī)范的數(shù)據(jù)庫,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 標準的數(shù)據(jù)庫
ShardingJDBC 初始化流程
1)配置 ShardingRuleConfiguration 對象
2)配置表分片規(guī)則 TableRuleConfiguration 對象,設置分庫、分表策略
3)通過 Factory 對象將 Rule 對象與 DataSource 對象裝配
4)ShardingJDBC 使用 DataSource 對象進行分庫
ShardingJDBC 集成配置
1)maven 依賴
2)規(guī)則配置 application.yml
3)創(chuàng)建 DataSource
驗證應用服務動態(tài)擴容
// 動態(tài)數(shù)據(jù)源配置實現(xiàn)擴容 Properties properties = loadPropertiesFile("datasource1.properties"); try { log.info("load datasource config url: " + properties.get("url")); DruidDataSource druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); druidDataSource.setRemoveAbandoned(true); druidDataSource.setRemoveAbandonedTimeout(600); druidDataSource.setLogAbandoned(true); // 設置數(shù)據(jù)源錯誤重連時間 druidDataSource.setTimeBetweenConnectErrorMillis(60000); druidDataSource.init(); OrchestrationShardingDataSource dataSource = SpringContextUtil.getBean("tradeSystemDataSource", OrchestrationShardingDataSource.class); MapdataSourceMap = dataSource.getDataSource().getDataSourceMap(); dataSourceMap.put(DatasourceEnum.DATASOURCE_2.getValue(), druidDataSource); Map dataSourceConfigMap = new HashMap (); for(String key : dataSourceMap.keySet()) { dataSourceConfigMap.put(key, DataSourceConfiguration.getDataSourceConfiguration(dataSourceMap.get(key))); } String result = SHARDING_RULE_TABLE_ORDER.replace(SHARDING_RULE_DATASOURCE, newRule); replaceActualDataNodes(result); SHARDING_RULE_DATASOURCE = newRule; dataSource.renew(new DataSourceChangedEvent( "/" + DruidSystemDataSourceConfiguration.DYNAMIC_SHARDING + "/config/schema/logic_db/datasource", dataSourceConfigMap)); return; } catch (Exception e) { log.error(e.getMessage(), e); }
配置兩個數(shù)據(jù)源,分別指向 Server1 和 Server2
分片只配置一個數(shù)據(jù)源
動態(tài)增加另一個數(shù)據(jù)源
注意事項
Sharding JDBC, Mycat, Drds 等產(chǎn)品都是分布式數(shù)據(jù)庫中間件,相比直接的數(shù)據(jù)源操作,會存在一些限制,Sharding JDBC 在使用時,要注意以下問題:
有限支持子查詢
不支持 HAVING
不支持 OR,UNION 和 UNION ALL
不支持特殊 INSERT
每條 INSERT 語句只能插入一條數(shù)據(jù),不支持 VALUES 后有多行數(shù)據(jù)的語句
不支持 DISTINCT 聚合
不支持 dual 虛擬表查詢
不支持 SELECT LAST_INSERT_ID (), 不支持自增序列
不支持 CASE WHEN
2.2 實現(xiàn)數(shù)據(jù)庫的秒級平滑 2N 擴容
** 擴容部署架構:**
2.2.1 新增數(shù)據(jù)庫 VIP
在 Server2 節(jié)點, 增加 VIP
修改 /etc/keepalived/keepalived.conf
global_defs { router_id vip2 } vrrp_instance VI_1 { #vrrp實例定義 state BACKUP #lvs的狀態(tài)模式,MASTER代表主, BACKUP代表備份節(jié)點 interface ens33 #綁定對外訪問的網(wǎng)卡 virtual_router_id 112 #虛擬路由標示,同一個vrrp實例采用唯一標示 priority 100 #優(yōu)先級,100代表最大優(yōu)先級, 數(shù)字越大優(yōu)先級越高 advert_int 1 #master與backup節(jié)點同步檢查的時間間隔,單位是秒 authentication { #設置驗證信息 auth_type PASS #有PASS和AH兩種 auth_pass 6666 #驗證密碼,BACKUP密碼須相同 } virtual_ipaddress { #KeepAlived虛擬的IP地址 192.168.116.151 } } virtual_server 192.168.116.151 3306 { #配置虛擬服務器IP與訪問端口 delay_loop 6 #健康檢查時間 persistence_timeout 0 #會話保持時間,這里要做測試, 所以設為0, 實際可根據(jù)session有效時間配置 protocol TCP #轉發(fā)協(xié)議類型,支持TCP和UDP real_server 192.168.116.141 3306{ #配置服務器節(jié)點VIP1 notify_down /usr/local/shell/mariadb.sh weight 1 #設置權重,越大權重越高 TCP_CHECK { #r狀態(tài)監(jiān)測設置 connect_timeout 10 #超時配置, 單位秒 retry 3 #重試次數(shù) delay_before_retry 3 #重試間隔 connect_port 3306 #連接端口, 和上面保持一致 } } }
注意配置項:
virtual_router_id 112 #虛擬路由標示,同一個vrrp實例采用唯一標示 priority 100 #優(yōu)先級,100代表最大優(yōu)先級, 數(shù)字越大優(yōu)先級越高
2.2.2 應用服務增加動態(tài)數(shù)據(jù)源
修改應用服務配置, 增加新的數(shù)據(jù)源, 指向新設置的 VIP:192.168.116.151
通過應用服務接口, 動態(tài)擴容調整
2.2.3 解除原雙主同步
mysql -uroot -p654321
進入 Server1:
MariaDB [(none)]> stop slave;
進入 Server2:
MariaDB [(none)]> stop slave;
通過應用服務接口驗證數(shù)據(jù)是否解除同步
2.2.4 安裝 MariaDB 擴容服務器
新建兩臺虛擬機, 分別為 Server3 和 Server4。
在 Server3 和 Server4 兩臺節(jié)點上安裝 MariaDB 服務
參考 2.1.1 MariaDB 服務安裝
配置 Server3 與 Server1,實現(xiàn)新的雙主同步
[mysqld] server-id = 3 log-bin=mysql-bin relay-log = mysql-relay-bin replicate-wild-ignore-table=mysql.% replicate-wild-ignore-table=information_schema.% log-slave-updates=on slave-skip-errors=all auto-increment-offset=2 auto-increment-increment=2 binlog_format=mixed expire_logs_days=10
service mariadb restart
MariaDB [(none)]> grant replication slave, replication client on *.* to 'replica'@'%' identified by 'replica'; mysql> flush privileges;
mysqldump -uroot -p654321 --routines --single_transaction --master-data=2 --databases smooth > server1.sql
... -- -- Position to start replication or point-in-time recovery from -- -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=17748; ...
scp server1.sql root@192.168.116.142:/usr/local/
mysql -uroot -p654321 < /usr/local/server1.sql
根據(jù)上面的 master status 信息, 在 Server3 中執(zhí)行:
MariaDB [(none)]> change master to master_host='192.168.116.140',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000016', master_log_pos=1754, master_connect_retry=30; Query OK, 0 rows affected (0.01 sec)
MariaDB [(none)]> start slave; Query OK, 0 rows affected (0.00 sec)
如果出現(xiàn)問題, 復原主從同步信息:
MariaDB [(none)]> reset slave; Query OK, 0 rows affected (0.01 sec)
MariaDB [(none)]> show slave status G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.10.20.125 Master_User: replica Master_Port: 3306 Connect_Retry: 30 Master_Log_File: mysql-bin.000004 Read_Master_Log_Pos: 11174 Relay_Log_File: mysql-relay-bin.000002 Relay_Log_Pos: 1746 Relay_Master_Log_File: mysql-bin.000004 Slave_IO_Running: Yes Slave_SQL_Running: Yes
查看 Server3 的日志信息:
MariaDB [(none)]> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000001 | 4781 | | | +------------------+----------+--------------+------------------+
在 Server1 節(jié)點, 配置同步信息:
MariaDB [(none)]> reset slave; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]> change master to master_host='192.168.116.142',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000005', master_log_pos=6931, master_connect_retry=30; MariaDB [(none)]> start slave; Query OK, 0 rows affected (0.00 sec)
配置 Server1 與 Server3 節(jié)點的同步
檢查同步狀態(tài)信息:
開啟主從同步:
配置主從同步信息
將數(shù)據(jù)還原至 Server3 節(jié)點:
將備份的 server1.sql 通過 scp 命令拷貝至 Server3 節(jié)點。
查看并記錄 master status 信息:
在 Server1 節(jié)點,進行數(shù)據(jù)全量備份:
創(chuàng)建 replica 用于主從同步的用戶:
重啟 Server3 數(shù)據(jù)庫
Server3 節(jié)點, 修改 /etc/my.cnf:
配置 Server4 與 Server2 的雙主同步
[mysqld] server-id = 4 log-bin=mysql-bin relay-log = mysql-relay-bin replicate-wild-ignore-table=mysql.% replicate-wild-ignore-table=information_schema.% log-slave-updates=on slave-skip-errors=all auto-increment-offset=2 auto-increment-increment=2 binlog_format=mixed expire_logs_days=10
service mariadb restart
MariaDB [(none)]> grant replication slave, replication client on *.* to 'replica'@'%' identified by 'replica'; mysql> flush privileges;
mysqldump -uroot -p654321 --routines --single_transaction --master-data=2 --databases smooth > server2.sql
... -- -- Position to start replication or point-in-time recovery from -- -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=4208; ...
scp server2.sql root@192.168.116.143:/usr/local/
mysql -uroot -p654321 < /usr/local/server2.sql
根據(jù)上面的 master status 信息, 在 Server4 中執(zhí)行:
MariaDB [(none)]> change master to master_host='192.168.116.141',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000007', master_log_pos=3006, master_connect_retry=30; Query OK, 0 rows affected (0.01 sec)
MariaDB [(none)]> start slave; Query OK, 0 rows affected (0.00 sec)
注意, 如果出現(xiàn)問題, 復原主從同步信息:
MariaDB [(none)]> reset slave; Query OK, 0 rows affected (0.01 sec)
MariaDB [(none)]> show slave status G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.10.20.125 Master_User: replica Master_Port: 3306 Connect_Retry: 30 Master_Log_File: mysql-bin.000004 Read_Master_Log_Pos: 11174 Relay_Log_File: mysql-relay-bin.000002 Relay_Log_Pos: 1746 Relay_Master_Log_File: mysql-bin.000004 Slave_IO_Running: Yes Slave_SQL_Running: Yes
查看 Server4 的日志信息:
MariaDB [(none)]> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000001 | 3696 | | | +------------------+----------+--------------+------------------+
在 Server2 節(jié)點, 配置同步信息:
MariaDB [(none)]> reset slave; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]> change master to master_host='192.168.116.143',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000005', master_log_pos=5787, master_connect_retry=30; MariaDB [(none)]> start slave; Query OK, 0 rows affected (0.00 sec)
配置 Server2 與 Server4 節(jié)點的同步
檢查同步狀態(tài)信息:
開啟主從同步:
配置主從同步信息
將數(shù)據(jù)還原至 Server4 節(jié)點:
將備份的 server2.sql 通過 scp 命令拷貝至 Server4 節(jié)點。
查看并記錄 master status 信息:
在 Server2 節(jié)點,進行數(shù)據(jù)全量備份:
創(chuàng)建 replica 用于主從同步的用戶:
重啟 Server4 數(shù)據(jù)庫
Server4 節(jié)點, 修改 /etc/my.cnf:
2.2.5 增加 KeepAlived 服務實現(xiàn)高可用
確保新增的 Server3 和 Server4 節(jié)點安裝 Keepalived 服務。
修改 Server3 節(jié)點配置
global_defs { router_id vip3 # 機器標識,一般設為hostname,故障發(fā)生時,郵件通知會使用到。 } vrrp_instance VI_1 { #vrrp實例定義 state BACKUP #lvs的狀態(tài)模式,MASTER代表主, BACKUP代表備份節(jié)點 interface ens33 #綁定對外訪問的網(wǎng)卡 virtual_router_id 111 #虛擬路由標示,同一個vrrp實例采用唯一標示 priority 98 #優(yōu)先級,100代表最大優(yōu)先級, 數(shù)字越大優(yōu)先級越高 advert_int 1 #master與backup節(jié)點同步檢查的時間間隔,單位是秒 authentication { #設置驗證信息 auth_type PASS #有PASS和AH兩種 auth_pass 6666 #驗證密碼,BACKUP密碼須相同 } virtual_ipaddress { #KeepAlived虛擬的IP地址 192.168.116.150 } } virtual_server 192.168.116.150 3306 { #配置虛擬服務器IP與訪問端口 delay_loop 6 #健康檢查時間 persistence_timeout 0 #會話保持時間,這里要做測試, 所以設為0, 實際可根據(jù)session有效時間配置 protocol TCP #轉發(fā)協(xié)議類型,支持TCP和UDP real_server 192.168.116.142 3306{ #配置服務器節(jié)點VIP3 notify_down /usr/local/shell/mariadb.sh weight 1 #設置權重,越大權重越高 TCP_CHECK { #r狀態(tài)監(jiān)測設置 connect_timeout 10 #超時配置, 單位秒 retry 3 #重試次數(shù) delay_before_retry 3 #重試間隔 connect_port 3306 #連接端口, 和上面保持一致 } } }
注意里面 IP 配置正確, 修改完成后重啟服務。
創(chuàng)建關閉腳本 mariadb.sh
/usr/local/shell/mariadb.sh:
pkill keepalived
加入執(zhí)行權限:
chmod a+x mariadb.sh
修改 Server4 節(jié)點配置
global_defs { router_id vip4 # 機器標識,一般設為hostname,故障發(fā)生時,郵件通知會使用到。 } vrrp_instance VI_1 { #vrrp實例定義 state BACKUP #lvs的狀態(tài)模式,MASTER代表主, BACKUP代表備份節(jié)點 interface ens33 #綁定對外訪問的網(wǎng)卡 virtual_router_id 112 #虛擬路由標示,同一個vrrp實例采用唯一標示 priority 98 #優(yōu)先級,100代表最大優(yōu)先級, 數(shù)字越大優(yōu)先級越高 advert_int 1 #master與backup節(jié)點同步檢查的時間間隔,單位是秒 authentication { #設置驗證信息 auth_type PASS #有PASS和AH兩種 auth_pass 6666 #驗證密碼,BACKUP密碼須相同 } virtual_ipaddress { #KeepAlived虛擬的IP地址 192.168.116.151 } } virtual_server 192.168.116.151 3306 { #配置虛擬服務器IP與訪問端口 delay_loop 6 #健康檢查時間 persistence_timeout 0 #會話保持時間,這里要做測試, 所以設為0, 實際可根據(jù)session有效時間配置 protocol TCP #轉發(fā)協(xié)議類型,支持TCP和UDP real_server 192.168.116.143 3306{ #配置服務器節(jié)點VIP4 notify_down /usr/local/shell/mariadb.sh weight 1 #設置權重,越大權重越高 TCP_CHECK { #r狀態(tài)監(jiān)測設置 connect_timeout 10 #超時配置, 單位秒 retry 3 #重試次數(shù) delay_before_retry 3 #重試間隔 connect_port 3306 #連接端口, 和上面保持一致 } } }
創(chuàng)建關閉腳本 mariadb.sh
/usr/local/shell/mariadb.sh:
pkill keepalived
給所有的用戶組加入執(zhí)行權限:
chmod a+x mariadb.sh
修改完后重啟 Keepalived 服務。
2.2.6 清理數(shù)據(jù)并驗證
通過應用服務動態(tài)擴容接口做調整和驗證
在 Server1 節(jié)點清理數(shù)據(jù)
根據(jù)取模規(guī)則, 保留 accountNo 為偶數(shù)的數(shù)據(jù)
delete from t_trade_order where accountNo % 2 != 0
在 Server2 節(jié)點清理數(shù)據(jù)
根據(jù)取模規(guī)則, 保留 accountNo 為奇數(shù)的數(shù)據(jù)
delete from t_trade_order where accountNo % 2 != 1
3.keepalived 高可用配置大全
在 Server1(192.168.116.140)中執(zhí)行:
MariaDB [(none)]> change master to master_host='192.168.116.141',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000005', master_log_pos=3207, master_connect_retry=30;
在 Server2(192.168.116.141)中執(zhí)行:
MariaDB [(none)]> change master to master_host='192.168.116.140',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000012', master_log_pos=1951, master_connect_retry=30;
在 Server3(192.168.116.142)中執(zhí)行:
MariaDB [(none)]> change master to master_host='192.168.116.140',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000013', master_log_pos=2781, master_connect_retry=30; Query OK, 0 rows affected (0.01 sec)
在 Server4(192.168.116.143)中執(zhí)行:
MariaDB [(none)]> change master to master_host='192.168.116.141',master_user='replica', master_password='replica', master_port=3306, master_log_file='mysql-bin.000005', master_log_pos=7358, master_connect_retry=30; Query OK, 0 rows affected (0.01 sec)
Server1 和 Server2 雙主關系
Server1: keepalived.conf
vi /etc/keepalived/keepalived.conf
global_defs { router_id vip1 } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 111 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 6666 } virtual_ipaddress { 192.168.116.150 } } virtual_server 192.168.116.150 3306 { delay_loop 6 lb_algo rr lb_kind DR // NAT|DR|TUN persistence_timeout 0 protocol TCP real_server 192.168.116.140 3306 { notify_down /usr/local/shell/mariadb.sh weight 1 TCP_CHECK { connect_timeout 10 retry 3 delay_before_retry 3 connect_port 3306 } } }
Server2:keepalived.conf
vi /etc/keepalived/keepalived.conf
global_defs { router_id vip2 } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 111 priority 98 advert_int 1 authentication { auth_type PASS auth_pass 6666 } virtual_ipaddress { 192.168.116.150 } } virtual_server 192.168.116.150 3306 { delay_loop 6 lb_algo rr lb_kind DR persistence_timeout 0 protocol TCP real_server 192.168.116.141 3306{ notify_down /usr/local/shell/mariadb.sh weight 1 TCP_CHECK { connect_timeout 10 retry 3 delay_before_retry 3 connect_port 3306 } } }
新增數(shù)據(jù)庫 VIP
Server2:keepalived.conf
vi /etc/keepalived/keepalived.conf
global_defs { router_id vip2 } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 112 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 6666 } virtual_ipaddress { 192.168.116.151 } } virtual_server 192.168.116.151 3306 { delay_loop 6 persistence_timeout 0 protocol TCP real_server 192.168.116.141 3306{ notify_down /usr/local/shell/mariadb.sh weight 1 TCP_CHECK { connect_timeout 10 retry 3 delay_before_retry 3 connect_port 3306 } } }
Server1 和 Server3 雙主關系
Server3: keepalived.conf
vi /etc/keepalived/keepalived.conf
global_defs { router_id vip3 } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 111 priority 98 advert_int 1 authentication { auth_type PASS auth_pass 6666 } virtual_ipaddress { 192.168.116.150 } } virtual_server 192.168.116.150 3306 { delay_loop 6 lb_algo rr lb_kind DR persistence_timeout 0 protocol TCP real_server 192.168.116.142 3306 { notify_down /usr/local/shell/mariadb.sh weight 1 TCP_CHECK { connect_timeout 10 retry 3 delay_before_retry 3 connect_port 3306 } } }
Server2 和 Server4 雙主關系
Server4: keepalived.conf
vi /etc/keepalived/keepalived.conf
global_defs { router_id vip4 } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 112 priority 98 advert_int 1 authentication { auth_type PASS auth_pass 6666 } virtual_ipaddress { 192.168.116.151 } } virtual_server 192.168.116.151 3306 { delay_loop 6 lb_algo rr lb_kind DR persistence_timeout 0 protocol TCP real_server 192.168.116.143 3306{ notify_down /usr/local/shell/mariadb.sh weight 1 TCP_CHECK { connect_timeout 10 retry 3 delay_before_retry 3 connect_port 3306 } } }
審核編輯:劉清
-
TCP
+關注
關注
8文章
1402瀏覽量
81042 -
UDP
+關注
關注
0文章
330瀏覽量
34649 -
虛擬機
+關注
關注
1文章
966瀏覽量
29358 -
MYSQL數(shù)據(jù)庫
關注
0文章
96瀏覽量
9880
原文標題:6種MySQL數(shù)據(jù)庫平滑擴容方案剖析
文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
labview有調用mysql數(shù)據(jù)庫問題????
MySQL數(shù)據(jù)庫如何安裝和使用說明
華為云數(shù)據(jù)庫-RDS for MySQL數(shù)據(jù)庫
【數(shù)據(jù)庫數(shù)據(jù)恢復】MySQL數(shù)據(jù)庫Delete誤刪除的數(shù)據(jù)恢復案例

有哪些不同的MySQL數(shù)據(jù)庫引擎?
MySQL數(shù)據(jù)庫管理與應用
mysql是一個什么類型的數(shù)據(jù)庫
數(shù)據(jù)庫mysql基本增刪改查
MySQL數(shù)據(jù)庫基礎知識
mysql數(shù)據(jù)庫基礎命令
數(shù)據(jù)庫數(shù)據(jù)恢復—未開啟binlog的Mysql數(shù)據(jù)庫數(shù)據(jù)恢復案例

數(shù)據(jù)庫數(shù)據(jù)恢復—Mysql數(shù)據(jù)庫表記錄丟失的數(shù)據(jù)恢復流程

MySQL數(shù)據(jù)庫的安裝

評論