MyCat简介

什么是MyCat

MyCat 是目前最流行的基于 java 语言编写的数据库中间件,是一个实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL 原生协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信,其核心功能是分库分表。配合数据库的主从模式还可实现读写分离。

MyCat 是基于阿里开源的 Cobar 产品而研发,Cobar 的稳定性、可靠性、优秀的架构和性能以及众多成熟的使用案例使得 MyCat 变得非常的强大。

MyCat 发展到目前的版本,已经不是一个单纯的 MySQL 代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL 等主流数据库,也支持 MongoDB 这种新型NoSQL 方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在 MyCat 里,都是一个传统的数据库表,支持标准的 SQL 语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度。

MyCat 官网:http://mycat.org.cn/

使用MyCat后的结构图

使用MyCat的优势

数据量级

单一的MySQL其数据存储量级和操作量级有限。MyCat可以管理若干MySQL数据库,同时实现数据的存储和操作。

开源性质

Mycat 是 java 编写的中间件. 开源,免费。

有非常多的人和组织对 Mycat 实行开发,维护,管理,更新。

Mycat 版本提升较快,可以跟随环境发展.如果有问题,可以快速解决。

Mycat 有开源网站和开源社区.且有官方发布的电子书籍。

Mycat 是阿里原应用 corba 转型而来的。

市场应用

2015年左右MyCat在互联网应用中占比非常高

MyCat中的概念

切分

逻辑上的切分,在物理层面,是使用多库[database],多表[table]实现的切分

纵向切分/垂直切分

就是把原本存储于一个库的数据存储到多个库上。

由于对数据库的读写都是对同一个库进行操作,所以单库并不能解决大规模并发写入的问题。

例如,我们会建立定义数据库 workDB、商品数据库 payDB、用户数据库 userDB、日志数据库 logDB 等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等。

优点:

  1. 减少增量数据写入时的锁(共享锁,更新锁,排他锁等,如果上了排他锁时,那么写入数据时不能查询)对查询的影响。
  2. 由于单表数量下降,常见的查询操作由于减少了需要扫描的记录,使得单表单次查询所需的检索行数变少,减少了磁盘 IO,时延变短

缺点:无法解决单表数据量太大的问题。

横向切分/水平切分

把原本存储于一个表的数据分块存储到多个表上。当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,进行划分,然后存储到多个结构相同的表,和不同的库上。

例如,我们 userDB 中的 userTable 中数据量很大,那么可以把 userDB 切分为结构相同的多个 userDB:part0DB、part1DB 等,再将 userDB 上的 userTable,切分为很多 userTable:userTable0、userTable1 等,然后将这些表按照一定的规则存储到多个 userDB 上。

优点:

  1. 单表的并发能力提高了,磁盘 I/O 性能也提高了。
  2. 如果出现高并发的话,总表可以根据不同的查询,将并发压力分到不同的小表里面。

缺点:无法实现表连接查询

逻辑库-Schema

MyCat中定义的database是逻辑上存在的,但是物理上是不存在的。主要是针对纵向切分提供的概念。

逻辑表-table

MyCat中定义的table是逻辑上存在,物理上是不存在的。主要是针对横向切分提供的概念。

默认端口

MySQL的默认端口是3306

MyCat默认端口是8066

Tomcat 默认端口是 8080

Oracle 默认端口是 1521

nginx 默认端口是 80

http 协议默认端口 80

redis 默认端口 6379

数据主机-dataHost

物理MySQL存放的主机地址,可以使用主机名,IP,域名定义。

数据节点-dataNode

配置物理的database,数据保存的物理节点就是database.

分片规则

当控制数据的时候,如何访问物理 database 和 table。

就是访问 dataHost 和 dataNode 的算法。

在 Mycat 处理具体的数据 CRUD 的时候,如何访问 dataHost 和 dataNode 的算法.如:哈希算法,crc32 算法等。

MyCat的使用

读写分离

原理:需要搭建主从模式,让主数据库(master)处理事务性增、删、改操作(INSERT、UPDATE、DELETE),而从数据库(slave)处理查询( SELECT )操作。

Mycat 配合数据库本身的复制功能,可以解决读写分离的问题。

主从备份概念

什么是主从备份: 就是一种主备模式的数据库应用。

主库(Master)数据与备库(Slave)数据完全一致。

实现数据的多重备份, 保证数据的安全。

可以在 Master[InnoDB支持事务]和 Slave[MyISAM不支持事务](查询效率高)中使用不同的数据库引擎,实现读写的分离。

