Mysql高可用架构

目录

架构选择

主从复制的原理

MySQL 主从形式

MySQL主从复制的方式

Mysql 读写分离

Mycat中间件

Mycat 简介

1、数据库中间件

2、为什么要用Mycat?

3、数据库中间件对比

4、Mycat的作用

5 Mycat的原理

Docker安装Mycat

构建Mycat镜像的过程

docker部署mycat的过程

docker-compose.yml部署Mysql主从(一主多从)加Mycat

Mycat配置读写分离

检验读写分离

Mysql 双主双从+多主多从的配置+MyCat实现主从切换

1 在两个主节点的配置文件中加上这一行

2 两个主节点也要写配置主节点信息的脚本

3 分别设置两个主节点的从节点以及它们的配置主节点信息的脚本

4 执行docker-compose.yml部署Mysql双主双从-加Mycat

Mysql 主从切换





架构选择

市面上有非常厉害牛逼的数据高可用框架,但是对于绝大多数中小型公司来说成本过高,我们选择一个架构时不是它牛逼,厉害就去选择,要考虑实际情况,也要考虑我们项目业务具体的情况,我们项目并发最高不到1W我们难道要去做到10W并发?这明显是过度开发,造成资源和时间的浪费,不仅对Mysql高可用架构是如此,其他中间件高可用架构也是如此。此外我们引入的架构,要尽量减少或者不对我们的项目代码有侵入性,否则维护和开发成本极高

对于一般中小型公司,来说 MHA+Atlas+lvs+keepalived+Mysql主从是一个不错的Mysql高可用架构,搭建成本和维护成本不高,且易用,能够满足主从复制,数据备份,主备迁移,读写分离这几个功能,提高了数据库读的性能分担了单节点的压力,提高数据的高可用性。

主从复制的原理

第一阶段:

我们往主节点中写入一条数据时,会同时添加一条执行记录到主节点的二进制日志(binary log)中

第二阶段:

slave开始一个工作线程——I/O线程。slave I/O线程在master上打开一个普通的连接,然后开始binlog dump process。Binlog dump process从master的二进制日志中读取事件,如果已经跟上master,它会睡眠并等待master产生新的事件。slave I/O线程将这些事件写入slave 的中继日志(relay binlog)。

之后从节点的SQL 线程 会把中继日志(relay binlog)写入到从节点数据中和从节点的binlog中,使其与master中的数据一致。

只要SQL 线程 与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。

注意:

1 此外,在master中也有一个工作线程:和其它MySQL的连接一样,slave在master中打开一个连接也会使得master开始一个线程。复制过程有一个很重要的限制——复制在slave上是串行化的,也就是说master上的并行更新操作不能在slave上并行操作(有一定的延时)。

2 binlog 只存储 DML语句执行记录,不存储查询记录

MySQL 主从复制的主要用途

1 读写分离

在开发工作中,有时候会遇见某个sql 语句需要锁表,导致暂时不能使用读的服务,这样就会影响现有业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作。

2 数据实时(热)备份,当系统中某个节点发生故障时,可以方便的故障切换(主从切换)

 提高数据安全-因为数据已复制到从服务器,从服务器可以终止复制进程,所以,可以在从服务器上备份而不破坏主服务器相应数据;

3 高可用(HA)

因为数据库服务器中的数据都是相同的,当Master挂掉后,可以指定一台Slave充当Master继续保证服务的运行,因为数据是一致性的(如果当插入时Master就挂掉,可能不一致,因为同步也需要时间)当然这种配置不是简单的把一台Slave充当Master,毕竟还要考虑后续的Slave的数据同步到Master。

在主服务器上执行写入和更新,在从服务器上向外提供读功能,达到读写分离的效果,也可以动态地调整从服务器的数量,从而调整整个数据库的性能。

在主服务器上生成实时数据,而在从服务器上分析这些数据,从而提高主服务器的性能。

4 提高性能提高,分担单节点压力

随着系统中业务访问量的增大,如果是单机部署数据库,就会导致I/O访问频率过高。有了主从复制,增加多个数据存储节点,将负载分布在多个从节点上,降低单机磁盘I/O访问的频率,提高单个机器的I/O性能。

MySQL 主从复制带来的问题已经4种主从复制的模式

1 从节点同步需要时间,牺牲了数据的一致性(强制读取主节点)

