作者:楊濤濤
資深資料庫專家,專研 MySQL 十餘年。擅長 MySQL、PostgreSQL、MongoDB 等開源資料庫相關的備份恢復、SQL 調優、監控運維、高可用架構設計等。目前任職於愛可生,為各大運營商及銀行金融企業提供 MySQL 相關技術支援、MySQL 相關課程培訓等工作。
本文來源:原創投稿
*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。
MySQL InnoDB Cluster(MIC) 想必大家已經非常熟悉,由三個元件組成,分別為:MySQL Shell、MySQL Router 、MySQL MGR 。
MySQL Shell 用來進行MGR的日常運維,MySQL Router 對上層應用提供一個簡單的讀寫分離入口,MySQL MGR 則是用來存放真實資料的多個 MySQL 例項。對應的架構如下:
那如果想針對 MIC 做一個容災功能,該怎麼做?如果你一直使用 MySQL 8.0,並且保持 MySQL 版本一直為最新,那答案是肯定的(最新的 MySQL 8.0 小版本為 8.0.28),新名字為 MySQL InnoDB Cluster Set(MICS)。
這個新特性其實就是基於純粹的 MIC 做容災。 比如北京上地一套 MIC 對外提供服務,東直門另外一套 MIC 用來做災備,兩套 MIC 通過專用複製通道clusterset_replication來同步資料。 擷取官網的架構如下:
MICS 雖然看起來挺好,但是限制很多,幾個主要限制如下:
MICS 的最大特性是高可用,而不是一致性。由於資料傳輸依賴傳統的 MySQL 非同步複製(不能使用半同步),無法避免非同步複製的缺陷:資料延遲、資料一致性、需要手動故障轉移等等。
從庫不能是已有的 MIC ,必須新建。所以在搭建 MICS 前,得想辦法解決已有資料的儲存問題。
MICS 內部 MIC 限制為單主模式,不能多主。
MICS 內部只有一套 MIC 對外提供服務,其他只能作為備庫。
只支援 MySQL 8.0 。
我來簡單演示下搭建過程:(為了簡單起見,沒有包含 MySQL ROUTER 元件)
準備至少6臺 MySQL 例項。(埠分別為3381、3382、3383、3384、3385、3386)
MySQL Py > for i in range(3381,3387):
-> dba.deploy_sandbox_instance(i,{"password":"root"})
->
A new MySQL sandbox instance will be created on this host in
/root/mysql-sandboxes/3381
...
埠3381、3382、3383 搭建一套 MGR ,名字為 ytt-rc1 。
MySQL Py > \c root:root@localhost:3381
Creating a session to 'root@localhost:3381'
...
Server version: 8.0.28 MySQL Community Server - GPL
...
MySQL localhost:3381 ssl Py > rc1 = dba.create_cluster('ytt-rc1');
A new InnoDB cluster will be created on instance 'localhost:3381'.
...
MySQL localhost:3381 ssl Py > rc1.add_instance("root:root@localhost:3382",{"recoveryMethod":"clone"})
...
MySQL localhost:3381 ssl Py > rc1.add_instance("root:root@localhost:3383",{"recoveryMethod":"clone"})
...
建立一套 MICS ,命名為:ytt-rc-set ,主庫為 ytt-rc1 。
MySQL localhost:3381 ssl Py > rc1_set = rc1.create_cluster_set('ytt-rc-set')
A new ClusterSet will be created based on the Cluster 'ytt-rc1'.
* Validating Cluster 'ytt-rc1' for ClusterSet compliance.
* Creating InnoDB ClusterSet 'ytt-rc-set' on 'ytt-rc1'...
* Updating metadata...
ClusterSet successfully created. Use ClusterSet.create_replica_cluster() to add Replica Clusters to it.
檢視 MICS 狀態: 名字為 ytt-rc-set 、叢集角色為 primary 、對應主例項為 127.0.0.1:3381 、狀態為 healthy 。
MySQL localhost:3381 ssl Py > rc1_set.status()
{
"clusters": {
"ytt-rc1": {
"clusterRole": "PRIMARY",
"globalStatus": "OK",
"primary": "127.0.0.1:3381"
}
},
"domainName": "ytt-rc-set",
"globalPrimaryInstance": "127.0.0.1:3381",
"primaryCluster": "ytt-rc1",
"status": "HEALTHY",
"statusText": "All Clusters available."
接下來為 ytt-rc-set 新增從庫,從庫必須不屬於任何 MIC 。
建立 MICS 管理使用者,用來同步主庫資料:
MySQL localhost:3381 ssl Py > dba.configure_instance("root:root@localhost:3384",{"clusterAdmin":"ics_admin","clusterAdminPassword":"root"})
Configuring local MySQL instance listening at port 3384 for use in an InnoDB cluster...
...
Cluster admin user 'ics_admin'@'%' created.
The instance '127.0.0.1:3384' is already ready to be used in an InnoDB cluster.
Successfully enabled parallel appliers.
新增從庫:從庫 MIC 的名為為 ytt-rc2 。
MySQL localhost:3381 ssl Py > rc2=rc1_set.create_replica_cluster("root:root@localhost:3384","ytt-rc2",{"recoveryMethod":"clone","recoveryProgress":2})
Setting up replica 'ytt-rc2' of cluster 'ytt-rc1' at instance '127.0.0.1:3384'.
...
Replica Cluster 'ytt-rc2' successfully created on ClusterSet 'ytt-rc-set'.
給 ytt-rc2 新增其他例項:
MySQL localhost:3381 ssl Py > rc2.add_instance("root:root@localhost:3385",{"recoveryMethod":"clone"})
MySQL localhost:3381 ssl Py > rc2.add_instance("root:root@localhost:3386",{"recoveryMethod":"clone"})
看下 ytt-rc-set 的最新狀態:ytt-rc1 為主,ytt-rc2 為備。
MySQL localhost:3381 ssl Py > rc1_set.describe()
{
"clusters": {
"ytt-rc1": {
"clusterRole": "PRIMARY",
"topology": [
{
"address": "127.0.0.1:3381",
"label": "127.0.0.1:3381"
},
{
"address": "127.0.0.1:3382",
"label": "127.0.0.1:3382"
},
{
"address": "127.0.0.1:3383",
"label": "127.0.0.1:3383"
}
]
},
"ytt-rc2": {
"clusterRole": "REPLICA",
"topology": [
{
"address": "127.0.0.1:3384",
"label": "127.0.0.1:3384"
},
{
"address": "127.0.0.1:3385",
"label": "127.0.0.1:3385"
},
{
"address": "127.0.0.1:3386",
"label": "127.0.0.1:3386"
}
]
}
},
"domainName": "ytt-rc-set",
"primaryCluster": "ytt-rc1"
}
寫條資料簡單測試下資料同步:主庫表t1插一條記錄。
MySQL localhost:3381 ssl SQL > \sql
MySQL localhost:3381 ssl SQL > create database ytt;
Query OK, 1 row affected (0.0167 sec)
MySQL localhost:3381 ssl SQL > use ytt;
Default schema set to `ytt`.
Fetching table and column names from `ytt` for auto-completion... Press ^C to stop.
MySQL localhost:3381 ssl ytt SQL > create table t1(id int primary key,r1 int);
Query OK, 0 rows affected (0.1561 sec)
MySQL localhost:3381 ssl ytt SQL > insert t1 values (1,100);
Query OK, 1 row affected (0.0185 sec)
MySQL localhost:3381 ssl ytt SQL > table t1;
+----+-----+
| id | r1 |
+----+-----+
| 1 | 100 |
+----+-----+
1 row in set (0.0004 sec)
切換到從庫,看下效果:表t1資料已經正常同步,並且只讀。
MySQL localhost:3381 ssl ytt SQL > \c root@localhost:3384
Creating a session to 'root@localhost:3384'
...
MySQL localhost:3384 ssl SQL > use ytt
...
MySQL localhost:3384 ssl ytt SQL > table t1;
+----+-----+
| id | r1 |
+----+-----+
| 1 | 100 |
+----+-----+
1 row in set (0.0006 sec)
MySQL localhost:3384 ssl ytt SQL > create table t2 like t1;
ERROR: 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement
手動進行主備切換:ytt-rc2 已經被提升了新的主。
MySQL localhost:3384 ssl ytt Py > rc1_set.set_primary_cluster('ytt-rc2')
Switching the primary cluster of the clusterset to 'ytt-rc2'
...
Cluster 'ytt-rc2' was promoted to PRIMARY of the clusterset. The PRIMARY instance is '127.0.0.1:3384'
看下 ytt-rc-set 最新狀態:ytt-rc1 變為備,新主為 ytt-rc2 。
MySQL localhost:3384 ssl ytt Py > rc1_set.status()
{
"clusters": {
"ytt-rc1": {
"clusterRole": "REPLICA",
"clusterSetReplicationStatus": "OK",
"globalStatus": "OK"
},
"ytt-rc2": {
"clusterRole": "PRIMARY",
"globalStatus": "OK",
"primary": "127.0.0.1:3384"
}
},
"domainName": "ytt-rc-set",
"globalPrimaryInstance": "127.0.0.1:3384",
"primaryCluster": "ytt-rc2",
"status": "HEALTHY",
"statusText": "All Clusters available."