MySQL5.5, 5.6 版本后本身支持主从备份

在老旧版本的 MySQL 数据库系统中,不支持主从备份,需要安装额外的 RPM 包.如果需要安装 RPM,只能在一个位置节点安装。

主从备份目的

实现主备模式

保证数据的安全,尽量避免数据丢失的可能。

实现读写分离

使用不同的数据库引擎,实现读写分离,提高所有的操作效率。

InnoDB(支持事务)使用DML语法操作,MyISAM(不支持事务)使用DQL语法操作。

主从备份效果

主库操作同步到备库

所有对 Master 的操作,都会同步到 Slave 中.

如果 Master和 Salve 天生上环境不同,那么对 Master 的操作,可能会在 Slave中出现错误

如:在创建主从模式之前,Master有 database : db1, db2, db3. Slave有 database: db1,db2.

创建主从模式现在的情况Master和Slave天生不同。

主从模式创建成功后,在 Master中 drop database db3. Slave中抛出数据库 SQL异常后续所有的命令不能同步。

一旦出现错误. 只能重新实现主从模式。

主从模式下的逻辑图

MySQL的主从模式搭建

安装MYSQL(参考文章

已安装

主库IP(master):192.168.43.181

从库IP(slave):192.168.43.238

Mater[主库]配置

修改Master配置文件

打开mysql的配置文件

路径:/etc/my.cnf

1
vi /etc/my.cnf

添加server_id

本环境中 server_id 是1

MySQL服务唯一标识

配置要求:

  1. server_id 任意配置,只要是数字即可。

  2. server_id Master 唯一标识数数字必须小于Slave唯一标识数字。

添加log_bin

本环境 log_bin值:master_log 开启日志功能以及日志文件命名,log_bin=master_log;

变量的值就是日志文件名称,是日志文件名称的主题。

MySQL数据库自动增加文件名后缀和文件类型

Master主库配置

重启MySQL

1
service mysqld restart

配置Master

登录MySQL
1
mysql -u root -proot
创建用户

在MySQL数据库中,为不存在的用户授权,就是同步创建用户并授权。此用户是从库访问主库使用的用户

IP 地址不能写为%,因为主从备份中,当前创建的用户是给从库slave访问主库master使用的,用户必须有指定的访问地址,不是通用地址。

格式:grant all privileges on *.* to 'username'@'ip' identified by 'password' with grant option; 刷新flush privileges;

为我的从库slave授权访问主库权限

1
grant all privileges on *.* to myslave@'192.168.43.238' identified by 'root' with grant option;
1
flush privileges;

查看用户
1
use mysql;
1
select host,user from user;

查看Master信息
1
show master status;

我们可以找到二进制文件:master_log.000002(000002是自动生成的编号)日志所在的地方,master_log.index是配置master_log.000002的位置的。

关闭防火墙或者开启3306端口Centos7
  1. 关闭防火墙

    1
    systemctl stop firewalld
  2. 开启3306端口

    1
    firewall-cmd --zone=public --add-port=3306/tcp --permanent
    1
    systemctl restart firewalld

Slave[从库]配置

修改Slave配置文件

1
vi /etc/my.cnf

Slave从库配置

重启MySQL服务

1
service mysqld restart

配置Slave

登录mysql
1
mysql -u root -proot
停止Slave功能(默认是开启的)
1
stop slave;
配置主库信息

需要修改的数据是依据Master信息修改的,ip是Master所在物理机的ip,用户名和密码是Master提供的Slave访问用户名和密码,日志文件是在Master中查看的主库信息提供的,在之前的Master中使用命令show master status;查看了日志文件名称。

把从库于主库挂钩

格式:change master to master_host='ip', master_user='username', master_password='password', master_port=3306,master_log_file='log_file_name',master_log_pos=查出来的position值;

1
change master to master_host='192.168.43.181',master_user='myslave',master_password='root',master_port=3306,master_log_file='master_log.000002',master_log_pos=154;
启动Salve功能
1
start slave;
查看Slave配置
1
show slave status \G;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
mysql> show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.43.181 //同步主库的IP
Master_User: myslave //同步主库的账号
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master_log.000002
Read_Master_Log_Pos: 154
Relay_Log_File: localhost-relay-bin.000005
Relay_Log_Pos: 369
Relay_Master_Log_File: master_log.000002
Slave_IO_Running: Yes //必须为Yes
Slave_SQL_Running: Yes //必须为Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 154
Relay_Log_Space: 747
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1 //必须有值
Master_UUID: 452ab4d5-009a-11eb-9eeb-000c29a618c2 //必须有值
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)