2 网络原因,导致同步失败,采用半同步的方式(第三方插件)(Mysql中有4种主从复制方式一种是异步复制(默认方式),另一种是半同步复制,第三种是全同步复制

1(半同步,只有检测到中继日志(relay binlog)同步到数据,DML操作才会响应成功)(可以提高数据的一致性的可靠性,但是牺牲写的性能,如果有多个从节点的话,对一个从节点进行版同步即可)

2(异步复制是指主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,写入性能较高)

3 (全同步复制指当主库执行完一个事务,数据已经写到所有的从库中去了DML操作才会响应成功,所以全同步复制的性能必然会收到严重的影响。)

4 GTID复制模式

在传统的复制里面,当发生故障,需要主从切换,需要找到bin-log和pos点(指从库更新到了主库bin-log的哪个位置,这个位置之前都已经更显完毕,这个位置之后未更新),然后将主节点指向新的主节点,相对来说比较麻烦,也容易出错。在MySQL 5.6里面,不用再找bin-log和pos点,我们只需要知道主节点的ip,端口,以及账号密码就行,因为复制是自动的,MySQL会通过内部机制GTID自动找点同步。

基于GTID的复制是MySQL 5.6后新增的复制方式.

GTID (global transaction identifier) 即全局事务ID, 保证了在每个在主库上提交的事务在集群中有一个唯一的ID.

GTID复制原理

在原来基于日志的复制中, 从库需要告知主库要从哪个偏移量进行增量同步, 如果指定错误会造成数据的遗漏, 从而造成数据的不一致.

而基于GTID的复制中, 从库会告知主库已经执行的事务的GTID的值, 然后主库会将所有未执行的事务的GTID的列表返回给从库. 并且可以保证同一个事务只在指定的从库执行一次.通过全局的事务ID确定从库要执行的事务的方式代替了以前需要用bin-log和pos点确定从库要执行的事务的方式。

GTID是由server_uuid和事物id组成,格式为:GTID=server_uuid:transaction_id。server_uuid是在数据库启动过程中自动生成,每台机器的server-uuid不一样。uuid存放在数据目录的auto.conf文件中,而transaction_id就是事务提交时系统顺序分配的一个不会重复的序列号。

主节点更新数据时,会在事务前产生GTID,一起记录到bin-log日志中。从节点的I/O线程将变更的bin-log,写入到本地的relay-log中。SQL线程从relay-log中获取GTID,然后对比本地bin-log是否有记录(所以MySQL从节点必须要开启binary-log)。如果有记录,说明该GTID的事务已经执行,从节点会忽略。如果没有记录,从节点就会从relay-log中执行该GTID的事务,并记录到binlog。在解析过程中会判断是否有主键,如果没有就用二级索引,如果有就用全部扫描。

GTID的好处

GTID使用master_auto_position=1代替了binlog和position号的主从复制搭建方式,相比binlog和position方式更容易搭建主从复制。

GTID方便实现主从之间的failover(主从切换),不用一步一步的去查找position和binlog文件。

GTID模式复制局限性

不能使用create table table_name  select * from table_name模式的语句

在一个事务中既包含事务表的操作又包含非事务表

不支持CREATE TEMPORARY TABLE  or DROP TEMPORARY TABLE语句操作

使用GTID复制从库跳过错误时,不支持sql_slave_skip_counter参数的语法

MySQL 主从形式

1 一主多从(用的最多)(适合读操作比较的的项目)

提高系统的读性能

一主一从仅提高数据的可用性,做数据热备份(容灾)(不能替代我们自己对Mysql数据的备份)(我们自己还是要做一个备份),无需考虑数据的一致性和一主多从(可以提高读性能)从是最常见的主从架构,实施起来简单并且有效,不仅可以实现HA,而且还能读写分离,进而提升集群的并发能力。

注意:因为如果主节点有太多的从节点,就会损耗一部分性能用于replication(复制),一般不弄太多(一般2到4个)(每个从节点的作用可能不同,比如3个从节点用来读,另外一个用来做慢查询或者测试的操作,这就做了数据库的隔离)

2 多主一从 (从5.7开始支持)(适合写操作比较多的操作)

多主一从可以将多个mysql数据库备份到一台存储性能比较好的服务器上。

3 双主复制(适合写操作比较多的操作)

双主复制,也就是互做主从复制,每个master(主)既是master,又是另外一台服务器的slave(从)。这样任何一方所做的变更,都会通过复制应用到另外一方的数据库中。

但是如果有一个主节点挂了就容易造成数据紊乱。

4 级联复制

级联复制模式下,部分slave的数据同步不连接主节点,而是连接从节点。因为如果主节点有太多的从节点,就会损耗一部分性能用于replication(复制),那么我们可以让3~5个从节点连接主节点,其它从节点作为二级或者三级与从节点连接,这样不仅可以缓解主节点的压力,并且对数据一致性没有负面影响。级联复制下从节点也要开启binary log(bin-log)功能。

5 环形多主(适合写操作巨大的操作,瞬间提高写入能力)

以上主从形式,由上到下,性能逐渐下降,尽可能使用前面的

MySQL主从复制的方式

MySQL 主从复制有三种方式:基于SQL语句的复制(statement-based replication,SBR),基于行的复制(row-based replication,RBR),混合模式复制(mixed-based replication,MBR)。对应的bin-log文件的格式也有三种:STATEMENT,  ROW,  MIXED。

1 Statement-base Replication (SBR)  

就是记录sql语句在bin-log中,Mysql 5.1.4 及之前的版本都是使用的这种复制格式。优点是只需要记录会修改数据的sql语句到bin-log中,减少了bin-log日质量,节约I/O,提高性能。缺点是在某些情况下,会导致主从节点中数据不一致(比如sleep(),now()等)。

2 Row-based Relication(RBR)

mysql master将SQL语句分解为基于Row更改的语句并记录在bin-log中,也就是只记录哪条数据被修改了,修改成什么样。优点是不会出现某些特定情况下的存储过程、或者函数、或者trigger的调用或者触发无法被正确复制的问题。缺点是会产生大量的日志,尤其是修改table的时候会让日志暴增,同时增加bin-log同步时间。也不能通过bin-log解析获取执行过的sql语句,只能看到发生的data变更。

3 Mixed-format Replication(MBR)

MySQL NDB cluster 7.3 和7.4 使用的MBR。是以上两种模式的混合,对于一般的复制使用STATEMENT模式保存到bin-log,对于STATEMENT模式无法复制的操作则使用ROW模式来保存,MySQL会根据执行的SQL语句选择日志保存方式。

Mysql 读写分离

我们做了一主多从之后,从节点用于读,主节点用于写,该怎么去实现呢?

方法1 :使用多个数据源,druid+aop可以实现

方式2:使用Mycat或者其他数据库中间件框架

以上两种方式都需要修改源码成本过高,此外,每当我们扩展一个新的从节点都要修改源码,对代码侵入性非常高-

方式3 :加代理节点atlas

我们所有的请求都往代理节点中发,由代理节点(中间件)去解析SQL操作是读还是写,如果是读 就把请求根据负载均衡原理往从节点转发,写就把请求往主节点转发

好处:对代码的侵入性很小,我们增加从节点也不要该代码,直接修改atlas中的配置即可

注意:代理节点atlas也可能会挂,可以让代理节点atlas去整合LVS+keepalived,形成代理节点高可用

docker-compose部署主从+atlas读写分离

mysql的配置文件 my.cnf

[mysqld]

##id

server-id=1            

##启用二进制文件           

log-bin=mysql-bin    

# 需要同步的日志的数据库

binlog-do-db=test1

binlog-do-db=test2

# 不需要同步的日志的数据库

binlog-ignore-db=mysql

binlog-ignore-db=sys

binlog-ignore-db=information_schema

binlog-ignore-db=performance_schema

master主节点的配置文件

注意每个mysql的server-id(实例IP)不能相同

查看主节点状态

show master status;

从节点的配置文件

slave配置文件

[mysqld]
##id
server-id=2            
##启用二进制文件           
log-bin=mysql-bin    
#需要同步的数据库(可以写多个)
replicate-do-db=test1     
replicate-do-db=test2      


#不同步的数据库(即Mysql自带的那些库)
replicate-ignore-db=mysql

配置主节点信息的脚本

设置主节点的ip和用户信息(是个动态的配置要在Mysql节点启动前,或者关闭后执行)

change master to master_host='master', master_user='root', master_password='123456' ;
reset slave;
start slave;

docker-compose.yml部署Mysql主从

version: '3'

services:

  master:

        container_name: master

        image: mysql:5.7

        environment:

            MYSQL_ROOT_PASSWORD: root

        ports:

            - "3306:3306"

        volumes:

            - /usr/local/mysqlcluster/master/my.cnf:/etc/mysql/my.cnf

        command: [

            '--character-set-server=utf8mb4',

            '--collation-server=utf8mb4_unicode_ci',

            '--lower_case_table_names=1'

                ]

        restart: always

slave1:

        container_name: slave1

        image: mysql:5.7

        environment:

            MYSQL_ROOT_PASSWORD: root

        ports:

            - "3305:3306"

        volumes:

            - /usr/local/mysqlcluster/slave1/:/docker-entrypoint-initdb.d/           

- /usr/local/mysqlcluster/slave1/my.cnf:/etc/mysql/my.cnf

        command: [

            '--character-set-server=utf8mb4',

            '--collation-server=utf8mb4_unicode_ci',

            '--lower_case_table_names=1'

                ]

        restart: always

depends_on:

            - master

slave2:

        container_name: slave2

        image: mysql:5.7

        environment:

            MYSQL_ROOT_PASSWORD: root

        ports:

            - "3304:3306"

        volumes:

            - /usr/local/mysqlcluster/slave2/:/docker-entrypoint-initdb.d/           

- /usr/local/mysqlcluster/slave2/my.cnf:/etc/mysql/my.cnf

        command: [

            '--character-set-server=utf8mb4',

            '--collation-server=utf8mb4_unicode_ci',

            '--lower_case_table_names=1'

                ]

        restart: always

depends_on:

            - master

360atlas:

        container_name: atlas1

        image: 5a6a2ff997c6

        ports:

            - "1234:1234"

- "2345:2345"

        volumes:

            - /usr/local/mysql-proxy/conf:/usr/local/mysql-proxy/conf

  command: [

            '/usr/local/mysql-proxy/bin/mysql-proxyd test start'

                ]         

        restart: always

depends_on:

            - master

- slave1

- slave2

创建好主从之后,我们要测试从节点和主节点的状态

show master STATUS;

show SLAVE STATUS; 从节点中只要slave_io_running和slave_sql_running就代表主从复制成功了

此外,我们在主节点创建要主从复制的表再查看从节点是否复制过来了

atlas配置文件

[mysql-proxy]

#带#号的为非必需的配置项目

#管理接口的用户名

admin-username = root

#管理接口的密码

admin-password = root

#Atlas后端连接的MySQL主库的IP和端口,可设置多项,用逗号分隔

proxy-backend-addresses = 192.168.43.180:3306

#Atlas后端连接的MySQL从库的IP和端口,@后面的数字代表权重,用来作负载均衡,若省略则默认为1,可设置多项,用逗号分隔

proxy-read-only-backend-addresses = 192.168.43.180:3305@1,192.168.43.180:3304@2

#用户名与其对应的加密过的MySQL密码,密码使用PREFIX/bin目录下的加密程序encrypt加密,下行的user1和user2为示例,将其替换为你的MySQL的用户名和加密密码!

pwds = root:DAJnl8cVzy8=,root:DAJnl8cVzy8=,root:DAJnl8cVzy8=

#设置Atlas的运行方式,设为true时为守护进程方式,设为false时为前台方式,一般开发调试时设为false,线上运行时设为true,true后面不能有空格。

daemon = true

#设置Atlas的运行方式,设为true时Atlas会启动两个进程,一个为monitor,一个为worker,monitor在worker意外退出后会自动将其重启,设为false时只有worker,没有monitor,一般开发调试

时设为false,线上运行时设为true,true后面不能有空格。

keepalive = true

#工作线程数,对Atlas的性能有很大影响,可根据情况适当设置

event-threads = 8

#日志级别,分为message、warning、critical、error、debug五个级别

log-level = message

#日志存放的路径

log-path = /usr/local/mysql-proxy/log

#SQL日志的开关,可设置为OFF、ON、REALTIME,OFF代表不记录SQL日志,ON代表记录SQL日志,REALTIME代表记录SQL日志且实时写入磁盘,默认为OFF

sql-log = REALTIME

#慢日志输出设置。当设置了该参数时,则日志只输出执行时间超过sql-log-slow(单位:ms)的日志记录。不设置该参数则输出全部日志。

#sql-log-slow = 10

#实例名称,用于同一台机器上多个Atlas实例间的区分

instance = test

#Atlas监听的工作接口IP和端口

proxy-address = 0.0.0.0:1234

#Atlas监听的管理接口IP和端口

admin-address = 0.0.0.0:2345

#分表设置,此例中person为库名,mt为表名,id为分表字段,3为子表数量,可设置多项,以逗号分隔,若不分表则不需要设置该项

#tables = person.mt.id.3

#默认字符集,设置该项后客户端不再需要执行SET NAMES语句

charset = utf8

#允许连接Atlas的客户端的IP,可以是精确IP,也可以是IP段,以逗号分隔,若不设置该项则允许所有IP连接,否则只允许列表中的IP连接

#client-ips = 127.0.0.1, 192.168.1

#Atlas前面挂接的LVS的物理网卡的IP(注意不是虚IP),若有LVS且设置了client-ips则此项必须设置,否则可以不设置

#lvs-ips = 192.168.1.1

docker拉取并启动 atlas

1 docker pull mybbcat/docker-360atlas

2 docker run -it --name atlas -v /usr/local/mysql-proxy/conf:/usr/local/mysql-proxy/conf -v /usr/local/mysql-proxy/log:/usr/local/mysql-proxy/log -p 1234:1234 -p 2345:2345  mybbcat/docker-360atlas /bin/bash

#注意这里不是后台启动的,exit 后atlas就被关闭了,要重启该容器,必须进入容器再次执行3步骤命令

3 /usr/local/mysql-proxy/bin/mysql-proxyd test start #启动atlas

4 查看日志 /usr/local/mysql-proxy/log 看是否启动成功

atlas容器执行该命令生成对应的加密密码(root为数据库密码)

/usr/local/mysql-proxy/bin/encrypt root #执行后会生成一个加密后的密码

java整合atlas

注意:

1 使用java去连接atlas时,直接连接Mysql主从中的主库即可,不要直接连接atlas:1234,会无法连接,只能用navicat去连接atlas的管理端

2 此外不要使用默认的springboot默认的连接池HikariCP,会报错,要引入和指定阿里的druid连接池

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    #docker环境中的mysql
    #url: jdbc:mysql://192.168.0.13:3306/fmjava?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    # 本地环境url
    url: jdbc:mysql://192.168.43.180:1234/test1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    password: root
    username: root
    #jdbc:mysql://127.0.0.1:3307/mybatisplustest?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    type: com.alibaba.druid.pool.DruidDataSource

java连接成功后测试几个查询和DML然后查看atlas日志,可以发现读都在从节点,写都在主节点,引入成功

Mycat中间件

Mycat 简介

Mycat 是数据库中间件。

1、数据库中间件

中间件:是一类连接软件组件和应用的计算机软件,以便于软件各部件之间的沟通。例子:Tomcat,web中间件。

数据库中间件:连接java应用程序和数据库

2、为什么要用Mycat

① Java与数据库紧耦合(Url地址写死,一旦Mysql做多节点要切换数据库时就要改代码)。

使用Mycat后,Java直接连接Mycat即可,不用管有多少个数据库节点,所有的数据库节点都交给Mycat去管理

② 高访问量高并发对数据库的压力。

读写分离-作用与atlas一样,读请求负载均衡分发到从节点中,写请求直接到主节点中

③ 读写请求数据不一致(主从复制)

3、数据库中间件对比

① Cobar属于阿里B2B事业群,始于2008年,在阿里服役3年多,接管3000+个MySQL数据库的schema,

集群日处理在线SQL请求50亿次以上。由于Cobar发起人的离职,Cobar停止维护。

Mycat是开源社区在阿里cobar基础上进行二次开发,解决了cobar存在的问题,并且加入了许多新的功能在其中。青出于蓝而胜于蓝。

③ OneProxy基于MySQL官方的proxy思想利用c进行开发的,OneProxy是一款商业收费的中间件。舍弃了一些功能,专注在性能和稳定性上。

④ kingshard由小团队用go语言开发,还需要发展,需要不断完善。

⑤ Vitess是Youtube生产在使用,架构很复杂。不支持MySQL原生协议,使用需要大量改造成本。

Atlas360团队基于mysql proxy改写,功能还需完善,高并发下不稳定

(文档较少,与SpringBoot等框架整合的有问题,社区无人更新)

⑦ MaxScale是mariadb(MySQL原作者维护的一个版本) 研发的中间件

⑧ MySQLRoute是MySQL官方Oracle公司发布的中间件

4、Mycat的作用

1 读写分离

读请求分发到从节点,当有多个从节点的时候还可以负载均衡,写请求分发到主节点)(提高了数据库整体的读性能,缓解了单节点的压力)

