MySQL Shell:02 部署InnoDB ReplicaSet

如下文章來源於萬能修實驗室,做者公先生

2020年1月除了來源信息如迷之故事會通常的冠狀病毒外,MySQL 官方發佈了全新的8.0.19版本,而其中最大的亮點莫過於ReplicaSet功能。mysql

InnoDBReplicaSet由一個主數據庫和多個輔助數據庫組成 , 可使用ReplicaSet對象和AdminAPI操做,在發生故障時手動故障轉移到新的主數據庫。sql

官方的MySQL Router一樣也支持ReplicaSet,可自動配置、使用InnoDB ReplicaSet,無需手動配置文件。shell

InnoDB ReplicaSet 先決條件:數據庫

  • 僅支持MySQL 8.0及更高版本
  • 僅支持基於GTID的複製
  • 僅支持基於行的複製(RBR),不支持基於語句的複製(SBR)
  • 不支持複製過濾,好比只複製某個庫、表。
  • 一個副本集最多包含一個 primary 主實例,支持一個或多個 secondaries 輔助實例。能夠添加的輔助實例數量無限制,但因爲會佔用路由資源,也不要搞得太多。
  • ReplicaSet必須由MySQL Shell管理, 如:複製賬戶的建立和管理。直接用SQL語句沒法操做副本集

一般InnoDB ReplicaSet自己不能提供高可用性。與InnoDB Cluster相比,InnoDB ReplicaSet並不十分完善,使用起來也有很多限制,所以仍是建議儘量部署InnoDB Cluster。服務器

InnoDB ReplicaSet的限制包括:網絡

  • 沒有自動故障轉移。若是主服務器不可用,則須要使用AdminAPI手動觸發故障轉移,而後才能再次進行任何更改。
  • 故障以前還沒有應用的事務可能會丟失。雖然輔助實例仍然可用於讀取,可是沒法防止因意外中止或不可用而致使部分數據丟失。
  • 沒法防止崩潰或不可用後出現不一致狀況。若是故障轉移在輔助節點仍可用的狀況下提高了輔助節點(例如,因爲網絡分區),則可能會因腦裂而引發不一致。session

    • *

1. 配置ReplicaSetapp

# 在/etc/hosts 中加入主機名less

192.168.0.77    bj77async

192.168.0.78    bj78

192.168.0.79    bj79

# 在3個庫中修改參數文件,確保server_id不一樣,gtid開啓:

# 測試庫1:

report_host = bj77

report_port = 3306

enforce_gtid_consistency = 1

gtid_mode = 1

server_id = 777

# 測試庫2:

report_host = bj78

report_port = 3306

enforce_gtid_consistency = 1

gtid_mode = 1

server_id = 888

# 測試庫3:

report_host = bj79

report_port = 3306

enforce_gtid_consistency = 1

gtid_mode = 1

server_id = 999

# 在節點1 (192.168.0.77  bj77)啓動MySQL Shell:

mysqlsh -uroot -P3306 -p"*****"

# 配置ReplicaSet實例,根據提示輸入root密碼

MySQL 8.0.19 localhost:33060+ ssl  JS > dba.configureReplicaSetInstance('root@localhost:3306', {clusterAdmin: "'rsadmin'@'%'", clusterAdminPassword: 'TestRepl@123'});Please provide the password for 'root@localhost:3306': *********Save password for 'root@localhost:3306'? [Y]es/[N]o/Ne[v]er (default No): Configuring local MySQL instance listening at port 3306 for use in an InnoDB ReplicaSet...This instance reports its own address as bj77:3306Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.The instance 'bj77:3306' is valid to be used in an InnoDB ReplicaSet.Cluster admin user 'rsadmin'@'%' created.The instance 'bj77:3306' is already ready to be used in an InnoDB ReplicaSet.

# 建立ReplicaSet 

MySQL 8.0.19 localhost:33060+ ssl  JS > var rs = dba.createReplicaSet("TestReplicaset")A new replicaset with instance 'bj77:3306' will be created.* Checking MySQL instance at bj77:3306This instance reports its own address as bj77:3306bj77:3306: Instance configuration is suitable.* Updating metadata...ReplicaSet object successfully created for bj77:3306.Use rs.addInstance() to add more asynchronously replicated instances to this replicaset and rs.status() to check its status.