常见问题

  1. Slave_IO_Running:Connecting

    检查主库的端口是否放行,防火墙是否关闭。

  2. Slave_IO_Running:No

    先执行stop slave;,然后reset slave;、最后start slave;

测试主从库

先在主库中新建一个数据库

1
create database mytest default character set utf8;

然后在主库和从库中的mysql里可以看到两个都要mytest这个库了

1
show databases;

同理,在主库新建表和插入数据后,同样可以在从表看到。

1
2
3
4
5
6
CREATE TABLE `t_users` (
`id` int(11) NOT NULL,
`name` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
1
insert into t_users values(1,'admin')

安装MyCat

安装环境

MyCat(虚拟机):192.168.43.182

  1. 我们先下载MyCat1.6

  2. 然后将Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz上传至服务器。

  3. 将压缩包解解压。建议将mycat放到/usr/local/目录下。

    1
    tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
    1
    mv mycat /usr/local

MyCat目录介绍

  1. bin 目录里是启动脚本

  2. conf 目录里是配置文件

  3. catlet 为 Mycat 的一个扩展功能

  4. lib 目录里是 Mycat 和它的依赖 jar

  5. logs 目录里是 console.log 用来保存控制台日志,和 mycat.log 用来保存 mycat 的 log4j日志

安装JDK

我们这里直接yum 安装jdk8,安装时一直同意下去。安装完成后输入java -version检查安装。

1
yum install java-1.8.0-openjdk* -y

MyCat配置文件

Mycat 的架构其实很好理解,Mycat 是代理,Mycat 后面就是物理数据库。和 Web 服务器的 Nginx 类似。对于使用者来说,访问的都是 Mycat,不会接触到后端的数据库。我们现在做一个主从、读写分离。结构如下:

Mycat 的配置文件都在 conf 目录里面,这里介绍几个常用的文件

文件说明
server.xmlMyCat的配置文件,设置账号、参数等
schema.xmlMyCat 对应的物理数据库和数据库表的配置
rule.xmlMyCat 分片(分库分表)规则

server.xml逻辑库配置

常见修改内内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<property name="serverPort">8066</property> <!-- Mycat 服务端口号 --> 
<property name="managerPort">9066</property><!-- Mycat 管理端口号 -->
<user name="root"><!-- mycat 用户名 -->
<property name="password">密码</property>
<property name="schemas">用户可访问逻辑库名</property>
<!-- 表级 DML 权限设置 -->
<!-- 不检查 SQL 语法结果
<privileges check="false">
<schema name="逻辑库名" dml="0110" >
<table name="逻辑表名" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges> -->
</user>
<user name="user"><!-- 其他用户名 -->
<property name="password">密码</property>
<property name="schemas">可访问逻辑库名</property>
<property name="readOnly">是否只读</property>
</user>

配置Mycat服务信息

如: Mycat 中的用户,用户可以访问的逻辑库,可以访问的逻辑表,服务的端口号等

user用户配置的节点
–name登录的用户名,也就是连接 Mycat 的用户名
–password登录的密码,也就是连接 Mycat 的密码
–schemas逻辑库名,这里会和 schema.xml 中的配置关联,多个用逗号分开,
例如需要这个用户管理两个数据库db1、db2,则配置db1、db2。
–privileges配置用户针对表的增删改查的权限

默认配置了一个账号 root 密码也是 123456,针对数据库 TESTDB,读写权限都有,没有针对表做任何特殊的权限。

配置权限

参数说明事例(禁止增删改查)
dmlinsert、update、select、dalete0000

dml 权限顺序为:insert(新增),update(修改),select(查询),delete(删除),0000–> 1111,0 为禁止权限,1 为开启权限。

schemea.xml

schema.xml 是最主要的配置文件,首先看默认的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- auto sharding by id (long) -->
<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<!-- global table is auto cloned to all defined data nodes ,so can join
with any table whose sharding node is in the same data node -->
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
<table name="goods" primaryKey="ID" type="global" dataNode="dn1,dn2" />
<!-- random sharding using mod sharind rule -->
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3"
rule="mod-long" />
<!-- <table name="dual" primaryKey="ID" dataNode="dnx,dnoracle2" type="global"
needAddLimit="false"/> <table name="worker" primaryKey="ID" dataNode="jdbc_dn1,jdbc_dn2,jdbc_dn3"
rule="mod-long" /> -->
<table name="employee" primaryKey="ID" dataNode="dn1,dn2"
rule="sharding-by-intfile" />
<table name="customer" primaryKey="ID" dataNode="dn1,dn2"
rule="sharding-by-intfile">
<childTable name="orders" primaryKey="ID" joinKey="customer_id"
parentKey="id">
<childTable name="order_items" joinKey="order_id"
parentKey="id" />
</childTable>
<childTable name="customer_addr" primaryKey="ID" joinKey="customer_id"
parentKey="id" />
</table>
<!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate"
/> -->
</schema>
<!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"
/> -->
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
<!--<dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" />
<dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" />
<dataNode name="jdbc_dn2" dataHost="jdbchost" database="db2" />
<dataNode name="jdbc_dn3" dataHost="jdbchost" database="db3" /> -->
<dataHost name="localhost1" 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="localhost:3306" user="root"
password="123456">
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />
</writeHost>
</dataHost>
</mycat:schema>

用于定义逻辑库和逻辑表的配置文件

在配置文件中可以定义读写分离,逻辑库,逻辑表,dataHost,dataNode等信息

schema配置逻辑库,name 与 server.xml 中 schema 对应
dataHost物理数据库,真正存储数据的数据库
dataNode定义数据节点的标签,也就是分库相关配置

节点与属性介绍

标签 schema

配置逻辑库的标签

  1. 属性 name

    逻辑库名称

  2. 属性 checkSQLschema

    是否检测 SQL 语法中的 schema 信息。

    如: Mycat 逻辑库名称 A, dataNode 名称 B

    SQL : select * from A.table;

    checkSQLschema 值是 true, Mycat 发送到数据库的 SQL 是 select * from table;

    checkSQLschema 只是 false,Mycat 发送的数据库的 SQL 是 select * from A.table;

  3. sqlMaxLimit

    Mycat 在执行 SQL 的时候,如果 SQL 语句中没有 limit 子句.自动增加 limit 子句. 避免一次性得到过多的数据,影响效率. limit 子句的限制数量默认配置为 100.如果 SQL 中有具体的 limit子句,当前属性失效。

    1
    select * from table

    mycat 解析后:

    1
    select * from table limit 100

    下面这种mycat 不做任何操作修改。

    1
    select * from table limit 10
标签 table

定义逻辑表的标签

属性含义
name逻辑表名
dataHost数据节点名称。即物理数据库中的 database 名称。多个名称使用逗号分隔。
rule分片规则名称。具体的规则名称参考 rule.xml 配置文件。
标签 dataNode

定义数据节点的标签

属性含义
name数据节点名称, 是定义的逻辑名称,对应具体的物理数据库 database
dataHost引用 dataHost 标签的 name 值,代表使用的物理数据库所在位置和配置信息
database在dataHost 物理机中,具体的物理数据库 database 名称
标签 dataHost

定义数据主机的标签

属性含义
name定义逻辑上的数据主机名称
maxCon
minCon
最大连接数, max connections
最小连接数, min connections
dbType数据库类型:mysql数据库
dbDriver数据库驱动类型, native,使用 mycat 提供的本地驱动。
dataHost 子标签 writeHost

写数据的数据库定义标签. 实现读写分离操作。

属性含义
host数据库命名
url数据库访问路径
user数据库访问用户名
password数据库访问用户密码
writeHost子标签readHost
属性含义
host数据库命名
url数据库访问路径
user数据库访问用户名
password数据库访问用户密码

rule.xml

用于定义分片规则的配置文件. mycat 默认的分片规则:

以 500 万为单位,实现分片规则。

逻辑库 A 对应 dataNode - db1 和 db2. 1-500 万保存在 db1 中, 500 万零 1 到 1000 万保存在 db2 中,1000 万零 1 到 1500 万保存在 db1 中.依次类推。

1
2
3
4
5
6
<tableRule name="rule1">
<rule>
<columns>id</columns>
<algorithm>func1</algorithm>
</rule>
</tableRule>
tableRule
name属性指定唯一的名字,用于标识不同的分片规则。
内嵌的rule标签则指定对物理表中的哪一列进行拆分和使用什么分片算法。
columns指定要拆分的列名字
algorithm使用 function 标签中的 name 属性。连接表规则和具体分片算法。table标签内使用。
让逻辑表使用这个规则进行分片。
function
1
2
3
4
<function name="func1" class="io.mycat.route.function.PartitionByLong"> 
<property name="partitionCount">8</property>
<property name="partitionLength">128</property>
</function>
name指定算法的名字
class制定分片算法具体的类名字
property为具体算法需要用到的一些属性

实现读写分离

配置读写分离

Schema.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--配置mycat的逻辑库信息-->
<schema name="test" checkSQLschema="true" sqlMaxLimit="100">
<table name="t_users" dataNode="dn1"/>
</schema>
<!--配置物理库-->
<dataNode name="dn1" dataHost="localhost1" database="mytest" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!--心跳包:指定一个查询的sql语句-->
<heartbeat>select user()</heartbeat>
<!-- 主库配置 -->
<writeHost host="hostM1" url="192.168.43.181:3306" user="root" password="root">
<!-- 从库配置 -->
<readHost host="hostS2" url="192.168.43.238:3306" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>

/usr/local/mycat/conf/schema.xml

当Mycat查表报错find no Route:select * from 'test'.'t_users' limit 0, 100时,你需要修改schema.xmlcheckSQLschema=“false”,改为true即可。

当该值为true时,例如我们执行语句select * from TESTDB.company 。mycat会把语句修改为 select * from company 去掉TESTDB。

Server.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<user name="root">  <!--配置mycat的用户名和密码-->
<property name="password">123456</property>
<property name="schemas">suibianxie</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
<user name="user">
<property name="password">user</property>
<property name="schemas">suibianxie</property>
<property name="readOnly">true</property>
</user>

/usr/local/mycat/conf/server.xml

测试读写分离

记住放行端口8066和管理端口9066

先进入mycat的bin目录下

1
cd /usr/local/mycat/bin
启动MyCat命令
1
./mycat start

停止MyCat命令

1
./mycat stop

重启MyCat命令

1
./mycat restart

查看MyCat状态

1
./mycat status

查看MyCat日志

日志中记录的是所有的 mycat 操作. 查看的时候主要看异常信息 caused by 信息。

1
cat /usr/local/mycat/logs/wrapper.log

balance

  1. balance=”0”, 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上

  2. balance=”1”,全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡

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

  4. balance=”3”, 所有读请求随机的分发到 writeHost 对应的 readhost 执行,writerHost不负担读压力

在从库中加入一条数据,这时候主库中是没有的,因为从库同步主库,但主库不会同步从库,我们在mycat中查询所有用户发现没有从库中新添加的数据

这时候就要修改schema.xml中的balance的值

修改完记得重启MyCat

测试访问方式

我这里用的是Navicat软件连接MyCat和MySQL

MaCat登录

MyCat连接成功

MyCat分库

MyCat允许我们在mysql中定义多个库,允许数据按照分片规则分散存储到多个库中,可以降低每个数据库的压力

分片规则

auto-sharding-long 范围约定

1
2
3
4
5
6
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>

以 500 万为单位,实现分片规则。

逻辑库 A 对应 dataNode - db1 和 db2. 1-500 万保存在 db1 中, 500 万零 1 到 1000 万保存在 db2 中,1000 万零 1 到 1500 万保存在 db1 中.依次类推。

crc32slot规则

在 CRUD 操作时,根据具体数据的 crc32 算法计算,数据应该保存在哪一个 dataNode 中

在mycat的rule.xml中全是由分片规则tableRule和分片函数function 标签组成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<tableRule name="crc32slot">
<rule>
<columns>id</columns>
<algorithm>crc32slot</algorithm>
</rule>
</tableRule>
以上表示对id(id要用表中的id名)列作crc32slot算法的处理

<function name="crc32slot"
class="io.mycat.route.function.PartitionByCRC32PreSlot">
<property name="count">2</property>
</function>
crc32slot算法对应的处理类在function中由class指定
这个配置有点像web.xml中<servlet><servlet-mapping>标签的绑定关系

配置分片规则需要注意的地方

  1. id中推荐配置主键列

  2. 所有的 tableRule 只能使用一次。如果需要为多个表配置相同的分片规则,那么需要在rule.xml中向下复制一份该规则,让其name值不同即可。

  3. 在 crc32Slot 算法中的分片数量一旦给定,MyCat 会将该分片数量和 Slot 的取值范围

    1
    2
    3
    4
    5
    <function name="crc32slot"
    class="io.mycat.route.function.PartitionByCRC32PreSlot">
    <property name="count">2</property>
    <!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
    </function>

    这个数量2对应schema.xml中节点的数量

    保存到文件中。再次修改分片数量时是不会生效的,需要将该文件删除。文件位置位于 conf目录中的 ruledata 目录中(删除它)。

配置分库

需求

  1. 在master中创建3个数据库
  2. 在MyCat中配置分库

创建数据库

1
2
3
create database demo1 default character set utf8; 
create database demo2 default character set utf8;
create database demo3 default character set utf8;

创建相同的t_users

1
2
3
4
5
CREATE TABLE `t_users` ( 
`id` int(11) NOT NULL,
`name` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8;

修改Schema.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--配置macat逻辑数据库的库名 -->
<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
<!-- 访问的数据库表名,有多少的物理表就配多少个table,名字对应物理表名 -->
<table name="t_users" dataNode="dn1,dn2,dn3" rule="crc32slot"/>
</schema>
<!-- 配主库就可以,因为从库中的数据是同步过去的;database物理库的库名-->
<dataNode name="dn1" dataHost="localhost1" database="demo1" />
<dataNode name="dn2" dataHost="localhost1" database="demo2" />
<dataNode name="dn3" dataHost="localhost1" database="demo3" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="2"
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.43.181:3306" user="root" password="root"> <!-- 主库写 -->
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.43.237:3306" user="root" password="root" /> <!-- 从库读,host中只要和主库中的host名字不冲突即可 -->
</writeHost>
</dataHost>
</mycat:schema>

修改rule.xml

1
2
3
4
5
<function name="crc32slot"
class="io.mycat.route.function.PartitionByCRC32PreSlot">
<property name="count">3</property>
<!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
</function>

配置好了分库分表,使用mycat创建表,插入数据即可(库还是得手动创建),数据会写入master,查询时根据配置的balance规则。

测试

schema.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--配置macat逻辑数据库的库名 -->
<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
<!-- 访问的数据库表名,有多少的物理表就配多少个table,名字对应物理表名 -->
<table name="t_users" dataNode="dn1,dn2,dn3" rule="crc32slot"/>
<!-- 写上准备要建的表名,节点及分库规则 -->
<table name="persen" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"/>
</schema>
<!-- 配主库就可以,因为从库中的数据是同步过去的;database物理库的库名-->
<dataNode name="dn1" dataHost="localhost1" database="demo1" />
<dataNode name="dn2" dataHost="localhost1" database="demo2" />
<dataNode name="dn3" dataHost="localhost1" database="demo3" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="2"
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.43.181:3306" user="root" password="root"> <!-- 主库写 -->
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.43.237:3306" user="root" password="root" /> <!-- 从库读,host中只要和主库中的host名字不冲突即可 -->
</writeHost>
</dataHost>
</mycat:schema>
rule.xml
1
2
3
4
5
6
7
<tableRule name="auto-sharding-long">
<rule>
<!-- pid是和表中的id名一致 -->
<columns>pid</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
在MyCat中
1
2
3
4
5
6
7
8
9
10
11
use test;#切换库
create table persen(
pid int(5) primary key,
pname varchar(20),
pwd varchar(30)
)
#在mycat中插入数据一定要加上每一列的列名
insert into persen (pid,pname,pwd) values(1,'张三','123');#发现不能使用mysql中的id自增了
insert into persen (pid,pname,pwd) values(2,'株洲','123');
insert into persen (pid,pname,pwd) values(3,'株洲2','123');

使用MyCat的全局自增(MYCATSEQ_GLOBAL)

#全局自增序列是个字符串

1
select next value for MYCATSEQ_GLOBAL;

schema.xml中加上primaryKey中的值得大写

1
<table name="persen" dataNode="dn1,dn2,dn3" autoIncrement="true" primaryKey="PID" type="global" rule="auto-sharding-long"/>

把表的pid修改为vachar类型

1
2
3
4
5
create table persen(
pid varchar(100) primary key, #主键的数据类型是字符型
pname varchar(20),
pwd varchar(30)
)
1
insert into persen (pid,pname,pwd) values(next value for MYCATSEQ_GLOBAL,'株洲8','123');

注意:

  1. 使用 MyCat 实现分库时,先在 MyCat 中定义逻辑库与逻辑表,然后在 MyCat 的链接中执行创建表的命令必须要在 MyCat 中运行。因为 MyCat 在创建表时,会在表中添加一个新的列,列名为_slot。
  2. 使用 MyCat 插入数据时,语句中必须要指定所有的列。即便是一个完全项插入也不允许省略列名。