2 数据分片(分库分表)

垂直拆分(分库)、水平拆分(分表)、垂直+水平拆分(分库分表)

水平分库:一个数据库中的数据量很大,把同一个表中的数据按一定的规则拆分到多个数据库中(每个数据库中都有相同结构的该表)。这些数据库一般分布在多个数据库节点中

解决的是:单库容量达到了极限。

水平分表: 一个表中的数据量可能非常大,这时我们可以把这张表中的数据,分散到多个表中去存储。水平分表是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。

解决的是:单表容量达到了极限。

垂直分库是指按照业务将表进行拆分,分布到不同的数据库上面,这些数据库一般分布在多个数据库节点中,每个库可以放在不同的服务器上,它的核心理念是专库专用。

解决业务层面的耦合,业务清晰,这样也将数据或者说压力分担到不同的库上面

系统被切分成了,用户,订单交易,支付几个库。如果不使用Mycat的话,对于单体架构的java程序就要配置多个数据源,对于微服务架构,也要在不同的微服务配置中修改数据库URL,耦合性非常强,当使用Mycat后我们根本不用管有几个库都交给Mycat去管理即可,JAVA直接访问Mycat

垂直分库的原则:有紧密关联关系的表应该在一个库里,相互没有关联关系的表可以分到不同的库里

