MyCat介绍
系统开发中,数据库是非常重要的一个点。除了程序的本身的优化,如:SQL语句优化、代码优化,数据库的处理本身优化也是非常重要的。主从、热备、分表分库等都是系统发展迟早会遇到的技术问题问题。
MyCat是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。
MyCat发展到目前的版本,已经不是一个单纯的MySQL代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流数据库,也支持MongoDB这种新型NoSQL方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在MyCat里,都是一个传统的数据库表,支持标准的SQL语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度。
MyCat 是使用 Java 编写的数据库中间件,运行在代码应用和 MySQL 数据库之间的应用。它的前身是阿里开发的数据库中间件corba,实现 MySQL 数据库分库分表集群管理的中间件,曾经出现过重大事故而放弃,被部分人员进行二次开发,形成 Mycat,使用 MyCat 之后,编写的所有的 SQL 语句必须严格遵守 SQL 标准规范.
使用 MyCat 中间件后的结构图如下:
当我们的应用只需要一台数据库服务器的时候我们并不需要Mycat,而如果你需要分库甚至分表,这时候应用要面对很多个数据库的时候,这个时候就需要对数据库层做一个抽象,来管理这些数据库,而最上面的应用只需要面对一个数据库层的抽象或者说数据库中间件就好了,这就是Mycat的核心作用。
所以可以这样理解:数据库是对底层存储文件的抽象,而Mycat是对数据库的抽象。
数据库切分
逻辑上的切分,在物理层面,是使用多库[database],多表[table]实现切分。分为横向和纵向切分;
横向切分:把一个表切分成多个表,相比纵向切分配置麻烦,无法实现表连接查询. 将一张表的字段,分散到若干张表中,将若干表连接到一起,才是当前表的完整数据。
纵向切分:把一个数据库切分成多个数据库,配置方便,只能实现两张表的表连接查询,将一张表中的数据,分散到若干个 database 的同结构表中。多个表的数据的集合是当前表格的数据。
Mycat 中的DB
Mycat 中定义的 database是逻辑上的,但是物理上未必存在。主要是针对纵向切分提供的概念,访问 MyCat,就是将 MyCat 当做 MySQL 使用。
db 数据库是 MyCat 中定义的 database。通过 SQL 访问 MyCat 中的 db 库的时候,对应的是 MySQL 中的 db1,db2,db3 三个库。物理上的 database 是 db1,db2,db3.逻辑上的
database 就是 db。
Mycat 中的表
逻辑意义上的表,一个MaCat的table可以对应物理库上的多个表。
Mycat 默认端口:8066
数据主机:dataHost
物理 MySQL 存放的主机地址,可以使用主机名、IP、域名定义。
数据节点:dataNode 物理的 database 是什么,数据保存的物理节点就是 database。
分片规则:当控制数据的时候,如何访问物理 database 和 table, 就是访问 dataHost 和 dataNode 的算法。
在 Mycat 处理具体的数据 CRUD 的时候,如何访问 dataHost 和 dataNode 的算法.如:哈希算法,crc16 算法等
MyCat实现分库分表读写分离
一、搭建准备
MyCat:192.168.1.105
MySQL master:192.168.1.103
MySQL slave:192.168.1.104
在需要搭建环境的机器上安装jdk,MySQL。这里使用上个例子的节点,关闭MySQL-Proxy功能,在原有的MySQL-Proxy机器上安装MyCat。
两台数据库上设置可供mycat访问的用户密码
mysql> grant all privileges on *.* to 'mycat'@'%' identified by 'mycat' with grant option;
mysql> flush privileges;
二、安装配置MyCat
1、下载Mycat官网:http://www.mycat.io/
下载Mycat-server-1.6.6.1-release-20181031195535-linux.tar.gz到Linux并解压到/usr/local/mycat
bin mycat命令,启动、重启、停止等
catlet catlet为Mycat的一个扩展功能
conf Mycat 配置信息,重点关注
lib Mycat引用的jar包,Mycat是java开发的
logs 日志文件,包括Mycat启动的日志和运行的日志。
直接运行测试一下:
[root@localhost mycat]# bin/mycat start
Starting Mycat-server...
[root@localhost mycat]# bin/mycat status
Mycat-server is running (20038).
远程登录看一下
[root@localhost mysql-proxy]# mysql -uroot -p123456 -h192.168.1.105 -P8066
mysql> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB |
+----------+
mysql> use TESTDB;
mysql> show tables;
+------------------+
| Tables in TESTDB |
+------------------+
| company |
| customer |
| customer_addr |
| employee |
| goods |
| hotnews |
| orders |
| order_items |
| travelrecord |
+------------------+
初步测试MyCat没问题,配置文件中配置的逻辑表和库都能查到,但是不能查看数据,因为数据永远在物理库中,并且默认也米有配置对应真实的物理库。
2、配置示例测试
示例、配置分库,比如一个库demo的user表中有20w数据,现在进行分3个库,表结构一样,大概分别存6w左右数据。
第一步,创建好分库,表可以先不用创建。分别为demo1,demo2,demo3。
schema.xml配置如下:需要分库的数据库也放进来。
1 |
|
第二步:创建表,直接在MyCat里创建,都会分别同步到demo123库中
1 | CREATE TABLE `user` ( |
在三个库查看表结构,都ok,slave节点也查看。
mysql> show tables from demo3;
+-----------------+
| Tables_in_demo3 |
+-----------------+
| user |
+-----------------+
mysql> desc demo1.user;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(10) | NO | PRI | NULL | |
| name | varchar(20) | YES | | NULL | |
| _slot | int(11) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
发现多了一个_slot字段,这是因为使用了crc32slot分片规则得到的hash值。、
通过写脚本或者其他方式把源数据通过到新库。
读写分离设置
balance=”1”,全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。
balance=”3”,所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力,注意 balance=3 只在 1.4 及其以后版本有,1.3 没有。
示例:分表,单裤分表
场景:demo库中t_user表分成两个库t_users1/t_users2
核心配置:
1
2
3
4
5
6
7
8<schema name="olddemo" checkSQLschema="false" sqlMaxLimit="1000000">
<table name="user" dataNode="demo_old"/>
<table name="t_users" subTables="t_users$1-2" primaryKey="id" dataNode="demo_old" rule="mod-long" />
</schema>
rule:
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">2</property>
</function>
在数据库分别创建好t_users1/t_users2
1 | CREATE TABLE `t_users1` ( |
启动并连接到MyCat:把老表中的数据通过MyCat复制到新表,在、查看数据,可以看出,根据mod-long规则每条数据交替存储到新表。
查询分析可以看出去两个物理表中查询数据:
查询结果是把两个表结果合并起来:
bin/mycat console 启动不成功可以通过这个命令查看原因
logs/wrapper.log 日志中记录的是所有的 mycat 操作. 查看的时候主要看异常信息 caused by 信息
注:使用分片规则,MyCat启动后会在conf目录中创建一个ruledata下有使用规则信息分配的一个缓存文件,如果修改的分配规则,再次重启MyCat发现运用这个规则的缓存文件在就不会去重新生成,可能就得不到预计结果,所以,在重启前把这个目录对应的文件删除,让MyCat重新生成。
不能创建未在 schema.xml 中配置的逻辑表
注意:如何部署
1、停机部署法:大致思路就是半夜停机升级,服务停了,跑数据迁移程序,进行数据迁移。
写一个迁移程序,读db-old数据库,通过中间件写入新库db-new1和db-new2。
校验迁移前后一致性,没问题就切该部分业务到新库
缺点:只能是访问量不高的,而且部署或有问题,没部署好,早上又要把数据切回去,晚上有的忙,身心累。
2、双写部署1
上线一段代码:和表有关业务之前加一段代码,同时往老新库写。
历史数据:在该次部署前,数据库表test_tb的有关数据,我们称之为历史数据。
增量数据:在该次部署后,数据库表test_tb的新产生的数据,我们称之为增量数据。
多加一条往消息队列中发消息的代码,只是写的sql。
等到db-old中的历史数据迁移完毕,则开始迁移增量数据,也就是在消息队列里的数据。将迁移程序下线,写一段订阅程序订阅消息队列中的数据,订阅程序将订阅到到数据,通过中间件写入新库,新老库一致性验证,去除代码中的双写代码,将涉及到test_tb表的读写操作,指向新库。
3、双写部署2
上面方式造成了严重的代码入侵。将非业务代码嵌入业务代码。
替代办法是,记录日志。往消息队列里发的消息,都是写操作的消息。而binlog日志记录的也是写操作。所以订阅该日志,也能满足我们的需求。
打开binlog日志,迁移好历史数据后,写一个订阅程序,订阅binlog(mysql中有canal)。然后将订阅到的数据通过中间件,写入新库。
MyCat配置文件介绍
Mycat的配置文件都在conf目录里面,这里介绍几个常用的文件:
server.xml Mycat的配置文件,设置账号、参数等
schema.xml Mycat对应的物理数据库和数据库表的配置
rule.xml Mycat分片(分库分表)规则
server.xml
配置 Mycat 服务信息,如: Mycat中的用户,用户可以访问的逻辑库/表,服务的端口号等等。
1 | <mycat:server xmlns:mycat="http://io.mycat/"> |
rule.xml
用于定义分片规则的配置文件,主要是查看,很少修改。里边定义了很多分配规则比如:
1 | <tableRule name="auto-sharding-long"> |
auto-sharding-long:mycat 默认的分片规则:以 500 万为单位,实现分片规则。逻辑库 A 对应 dataNode - db1 和 db2. 1-500 万保存在 db1 中, 500 万零 1 到 1000 万保存在 db2 中,1000 万零 1 到 1500 万保存在 db1 中,依次类推。
crc32slot:在 CRUD 操作时,根据具体数据的 crc32 算法计算,数据应该保存在哪一个dataNode 中. 算法类似模运算。
server.xml
用于定义逻辑库和逻辑表的配置文件.在配置文件中可以定义读写分离,逻辑库,逻辑表,dataHost,dataNode 等信息。
1 | <mycat:schema xmlns:mycat="http://io.mycat/"> |
配置解释
schema:数据库设置,此数据库为逻辑数据库
name 逻辑数据库名,与server.xml中的schema对应
checkSQLschema 数据库前缀相关设置,是否检测SQL语法的schema信息,比如逻辑库名称是D,SQL:select * from D.table 当为true时:mycat发送到数据库的sql是select * from table;false时:发送的是select * from A.table
sqlMaxLimit select 时默认的limit,Mycat 在执行 SQL 的时候,如果 SQL 语法中没有 limit 子句.自动增加 limit 子句.避免查询全表
table标签:定义逻辑表,要求逻辑表和物理表的名字一致。
name 表名,物理数据库中表名
dataNode 表存储到哪些节点,多个节点用逗号分隔。节点为下文dataNode设置的name,多个也就分库了
primaryKey 主键字段名,自动生成主键时需要设置
autoIncrement 是否自增
rule 使用分片规则名,规则都在rule.xml。SQL 语句发送到 Mycat 中后,Mycat 如何计算,应该将当期的 SQL 发送到哪一个物理数
据库管理系统或物理 database 中
ruleRequired 表是否绑定分片规则,如果配置为 true,但没有配置具体 rule 的话 ,程序会报错。
type 逻辑表的类型,目前逻辑表只有"全局表"和"普通表"两种类型。全局表:global。
childTable 标签
name 定义子表的表名。
joinKey 插入子表的时候会使用这个列的值查找父表存储的数据节点。
parentKey 属性指定的值一般为与父表建立关联关系的列名。程序首先获取 joinkey 的值,再通过 parentKey 属性指定的列名产生查询语句,通过执行该语句得到父表存储在哪个分片上。从而确定子表存储的位置。
dataNode 分片信息,也就是分库相关配置
name 节点名,定义的逻辑名称,对应具体的物理数据库 database
datahost 物理数据库名,代表使用的物理数据库所在位置和配置信息,与datahost中name对应
database 物理数据库中数据库名
dataHost 物理数据库,真正存储数据的数据库
name 物理数据库名,与dataNode中dataHost对应
balance 均衡负载的方式
balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
balance="1",全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双 主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。
balance="2",所有读操作都随机的在 writeHost、readhost 上分发。
balance="3",所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力,注意 balance=3 只在 1.4 及其以后版本有,1.3 没有。
maxCon/minCon 最大最小连接数
writeType 负载均衡类型,目前的取值有 3 种:
1. writeType="0", 所有写操作发送到配置的第一个 writeHost,第一个挂了切到还生存的第二个writeHost,重新启动后已切换后的为准,切换记录在配置文件中:dnindex.properties .
2. writeType="1",所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐。
dbType 数据库类型
dbDriver 数据库驱动类型,,native,使用 mycat 提供的本地驱动
switchType
-1 表示不自动切换
1 默认值,自动切换 可能有IO延迟问题
2 基于 MySQL 主从同步的状态决定是否切换 心跳语句为 show slave status
3 基于 MySQL galary cluster 的切换机制(适合集群) 心跳语句为 show status like 'wsrep%'
heartbeat 心跳检测语句,注意语句结尾的分号要加。
子标签 writeHost:写数据的数据库定义标签. 实现读写分离操作,下面的readHost表示只读
host 数据库命名
url 数据库访问路径
user 数据库访问用户名
password 密码