Mycat学习笔记

1,265 阅读16分钟

1、Mycat用途

1.1 读写分离

读写分离.png

1.2 数据分片

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

分库分表.png

1.3、多数据源整合

多数据源整合.png

2、Mycat原理

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

image.png

当 Mycat 收到一个 SQL 时,会先解析这个 SQL,查找涉及到的表,然后看此表的定义,如果有分片规则,
则获取到 SQL 里分片字段的值,并匹配分片函数,得到该 SQL 对应的分片列表,然后将 SQL 发往这些分片去执
行,最后收集和处理所有分片返回的结果数据,并输出到客户端。以 select * from Orders where prov=?语句为
例,查到 prov=wuhan,按照分片函数,wuhan 返回 dn1,于是 SQL 就发给了 MySQL1,去取 DB1 上的查询
结果,并返回给用户。

3、Mycat安装与启动

3.1 Mycat安装

mac版本下载地址
github.com/MyCATApache…
主要配置文件 config目录下

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

  2. rule.xml:定义分片规则

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

3.2 Mycat启动

配置server.xml

	<user name="mycat">
		<property name="password">123456</property>
		<property name="schemas">TESTDB</property>
	</user>

配置sehema.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="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <!-- can have multi write hosts -->
        <writeHost host="hostM1" url="192.168.1.128:3306" user="root" password="123123">
            <!-- can have multi read hosts -->
            <readHost host="hostS1" url="192.168.1.127:3306" user="root" password="123123" />
        </writeHost>
    </dataHost>
</mycat:schema>

启动mycat

#控制台启动
./mycat console
#后台启动
./mycat start

登陆mycat,验证配置是否dnal

mysql -umycat -p123456 -P 9066 -h *.*.*.*

4、读写分离配置

4.1 一主一从环境搭建

对于 MySQL 来说,标准的读写分离是主从模式,一个写节点 Master 后面跟着多个读节点,读节点的数量取
决于系统的压力,通常是 1-3 个读节点的配置,如下图所示:

image.png

MySQL 支持更多的主从复制的拓扑关系,如下图所示,但通常我们不会采用双向主从同步以及环状的拓扑:
image.png

MySQL主从复制的原理如下:
image.png

第一步是在主库上记录二进制日志(Binary log)。在每次准备提交事务完成数 据更新前,主库将数
据更新的事件记录到二进制日志中。MySQL 会按事务提交的顺序 而非每条语句的执行顺序来记录二进制日志。
在记录二进制日志后,主库会告诉存储引 擎可以提交事务了。