如 订单表600W,订单表详情600W,订单状态表20条,客户表20W数据

客户表分在一个数据库,另外三张都需要关联查询(关联查询一般不是做JOIN),分在另外一个数据库。

跟用户相关的表放在一个库中,跟订单相关的表放在一个库中,跟商品相关的表放在一个库中,这些库最好在不同的数据库节点中

垂直分表定义:将一个表按照字段分成多表,每个表存储其中一部分字段。

它带来的提升是:

1.为了避免IO争抢并减少锁表的几率,查看详情的用户与商品信息浏览互不影响

2.充分发挥热门数据的操作效率,商品信息的操作的高效率不会被商品描述的低效率所拖累。

当我们进行分库分表后,数据被拆分到不同的数据库上,数据库又在不同的数据库节点中,那我们应该如何正确的找到数据呢?

解决方式1 ;在Java中配置多个数据源,然后编写规则,什么数据去哪个库中查,什么数据写到哪个库中去,耦合性太高了。

解决方式2:使用数据库中间件(如Mycat),我们根本就不用分库分表后数据哪个节点查,数据储存到哪个节点中去,全部交给Mycat去解决

3 多数据源整合

可以同时整合数据库集群,关系型数据库 Mysql,orcal,   Nosql:redis

5 Mycat的原理

       Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的 SQL 语句,首先对 SQL 语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户

 这种方式把数据库的分布式从代码中解耦出来,程序员察觉不出来后台使用Mycat 还是MySQL。