# 檢查狀態:

# 添加第二個節點:bj78 192.168.0.78 (仍然在節點1操做)

# 這裏用剛纔創建的用戶配置失敗,這是由於rsadmin並無在節點2建立。

# 所以偷個懶不指定用戶,由系統自動生成

# 從庫數據恢復方式:選擇 C 克隆方式

# 再次查看集羣狀態,能夠看到節點2已經添加,角色SECONDARY

# 一樣方式添加節點3:bj79  192.168.0.79 (仍然在節點1操做)

# 查看集羣狀態,3個節點都有了,1個PRIMARY,2個SECONDARY

MySQL 8.0.19 localhost:3306 ssl  JS > rs.status() {    "replicaSet": {        "name": "TestReplicaset",         "primary": "bj77:3306",         "status": "AVAILABLE",         "statusText": "All instances available.",         "topology": {            "bj77:3306": {                "address": "bj77:3306",                 "instanceRole": "PRIMARY",                 "mode": "R/W",                 "status": "ONLINE"            },             "bj78:3306": {                "address": "bj78:3306",                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "receiverStatus": "ON",                     "receiverThreadState": "Waiting for master to send event",                     "replicationLag": null                },                 "status": "ONLINE"            },             "bj79:3306": {                "address": "bj79:3306",                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "receiverStatus": "ON",                     "receiverThreadState": "Waiting for master to send event",                     "replicationLag": null                },                 "status": "ONLINE"            }        },         "type": "ASYNC"    }}

# 鏈接到2個SECONDARY節點,看到複製狀態正常。

# 而自動創建複製帳號的格式:mysql_innodb_rs_ 加 本機的server_id

[root@localhost][(none)]> show slave status\G*************************** 1. row ***************************               Slave_IO_State: Waiting for master to send event                  Master_Host: bj77                  Master_User: mysql_innodb_rs_888  <---節點2的server_id=888                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: binlog.000007          Read_Master_Log_Pos: 28380               Relay_Log_File: relay.000002                Relay_Log_Pos: 3637        Relay_Master_Log_File: binlog.000007             Slave_IO_Running: Yes            Slave_SQL_Running: Yes[root@localhost][(none)]> show slave status\G*************************** 1. row ***************************               Slave_IO_State: Waiting for master to send event                  Master_Host: bj77                  Master_User: mysql_innodb_rs_999 <---節點3的server_id=999                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: binlog.000007          Read_Master_Log_Pos: 32768               Relay_Log_File: relay.000002                Relay_Log_Pos: 3579        Relay_Master_Log_File: binlog.000007             Slave_IO_Running: Yes            Slave_SQL_Running: Yes

# 在user表裏也可看到這3個用戶,和對應的權限


2管理ReplicaSet

經常使用的命令:      

  • ReplicaSet.addInstance()     添加實例 
  • ReplicaSet.removeInstance()  移除實例
  • ReplicaSet.getName()         檢查ReplicaSet名稱 
  • ReplicaSet.status()          檢查ReplicaSet的狀態信息
  • ReplicaSet.rejoinInstance()  從新加入集羣
  • \help ReplicaSet或 ReplicaSet.help()  獲取幫助命令
MySQL 8.0.19 localhost:3306 ssl  mysql  JS > \help ReplicaSetNAME      ReplicaSet - Represents an InnoDB ReplicaSet.DESCRIPTION      The ReplicaSet object is used to manage MySQL server topologies that use      asynchronous replication. It can be created using the      dba.createReplicaSet() or dba.getReplicaSet() functions.PROPERTIES      name            Returns the name of the replicaset.FUNCTIONS      addInstance(instance[, options])            Adds an instance to the replicaset.      disconnect()            Disconnects all internal sessions used by the replicaset object.      forcePrimaryInstance(instance, options)            Performs a failover in a replicaset with an unavailable PRIMARY.      getName()            Returns the name of the replicaset.      help([member])            Provides help about this class and it s members      listRouters([options])            Lists the Router instances.      rejoinInstance(instance[, options])            Rejoins an instance to the replicaset.      removeInstance(instance[, options])            Removes an Instance from the replicaset.      removeRouterMetadata(routerDef)            Removes metadata for a router instance.      setPrimaryInstance(instance, options)            Performs a safe PRIMARY switchover, promoting the given instance.      status([options])            Describe the status of the replicaset.      For more help on a specific function, use the \help shell command, e.g.:      \help ReplicaSet.addInstance