第二步,备库将主库的二进制日志复制到其本地的中继日志(Relay Log)中。首先,备库会启动一个工作线程,称为 I/O 线程,I/O 线程跟主库建立一个普通的客户端连接,然后在主库上启动一个特殊的二进制转储(binhg dump、线程(该线程没有对应的 SQL 命令),这个二进制转储 线程会读取主库上二进制日志中的事件。它不会对事件进行轮询。如果该线程 追赶上了主库,它将进入睡眠状态, 直到主库发送信号量通知其有新的事件产生时才会 被唤醒,备库 I/O 线程会将接收到的事件记录到中继日志中。

第三步:备库的 SQL 线程从中继日志中读取事件并在备库执行,从而实现备库数据的更新。

主机配置

修改配置文件:vim /etc/my.cnf
#主服务器唯一ID
server-id=1
#启用二进制日志
log-bin=mysql-bin
# 设置不要复制的数据库(可设置多个)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
#设置需要复制的数据库
binlog-do-db=需要复制的主数据库名字
#设置logbin格式,支持STATEMENT,ROW,MIXED
binlog_format=STATEMENT

从机配置

修改配置文件:vim /etc/my.cnf
#从服务器唯一ID
server-id=2
#启用中继日志
relay-log=mysql-relay

主机、从机重启 MySQL 服务

主机从机都关闭防火墙

在主机上建立帐户并授权 slave

#在主机MySQL里执行授权命令
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' IDENTIFIED BY '123123';

#查询master的状态
show master status;


在从机上配置复制的主机

#复制主机的命令
CHANGE MASTER TO MASTER_HOST='主机的IP地址',
MASTER_USER='slave',
MASTER_PASSWORD='123123',
MASTER_LOG_FILE='mysql-bin.具体数字',MASTER_LOG_POS=具体值;

#启动从服务器复制功能
start slave;

#查看从服务器状态
show slave status\G;

image.png

修改 Mycat 的配置文件 schema.xml

配置dataHost节点下的balance属性,这里可以配置为2

负载均衡类型,目前的取值有 3 种:
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",所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力,
注意 balance=3 只在 1.4 及其以后版本有,1.3 没有。

4.2 双主双从环境搭建

一个主机 m1 用于处理所有写请求,它的从机 s1 和另一台主机 m2 还有它的从机 s2 负责所有读请
求。当 m1 主机宕机后,m2 主机负责写请求,m1、m2 互为备机。

image.png

mysql服务器信息

编号 角色 IP
1 Master1 192.168.140.128
2 Slave2 192.168.140.127
3 Master2 192.168.140.126
4 Slave2 192.168.140.125

双主机配置-Master1

#主服务器唯一ID
server-id=1
#启用二进制日志
log-bin=mysql-bin
# 设置不要复制的数据库(可设置多个)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
#设置需要复制的数据库
binlog-do-db=需要复制的主数据库名字
#设置logbin格式
binlog_format=STATEMENT
# 在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates 
#表示自增长字段每次递增的量,指自增字段的起始值,其默认值是1,取值范围是1 .. 65535
auto-increment-increment=2 
# 表示自增长字段从哪个数开始,指字段一次递增多少,他的取值范围是1 .. 65535
auto-increment-offset=1

双主机配置-Master2

#主服务器唯一ID
server-id=3 #启用二进制日志
log-bin=mysql-bin
# 设置不要复制的数据库(可设置多个)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
#设置需要复制的数据库
binlog-do-db=需要复制的主数据库名字
#设置logbin格式
binlog_format=STATEMENT
# 在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates 
#表示自增长字段每次递增的量,指自增字段的起始值,其默认值是1,取值范围是1 .. 65535
auto-increment-increment=2 
# 表示自增长字段从哪个数开始,指字段一次递增多少,他的取值范围是1 .. 65535
auto-increment-offset=2

双主机配置-Salve1

#从服务器唯一ID
server-id=2
#启用中继日志
relay-log=mysql-relay

双主机配置-Salve2

#从服务器唯一ID
server-id=4 #启用中继日志
relay-log=mysql-relay

双主机、双从机重启 mysql 服务

主机从机都关闭防火墙

在两台主机上建立帐户并授权 slave

#在主机MySQL里执行授权命令
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' IDENTIFIED BY '123123';
#查询Master1的状态
show master status;

查询master1状态

image.png

查询master2状态


image.png

在从主机上配置对就的Master主机

CHANGE MASTER TO MASTER_HOST='主机的IP地址',
MASTER_USER='slave',
MASTER_PASSWORD='123123',
MASTER_LOG_FILE='mysql-bin.具体数字',MASTER_LOG_POS=具体值;

slave1配置

image.png

slave2配置
image.png

启动slave

start slave

查询slave状态

slave1复制的master1

image.png

slave2复制的master2
image.png

两个master的复制

Master2 复制 Master1,Master1 复制 Master2

CHANGE MASTER TO MASTER_HOST='主机的IP地址',
MASTER_USER='slave',
MASTER_PASSWORD='123123',
MASTER_LOG_FILE='mysql-bin.具体数字',MASTER_LOG_POS=具体值;

修改sehema.xml

balance="1": 全部的readHost与stand by writeHost参与select语句的负载均衡。
writeType="0": 所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个

<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="100">
    <heartbeat>select user()</heartbeat>
    <!-- can have multi write hosts -->
    <writeHost host="hostM1" url="192.168.140.128:3306" user="root" password="123123">
        <!-- can have multi read hosts -->
        <readHost host="hostS1" url="192.168.140.127:3306" user="root" password="123123" />
    </writeHost>
    <writeHost host="hostM2" url="192.168.140.126:3306" user="root" password="123123">
        <!-- can have multi read hosts -->
        <readHost host="hostS2" url="192.168.140.125:3306" user="root" password="123123" />
    </writeHost>
</dataHost>

5、垂直拆分-分库

一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,
分布到不同的数据库上面。

image.png

5.1 修改sehema.xml

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
    <table name="customer" dataNode="dn2"></table>
</schema>
<dataNode name="dn1" dataHost="host1" database="orders" />
<dataNode name="dn2" dataHost="host2" database="orders" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
    <heartbeat>select user()</heartbeat>
    <!-- can have multi write hosts -->
    <writeHost host="hostM1" url="192.168.140.128:3306" user="root" password="123123">
    </writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
    <heartbeat>select user()</heartbeat>
    <!-- can have multi write hosts -->
    <writeHost host="hostM2" url="192.168.140.127:3306" user="root" password="123123">
    </writeHost>
</dataHost>

5.2 新建空数据库

# 分库操作不是在原来的老数据库上进行操作,需要准备两台机器分别安装新的数据库
#在数据节点 dn1、dn2 上分别创建数据库 orders
CREATE DATABASE orders;

5.3 启动Mycat

./mycat console

5.4 访问Mycat进行分库

image.png

6、水平拆分-分表

水平拆分:

按照某个字段的某种规则来分散到多个库之中, 每个表中包含一部分数据。

6.1 实现分表

以订单表为例进行分表,分表字段为用户id。同一个用户的所有订单都在同一个节点。

修改sehema.xml配置文件

#为 orders 表设置数据节点为 dn1、dn2,并指定分片规则为 mod_rule(自定义的名字)
<table name="orders" dataNode="dn1,dn2" rule="mod_rule" ></table>

修改rule.xml配置文件

#在 rule 配置文件里新增分片规则 mod_rule,并指定规则适用字段为 customer_id, #还有选择分片算法 mod-long(对字段求模运算),customer_id 对两个节点求模,根据结果分片
#配置算法 mod-long 参数 count 为 2,两个节点
<tableRule name="mod_rule">
  <rule>
    <columns>customer_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>

6.2 分片join

Orders 订单表已经进行分表操作了,和它关联的 orders_detail 订单详情表如何进行 join 查询。

MyCAT 借鉴了 NewSQL 领域的新秀 Foundation DB 的设计思路,Foundation DB 创新性的提出了 Table
Group 的概念,其将子表的存储位置依赖于主表,并且物理上紧邻存放,因此彻底解决了 JION 的效率和性能问
题,根据这一思路,提出了基于 E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分
片上。

<table name="orders" dataNode="dn1,dn2" rule="mod_rule" >
	<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>

image.png

6.3 全局表

全局表具有以下特性:
① 全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
② 全局表的查询操作,只从一个节点获取
③ 全局表可以跟任何一个表进行 JOIN 操作

<table name="dict_order_type" dataNode="dn1,dn2" type="global" ></table>

6.4 常用分片规则

取模

此规则为对分片字段求模运算。

分片枚举

通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省
份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则。

修改schema.xml
<table name="orders_ware_info" dataNode="dn1,dn2" rule="sharding_by_intfile" ></table>
修改rule.xml

<tableRule name="sharding_by_intfile">
  <rule>
    <columns>areacode</columns>
    <algorithm>hash-int</algorithm>
  </rule>
</tableRule><function name="hash-int"
  class="io.mycat.route.function.PartitionByFileMap">
  <property name="mapFile">partition-hash-int.txt</property>
  <property name="type">1</property>
  <property name="defaultNode">0</property>
</function>

修改partition-hash-int.txt配置文件

110=0
120=1

范围约定

此分片适用于,提前规划好分片字段某个范围属于哪个分片,

#(1)修改schema.xml配置文件
<table name="payment_info" dataNode="dn1,dn2" rule="auto_sharding_long" ></table>
#(2)修改rule.xml配置文件
<tableRule name="auto_sharding_long">
  <rule>
    <columns>order_id</columns>
    <algorithm>rang-long</algorithm>
  </rule>
</tableRule><function name="rang-long"
  class="io.mycat.route.function.AutoPartitionByLong">
  <property name="mapFile">autopartition-long.txt</property>
  <property name="defaultNode">0</property>
</function>

#(3)修改autopartition-long.txt配置文件
0-102=0
103-200=1
#(4)重启 Mycat
#(5)访问Mycat创建表

按日期分片

此规则为按天分片。

#(1)修改schema.xml配置文件
<table name="login_info" dataNode="dn1,dn2" rule="sharding_by_date" ></table>
#(2)修改rule.xml配置文件
<tableRule name="sharding_by_date">
  <rule>
    <columns>login_date</columns>
    <algorithm>shardingByDate</algorithm>
  </rule>
</tableRule><function name="shardingByDate" class="io.mycat.route.function.PartitionByDate">
	<property name="dateFormat">yyyy-MM-dd</property> 
  <property name="sBeginDate">2019-01-01</property>
	<property name="sEndDate">2019-01-04</property>
  <property name="sPartionDay">2</property> 
</function>
# columns:分片字段,algorithm:分片函数
#dateFormat :日期格式
#sBeginDate :开始日期
#sEndDate:结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入
#sPartionDay :分区天数,即默认从开始日期算起,分隔 2 天一个分区
#(3)重启 Mycat
#(4)访问Mycat创建表

6.5 全局序列

数据切分后,原有的关系数据库中的主键约束在分布式条件下将无法使用,因此需要引入外部机制保证数据
唯一性标识,这种保证全局性的数据唯一标识的机制就是全局序列号(sequence)。

在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,MyCat 提供了全局
sequence,并且提供了包含本地配置和数据库配置等多种实现方式。

本地文件方式

**原理:**此方式 MyCAT 将 sequence 配置到文件中,当使用到 sequence 中的配置后,MyCAT 会更下
classpath 中的 sequence_conf.properties 文件中 sequence 当前的值。

数据库方式

在数据库中建立一张表,存放 sequence 名称(name),sequence 当前值(current_value),步长(increment
int 类型每次读取多少个 sequence)等信息;

利用数据库一个表 来进行计数累加。但是并不是每次生成序列都读写数据库,这样效率太低。
Mycat 会预加载一部分号段到 Mycat 的内存中,这样大部分读写序列都是在内存中完成的。
如果内存中的号段用完了 Mycat 会再向数据库要一次。

本地时间戳方式

ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加)
换算成十进制为 18 位数的 long 类型,每毫秒可以并发 12 位二进制的累加。