Docker安装Mycat

docker pull  longhronshens/mycat-docker

docker run --name mycat --privileged=true -p 8066:8066 -p 9066:9066 -d longhronshens/mycat-docker

不过网上的镜像大多都有问题BUG无法实现读写分离比如上面的镜像,这里弄了一个自定义镜像(后面会把该镜像放到GITHUB上,供大家直接下载)

构建Mycat镜像的过程

参考文档:基于docker创建mycat的镜像_m0_37581001的博客-CSDN博客_mycat镜像

下载java jre的tar

https://www.java.com/zh-CN/download/linux_manual.jsp

Mycat的tar请自行下载尽量使用1.6.7.1其他版本有BUG

From centos:7

MAINTAINER  xxr

#添加java的环境

RUN  mkdir /usr/local/java

ADD  jre-8u301-linux-x64.tar.gz  /usr/local/java

RUN  ln -s /usr/local/java/jre1.8.0_301 /usr/local/java/jre

ENV  JRE_HOME /usr/local/java/jre

ENV  JAVA_HOME /usr/local/java/jre

ENV  CLASSPATH ${JRE_HOME}/lib

ENV  PATH $PATH:${JRE_HOME}/bin

ADD Mycat-server-1.6.7.1-release-20190627191042-linux.tar.gz /usr/local