3主從切換測試

# 目前的集羣角色:

192.168.0.77   bj77  --主

192.168.0.78   bj78  --從

192.168.0.79   bj79  --從

# 使用setPrimaryInstance能夠手動切換:

# 在此示例中,將把 bj78 更改成PRIMARY並將 bj77 更改成 SECONDARY成員。

 MySQL 8.0.19 localhost:3306 ssl  mysql  JS > var rs = dba.getReplicaSet()  You are connected to a member of replicaset 'TestReplicaset'. MySQL 8.0.19 localhost:3306 ssl  mysql  JS > rs.setPrimaryInstance('bj78:3306')bj78:3306 will be promoted to PRIMARY of 'TestReplicaset'.The current PRIMARY is bj77:3306.* Connecting to replicaset instances** Connecting to bj77:3306** Connecting to bj78:3306** Connecting to bj79:3306** Connecting to bj77:3306** Connecting to bj78:3306** Connecting to bj79:3306* Performing validation checks** Checking async replication topology...** Checking transaction state of the instance...* Synchronizing transaction backlog at bj78:3306* Updating metadata* Acquiring locks in replicaset instances** Pre-synchronizing SECONDARIES** Acquiring global lock at PRIMARY** Acquiring global lock at SECONDARIES* Updating replication topology** Configuring bj77:3306 to replicate from bj78:3306** Changing replication source of bj79:3306 to bj78:3306bj78:3306 was promoted to PRIMARY.

# 檢查狀態,當前主庫已是bj78了。

MySQL 8.0.19 localhost:3306 ssl  mysql  JS > rs.status(){    "replicaSet": {        "name": "TestReplicaset",         "primary": "bj78:3306",         "status": "AVAILABLE",         "statusText": "All instances available.",         "topology": {            "bj77:3306": {                "address": "bj77:3306",                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "receiverStatus": "ON",                     "receiverThreadState": "Waiting for master to send event",                     "replicationLag": null                },                 "status": "ONLINE"            },             "bj78:3306": {                "address": "bj78:3306",                 "instanceRole": "PRIMARY",                 "mode": "R/W",                 "status": "ONLINE"            },             "bj79:3306": {                "address": "bj79:3306",                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "receiverStatus": "ON",                     "receiverThreadState": "Waiting for master to send event",                     "replicationLag": null                },                 "status": "ONLINE"            }        },         "type": "ASYNC"    }}

4故障轉移

與InnoDB Cluster不一樣,InnoDB ReplicaSet 沒有自動故障檢測與組複製機制。在主庫發生意外故障時,並不會自動故障轉移。若是主庫損壞不可用,ReplicaSet其實是隻讀的,必須選擇一個新主庫。

使用ReplicaSet.forcePrimaryInstance() 能夠強制配置(故障轉移)PRIMARY實例。這隻能在當前主庫不可用,且沒法還原的災難發生中才使用。

此次測試切換到bj79

# 目前的集羣角色:

192.168.0.77    bj77  從

192.168.0.78    bj78  主

192.168.0.79    bj79  從

# 當主庫正常可用時使用這個命令是無效的。

MySQL 8.0.19 localhost:3306 ssl  mysql  JS > rs.forcePrimaryInstance('bj79:3306')* Connecting to replicaset instances** Connecting to bj77:3306** Connecting to bj79:3306* Waiting for all received transactions to be applied** Waiting for received transactions to be applied at bj77:3306** Waiting for received transactions to be applied at bj79:3306bj79:3306 will be promoted to PRIMARY of the replicaset and the former PRIMARY will be invalidated.* Checking status of last known PRIMARYPRIMARY bj78:3306 is still available.Operation not allowed while there is still an available PRIMARY. Use setPrimaryInstance() to safely switch the PRIMARY.ReplicaSet.forcePrimaryInstance: PRIMARY still available (MYSQLSH 51116)

# 在主庫bj78 上kill掉mysqld_safe、mysqld

步驟略