自主生成全局序列

在Java代码里指定id。

7、Mycat高可用

由于 Mycat 自身是属于无状态的中间件(除 了主从切换过程中记录的 dnindex.properties 文件),因此 Mycat 很容易部署为集群方式,提供高可用方案。
原先有规划 Mycat-balance 组件,专门用于 Mycat 负载均衡,但由于缺乏志愿者,也没有经过生产实践验证,
156 因此暂时不建议使用,官方建议是采用基于硬件的负载均衡器或者软件方式的 HAproxy,HAProxy 相比 LVS 的
使用要简单很多,功能方面也很丰富,免费开源,稳定性也是非常好,可以与 LVS 相媲美,根据官方文档,
HAProxy 可以跑满 10Gbps-New benchmark of HAProxy at 10 Gbps using Myricom’s 10GbE NICs (Myri-
10G PCI-Express),这个作为软件级负载均衡,也是比较惊人的,下图是 HAproxy+Mycat 集群+MySQL 主从
所组成的高可用性方案:

image.png

如果还担心 HAproxy 的稳定性和单点问题,则可以用 keepalived 的 VIP 的浮动功能,加以强化:

image.png

8、Mycat安全控制


## 1、 user 标签权限控制
```xml #server.xml配置文件user部分 123456 TESTDB user TESTDB true ``` ## 2、** privileges 标签权限控制 **
在 user 标签下的 privileges 标签可以对逻辑库(schema)、表(table)进行精细化的 DML 权限控
制。
#server.xml配置文件privileges部分
#配置orders表没有增删改查权限
<user name="mycat">
  <property name="password">123456</property>
	<property name="schemas">TESTDB</property>
  <!-- 表级 DML 权限设置 -->
  <privileges check="true">
    <schema name="TESTDB" dml="1111" >
    	<table name="orders" dml="0000"></table>
    	<!--<table name="tb02" dml="1111"></table>-->
    </schema>
  </privileges>
</user>

9、SQL拦截

firewall 标签用来定义防火墙;firewall 下 whitehost 标签用来定义 IP 白名单 ,blacklist 用来定义
SQL 黑名单。

#设置白名单
#server.xml配置文件firewall标签
#配置只有192.168.140.128主机可以通过mycat用户访问
<firewall>
  <whitehost>
    <host host="192.168.140.128" user="mycat"/>
  </whitehost>
</firewall>
#设置黑名单
#server.xml配置文件firewall标签
#配置禁止mycat用户进行删除操作
<firewall>
  <whitehost>
 	 <host host="192.168.140.128" user="mycat"/>
  </whitehost>
  <blacklist check="true">
  	<property name="deleteAllow">false</property>
  </blacklist>
</firewall>

9、Mycat监控

MySQL数据库监控工具,实现了对MySQL节点的管理和监控,可供开发人员和DBA使用。后续版本将整合MyCAT2.0的管理和配置。