#容器打开端口

EXPOSE 8066 9066

ENV MYCAT_HOME=/usr/local/mycat

CMD /usr/local/mycat/bin/mycat console

docker build -t xxrmycat:v1 . #构建镜像

docker部署mycat的过程

可以用的镜像(自己构建的镜像)(拉取我的阿里云镜像)

docker pull registry.cn-hangzhou.aliyuncs.com/xxr-middleware/xxr-mycat:v1

docker run --name mycatsss -p 8066:8066 -itd xxrmycat:v1

docker cp xxrmycat :/usr/local/mycat/conf /data/mycat

docker cp xxrmycat :/usr/local/mycat/logs /data/mycat

里面的配置文件

1 schema.xml:定义逻辑库,表、分片节点等内容

server.xml: 定义用户以及系统相关变量,如端口等

3 rule.xml定义分片规则

修改配置文件:

schema.xml:定义逻辑库,表、分片节点等内容

删除<schema>(逻辑库)标签间的表信息,<dataNode>标签只留一个,<dataHost>标签只留一个,<writeHost>

修改配置Mycat的用户信息

server.xml: 定义用户以及系统相关变量,如端口等

只修改里面配置Mycat的用户信息,可以删除掉只读用户

正式启动容器:

docker run --name xxrmycat -p 8066:8066 -p 9066:9066 -v /usr/local/mycat/conf/schema.xml:/usr/local/mycat/conf/schema.xml -v /usr/local/mycat/conf/rule.xml:/usr/local/mycat/conf/rule.xml -v /usr/local/mycat/conf/server.xml:/usr/local/mycat/conf/server.xml -v /usr/local/mycat/logs:/usr/local/mycat/logs -itd xxrmycat:v1 

使用Navicat连接:

9066是Mycat管理端口,用来维护Mycat的

8066 是Mycat的访问端口,java程序就是访问这个端口

ip:8066 mycat root12测试是否连接成功

docker-compose.yml部署Mysql主从(一主多从)加Mycat

version: '3'
services:
  master:
        container_name: master
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3306:3306"
        volumes:
            - /usr/local/mysqlcluster/master/my.cnf:/etc/mysql/my.cnf
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
  slave1:
        container_name: slave1
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3305:3306"
        volumes:
            - /usr/local/mysqlcluster/slave1/:/docker-entrypoint-initdb.d/           
            - /usr/local/mysqlcluster/slave1/my.cnf:/etc/mysql/my.cnf
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
        depends_on:
            - master
     slave2:
        container_name: slave2
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3304:3306"
        volumes:
            - /usr/local/mysqlcluster/slave2/:/docker-entrypoint-initdb.d/           
            - /usr/local/mysqlcluster/slave2/my.cnf:/etc/mysql/my.cnf
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
        depends_on:
            - master
     mycat:
        container_name: xxrmycat
        image: xxrmycat:v1
        ports:
            - "8066:8066"
            - "9066:9066"
        volumes:
            - /usr/local/mycat/conf/schema.xml:/usr/local/mycat/conf/schema.xml
            - /usr/local/mycat/conf/rule.xml:/usr/local/mycat/conf/rule.xml
            - /usr/local/mycat/conf/server.xml:/usr/local/mycat/conf/server.xml         
            - /usr/local/mycat/logs:/usr/local/mycat/logs
        restart: always
        depends_on:
            - master
            - slave1
            - slave2

Mycat配置读写分离

修改 schema.xml中的 <dataHost>的balance属性,通过此属性配置读写分离的类型改为1

负载均衡类型,目前的取值有4 种:

(1) balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。(默认选项)

(2) balance="1",全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。

(3) balance="2",所有读操作都随机的在 writeHost、readhost 上分发。

(4) balance="3",所有读请求随机的分发到 readhost 执行,writerHost 不负担读压力

修改Mycat配置文件schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">


        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        </schema>
        <dataNode name="dn1" dataHost="host1" database="testdb" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="1"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="500">
                <heartbeat>select user()</heartbeat>
                <!-- can have multi write hosts -->
                <writeHost host="hostW1" url="192.168.43.180:3306" user="root"
                                   password="root">
                        <!-- can have multi read hosts -->
                        <readHost host="hostS1" url="192.168.43.180:3305" user="root" password="root" />
                        <readHost host="hostS2" url="192.168.43.180:3304" user="root" password="root" />
                </writeHost>
        </dataHost>


</mycat:schema>

检验读写分离

先在两个从节点中插入不同的数据如Slave1插入数据s1,Slave2插入数据s1,然后在主节点中插入数据is master,此时因为我们设置的balance是1,此时master节点中的数据is master而Slave1中的数据是is master和s1,而Slave2中的数据是is master和s2,当我们访问Mycat只能访问到,is master和s2和is master和s1,这就表明了读写分离的成功,我们此时只能访问到从节点中的数据了,无法读取到主节点中的数据了,此时继续在Mycat写入数据,发现Slave1和Slave2还是可以共享到,说明数据是在master节点写入的,不然从节点无法复制到数据

