您好,登录后才能下订单哦!
在CentOS8下搭建PXC集群一文中,演示了如何从零开始搭建一个三节点的PXC集群。但是光搭建了PXC集群还不够,因为在实际的企业应用中,可能会存在多个PXC集群,每个集群作为一个数据分片存在。因此,在完整的架构下我们还需要为集群引入数据库中间件,以实现数据分片和负载均衡等功能。
市面上有许多的数据库中间件,这些中间件主要分为两种类型,负载均衡型和数据切分型(通常数据切分型会具备负载均衡功能):
负载均衡型中间件的作用:
数据切分型中间件的作用:
以下是对常见的中间件进行的一个比较:
名称 | 是否开源免费 | 负载能力 | 开发语言 | 功能 | 文档 | 普及率 |
---|---|---|---|---|---|---|
MyCat | 开源免费 | 基于阿里巴巴的Corba中间件重构而来,有高访问量的检验 | Java | 功能全面,有丰富的分片算法以及读写分离、全局主键和分布式事务等功能 | 文档丰富,不仅有官方的《Mycat权威指南》,还有许多社区贡献的文档 | 电信、电商领域均有应用,是国内普及率最高的MySQL中间件 |
Atlas | 开源免费 | 基于MySQL Proxy,主要用于360产品,有每天承载几十亿次请求的访问量检验 | C语言 | 功能有限,实现了读写分离,具有少量的数据切分算法,不支持全局主键和分布式事务 | 文档较少,只有开源项目文档,无技术社区和出版物 | 普及率低,除了奇虎360外,仅在一些中小型项目在使用,可供参考的案例不多 |
OneProxy | 分为免费版和企业版 | 基于C语言的内核,性能较好 | C语言 | 功能有限,实现了读写分离,具有少量的数据切分算法,不支持全局主键和分布式事务 | 文档较少,官网不提供使用文档,无技术社区和出版物 | 普及率低,仅仅在一些中小型企业的内部系统中使用过 |
ProxySQL | 开源免费 | 性能出众,Percona推荐 | C++ | 功能相对丰富,支持读写分离、数据切分、故障转移及查询缓存等 | 文档丰富,有官方文档和技术社区 | 普及率相比于Mycat要低,但已有许多公司尝试使用 |
经过上一小节的介绍与比较,可以看出MyCat与ProxySQL是比较理想的数据库中间件。由于MyCat相对于ProxySQL功能更全面,普及率也更高一些,所以这里采用Mycat来做为PXC集群的中间件。关于Mycat的介绍与安装,可以参考我的另一篇Mycat 快速入门,这里就不再重复了。
本小节主要介绍如何配置Mycat的数据切分功能,让Mycat作为前端的数据切分中间件转发SQL请求到后端的PXC集群分片中。因此,这里我搭建了两个PXC集群,每个集群就是一个分片,以及搭建了两个Mycat节点和两个Haproxy节点用于后面组建双机热备。如图:
各个节点的信息如下表:
角色 | Host | IP |
---|---|---|
Haproxy-Master | Haproxy-Master | 192.168.190.140 |
Haproxy-Backup | Haproxy-Backup | 192.168.190.141 |
Mycat:Node1 | mycat-01 | 192.168.190.136 |
Mycat:Node2 | mycat-02 | 192.168.190.135 |
PXC分片-1:Node1 | PXC-Node1 | 192.168.190.132 |
PXC分片-1:Node2 | PXC-Node2 | 192.168.190.133 |
PXC分片-1:Node3 | PXC-Node3 | 192.168.190.134 |
PXC分片-2:Node1 | PXC-Node1 | 192.168.190.137 |
PXC分片-2:Node2 | PXC-Node2 | 192.168.190.138 |
PXC分片-2:Node3 | PXC-Node3 | 192.168.190.139 |
在每个分片里创建一个test
库,并在该库中创建一张t_user
表用作测试,具体的建表SQL如下:
CREATE TABLE `t_user` (
`id` int(11) NOT NULL,
`username` varchar(20) NOT NULL,
`password` char(36) NOT NULL,
`tel` char(11) NOT NULL,
`locked` char(10) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
完成以上准备后,接着我们开始配置Mycat,如果你对Mycat的配置文件不了解的话,可以参考我另一篇文章:Mycat 核心配置详解,本文就不赘述了。
1、编辑server.xml
文件,配置Mycat的访问用户:
<user name="admin" defaultAccount="true">
<property name="password">Abc_123456</property>
<property name="schemas">test</property>
<property name="defaultSchema">test</property>
</user>
2、编辑schema.xml
文件,配置Mycat的逻辑库、逻辑表以及集群节点的连接信息:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 配置逻辑库 -->
<schema name="test" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<!-- 配置逻辑表 -->
<table name="t_user" dataNode="dn1,dn2" rule="mod-long"/>
</schema>
<!-- 配置数据分片,每个分片都会有一个索引值,从0开始。例如,dn1索引值为0,dn2的索引值为1,以此类推 -->
<!-- 分片的索引与分片算法有关,分片算法计算得出的值就是分片索引 -->
<dataNode name="dn1" dataHost="pxc-cluster1" database="test" />
<dataNode name="dn2" dataHost="pxc-cluster2" database="test" />
<!-- 配置集群节点的连接信息 -->
<dataHost name="pxc-cluster1" maxCon="1000" minCon="10" balance="2"
writeType="1" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="W1" url="192.168.190.132:3306" user="admin"
password="Abc_123456">
<readHost host="W1-R1" url="192.168.190.133:3306" user="admin" password="Abc_123456"/>
<readHost host="W1-R2" url="192.168.190.134:3306" user="admin" password="Abc_123456"/>
</writeHost>
<writeHost host="W2" url="192.168.190.133:3306" user="admin"
password="Abc_123456">
<readHost host="W2-R1" url="192.168.190.132:3306" user="admin" password="Abc_123456"/>
<readHost host="W2-R2" url="192.168.190.134:3306" user="admin" password="Abc_123456"/>
</writeHost>
</dataHost>
<dataHost name="pxc-cluster2" maxCon="1000" minCon="10" balance="2"
writeType="1" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="W1" url="192.168.190.137:3306" user="admin" password="Abc_123456">
<readHost host="W1-R1" url="192.168.190.138:3306" user="admin" password="Abc_123456"/>
<readHost host="W1-R2" url="192.168.190.139:3306" user="admin" password="Abc_123456"/>
</writeHost>
<writeHost host="W2" url="192.168.190.138:3306" user="admin" password="Abc_123456">
<readHost host="W2-R1" url="192.168.190.137:3306" user="admin" password="Abc_123456"/>
<readHost host="W2-R2" url="192.168.190.138:3306" user="admin" password="Abc_123456"/>
</writeHost>
</dataHost>
</mycat:schema>
3、编辑rule.xml
文件,修改mod-long
分片算法的求模基数,由于只有两个集群作为分片,所以这里需要将基数改为2
:
<tableRule name="mod-long">
<rule>
<columns>id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property>
</function>
id
列的值对求模基数进行求模以得出数据分片的索引完成以上三个文件的配置后,启动Mycat:
[root@mycat-01 ~]# mycat start
Starting Mycat-server...
[root@mycat-01 ~]# more /usr/local/mycat/logs/wrapper.log |grep successfully
# 日志输出了 successfully 代表启动成功
INFO | jvm 1 | 2020/01/19 15:09:02 | MyCAT Server startup successfully. see logs in logs/mycat.log
启动完成后,进入Mycat中执行一条insert
语句,测试下是否能将该SQL转发到正确的集群分片上。具体步骤如下:
[root@mycat-01 ~]# mysql -uadmin -P8066 -h227.0.0.1 -p
mysql> use test;
mysql> insert into t_user(id, username, password, tel, locked)
-> values(1, 'Jack', hex(AES_ENCRYPT('123456', 'Test')), '13333333333', 'N');
上面这条insert
语句插入的是一条id
为1
的记录,而我们采用的是对id
列求模的分片算法,配置的求模基数为2
。因此,根据id
的值和求模基数进行求模计算的结果为:1 % 2 = 1
。得出来的1
就是分片的索引,所以正常情况下Mycat会将该insert
语句转发到分片索引为1
的集群上。
根据schema.xml
文件中的配置,索引为1
的分片对应的集群是pxc-cluster2
,即第二个PXC集群分片。接下来,我们可以通过对比这两个集群中的数据,以验证Mycat是否按照预期正确地转发了该SQL。
从下图中可以看到,Mycat正确地将该insert
语句转发到了第二个分片上,此时第一个分片是没有数据的:
接着我们再测试当id
为2
时,Mycat是否能将该SQL转发到第一个分片上。具体的SQL如下:
insert into t_user(id, username, password, tel, locked)
values(2, 'Jon', hex(AES_ENCRYPT('123456', 'Test')), '18888888888', 'N');
测试结果如图:
在完成以上的测试后,此时在Mycat上是能够查询出所有分片中的数据的:
上一小节的示例中,使用的就是主键求模切分,其特点如下:
mapFile
配置的映射关系来切分数据在Mycat 核心配置详解一文中,也介绍过枚举值切分算法。该算法相对于其他算法来说要用到一个额外的映射文件(mapFile
),所以这里就针对该算法的使用进行一个简单的演示。
需求:用户表中有一个存储用户所在区号的列,要求将该列作为分片列,实现让不同区号下的用户数据被分别存储到不同的分片中
1、首先,在Mycat的rule.xml
文件中,增加如下配置:
<!-- 定义分片规则 -->
<tableRule name="sharding-by-areafile">
<rule>
<!-- 定义使用哪个列作为分片列 -->
<columns>area_id</columns>
<algorithm>area-int</algorithm>
</rule>
</tableRule>
<!-- 定义分片算法 -->
<function name="area-int" class="io.mycat.route.function.PartitionByFileMap">
<!-- 定义mapFile的文件名,位于conf目录下 -->
<property name="mapFile">area-hash-int.txt</property>
</function>
2、在conf
目录下创建area-hash-int.txt
文件,定义区号与分片索引的对应关系:
[root@mycat-01 /usr/local/mycat]# vim conf/area-hash-int.txt
020=0
0755=0
0757=0
0763=1
0768=1
0751=1
3、配置schema.xml
,增加一个逻辑表,并将其分片规则设置为sharding-by-areafile
:
<schema name="test" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<table name="t_user" dataNode="dn1,dn2" rule="mod-long"/>
<table name="t_customer" dataNode="dn1,dn2" rule="sharding-by-areafile"/>
</schema>
4、进入Mycat中执行热加载语句,该语句的作用可以使Mycat不用重启就能应用新的配置:
[root@mycat-01 ~]# mysql -uadmin -P9066 -h227.0.0.1 -p
mysql> reload @@config_all;
完成以上配置后,我们来建个表测试一下,在所有的集群中创建t_customer
表。具体的建表SQL如下:
create table t_customer(
id int primary key,
username varchar(20) not null,
area_id int not null
);
进入Mycat中插入一条area_id
为020
的记录:
[root@mycat-01 ~]# mysql -uadmin -P8066 -h227.0.0.1 -p
mysql> use test;
mysql> insert into t_customer(id, username, area_id)
-> values(1, 'Jack', 020);
根据映射文件中的配置,area_id
为020
的数据会被存储到第一个分片中,如下图:
area_id
是int
类型的,所以前面的0
会被去掉然后再插入一条area_id
为0763
的记录:
insert into t_customer(id, username, area_id)
values(2, 'Tom', 0763);
根据映射文件中的配置,area_id
为0763
的数据会被存储到第二个分片中,如下图:
完成以上测试后,此时在Mycat中应能查询到所有分片中的数据:
当有关联的数据存储在不同的分片时,就会遇到表连接的问题,在Mycat中是不允许跨分片做表连接查询的。为了解决跨分片表连接的问题,Mycat提出了父子表这种解决方案。
父子表规定父表可以有任意的切分算法,但与之关联的子表不允许有切分算法,即子表的数据总是与父表的数据存储在一个分片中。父表不管使用什么切分算法,子表总是跟随着父表存储。
例如,用户表与订单表是有关联关系的,我们可以将用户表作为父表,订单表作为子表。当A用户被存储至分片1中,那么A用户产生的订单数据也会跟随着存储在分片1中,这样在查询A用户的订单数据时就不需要跨分片了。如下图所示:
了解了父子表的概念后,接下来我们看看如何在Mycat中配置父子表。首先,在schema.xml
文件中配置父子表关系:
<schema name="test" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<table name="t_customer" dataNode="dn1,dn2" rule="sharding-by-areafile">
<!-- 配置子表 -->
<childTable name="t_orders" joinKey="customer_id" parentKey="id"/>
</table>
</schema>
childTable
标签说明:
joinKey
属性:定义子表中用于关联父表的列parentKey
属性:定义父表中被关联的列childTable
标签内还可以继续添加childTable
标签完成以上配置后,让Mycat重新加载配置文件:
reload @@config_all;
接着在所有分片中创建t_orders
表,具体的建表SQL如下:
create table t_orders(
id int primary key,
customer_id int not null,
create_time datetime default current_timestamp
);
现在分片中有两个用户,id
为1
的用户存储在第一个分片,id
为2
的用户存储在第二个分片。此时,通过Mycat插入一条订单记录:
insert into t_orders(id, customer_id)
values(1, 1);
由于该订单记录关联的是id
为1
的用户,根据父子表的规定,会被存储至第一个分片中。如下图:
同样,如果订单记录关联的是id
为2
的用户,那么就会被存储至第二个分片中:
insert into t_orders(id, customer_id)
values(2, 2);
测试结果如下:
由于父子表的数据都是存储在同一个分片,所以在Mycat上进行关联查询也是没有问题的:
在以上小节的示例中,我们可以看到对后端数据库集群的读写操作都是在Mycat上进行的。Mycat作为一个负责接收客户端请求,并将请求转发到后端数据库集群的中间件,不可避免的需要具备高可用性。否则,如果Mycat出现单点故障,那么整个数据库集群也就无法使用了,这对整个系统的影响是十分巨大的。
所以本小节将演示如何去构建一个高可用的Mycat集群,为了搭建Mycat高可用集群,除了要有两个以上的Mycat节点外,还需要引入Haproxy和Keepalived组件。
其中Haproxy作为负载均衡组件,位于最前端接收客户端的请求并将请求分发到各个Mycat节点上,用于保证Mycat的高可用。而Keepalived则用于实现双机热备,因为Haproxy也需要高可用,当一个Haproxy宕机时,另一个备用的Haproxy能够马上接替。也就说同一时间下只会有一个Haproxy在运行,另一个Haproxy作为备用处于等待状态。当正在运行中的Haproxy因意外宕机时,Keepalived能够马上将备用的Haproxy切换到运行状态。
Keepalived是让主机之间争抢同一个虚拟IP(VIP)来实现高可用的,这些主机分为Master和Backup两种角色,并且Master只有一个,而Backup可以有多个。最开始Master先获取到VIP处于运行状态,当Master宕机后,Backup检测不到Master的情况下就会自动获取到这个VIP,此时发送到该VIP的请求就会被Backup接收到。这样Backup就能无缝接替Master的工作,以实现高可用。
引入这些组件后,最终我们的集群架构将演变成这样子:
Haproxy由于是老牌的负载均衡组件了,所以CentOS的yum
仓库中自带有该组件的安装包,安装起来就非常简单。安装命令如下:
[root@Haproxy-Master ~]# yum install -y haproxy
配置Haproxy:
[root@Haproxy-Master ~]# vim /etc/haproxy/haproxy.cfg
# 在文件的末尾添加如下配置项
# 监控界面配置
listen admin_stats
# 绑定的ip及监听的端口
bind 0.0.0.0:4001
# 访问协议
mode http
# URI 相对地址
stats uri /dbs
# 统计报告格式
stats realm Global\ statistics
# 用于登录监控界面的账户密码
stats auth admin:abc123456
# 数据库负载均衡配置
listen proxy-mysql
# 绑定的ip及监听的端口
bind 0.0.0.0:3306
# 访问协议
mode tcp
# 负载均衡算法
# roundrobin:轮询
# static-rr:权重
# leastconn:最少连接
# source:请求源ip
balance roundrobin
# 日志格式
option tcplog
# 需要被负载均衡的主机
server mycat_01 192.168.190.136:8066 check port 8066 weight 1 maxconn 2000
server mycat_02 192.168.190.135:8066 check port 8066 weight 1 maxconn 2000
# 使用keepalive检测死链
option tcpka
由于配置了3306
端口用于TCP
转发,以及4001
作为Haproxy监控界面的访问端口,所以在防火墙上需要开放这两个端口:
[root@Haproxy-Master ~]# firewall-cmd --zone=public --add-port=3306/tcp --permanent
[root@Haproxy-Master ~]# firewall-cmd --zone=public --add-port=4001/tcp --permanent
[root@Haproxy-Master ~]# firewall-cmd --reload
完成以上步骤后,启动Haproxy服务:
[root@Haproxy-Master ~]# systemctl start haproxy
然后使用浏览器访问Haproxy的监控界面,初次访问会要求输入用户名密码,这里的用户名密码就是配置文件中所配置的:
登录成功后,就会看到如下页面:
Haproxy的监控界面提供的监控信息也比较全面,在该界面下,我们可以看到每个主机的连接信息及其自身状态。当主机无法连接时,Status
一栏会显示DOWN
,并且背景色也会变为红色。正常状态下的值则为UP
,背景色为绿色。
另一个Haproxy节点也是使用以上的步骤进行安装和配置,这里就不再重复了。
Haproxy服务搭建起来后,我们来使用远程工具测试一下能否通过Haproxy正常连接到Mycat。如下:
连接成功后,在Haproxy上执行一些SQL语句,看看能否正常插入数据和查询数据:
我们搭建Haproxy是为了让Mycat具备高可用的,所以最后测试一下Mycat是否已具备有高可用性,首先将一个Mycat节点给停掉:
[root@mycat-01 ~]# mycat stop
Stopping Mycat-server...
Stopped Mycat-server.
[root@mycat-01 ~]#
此时,从Haproxy的监控界面中,可以看到mycat_01
这个节点已经处于下线状态了:
现在集群中还剩一个Mycat节点,然后我们到Haproxy上执行一些SQL语句,看看是否还能正常插入数据和查询数据:
从测试结果可以看到,插入和查询语句依旧是能正常执行的。也就是说即便此时关掉一个Mycat节点整个数据库集群还能够正常使用,说明现在Mycat集群是具有高可用性了。
实现了Mycat集群的高可用之后,我们还得实现Haproxy的高可用,因为现在的架构已经从最开始的Mycat面向客户端变为了Haproxy面向客户端。
而同一时间只需要存在一个可用的Haproxy,否则客户端就不知道该连哪个Haproxy了。这也是为什么要采用VIP的原因,这种机制能让多个节点互相接替时依旧使用同一个IP,客户端至始至终只需要连接这个VIP。所以实现Haproxy的高可用就要轮到Keepalived出场了,在安装Keepalived之前需要开启防火墙的VRRP协议:
[root@Haproxy-Master ~]# firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 --protocol vrrp -j ACCEPT
[root@Haproxy-Master ~]# firewall-cmd --reload
然后就可以使用yum
命令安装Keepalived了,需要注意Keepalived是安装在Haproxy节点上的:
[root@Haproxy-Master ~]# yum install -y keepalived
安装完成后,编辑keepalived的配置文件:
[root@Haproxy-Master ~]# mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak # 不使用自带的配置文件
[root@Haproxy-Master ~]# vim /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
state MASTER
interface ens32
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 123456
}
virtual_ipaddress {
192.168.190.100
}
}
配置说明:
state MASTER
:定义节点角色为master,当角色为master时,该节点无需争抢就能获取到VIP。集群内允许有多个master,当存在多个master时,master之间就需要争抢VIP。为其他角色时,只有master下线才能获取到VIPinterface ens32
:定义可用于外部通信的网卡名称,网卡名称可以通过ip addr
命令查看virtual_router_id 51
:定义虚拟路由的id,取值在0-255,每个节点的值需要唯一,也就是不能配置成一样的priority 100
:定义权重,权重越高就越优先获取到VIPadvert_int 1
:定义检测间隔时间为1秒authentication
:定义心跳检查时所使用的认证信息
auth_type PASS
:定义认证类型为密码auth_pass 123456
:定义具体的密码virtual_ipaddress
:定义虚拟IP(VIP),需要为同一网段下的IP,并且每个节点需要一致完成以上配置后,启动keepalived服务:
[root@Haproxy-Master ~]# systemctl start keepalived
当keepalived服务启动成功,使用ip addr
命令可以查看到网卡绑定的虚拟IP:
另一个节点也是使用以上的步骤进行安装和配置,这里就不再重复了。
以上我们完成了Keepalived的安装与配置,最后我们来测试Keepalived服务是否正常可用,以及测试Haproxy是否已具有高可用性。
首先,在其他节点上测试虚拟IP能否正常ping
通,如果不能ping
通就需要检查配置了。如图,我这里是能正常ping
通的:
常见的虚拟IP ping不通的情况:
确认能够从外部ping
通Keepalived的虚拟IP后,使用Navicat测试能否通过虚拟IP连接到Mycat:
连接成功后,执行一些语句测试能否正常插入、查询数据:
到此就基本没什么问题了,最后测试一下Haproxy的高可用性,将其中一个Haproxy节点上的keepalived服务给关掉:
[root@Haproxy-Master ~]# systemctl stop keepalived
然后再次执行执行一些语句测试能否正常插入、查询数据,如下能正常执行代表Haproxy节点已具有高可用性:
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。