1.架构存在的问题
在日常系统的DBServer方面中,有些只使用了单节点服务,如果面对大并发,海量数据的存储,显然单节点的系统架构将存在很严重的问题,所以接下来,针对应对大并发、海量数据存储等问题,我们将实施Mysql的集群演化过程。
2.Mysql集群方案演化
2.1读写分离架构
2.1.1说明
我们一般应用对数据库而言都是“读多写少”,也就是说对数据库读取数据的压力比较大,有一个数据库集群的方案就是:其中一个是主库,负责写入数据,我们称之为“写库”,其它都是从库,负责读取数据,我们称之为“读库”。
因此会衍生出一种下面的架构
从上面的架构中,可以看出:
1)数据库从之前的单节点变成多节点提供服务
2)主节点数据,同步到从节点数据
3)应用程序需要连接到2个数据库节点,并且在程序内部实现判断读写操作。
2.1.2新问题
这种架构存在2个问题
1)应用程序需要连接到多个节点,对应用程序开发而言变得复杂
#解决方案:
(1)通过中间件
(2)使用Spring的AOP功能实现
2)主从之间的同步,是异步完成的,也就意味着这是一种“弱一致性”
#解决方案
(1)通过PXC集群解决
2.2中间件
2.2.1说明
通过上面的架构,可以看出,应用程序会连接到多个节点,使得应用程序的复杂度会提升,可以通过中间件解决,如下:
从架构中,可以看出:
1)应用程序只需要连接到中间件即可,无需连接多个数据库节点
2)应用程序无需区分读写操作,对中间件直接进行读写操作即可
3)在中间件进行读写的分离,读发送到从节点,写发送到主节点
2.2.2新问题
该架构中也存在问题,中间件的性能成为了系统的瓶颈,那么继续演化吧
这样的话,中间的性能得到了保证,但是也带来了新的问题,应用系统依然需要连接到两个中间件,又为系统带来了复杂度。
2.3.负载均衡
2.3.1说明
为了解决以上问题,我们将继续优化架构,在应用程序和中间件之间增加proxy代理,有代理来完成负载均衡的功能,应用程序只需要对接到peoxy‘即可。
2.4PXC集群架构
2.4.1说明
在前面的架构中,都是基于MYSQL主从的架构,那么在主从架构中,弱一致性问题依然没有解决,如果在需要强一致性的需求中,显然这架构是不能应对的,比如:交易数据。
PXC提供了读写强一致的功能,可以保证数据在任何一个节点写入的同时,可以同步到其它节点,也就意味着可以从其它的任何节点进行读操作,无延迟。
架构如下:
2.5混合架构
在前面的PXC架构中,虽然可以实现事务的强一致性,但是它是通过牺牲了性能换来的一致性,如果在某些业务场景下,没有强一致性的需求,那么PXC就不适合了。所以,在我们的系统架构中,需要将这两种方案综合起来,这样才是一个较为完善的架构。
3.安装docker
为了能够快速模拟上面的架构演进过程,我们选取了centos7系统的docker镜像方式,来快速搭建每个组件,以在短时间内完成实验。在生产过程中,只需将每个组件的搭建过程,进行相应的替换即可。
3.1安装docker
cat /etc/redhat-release
uname -r
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum makecache #生成仓库缓存
yum install docker -y
3.2启动docker
systemctl start docker #启动docker
systemctl enable docker #开机启动docker
systemctl status docker #查看docker状态
3.3docker版本
[root@bigdata1 ~]# docker version
Client:
Version: 1.13.1
API version: 1.26
Package version: docker-1.13.1-162.git64e9980.el7.centos.x86_64
Go version: go1.10.3
Git commit: 64e9980/1.13.1
Built: Wed Jul 1 14:56:42 2020
OS/Arch: linux/amd64
Server:
Version: 1.13.1
API version: 1.26 (minimum version 1.12)
Package version: docker-1.13.1-162.git64e9980.el7.centos.x86_64
Go version: go1.10.3
Git commit: 64e9980/1.13.1
Built: Wed Jul 1 14:56:42 2020
OS/Arch: linux/amd64
Experimental: false
3.4镜像加速
vi /etc/docker/daemon.json
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
#重启docker
systemctl restart docker
3.5小妙招
#快速清理多个docker容器
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
4.搭建主从复制集群
4.1搭建主从集群01
4.1.1搭建01主库
#创建目录
mkdir -p /data/mysql/master01
cd /data/mysql/master01
mkdir conf data
chmod -R 7777 *
#创建配置文件
vi /data/mysql/master01/conf/my.cnf
#输入以下内容
[mysqld]
log-bin=mysql-bin
server-id=1
lower_case_table_names=1
#创建容器
docker create --name percona-master01 -v /data/mysql/master01/data:/var/lib/mysql -v /data/mysql/master01/conf:/etc/my.cnf.d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root percona
#启动
docker start percona-master01 && docker logs -f percona-master01
#创建同步账户以及授权
create user 'backup'@'%' identified by '123';
grant replication slave on *.* to 'backup'@'%';
flush privileges;
#查看master状态
SHOW MASTER STATUS;
#查看二进制日志相关的配置项
SHOW GLOBAL VARIABLES LIKE 'binlog%';
#查看server相关的配置项
SHOW GLOBAL VARIABLES LIKE 'server%';
4.1.2搭建01从库
#创建目录
mkdir -p /data/mysql/slave01
cd /data/mysql/slave01
mkdir conf data
chmod -R 7777 *
#创建配置文件
vi /data/mysql/slave01/conf/my.cnf
#输入以下内容
[mysqld]
log-bin=mysql-bin
server-id=2
lower_case_table_names=1
#创建容器
docker create --name percona-slave01 -v /data/mysql/slave01/data:/var/lib/mysql -v /data/mysql/slave01/conf:/etc/my.cnf.d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root percona
#启动
docker start percona-slave01 && docker logs -f percona-slave01
#设置master相关信息
CHANGE MASTER TO
MASTER_HOST='192.168.56.11',
MASTER_USER='backup',
MASTER_PASSWORD='123',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000003',
MASTER_LOG_POS=743;
#启动同步
START SLAVE;
#查看master状态
show slave status;
4.2搭建主从集群02
4.2.1搭建02主库
#创建目录
mkdir -p /data/mysql/master02
cd /data/mysql/master02
mkdir conf data
chmod -R 7777 *
#创建配置文件
vi /data/mysql/master02/conf/my.cnf
#输入以下内容
[mysqld]
log-bin=mysql-bin
server-id=1
lower_case_table_names=1
#创建容器
docker create --name percona-master02 -v /data/mysql/master02/data:/var/lib/mysql -v /data/mysql/master02/conf:/etc/my.cnf.d -p 3316:3306 -e MYSQL_ROOT_PASSWORD=root percona
#启动
docker start percona-master02 && docker logs -f percona-master02
#创建同步账户以及授权
create user 'backup'@'%' identified by '123';
grant replication slave on *.* to 'backup'@'%';
flush privileges;
#查看master状态
SHOW MASTER STATUS;
#查看二进制日志相关的配置项
SHOW GLOBAL VARIABLES LIKE 'binlog%';
#查看server相关的配置项
SHOW GLOBAL VARIABLES LIKE 'server%';
4.2.2搭建02从库
#创建目录
mkdir -p /data/mysql/slave02
cd /data/mysql/slave02
mkdir conf data
chmod -R 7777 *
#创建配置文件
vi /data/mysql/slave02/conf/my.cnf
#输入以下内容
[mysqld]
log-bin=mysql-bin
server-id=2
lower_case_table_names=1
#创建容器
docker create --name percona-slave02 -v /data/mysql/slave02/data:/var/lib/mysql -v /data/mysql/slave02/conf:/etc/my.cnf.d -p 3317:3306 -e MYSQL_ROOT_PASSWORD=root percona
#启动
docker start percona-slave02 && docker logs -f percona-slave02
#设置master相关信息
CHANGE MASTER TO
MASTER_HOST='192.168.56.11',
MASTER_USER='backup',
MASTER_PASSWORD='123',
MASTER_PORT=3316,
MASTER_LOG_FILE='mysql-bin.000003',
MASTER_LOG_POS=743;
#启动同步
START SLAVE;
#查看master状态
show slave status;
5.mycat中间件
基于阿里开源的Cobar产品而研发,Cobar的稳定性、可靠性、优秀的架构和性能以及众多成熟的使用案例使得MYCAT一开始就拥有一个很好的起点,站在巨人的肩膀上,我们能看到更远。业界优秀的开源项目和创新思路被广泛融入到MYCAT的基因中,使得MYCAT在很多方面都领先于目前其他一些同类的开源项目,甚至超越某些商业产品。
MYCAT背后有一支强大的技术团队,其参与者都是5年以上软件工程师、架构师、DBA等,优秀的技术团队保证了MYCAT的产品质量。
MYCAT并不依托于任何一个商业公司,因此不像某些开源项目,将一些重要的特性封闭在其商业产品中,使得开源项目成了一个摆设。
1)一个用于MySQL读写分离和与数据切分的高可用中间件
2)一个模拟为MySQLServer的超级数据库代理
3)一个能平滑扩展支持1000亿大表的分布式数据库系统 (普通单表1kw以下)
4)一个可管控多种关系数据库的数据库路由器
5.1mycat读写分离
cd /data
tar -xvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
cp -r /data/mycat /data/mycat01
cd /data/mycat01
#修改配置文件
vi /data/mycat01/conf/server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="nonePasswordLogin">0</property>
<property name="useHandshakeV10">1</property>
<property name="useSqlStat">0</property>
<property name="useGlobleTableCheck">0</property>
<property name="sequnceHandlerType">2</property>
<property name="subqueryRelationshipCheck">false</property>
<property name="processorBufferPoolType">0</property>
<property name="handleDistributedTransactions">0</property>
<property name="useOffHeapForMerge">1</property>
<property name="memoryPageSize">64k</property>
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<property name="systemReserveMemorySize">384m</property>
<property name="useZKSwitch">false</property>
</system>
<user name="itcast" defaultAccount="true">
<property name="password">123</property>
<property name="schemas">itcast</property>
</user>
</mycat:server>
vi /data/mycat01/conf/schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 配置数据表 -->
<schema name="itcast" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_user" dataNode="dn1" rule="mod-long" />
</schema>
<!-- 配置分片关系 -->
<dataNode name="dn1" dataHost="cluster1" database="itcast" />
<!-- 配置连接信息 -->
<dataHost name="cluster1" maxCon="1000" minCon="10" balance="3"
writeType="1" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="w1" url="192.168.56.11:3306" user="root" password="root">
<readHost host="w1r1" url="192.168.56.11:3307" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
vi /data/mycat01/conf/rule.xml
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">1</property>
</function>
#设置JMX端口
vi wrapper.conf
wrapper.java.additional.9=-Dcom.sun.management.jmxremote.port=11985
#设置服务端口
vi server.xml
<property name="serverPort">18067</property>
<property name="managerPort">19067</property>
#启动验证
./mycat console
vi startup_nowrap.sh
:set ff=unix
:wq
#启动
./startup_nowrap.sh
测试
create table `TB_USER`(
`id` bigint(20) not null auto_increment,
`type` int(10) default null,
`title` varchar(100) default null,
primary key(`id`)
) ENGINE=INNODB auto_increment=1 default charset=utf8;
insert into `TB_USER`(`id`,`type`,`title`) values('1','1','www.baidu.com');
insert into `TB_USER`(`id`,`type`,`title`) values('2','7','www.baidu.com');
insert into `TB_USER`(`id`,`type`,`title`) values('3','5','www.baidu.com');
insert into `TB_USER`(`id`,`type`,`title`) values('4','2','www.baidu.com');
5.2mycat双主双从读写分离
#修改配置文件
vi /data/mycat01/conf/server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="nonePasswordLogin">0</property>
<property name="useHandshakeV10">1</property>
<property name="useSqlStat">0</property>
<property name="serverPort">8066</property>
<property name="managerPort">9066</property>
<property name="useGlobleTableCheck">0</property>
<property name="sequnceHandlerType">2</property>
<property name="subqueryRelationshipCheck">false</property>
<property name="processorBufferPoolType">0</property>
<property name="handleDistributedTransactions">0</property>
<property name="useOffHeapForMerge">1</property>
<property name="memoryPageSize">64k</property>
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<property name="systemReserveMemorySize">384m</property>
<property name="useZKSwitch">false</property>
</system>
<user name="itcast" defaultAccount="true">
<property name="password">123</property>
<property name="schemas">itcast</property>
</user>
</mycat:server>
vi /data/mycat01/conf/schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 配置数据表 -->
<schema name="itcast" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_user" dataNode="dn1,dn2" rule="mod-long" />
</schema>
<!-- 配置分片关系 -->
<dataNode name="dn1" dataHost="cluster1" database="itcast" />
<dataNode name="dn2" dataHost="cluster2" database="itcast" />
<!-- 配置连接信息 -->
<dataHost name="cluster1" maxCon="1000" minCon="10" balance="3"
writeType="1" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="w1" url="192.168.56.11:3306" user="root" password="root">
<readHost host="w1r1" url="192.168.56.11:3307" user="root" password="root" />
</writeHost>
</dataHost>
<dataHost name="cluster2" maxCon="1000" minCon="10" balance="3"
writeType="1" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="w2" url="192.168.56.11:3316" user="root" password="root">
<readHost host="w2r2" url="192.168.56.11:3317" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
vi /data/mycat01/conf/rule.xml
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property>
</function>
测试
insert into `tb_user`(`id`,`type`,`title`) values('1','1','www.baidu.com');
insert into `tb_user`(`id`,`type`,`title`) values('2','7','www.baidu.com');
insert into `tb_user`(`id`,`type`,`title`) values('3','5','www.baidu.com');
insert into `tb_user`(`id`,`type`,`title`) values('4','2','www.baidu.com');
update `tb_user` set `type`='10' where `id`=2;
select * from `tb_user` order by id;
5.3mycat集群
cd /data
cp -R mycat01 mycat02
#设置jmx端口
vi /data/mycat02/conf/wrapper.conf
wrapper.java.additional.6=-Dcom.sun.management.jmxremote.port=1984
#修改服务端口及管理端口
vi /data/mycat01/conf/server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="nonePasswordLogin">0</property>
<property name="useHandshakeV10">1</property>
<property name="useSqlStat">0</property>
<property name="serverPort">8067</property>
<property name="managerPort">9067</property>
<property name="useGlobleTableCheck">0</property>
<property name="sequnceHandlerType">2</property>
<property name="subqueryRelationshipCheck">false</property>
<property name="processorBufferPoolType">0</property>
<property name="handleDistributedTransactions">0</property>
<property name="useOffHeapForMerge">1</property>
<property name="memoryPageSize">64k</property>
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<property name="systemReserveMemorySize">384m</property>
<property name="useZKSwitch">false</property>
</system>
<user name="itcast" defaultAccount="true">
<property name="password">123</property>
<property name="schemas">itcast</property>
</user>
</mycat:server>
6.负载均衡
6.1Haproxy
#拉取镜像
docker pull docker.io/haproxy
#创建目录,用于存放配置文件
mkdir -p /data/haproxy
vi /data/haproxy/haproxy.cfg
global
maxconn 10000
log 127.0.0.1 local0
daemon
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 30s
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-keep-alive 10s
timeout check 10s
maxconn 8000
listen admin_status
bind 0.0.0.0:4001
mode http
stats uri /stats
stats realm "HAProxy statistics"
stats auth admin:admin123
listen mysql_proxy
bind 0.0.0.0:4002
mode tcp
balance roundrobin
option tcplog
server mycat01 192.168.56.11:8066 check port 8066 maxconn 2000 inter 5000 rise 2 fall 3
server mycat02 192.168.56.11:8067 check port 8067 maxconn 2000 inter 5000 rise 2 fall 3
#创建容器
docker create --name=haproxy --net host -v /data/haproxy:/usr/local/etc/haproxy haproxy
#启动容器
docker start haproxy && docker logs -f haproxy
#访问管理界面
http://192.168.56.11:4001/stats
7.PXC集群
7.1简介
Percona XtraDB Cluster是MySQL高可用性和可扩展性的解决方案,Percona XtraDB Cluster提供的特性如下:
1).同步复制,事务要么在所有节点提交或不提交。
2).多主复制,可以在任意节点进行写操作。
3).在从服务器上并行应用事件,真正意义上的并行复制。
4).节点自动配置。
5).数据一致性,不再是异步复制。
Percona XtraDB Cluster完全兼容MySQL和Percona Server,表现在:
1).数据的兼容性
2).应用程序的兼容性:无需更改应用程序
1).集群是有节点组成的,推荐配置至少3个节点,但是也可以运行在2个节点上
2).每个节点都是普通的mysql/percona服务器,可以将现有的数据库服务器组成集群,反之,也可以将集群拆分成单独的服务器。
3).每个节点都包含完整的数据副本
7.2优劣势对比
7.2.1优势
1).当执行一个查询时,在本地节点上执行。因为所有数据都在本地,无需 远程访问
2).无需集中管理。可以在任何时间点失去任何节点,但是集群将照常工作, 不受影响
3).良好的读负载扩展,任意节点都可以查询
7.2.2劣势
1).加入新节点,开销大。需要复制完整的数据
2).不能有效的解决写缩放问题,所有的写操作都将发生在所有节点上
3).有多少个节点就有多少重复的数据
7.3架构图
PXC集群主要由两部分组成:Percona Server with XtraDB和Write Set Replication patches(使用了Galera library,一个通用的用于事务型应用的同步、多主复制插件)
7.4回顾CAP理论
CAP理论作为分布式系统的基础理论,它描述的是一个分布式系统在以下三个特性中:
- 一致性(Consistency):指的分布式系统中的某个节点或者网络分区出现了故障的时候,整个系统仍然能对外提供满足一致性和可用性的服务。也就是说部分故障不影响整体使用。事实上我们在设计分布式系统是都会考虑到bug,硬件,网络等各种原因造成的故障,所以即使部分节点或者网络出现故障,我们要求整个系统还是要继续使用的(不继续使用,相当于只有一个分区,那么也就没有后续的一致性和可用性了)。
- 可用性(Availability):一直可以正常的做读写操作。简单而言就是客户端一直可以正常访问并得到系统的正常响应。用户角度来看就是不会出现系统操作失败或者访问超时等问题。
- 分区容错性(Partition tolerance):在分布式系统完成某写操作后任何读操作,都应该获取到该写操作写入的那个最新的值。相当于要求分布式系统中的各节点时时刻刻保持数据的一致性。
最多满足其中的两个特性。也就是下图所描述的。分布式系统要么满足CA,要么CP,要么AP。无法同时满足CAP。
7.5PXC与Replication的区别
1)MySQL Replication: 可用性和分区容忍性
2)Percona XtraDB Cluster: 一致性和可用性
因此MySQL Replication并不保证数据的一致性,而Percona XtraDB Cluster提供数据一致性
1)PXC集群方案所有节点都是可读写的,Replication从节点不能写入,因为主从同步是单向的,无法从slave节点向master节点同步。
2)PXC同步机制是同步进行的,这也是它能保证数据强一致性的根本原因,Replication同步机制是异步进行的,它如果从节点停止同步,依然可以向主节点插入数据,正确返回,可能会造成数据主从的不一致性。
3)PXC是牺牲性能保证数据一致性的,Replication在性能上是高于PXC的。所以两者用途也不一致。PXC是用于重要信息的存储,例如:订单、用户信息等;Replication用于一般信息的存储,能容忍数据丢失,例如:购物车、用户行为日志等。
7.6PXC部署
7.6.1集群规划
节点 | 端口 | 容器名称 | 数据卷 |
node1 | 13306 | pxc-node1 | v1 |
node2 | 13307 | pxc-node2 | v2 |
node3 | 13308 | pxc-node3 | v3 |
7.6.2集群部署
#创建数据卷(存储路径:/usr/lib/docker/volumes)
docker volume create v1
docker volume create v2
docker volume create v3
#拉取镜像
docker pull percona/percona-xtradb-cluster:5.7
docker tag percona/percona-xtradb-cluster:5.7 pxc
#创建网络
docker network create --subnet=172.30.0.0/24 pxc-network
#创建容器
#第一节点
docker create -p 13306:3306 -v v1:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e CLUSTER_NAME=pxc --name=pxc_node1 --net=pxc-network --ip=172.30.0.2 pxc
#第二节点(增加了CLUSTER_JOIN参数)
docker create -p 13307:3306 -v v2:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e CLUSTER_NAME=pxc --name=pxc_node2 -e CLUSTER_JOIN=pxc_node1 --net=pxc-network --ip=172.30.0.3 pxc
#第三节点(增加了CLUSTER_JOIN参数)
docker create -p 13308:3306 -v v3:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e CLUSTER_NAME=pxc --name=pxc_node3 -e CLUSTER_JOIN=pxc_node1 --net=pxc-network --ip=172.30.0.4 pxc
#需要注意的是:先启动第一个节点,等到mysql客户端可以连接到服务器之后,再启动其它节点
docker start pxc_node1 && docker logs -f pxc_node1
docker start pxc_node2 && docker logs -f pxc_node2
docker start pxc_node3 && docker logs -f pxc_node3
#查看集群节点
show status like 'wsrep_cluster%'
7.6.3测试
#可在任意一个节点进行操作
create table `tb_user`(
`id` bigint(20) not null auto_increment,
`type` int(10) default null,
`title` varchar(100) default null,
primary key(`id`)
) ENGINE=INNODB auto_increment=1 default charset=utf8;
insert into `tb_user`(`id`,`type`,`title`) values('1','1','www.baidu.com');
insert into `tb_user`(`id`,`type`,`title`) values('2','7','www.baidu.com');
insert into `tb_user`(`id`,`type`,`title`) values('3','5','www.baidu.com');insert into `TB_USER`(`id`,`type`,`title`) values('4','2','www.baidu.com');
7.6.4集群说明
1)尽可能的控制PXC集群的规模,节点越多,数据同步速度就越慢
2)所有PXC节点的硬件配置要一致,如果不一致,配置低的节点将拖慢数据同步速度
3)PXC集群只能支持Innodb引擎,不支持其他的存储引擎。
8综合应用
前面学习了主从架构、mycat中间件、haproxy负载均衡、PXC集群架构,在实际的项目中,往往不单单是一种架构,更多的使用是混合架构,下面我们模拟一种混合架构的方式完善数据库的集群,操作之前,请清理前面的所有docker。
8.1架构
说明:
1)HAProxy作为负载均衡器
2)部署2个mycat节点作为数据库中间件
3)部署了2个PXC集群节点,作为2个mycat分片,每个PXC集群中有2个节点,作为数据的同步存储。
4)部署了1个主从复制集群
5)非丢失性数据保存至PXC分片,其余数据保存至主从数据库中。
8.2部署PXC集群
6.2.1集群规划
集群1:
节点 | 端口 | 容器名称 | 数据卷 |
node1 | 13306 | pxc-node1 | zjs-v1 |
node2 | 13307 | pxc-node2 | zjs-v2 |
集群2:
节点 | 端口 | 容器名称 | 数据卷 |
node3 | 13308 | pxc-node3 | zjs-v3 |
node4 | 13309 | pxc-node4 | zjs-v4 |
8.2.2集群PXC部署
#创建数据卷(存储路径:/usr/lib/docker/volumes)
docker volume create zjs-v1
docker volume create zjs-v2
docker volume create zjs-v3
docker volume create zjs-v4
#拉取镜像
docker pull percona/percona-xtradb-cluster:5.7
docker tag percona/percona-xtradb-cluster:5.7 pxc
#创建网络
docker network create --subnet=172.30.0.0/24 pxc-network
#==========PXC集群1===================
#第1节点
docker create -p 13306:3306 -v zjs-v1:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e CLUSTER_NAME=pxc --name=pxc_node1 --net=pxc-network --ip=172.30.0.2 pxc
#第2节点(增加了CLUSTER_JOIN参数)
docker create -p 13307:3306 -v zjs-v2:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e CLUSTER_NAME=pxc --name=pxc_node2 -e CLUSTER_JOIN=pxc_node1 --net=pxc-network --ip=172.30.0.3 pxc
#启动集群(先启动第一个节点)
docker start pxc_node1 && docker logs -f pxc_node1
docker start pxc_node2 && docker logs -f pxc_node2
#查看集群节点
show status like 'wsrep_cluster%'
#==========PXC集群2===================
#第1节点
docker create -p 13308:3306 -v zjs-v3:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e CLUSTER_NAME=pxc --name=pxc_node3 --net=pxc-network --ip=172.30.0.4 pxc
#第2节点(增加了CLUSTER_JOIN参数)
docker create -p 13309:3306 -v zjs-v4:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e CLUSTER_NAME=pxc --name=pxc_node4 -e CLUSTER_JOIN=pxc_node3 --net=pxc-network --ip=172.30.0.5 pxc
#启动集群(先启动第一个节点)
docker start pxc_node3 && docker logs -f pxc_node3
docker start pxc_node4 && docker logs -f pxc_node4
#查看集群节点
show status like 'wsrep_cluster%'
8.3部署主从集群
6.3.1集群规划
节点 | 端口 | 容器名称 | 数据卷 |
node5 | 23306 | percona-master01 | /data/mysql/master01 |
node6 | 23307 | percona-slave01 | /data/mysql/slave01 |
8.3.2集群部署
#==========创建主库==========
#创建目录
mkdir -p /data/mysql/master01
cd /data/mysql/master01
mkdir conf data
chmod -R 7777 *
#创建配置文件
vi /data/mysql/master01/conf/my.cnf
#输入以下内容
[mysqld]
log-bin=mysql-bin
server-id=1
lower_case_table_names=1
#创建容器
docker create --name percona-master01 -v /data/mysql/master01/data:/var/lib/mysql -v /data/mysql/master01/conf:/etc/my.cnf.d -p 23306:3306 -e MYSQL_ROOT_PASSWORD=root percona
#启动
docker start percona-master01 && docker logs -f percona-master01
#创建同步账户以及授权
create user 'backup'@'%' identified by '123';
grant replication slave on *.* to 'backup'@'%';
flush privileges;
#查看master状态
SHOW MASTER STATUS;
#查看二进制日志相关的配置项
SHOW GLOBAL VARIABLES LIKE 'binlog%';
#查看server相关的配置项
SHOW GLOBAL VARIABLES LIKE 'server%';
#==========创建从库==========
#创建目录
mkdir -p /data/mysql/slave01
cd /data/mysql/slave01
mkdir conf data
chmod -R 7777 *
#创建配置文件
vi /data/mysql/slave01/conf/my.cnf
#输入以下内容
[mysqld]
log-bin=mysql-bin
server-id=2
lower_case_table_names=1
#创建容器
docker create --name percona-slave01 -v /data/mysql/slave01/data:/var/lib/mysql -v /data/mysql/slave01/conf:/etc/my.cnf.d -p 23307:3306 -e MYSQL_ROOT_PASSWORD=root percona
#启动
docker start percona-slave01 && docker logs -f percona-slave01
#设置master相关信息
CHANGE MASTER TO
MASTER_HOST='192.168.56.11',
MASTER_USER='backup',
MASTER_PASSWORD='123',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000003',
MASTER_LOG_POS=743;
#启动同步
START SLAVE;
#查看master状态
show slave status;
8.4部署mycat集群
8.4.1集群规划
节点 | 端口 |
mycat01 | 18067 |
mycat02 | 28067 |
8.4.2集群部署
节点1:
#========Mycat01=========
cd /data
tar -xvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
cp -r /data/mycat /data/mycat01
cd /data/mycat01
#修改配置文件
vi /data/mycat01/conf/server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="nonePasswordLogin">0</property>
<property name="useHandshakeV10">1</property>
<property name="useSqlStat">0</property>
<property name="useGlobleTableCheck">0</property>
<property name="sequnceHandlerType">2</property>
<property name="subqueryRelationshipCheck">false</property>
<property name="processorBufferPoolType">0</property>
<property name="handleDistributedTransactions">0</property>
<property name="useOffHeapForMerge">1</property>
<property name="memoryPageSize">64k</property>
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<property name="systemReserveMemorySize">384m</property>
<property name="useZKSwitch">false</property>
</system>
<user name="zjs" defaultAccount="true">
<property name="password">123</property>
<property name="schemas">zjs</property>
</user>
</mycat:server>
vi /data/mycat01/conf/schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 配置数据表,不同表访问不同数据库 -->
<schema name="zjs" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_user" dataNode="dn1,dn2" rule="mod-long" />
<table name="tb_ad" dataNode="dn3" />
</schema>
<!-- 配置分片关系 -->
<dataNode name="dn1" dataHost="cluster1" database="zjs" />
<dataNode name="dn2" dataHost="cluster2" database="zjs" />
<dataNode name="dn3" dataHost="cluster3" database="zjs" />
<!-- 配置PXC1连接信息 balance=2:所有节点可读写 -->
<dataHost name="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.56.11:13306" user="root" password="root">
<readHost host="w1r1" url="192.168.56.11:13307" user="root" password="root" />
</writeHost>
</dataHost>
<!-- 配置PXC2连接信息 balance=2:所有节点可读写-->
<dataHost name="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.56.11:13308" user="root" password="root">
<readHost host="w1r1" url="192.168.56.11:1339" user="root" password="root" />
</writeHost>
</dataHost>
<!-- 配置主从连接信息 balance=3:读写分离-->
<dataHost name="cluster3" maxCon="1000" minCon="10" balance="3"
writeType="1" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="w1" url="192.168.56.11:23306" user="root" password="root">
<readHost host="w1r1" url="192.168.56.11:23307" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
vi /data/mycat01/conf/rule.xml
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property>
</function>
#设置JMX端口
vi wrapper.conf
wrapper.java.additional.9=-Dcom.sun.management.jmxremote.port=11985
#设置服务端口
vi server.xml
<property name="serverPort">18067</property>
<property name="managerPort">19067</property>
#启动验证
./mycat console
vi startup_nowrap.sh
:set ff=unix
:wq
#启动
./startup_nowrap.sh
节点2:
#========Mycat02=========
cd /data
#复制mycat01的内容
cp -r /data/mycat01 /data/mycat02
cd /data/mycat02
#设置JMX端口
vi wrapper.conf
wrapper.java.additional.9=-Dcom.sun.management.jmxremote.port=21985
#设置服务端口
vi server.xml
<property name="serverPort">28067</property>
<property name="managerPort">29067</property>
#启动验证
./mycat console
vi startup_nowrap.sh
:set ff=unix
:wq
#启动
./startup_nowrap.sh
8.5部署HAProxy
#拉取镜像
docker pull docker.io/haproxy
#创建目录,用于存放配置文件
mkdir -p /data/haproxy
vi /data/haproxy/haproxy.cfg
global
maxconn 10000
log 127.0.0.1 local0
daemon
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 30s
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-keep-alive 10s
timeout check 10s
maxconn 8000
listen admin_status
bind 0.0.0.0:4001
mode http
stats uri /stats
stats realm "HAProxy statistics"
stats auth admin:admin123
listen mysql_proxy
bind 0.0.0.0:4002
mode tcp
balance roundrobin
option tcplog
server mycat01 192.168.56.11:18067 check port 18067 maxconn 2000 inter 5000 rise 2 fall 3
server mycat02 192.168.56.11:28067 check port 28067 maxconn 2000 inter 5000 rise 2 fall 3
#创建容器
docker create --name=haproxy --net host -v /data/haproxy:/usr/local/etc/haproxy haproxy
#启动容器
docker start haproxy && docker logs -f haproxy
#访问管理界面
http://192.168.56.11:4001/stats
至此,一套完整的综合应用数据库部署已完成。当然,MYSQL集群的演化过程不止这一种,结合不同的组件,可以演化出适合自身的架构来。