Mysql 双主双从+多主多从的配置+MyCat实现主从切换

在一主多从模式下,一旦主节点挂了,就无法写入数据了,我们可以使用MHA去进行主从切换,当然也可以利用更简单的框架MyCat去实现主从切换

MyCat要实现主从切换必须是双主双从,或者双主多从模式或者多主多从模式

双主双从或者多主多从模式下相当于所有的节点都是从节点

配置过程

1 在两个主节点的配置文件中加上这一行

log-slave-updates #当一个节点即是主节点又是从节点时要加上这个。

2 两个主节点也要写配置主节点信息的脚本

设置主节点的ip和用户信息(是个动态的配置要在Mysql节点启动前,或者关闭后执行)

分别未

change master to master_host='master2', master_user='root', master_password='123456' ;
reset slave;
start slave;

change master to master_host='master', master_user='root', master_password='123456' ;
reset slave;
start slave;

3 分别设置两个主节点的从节点以及它们的配置主节点信息的脚本

4 执行docker-compose.yml部署Mysql双主双从-加Mycat

version: '3'
services:
  master:
        container_name: master
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3306:3306"
        volumes:
            - /usr/local/mysqlcluster/master/my.cnf:/etc/mysql/my.cnf
            - /usr/local/mysqlcluster/master/:/docker-entrypoint-initdb.d/
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
  maser2:
        container_name: master2
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3303:3306"
        volumes:
            - /usr/local/mysqlcluster/master2/my.cnf:/etc/mysql/my.cnf
            - /usr/local/mysqlcluster/master2/:/docker-entrypoint-initdb.d/
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
  slave1:
        container_name: slave1
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3305:3306"
        volumes:
            - /usr/local/mysqlcluster/slave1/:/docker-entrypoint-initdb.d/
            - /usr/local/mysqlcluster/slave1/my.cnf:/etc/mysql/my.cnf
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
        depends_on:
            - master
  slave2:
        container_name: slave2
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3304:3306"
        volumes:
            - /usr/local/mysqlcluster/slave2/:/docker-entrypoint-initdb.d/
            - /usr/local/mysqlcluster/slave2/my.cnf:/etc/mysql/my.cnf
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
        depends_on:
            - master
  xmycat:
        container_name: xxrmycats
        image: cd3a3c309528
        ports:
            - "8066:8066"
            - "9066:9066"
        volumes:
            - /usr/local/mycat/conf/:/usr/local/mycat/conf/
            - /usr/local/mycat/logs:/usr/local/mycat/logs
        restart: always
        depends_on:
            - master
            - maser2
            - slave1
            - slave2

#balance="1": 全部的readHost与stand by writeHost参与select语句的负载均衡。#writeType="0": 所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个#writeType="1",所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐#writeHost,重新启动后以切换后的为准,切换记录在配置文件中:dnindex.properties 。

Mycat配置读写分离

修改 schema.xml中的 <dataHost>的balance属性,通过此属性配置读写分离的类型改为1

负载均衡类型,目前的取值有4 种:

(1) balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。(默认选项)

(2) balance="1",全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。

(3) balance="2",所有读操作都随机的在 writeHost、readhost 上分发。

(4) balance="3",所有读请求随机的分发到 readhost 执行,writerHost 不负担读压力

修改Mycat配置文件schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">


        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        </schema>
        <dataNode name="dn1" dataHost="host1" database="testdb" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="1"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="500">
                <heartbeat>select user()</heartbeat>
                <!-- can have multi write hosts -->
                <writeHost host="hostW1" url="192.168.43.180:3306" user="root"
                                   password="root">
                        <!-- can have multi read hosts -->
                        <readHost host="hostS1" url="192.168.43.180:3305" user="root" password="root" />
                        <readHost host="hostS2" url="192.168.43.180:3304" user="root" password="root" />
                </writeHost>
        </dataHost>


</mycat:schema>

检验读写分离

先在两个从节点中插入不同的数据如Slave1插入数据s1,Slave2插入数据s1,然后在主节点中插入数据is master,此时因为我们设置的balance是1,此时master节点中的数据is master而Slave1中的数据是is master和s1,而Slave2中的数据是is master和s2,当我们访问Mycat只能访问到,is master和s2和is master和s1,这就表明了读写分离的成功,我们此时只能访问到从节点中的数据了,无法读取到主节点中的数据了,此时继续在Mycat写入数据,发现Slave1和Slave2还是可以共享到,说明数据是在master节点写入的,不然从节点无法复制到数据

Mysql 双主双从+多主多从的配置+MyCat实现主从切换

在一主多从模式下,一旦主节点挂了,就无法写入数据了,我们可以使用MHA去进行主从切换,当然也可以利用更简单的框架MyCat去实现主从切换

MyCat要实现主从切换必须是双主双从,或者双主多从模式或者多主多从模式

双主双从或者多主多从模式下相当于所有的节点都是从节点

1 在两个主节点的配置文件中加上这一行