# 鏈接到bj79,確認當前集羣主庫bj78已不可用報錯。

 MySQL 8.0.19 localhost:3306 ssl  JS > var rs = dba.getReplicaSet() You are connected to a member of replicaset 'TestReplicaset'. MySQL 8.0.19 localhost:3306 ssl  JS > rs.status()ERROR: Unable to connect to the PRIMARY of the replicaset TestReplicaset: bj78:3306: Can't connect to MySQL server on 'bj78' (111)Cluster change operations will not be possible unless the PRIMARY can be reached.If the PRIMARY is unavailable, you must either repair it or perform a forced failover.See \help forcePrimaryInstance for more information.WARNING: MYSQLSH 51118: PRIMARY instance is unavailable{    "replicaSet": {        "name": "TestReplicaset",         "primary": "bj78:3306",         "status": "UNAVAILABLE",         "statusText": "PRIMARY instance is not available, but there is at least one SECONDARY that could be force-promoted.",         "topology": {            "bj77:3306": {                "address": "bj77:3306",                 "fenced": true,                 "instanceErrors": [                    "ERROR: Replication I/O thread (receiver) has stopped with an error."                ],                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "expectedSource": "bj78:3306",                     "receiverLastError": "error reconnecting to master 'mysql_innodb_rs_777@bj78:3306' - retry-time: 60 retries: 2 message: Can't connect to MySQL server on 'bj78' (111)",                     "receiverLastErrorNumber": 2003,                     "receiverLastErrorTimestamp": "2020-01-23 19:55:59.782554",                     "receiverStatus": "ERROR",                     "receiverThreadState": "",                     "replicationLag": null,                     "source": "bj78:3306"                },                 "status": "ERROR",                 "transactionSetConsistencyStatus": null            },             "bj78:3306": {                "address": "bj78:3306",                 "connectError": "bj78:3306: Can't connect to MySQL server on 'bj78' (111)",                 "fenced": null,                 "instanceRole": "PRIMARY",                 "mode": null,                 "status": "UNREACHABLE"            },             "bj79:3306": {                "address": "bj79:3306",                 "fenced": true,                 "instanceErrors": [                    "ERROR: Replication I/O thread (receiver) has stopped with an error."                ],                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "expectedSource": "bj78:3306",                     "receiverLastError": "error reconnecting to master 'mysql_innodb_rs_999@bj78:3306' - retry-time: 60 retries: 2 message: Can't connect to MySQL server on 'bj78' (111)",                     "receiverLastErrorNumber": 2003,                     "receiverLastErrorTimestamp": "2020-01-23 19:55:59.782926",                     "receiverStatus": "ERROR",                     "receiverThreadState": "",                     "replicationLag": null,                     "source": "bj78:3306"                },                 "status": "ERROR",                 "transactionSetConsistencyStatus": null            }        },         "type": "ASYNC"    }}

# 強制提高bj79爲新主庫

 MySQL 8.0.19 localhost:3306 ssl  JS > rs.forcePrimaryInstance('bj79:3306')* Connecting to replicaset instances** Connecting to bj77:3306** Connecting to bj79:3306* Waiting for all received transactions to be applied** Waiting for received transactions to be applied at bj79:3306** Waiting for received transactions to be applied at bj77:3306bj79:3306 will be promoted to PRIMARY of the replicaset and the former PRIMARY will be invalidated.* Checking status of last known PRIMARYNOTE: bj78:3306 is UNREACHABLE* Checking status of promoted instanceNOTE: bj79:3306 has status ERROR* Checking transaction set status* Promoting bj79:3306 to a PRIMARY...* Updating metadata...bj79:3306 was force-promoted to PRIMARY.NOTE: Former PRIMARY bj78:3306 is now invalidated and must be removed from the replicaset.* Updating source of remaining SECONDARY instances** Changing replication source of bj77:3306 to bj79:3306Failover finished successfully.

# 檢查當前狀態:

