MySQL Cluster 是一个高可用,高性能可扩展的事务性数据库具备ACID属性(TODO?),是 MySQL官方力推的集群解决方案,号称能达到 99.999% 的可用性。MySQL Cluster 隐藏了大量的分布式细节,可以自动分片和负载均衡,对于开发者来说,大多数情况下就像使用单机版的Mysql一样,无需关心数据分布的细节。MySQL Cluster 使用 NDB(Network DataBase) 作为存储引擎,NDB是一种内存性的存储引擎并 使用 Sarding-Nothing 的无共享的架构,Sarding-Nothing 指的是每个节点有独立的处理器,磁盘和内存,节点之间没有共享资源完全独立互不干扰,节点之间通过高速网络组织在一起,每个节点相当于是一个小型数据库,存储部分数据。这种架构的好处是可以利用节点的分布性并行处理数据,提高整体性能,另外还具有很好的水平扩展性,只需要增加节点就可以增加就能的处理能力。
在使用 MySql Cluster 之前需要先了解一下其中的几个关键概念,MySql Cluster 中包含三种节点,分别是管理节点、数据节点和 SQL查询节点,他们三者之间的关系如下图所示:
(https://dev.mysql.com/doc/refman/5.7/en/mysql-cluster-overview.html)
- 管理节点((Management Server Node 或 MGM)): 负责管理集群中的其他节点,如配置管理,启动、停止节点,数据备份等。在启动集群时,管理节点需要最先启动。集群的状态日志保存在管理节点上,其他节点的状态数据会传输到管理节点上。
- 数据节点(Data Node 或 NDB):负责存储集群中的数据分区和分区的副本,在 Mysql Cluster 中,数据节点通常分布在不停的主机上,一个数据节点对应一个ndbd 进程,但同一台主机也可以同时启动多个数据节点,但这样做没有太大的实际意义,实际的生产环境不推荐这样做。数据节点通过 ndbd 和 ndbmtd(多线程版本) 程序来启动。
- SQL 查询节点(SQL Node 或 API):对外提供数据查询服务,客户端(如通过 PHP、Java、Python 等语言实现的客户端)不能直接访问数据节点,需要通过请求SQL 查询节点来访问集群中的数据,一个集群中可以同时配置多个查询节点。
节点组(Node Group):一组数据节点的集合。节点组的数量通常由系统自动分配,且和节点数量、副本数量有直接关系,整个集群中各个节点组中的节点数要保持相同,节点组的计算公式如下:
阶段组数 = 节点数 / 副本数
比如有集群中 4个节点,副本数为2(对应 NoOfReplicas 的设置),那么节点组个数为2,本节后面的例子就是这个配置。
另外,在可用性方面,数据的副本在组内交叉分配,一个节点组内只有要一台机器可用,就可以保证整个集群的数据完整性,实现服务的整体可用。
分区(Partition):MySql Cluster 是一个分布式的存储结构,数据按照"分区"划分成多份存储在各数据节点中。分区个数有系统自动计算,其数量取决于数据节点数和 LDM(TODO ?) 线程数,其计算公式如下,
分区数 = 数据节点数 / LDM 线程数
对于启用 ndbd 的数据节点,LDM 线程数为1,有几个数据节点就有几个分区。对于使用 ndbmtd 的数据节点,线程数可以通过
MaxNoOfExecutionThreads
参数进行配置。副本(Replica):分区数据的备份,有几个分区就有几个副本。为了避免单点问题,保证 MySQL Cluster 的高可用,原始是数据对应的分区和其副本通常会保存在不同的主机上,在一个节点组内进行交叉备份。
下图展示了节点组、分区和副本之间的关系:
(https://dev.mysql.com/doc/refman/5.7/en/mysql-cluster-nodes-groups.html)
对于一个有 4 个数据节点(使用ndbd),副本数为2的集群,节点组被分为2组(4/2),数据被分为4个分区,数据分配情况如下:
- 分区0(Partition 0)保存节点组0(Node Group 0)中, 分区数据(也被叫做主副本 — Primary Replica)保存在节点1(Node 1)中,副本数据(也被叫做备份副本,Backup Replica)保存在节点2(Node 2)中。
- 分区1(Partition 1)保存节点组1(Node Group 1)中,主副本 (Partition 1 Primary Replica)保存在节点3(Node 3)中,备份副本保存在节点 4 中(Node 4)中,和分区0的顺序相反。
- 分区2(Partition 2)保存节点组0(Node Group 0)中,主副本 (Partition 2 Primary Replica)保存在节点2(Node 2)中,备份副本保存在节点 1 中(Node 1)中。
分区3(Partition 3)保存节点组1(Node Group 1)中,主副本 (Partition 3 Primary Replica)保存在节点4(Node 4)中,备份副本保存在节点 3 中(Node 3)中,和分区0的顺序相反。
在这样的分配方式下,每个分组只要有一个节点正常运行,整个集群就包含所有完整的数据,集群机可以正常运行。但任意一个节点组的所有机器都不可用,那么集群数据就不完整,此时集群整体不可用。
我们通过例子来演示 Mysql Cluster 的核心特性,服务器资源规划如下:
本例中我们搭建一个7个节点组成的集群,其中包括 1台管理节点,4台数据节点,2台查询节点,各个节点分配如下:
主机地址 | 角色 |
---|---|
192.168.56.101 | 管理节点 |
192.168.56.116 | 数据节点1 |
192.168.56.117 | 数据节点2 |
192.168.56.118 | 数据节点3 |
192.168.56.119 | 数据节点4 |
192.168.56.108 | SQL 查询节点2 |
192.168.56.109 | SQL 查询节点2 |
MySqL 集群中的各个节点通过 TCP/IP 方式进行通信,为了确保节点间的通信效率,建议所有节点部署在同一个子网下,不建议夸机房部署。
下载安装 MySql Cluster,MySq l官方提供在 Linux 环境下可直接运行的版本,无需编译,解压之后即可使用。
$ wget https://cdn.mysql.com/Downloads/MySQL-Cluster-7.5/mysql-cluster-gpl-7.5.8-linux-glibc2.12-x86_64.tar.gz
$ tar -zxvf mysql-cluster-gpl-7.5.8-linux-glibc2.12-x86_64.tar.gz
$ cd ./mysql-cluster-gpl-7.5.8-linux-glibc2.12-x86_64
$ ./bin/ndb_mgmd -V
MySQL Cluster Management Server mysql-5.7.20 ndb-7.5.8
MySQL distrib mysql-5.7.20 ndb-7.5.8, for linux-glibc2.12 (x86_64)
其他环境的安装方式参考官方文档:https://dev.mysql.com/doc/refman/5.7/en/mysql-cluster-installation.html
安装管理节点
创建管理节点配置文件
$ vim /etc/my_cluster_mgm.cnf
管理节点的简化配置如下:
[ndbd default]
# 数据节点的冗余副本数量为2
NoOfReplicas=2
# 配置管理节点
[ndb_mgmd]
# 管理节点的服务器地址
HostName=192.168.56.101
# 管理节点的日志存放目录
DataDir=/home/admin/mysql-cluster-mgm/data
# 管理节点的ID
NodeId=1
# 配置第一个数据节点
[ndbd]
# 数据节点服务器地址,只有在这里声明过的服务器才被允许和管理节点进行通信。
HostName=192.168.56.116
# 数据节点ID
NodeId=6
# 数据存放目录
DataDir=/home/admin/mysql-cluster-ndb/data
# 配置第二个数据节点
[ndbd]
HostName=192.168.56.117
NodeId=7
DataDir=/home/admin/mysql-cluster-ndb/data
# 配置第三个数据节点
[ndbd]
HostName=192.168.56.118
NodeId=8
DataDir=/home/admin/mysql-cluster-ndb/data
# 配置第四个数据节点
[ndbd]
HostName=192.168.56.119
NodeId=9
DataDir=/home/admin/mysql-cluster-ndb/data
# 配置第一个SQL查询节点
[mysqld]
# 查询节点服务器地址
HostName=192.168.56.108
# 查询节点服务器ID
NodeId=80
# 配置第二个SQL查询节点
[mysqld]
HostName=192.168.56.109
NodeId=90
ndb_mgmd 用于配置管理节点,ndbd 用于配置数据节点并可以设置多个,mysqld 用于配置客户端节点,Mysql Cluster 中定义的客户端类型有两类,一类是标注客端,就是通过PHP、JAVA等程序来访问Mysql 的客户端,另外一类是系统管理客户端,用来和管理节点进行通信,来控制集群、以及查询集群状态,比如后文会介绍的 ndb_desc 程序,这三种节点每种同时至少有一个。
- NoOfReplicas:用于设置数据的副本数量,默认为 2,表示同一条数据在整个急群众出现两份。该值最小为 1,最大为 4,当该值为1时,表示没有冗余副本,此时容易出现单点故障。
- DataDir:是可选字段,设置数据节点中数据的存放位置。
- NodeId:也是可选字段,如果不指定系统会自动分配,如果手工指定要确保在同一个集群中全局唯一。
集群中的其他节点要和管理节点进行通信,在所有节点中,要确保管理节点最先启动,启动管理节点方式如下:
$ mkdir -p /home/admin/mysql-cluster-mgm/data
$ ./bin/ndb_mgmd --initial -f /etc/my_cluster.cnf
MySQL Cluster Management Server mysql-5.7.20 ndb-7.5.8
其中 :
- -f,指定集群的配置文件路径。
- —initial,Mysql Cluster 启动之后会将所有配置以二进制文件的方式缓存起来,缓存文件默认位置为
/usr/local/mysql/mysql-cluster/ndb_1_config.bin.1
,--initial
用于清除所有二进制缓存配置文件,第一次启动或重新初始化时需要加该选项,后续启动则不需要。另外,也可以通过--skip-config-cache
跳过加载缓存配置。
通过 ndb_mgm 工具查看整个集群的运行状态:
$ ./bin/ndb_mgm -e 'show'
Connected to Management Server at: 192.168.56.101:1186
Cluster Configuration
---------------------
[ndbd(NDB)] 4 node(s)
id=6 (not connected, accepting connect from 192.168.56.116)
id=7 (not connected, accepting connect from 192.168.56.117)
id=8 (not connected, accepting connect from 192.168.56.118)
id=9 (not connected, accepting connect from 192.168.56.119)
[ndb_mgmd(MGM)] 1 node(s)
id=1 @192.168.56.101 (mysql-5.7.20 ndb-7.5.8)
[mysqld(API)] 2 node(s)
id=80 (not connected, accepting connect from 192.168.56.108)
id=90 (not connected, accepting connect from 192.168.56.109)
此时,除了 ndb_mgmd(MGM) 节点之外,其他节点都是 not connected 状态,下面我们一次配置其他节点。
安装数据节点
编辑数据节点配置文件
$ vim /etc/my_data_node.cnf
[mysqld]
ndbcluster
[mysql_cluster]
# 可以设置多个
ndb-connectstring=192.168.56.101:1186
启动数据节点
$ mkdir -p /home/admin/mysql-cluster-ndb/data
$ ./bin/ndbd --defaults-file=/etc/my_data_node.cnf --bind-address=192.168.56.116 --initial
2017-11-25 00:28:48 [ndbd] INFO -- Angel connected to '192.168.56.101:1186'
2017-11-25 00:28:48 [ndbd] INFO -- Angel allocated nodeid: 6
其中:
- --defaults-file,指定配置文件路径
- --bind-address,将服务绑定到指定IP地址。我们在前文提到过,只有在管理节点配置文件中声明过的数据节点地址才被允许和管理节点通信,当本机有多个IP地址时,需要用
--bind-address
选项进行指定。 - —initial,用于初始化并清除由之前 ndbd 实例创建的旧文件(保留数据文件 TODO?),在第一次启动或配置发生变化时使用该选项,后续正常启动则不需要。
(TODO,对mysqld 和 Mysql ndb-connectstring 进行解释)
如果没有配置文件,ndbd 也可以直接通过命令行启动,比如上述启动方式,可以替换为
$ ./bin/ndbd --ndb-connectstring=192.168.56.101:1186 --bind-address=192.168.56.116 --initial
在每个数据节点上一次执行上述操作。
安装SQL查询节点
编辑SQL查询节点配置文件
$ vim /etc/my_sql_node.cnf
配置文件选下,和数据节点配置方式类似
[mysqld]
ndbcluster
datadir=/home/admin/mysql-cluster-api/data
[mysql_cluster]
ndb-connectstring=192.168.56.101:1186
MySQL Cluster 的安装包中自带了 mysqld 和 mysqld_safe 的执行程序,启动方式和 Mysql 服务的启动方式类似,但 MySQL Cluster 中的 mysqld 和标准版的 mysqld 有很多不同,二者之间不可互换。启动方式如下:
$ sudo -u mysql mkdir -p /home/admin/mysql-cluster-api/data
$ sudo -u mysql ./bin/mysqld --defaults-file=/etc/my_sql_node.cnf --initialize
$ sudo -u mysql ./bin/mysqld_safe --defaults-file=/etc/my_sql_node.cnf --skip-grant-tables &
- --initialize,首次启动前,需要用该选项初始化 Mysql 表。
--defaults-file,指定配置文件路径。
--skip-grant-tables,跳过授权验证。在执行
mysqld --initialize
时,会自动生成一个密码,如:2017-11-25T06:57:15.074364Z 1 [Note] A temporary password is generated for root@localhost: /7M4=jnh*+_t
这里使用该选项是为了测试方便,免去甚至密码的麻烦。
在每个 SQL 查询节点执行上述操作。
如果启动顺利,使用 ndb_mgm 工具查看集群状态如下
$ ./bin/ndb_mgm -e "show"
Connected to Management Server at: 127.0.0.1:1186
Cluster Configuration
---------------------
[ndbd(NDB)] 4 node(s)
id=6 @192.168.56.116 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0, *)
id=7 @192.168.56.117 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0)
id=8 @192.168.56.118 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 1)
id=9 @192.168.56.119 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 1)
[ndb_mgmd(MGM)] 1 node(s)
id=1 @192.168.56.101 (mysql-5.7.20 ndb-7.5.8)
[mysqld(API)] 2 node(s)
id=80 @192.168.56.108 (mysql-5.7.20 ndb-7.5.8)
id=90 @192.168.56.109 (mysql-5.7.20 ndb-7.5.8)
(TODO 解释)
创建数据库和表
在集群模式下,数据表必须使用 NDB 引擎,即创建数据表使用 ENGINE =NDB 或 ENGINE = NDBCLUSTER,对于已有的数据表可以使用 ALTER TABLE 语句更改引擎。另外,数据表必须包含一个唯一主键,因为数据是分布式存储,系统会根据唯一主键来定位(比如对主键Hash方式)数据所在的分区。其他方面和非集群模式的MySql操作没有太大区别。
$ ./bin/mysql -h 192.168.56.108
mysql> CREATE DATABASE test_cluster;
Query OK, 1 row affected (0.06 sec)
mysql> use test_cluster;
Database changed
mysql> CREATE TABLE `city` (
-> `ID` int(11) NOT NULL auto_increment,
-> `Name` char(35) NOT NULL default '',
-> `CountryCode` char(3) NOT NULL default '',
-> `District` char(20) NOT NULL default '',
-> `Population` int(11) NOT NULL default '0',
-> PRIMARY KEY (`ID`)
-> ) ENGINE=NDBCLUSTER DEFAULT CHARSET=latin1 PARTITION BY KEY (ID);
Query OK, 0 rows affected (0.26 sec)
下面初始化数据,这里我们使用 MySQL 官方自带的例子,数据可以从 https://dev.mysql.com/doc/index-other.html 下载,本里中我们假设将数据导出到 /tmp/world.sql
文件中,其中数据数据例如:
INSERT INTO `city` VALUES (4055,'Arden-Arcade','USA','California',92040);
mysql> source /tmp/world.sql;
测试第二个SQL 查询节点 192.168.56.109
$ ./bin/mysql -h 192.168.56.109
mysql> SELECT * FROM city LIMIT 5;;
+------+----------------+-------------+----------------+------------+
| ID | Name | CountryCode | District | Population |
+------+----------------+-------------+----------------+------------+
| 612 | Port Said | EGY | Port Said | 469533 |
| 626 | al-Minya | EGY | al-Minya | 201360 |
| 715 | Port Elizabeth | ZAF | Eastern Cape | 752319 |
| 1028 | Hyderabad | IND | Andhra Pradesh | 2964638 |
| 1726 | Ebina | JPN | Kanagawa | 115571 |
+------+----------------+-------------+----------------+------------+
5 rows in set (0.00 sec)
mysql> SELECT count(*) FROM city;
+----------+
| count(*) |
+----------+
| 3428 |
+----------+
1 row in set (0.00 sec)
(TODO 解释)
使用产 ndb_desc 查看数据分布情况
$ ./bin/ndb_desc city -d test_cluster -c 192.168.56.101 -p
-d,指定数据库名称。
-c,指定管理节点地址。
-p,显示分区信息。
在前文介绍过,Mysql Cluster 中有两种客户端,一类是标准客户端,另外一种就是系统客户端,其中 ndb_desc 属于系统管理客户端,在使用前,也需要管理节点配置了了相应的客户端节点,如:
[mysqld]
HostName=192.168.56.110
NodeId=100
显示结果如下:
从结果可见,3428 条数据被相对均匀低分布在四个分区中。
测试集群的容灾能力
(TODO 详细解释)
Mysql Cluster 通过数据副本避免单点问题,这里我们将节点6和8分别关闭,例如:
$ ./bin/ndb_mgd
ndb_mgm> 6 stop
Node 6: Node shutdown initiated
Node 6: Node shutdown completed.
Node 6 has shutdown.
ndb_mgm> 8 stop
Node 8: Node shutdown initiated
Node 8: Node shutdown completed.
Node 8 has shutdown.
数据依然可以访问
mysql> SELECT * FROM city;
+------+----------------+-------------+----------------+------------+
| ID | Name | CountryCode | District | Population |
+------+----------------+-------------+----------------+------------+
| 612 | Port Said | EGY | Port Said | 469533 |
...(中间数据生路)
| 3173 | Riyadh | SAU | Riyadh | 3324000 |
| 3447 | Sumy | UKR | Sumy | 294000 |
+------+----------------+-------------+----------------+------------+
3427 rows in set (0.02 sec)
我们再尝试将 节点7关闭,此时系统会提示错误,如果关闭节点7会造成系统服务不可用。
ndb_mgm> 7 stop
Node 7: Node shutdown aborted
Shutdown failed.
* 2002: Stop failed
* Node shutdown would cause system crash: Permanent error: Application error
如果我们手动 kill 节点7的进程,通过 mysql 客户端会查询失败,例如:
mysql> SELECT * FROM city;
ERROR 1296 (HY000): Got error 4009 'Cluster Failure' from NDBCLUSTER
集群动态扩容
(TODO 解释)
我们将集群中添加两个新的数据节点,192.168.56.121 和 192.168.56.122,修改管理节点的配置文件,添加如下内容:
[ndbd]
HostName=192.168.56.121
NodeId=10
DataDir=/home/admin/mysql-cluster-ndb/data
[ndbd]
HostName=192.168.56.122
NodeId=11
DataDir=/home/admin/mysql-cluster-ndb/data
先重启管理节点,重启管理节点的过程中,不影响正常的数据请求服务。
关闭管理管理节点,前面我们配置管理节点的ID 是1
$ ./bin/ndb_mgm -e '1 stop'
启动管理节点,需要使用 --reload
选项刷新配置。
$ ./bin/ndb_mgmd --reload -f /etc/my_cluster.cnf
此时查看集群节点状态,如下:
$ ./bin/ndb_mgm -e "show"
Connected to Management Server at: 192.168.56.101:1186
Cluster Configuration
---------------------
[ndbd(NDB)] 6 node(s)
id=6 @192.168.56.116 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0, *)
id=7 @192.168.56.117 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0)
id=8 @192.168.56.118 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 1)
id=9 @192.168.56.119 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 1)
id=10 (not connected, accepting connect from 192.168.56.121)
id=11 (not connected, accepting connect from 192.168.56.122)
可以看到,新增的节点已经出现到数据节点列表中,但由于数据节点服务还没有启动,两个新节点出于not connected
状态。
依次启动两个新的数据节点
./bin/ndbd -c 192.168.56.101:1186 --bind-address=192.168.56.121 --initial
./bin/ndbd -c 192.168.56.101:1186 --bind-address=192.168.56.122 --initial
然后依次重启已有数据节点,切记不用同时重启所有节点,这回造成数据不完成是的造成服务不可用,重启数据节点如下:
$ ./bin/ndb_mgm
ndb_mgm> 6 restart
Node 6: Node shutdown initiated
Node 6: Node shutdown completed, restarting, no start.
Node 6 is being restarted
Node 6: Start initiated (version 7.5.8)
Node 6: Started (version 7.5.8)
重启查询节点:
$ pkill -9 mysql
$ sudo -u mysql ./bin/mysqld_safe --defaults-file=/etc/my_sql_node.cnf --skip-grant-tables &
(TODO 重启的作用是什么?)
此时再次查看集群状态,会发现两个节点已经注册成功,但并没有分配分组中,处于no nodegroup
窗台。
$ ./bin/ndb_mgm -e "show"
...
id=10 @192.168.56.121 (mysql-5.7.20 ndb-7.5.8, no nodegroup)
id=11 @192.168.56.122 (mysql-5.7.20 ndb-7.5.8, no nodegroup)
为新节点创建分组:
$ ./bin/ndb_mgm -e "create nodegroup 10,11"
Connected to Management Server at: 192.168.56.101:1186
Nodegroup 2 created
再次查看集群状态,会发现新节点被分配到 2 号分组中。
./bin/ndb_mgm -e "show"
Connected to Management Server at: 192.168.56.101:1186
Cluster Configuration
---------------------
[ndbd(NDB)] 6 node(s)
id=6 @192.168.56.116 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0, *)
id=7 @192.168.56.117 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0)
id=8 @192.168.56.118 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 1)
id=9 @192.168.56.119 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 1)
id=10 @192.168.56.121 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 2)
id=11 @192.168.56.122 (mysql-5.7.20 ndb-7.5.8, Nodegroup: 2)
此时,节点服务已经准备就绪,但数据已然在在 6 到 9 四个之前配置的数据节点,数据迁移需要通过 ALTER TABLE ... REORGANIZE PARTITION
语句手动执行,例如:
mysql> ALTER TABLE test_cluster.city REORGANIZE PARTITION;
Query OK, 0 rows affected (7.78 sec)
Records: 0 Duplicates: 0 Warnings: 0
数据迁移会花一段时间,迁移完成之后,产看数据的分布
$ ./bin/ndb_desc city -d test_cluster -c 192.168.56.101 -p
从结果中可见,数据已经从原来的 4 个分区被重新分配到6个分区中。
(使用 复制)
MySQL Cluster 常见的问题,
TODO