log-slave-updates #当一个节点即是主节点又是从节点时要加上这个。

2 两个主节点也要写配置主节点信息的脚本

设置主节点的ip和用户信息(是个动态的配置要在Mysql节点启动前,或者关闭后执行)

分别未

change master to master_host='master2', master_user='root', master_password='123456' ;
reset slave;
start slave;

change master to master_host='master', master_user='root', master_password='123456' ;
reset slave;
start slave;

3 分别设置两个主节点的从节点以及它们的配置主节点信息的脚本

4 执行docker-compose.yml部署Mysql双主双从-加Mycat

version: '3'
services:
  master:
        container_name: master
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3306:3306"
        volumes:
            - /usr/local/mysqlcluster/master/my.cnf:/etc/mysql/my.cnf
            - /usr/local/mysqlcluster/master/:/docker-entrypoint-initdb.d/
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
  maser2:
        container_name: master2
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3303:3306"
        volumes:
            - /usr/local/mysqlcluster/master2/my.cnf:/etc/mysql/my.cnf
            - /usr/local/mysqlcluster/master2/:/docker-entrypoint-initdb.d/
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
  slave1:
        container_name: slave1
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3305:3306"
        volumes:
            - /usr/local/mysqlcluster/slave1/:/docker-entrypoint-initdb.d/
            - /usr/local/mysqlcluster/slave1/my.cnf:/etc/mysql/my.cnf
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
        depends_on:
            - master
  slave2:
        container_name: slave2
        image: mysql:5.7
        environment:
            MYSQL_ROOT_PASSWORD: root
        ports:
            - "3304:3306"
        volumes:
            - /usr/local/mysqlcluster/slave2/:/docker-entrypoint-initdb.d/
            - /usr/local/mysqlcluster/slave2/my.cnf:/etc/mysql/my.cnf
        command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci',
            '--lower_case_table_names=1'
                ]
        restart: always
        depends_on:
            - master
  xmycat:
        container_name: xxrmycats
        image: cd3a3c309528
        ports:
            - "8066:8066"
            - "9066:9066"
        volumes:
            - /usr/local/mycat/conf/:/usr/local/mycat/conf/
            - /usr/local/mycat/logs:/usr/local/mycat/logs
        restart: always
        depends_on:
            - master
            - maser2
            - slave1
            - slave2

#balance="1": 全部的readHost与stand by writeHost参与select语句的负载均衡。

#writeType="0": 所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个#writeType="1",所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐

#writeHost,重新启动后以切换后的为准,切换记录在配置文件中:dnindex.properties 。

Mycat实现主从切换与读写分离

修改Mycat配置文件schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">


        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        </schema>
        <dataNode name="dn1" dataHost="host1" database="testdb" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="1"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="500">
                <heartbeat>select user()</heartbeat>
                <!-- can have multi write hosts -->
                <writeHost host="hostW1" url="192.168.43.45:3306" user="root"
                                   password="root">
                        <!-- can have multi read hosts -->
                        <readHost host="hostS1" url="192.168.43.45:3305" user="root" password="root" />
                </writeHost>
                 <writeHost host="hostW2" url="192.168.43.45:3303" user="root" password="root">
                        <!-- can have multi read hosts -->
                        <readHost host="hostS2" url="192.168.43.45:3304" user="root" password="root" />
                </writeHost>
        </dataHost>


</mycat:schema>

(需要有stand by writeHost 备用写节点,所以必须是双主双从,或者多主多从架构采用

#switchType="1": 1 默认值,自动切换。

#                        -1 表示不自动切换

#                        2 基于 MySQL 主从同步的状态决定是否切换。

检验主从切换与读写分离

假设 slave1的主节点是master1 , slave2的主节点是master2,master2与master1互为主从,且我们在配置中设置master2为备用写节点master1为主节点

先在从节点中插入不同的数据如Slave1,Slave2,然后在主节点中插入数据is master,此时因为我们设置的balance是1,所以备用写节点也会参与到读请求中,此时我们访问Mycat可以访问到三份不同的数据,第一份是备用写节点数据,第二份是Slave1,第三份是Slave2,这就表明了读写分离的成功,当我们关闭主节点时,继续访问Mycat,发现只能访问到Slave2的数据,且可以继续向Mycat中写入数据,这就证明了主从切换的成功,当之前的主节点master1恢复以后我们又可以查到三份数据了,但是此时的主节点是master2。

注意:

双主双从模式夏当主节点挂了,则备用写节点升级为主节点,此时只剩下了备用写节点和它的从节点可以使用了(虽然可以继续写入数据,但是读能力已经下降到和单节点差不多了,所以我们在设计双主双从时,应该设计未双主多从,就双主中每个主节点至少对应两个从节点,不然一旦发生主从切换,读能力就下降到与单节点差不都了)。

如果之前挂了的主节点又恢复了的话,它只能当stand by writeHost

Mysql 主从切换

上面完成了Mysql主从复制和读写分离,不过如果主节点挂了该怎么办,肯定要自动选一个从节点升级为主节点,这里使用的MHA框架或者Mycat框架去实现,当然使用和学习成本的话还是Mycat更低

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值