# 注意如今是AVAILABLE_PARTIAL即:主庫雖然可用,但有SECONDARY節點不可用

 MySQL 8.0.19 localhost:3306 ssl  JS > rs.status(){    "replicaSet": {        "name": "TestReplicaset",         "primary": "bj79:3306",         "status": "AVAILABLE_PARTIAL",         "statusText": "The PRIMARY instance is available, but one or more SECONDARY instances are not.",         "topology": {            "bj77:3306": {                "address": "bj77:3306",                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "receiverStatus": "ON",                     "receiverThreadState": "Waiting for master to send event",                     "replicationLag": null                },                 "status": "ONLINE"            },             "bj78:3306": {                "address": "bj78:3306",                 "connectError": "bj78:3306: Can't connect to MySQL server on 'bj78' (111)",                 "fenced": null,                 "instanceRole": null,                 "mode": null,                 "status": "INVALIDATED"            },             "bj79:3306": {                "address": "bj79:3306",                 "instanceRole": "PRIMARY",                 "mode": "R/W",                 "status": "ONLINE"            }        },         "type": "ASYNC"    }}

# 啓動原主庫bj78:

步驟略

# 啓動後的bj78仍然沒法加入集羣:

"bj78:3306": {     "address": "bj78:3306",      "fenced": false,      "instanceErrors": [          "WARNING: Instance was INVALIDATED and must be removed from the replicaset.",           "ERROR: Instance is NOT a PRIMARY but super_read_only option is OFF. Accidental updates to this instance are possible and will cause inconsistencies in the replicaset."     ],      "instanceRole": null,      "mode": null,      "status": "INVALIDATED",      "transactionSetConsistencyStatus": "OK"},

# 使用 rs.rejoinInstance() 從新加入集羣

 MySQL 8.0.19 localhost:3306 ssl  JS > rs.rejoinInstance('bj78:3306')  * Validating instance...** Checking transaction state of the instance...The safest and most convenient way to provision a new instance is through automatic clone provisioning, which will completely overwrite the state of 'bj78:3306' with a physical snapshot from an existing replicaset member. To use this method by default, set the 'recoveryMethod' option to 'clone'.WARNING: It should be safe to rely on replication to incrementally recover the state of the new instance if you are sure all updates ever executed in the replicaset were done with GTIDs enabled, there are no purged transactions and the new instance contains the same GTID set as the replicaset or a subset of it. To use this method by default, set the 'recoveryMethod' option to 'incremental'.Incremental state recovery was selected because it seems to be safely usable.* Rejoining instance to replicaset...** Configuring bj78:3306 to replicate from bj79:3306** Checking replication channel status...** Waiting for rejoined instance to synchronize with PRIMARY...* Updating the Metadata...The instance 'bj78:3306' rejoined the replicaset and is replicating from bj79:3306.

# 最後檢查一下,集羣已經徹底恢復正常

 MySQL 8.0.19 localhost:3306 ssl  JS > rs.status(){    "replicaSet": {        "name": "TestReplicaset",         "primary": "bj79:3306",         "status": "AVAILABLE",         "statusText": "All instances available.",         "topology": {            "bj77:3306": {                "address": "bj77:3306",                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "receiverStatus": "ON",                     "receiverThreadState": "Waiting for master to send event",                     "replicationLag": null                },                 "status": "ONLINE"            },             "bj78:3306": {                "address": "bj78:3306",                 "instanceRole": "SECONDARY",                 "mode": "R/O",                 "replication": {                    "applierStatus": "APPLIED_ALL",                     "applierThreadState": "Slave has read all relay log; waiting for more updates",                     "receiverStatus": "ON",                     "receiverThreadState": "Waiting for master to send event",                     "replicationLag": null                },                 "status": "ONLINE"            },             "bj79:3306": {                "address": "bj79:3306",                 "instanceRole": "PRIMARY",                 "mode": "R/W",                 "status": "ONLINE"            }        },         "type": "ASYNC"    }}

整體來講,ReplicaSet的管理方式和MongoDB挺像,可是目前提供出來的功能還不是太完善,並且目的也不是很明確。短時間內應該還不會有生產場景敢用,等後期周邊配套都完善後,咱們再作進一步的測試。


# 歷史文章歸檔

  • GitHub都在用的高可用工具Orch:

    Orchestrator:01 基礎篇

    Orchestrator:02 高可用方案VIP篇

    Orchestrator:03 高可用方案ProxySQL篇

    Orchestrator:04 高可用方式部署

  • Percona 全力打造的監控平臺 PMM:

    監控利器 PMM2.0X GA 版本發佈!

    PMM監控的告警配置

    PMM的Ansible部署與重點指標

    在PMM中添加Redis和ES

相關文章
相關標籤/搜索