mycat的使用

mycat:数据库中间件

由于mycat的团队过于恶心,所以不建议使用mycat,可以转为使用shardding JDBC

为什么要用mycat? mycat解决一下三个问题
  • 解决了Java应用与数据紧耦合的问题
  • 缓解了高访问量高并发的压力
  • 解决了读写请求数据不一致的问题
mycat主要做的事
  • 读写分离


    mycat的使用_第1张图片
    image.png
  • 数据分片:垂直拆分(分库)、水平拆分(分表)、垂直+水平拆分(分库分表)


    mycat的使用_第2张图片
    image.png
  • 多数据源整合


    mycat的使用_第3张图片
    image.png
mycat原理

Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的 SQL 语句,首先对 SQL 语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。这种方式把数据库的分布式从代码中解耦出来,程序员察觉不出来后台使用 Mycat 还是MySQL。

mycat的使用_第4张图片
image.png
mycat安装

上传压缩安装包到服务器上
然后解压到/usr/local目录下
tar -zxvf Mycat-server-1.6.7.1-release-20190627191042-linux.tar.gz -C /usr/local/
去/usr/local/mycat/conf目录下编辑 server.xml 和schema.xml文件。

mycat的使用_第5张图片
image.png

注意:这里的TESTDB指的是mycat逻辑库,并不是真正的库
mycat的使用_第6张图片
image.png

确保两台机器都安装好mysql并且可以远程登录
mysql -umysqlu1 -p12345678 -h 192.168.107.135 -P 3306
mysql -umysqlu1 -p12345678 -h 192.168.107.135 -P 3306

启动mycat
  • 控制台启动 :去 /usr/local/mycat/bin 目录下执行 ./mycat console
  • 后台启动 :去 /usr/local/mycat/bin 目录下 ./mycat start
登录mycat
  • 登录后台管理窗口(这个方法用于管理维护mycat)
    mysql -umycat -p123456 -P 9066 -h 192.168.107.135


    mycat的使用_第7张图片
    image.png
  • 登录数据窗口(这个方式用于通过mycat查询数据)
    mysql -umycat -p123456 -P 8066 -h 192.168.107.135


    mycat的使用_第8张图片
    image.png
搭建mysql主从复制 (一主一从)

修改主机配置文件

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

上面的logbin格式格外重要,分三种模式 STATEMENT,ROW,MIXED。各有优缺点,具体可以去网上看https://www.cnblogs.com/xingyunfashi/p/8431780.html

修改从机配置文件

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

重启主机和从机的mysql服务
systemctl restart mysqld
查看重启后的mysql状态
systemctl status mysqld

在主机上创建一个用户,用于从机来复制数据

GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' IDENTIFIED BY '12345678';

(命令解释:创建一个在所有库所有表都有主从复制权限的远程访问用户 ,用户名为slave,密码为123123)

查看主机master状态,在此之后 主机别操作了,防止主机状态值变化。
show master status;


mycat的使用_第9张图片
image.png

记下File 和 Position的值

在从机上配置需要复制的主机,(进入mysql从机命令行 输入以下命令)

CHANGE MASTER TO MASTER_HOST='192.168.107.135',
MASTER_USER='slave',
MASTER_PASSWORD='12345678',
MASTER_LOG_FILE='mysql-bin.mysql-bin.000002',MASTER_LOG_POS=154;
mycat的使用_第10张图片
image.png

启动从服务器复制功能
start slave;
查看服务器状态,下面两个都是yes才表示成功,如果有No,则下面的error行会报错原因。
show slave status\G;


mycat的使用_第11张图片
image.png

如果从机已经配置过主机,那么我们可以reset 主机;
先 stop slave; 然后reset master; 然后再执行上面的语句。

现在我们在主机里新建testdb库(仅仅是testdb库 建表 建数据,其他不行 因为前面配置了只复制testdb这个库),然后看看从机是否有响相应的复制数据。

配置mycat读写分离,负载均衡类型,目前的取值有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 不负担读压力
这里的配置,如果是一主一从,配置3就可以了,如果是多主(多主之间互为主备)多从,配置1就行了
这里我们可以验证mycat 的select路由功能了

搭建mysql主从复制 (多主多从)。这里搭建二主二从

一个主机 m1 用于处理所有写请求,它的从机 s1 和另一台主机 m2 还有它的从机 s2 负责所有读请求。当 m1 主机宕机后,m2 主机负责写请求,m1、m2 互为备机,这里m1 和m2 的地位是一样的。架构图如下

mycat的使用_第12张图片
image.png

我们这里是mysql服务器情况是
135 master1
129 master2
136 slave1
130 slave2
136 复制 135, 130 复制 129,129 和 135互相复制

依次关闭四台mysql服务,编辑mysql配置 vim /etc/my.cnf
master1的配置如下

#主服务器唯一ID
server-id=1
#启用二进制日志
log-bin=mysql-bin
# 设置不要复制的数据库(可设置多个)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
#设置需要复制的数据库
binlog-do-db=testdb
#设置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=testdb
#设置logbin格式
binlog_format=STATEMENT

# 在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates 
#表示自增长字段每次递增的量,指自增字段的起始值,其默认值是1,取值范围是1 .. 65535
auto-increment-increment=2 
# 表示自增长字段从哪个数开始,指字段一次递增多少,他的取值范围是1 .. 65535
auto-increment-offset=2 #  这里从2开始,不能和master1重复

slave1配置

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

slave2配置

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

双主机、双从机重启 mysql 服务,主机从机都关闭防火墙。在两台主机建立授权slave账户

GRANT REPLICATION SLAVE ON . TO 'slave'@'%' IDENTIFIED BY '12345678';

查看m1 ,m2 状态,记下 File和Position


image.png

mycat的使用_第13张图片
image.png

在两台从机配置 执行复制主机的动作。

#136 复制 135
CHANGE MASTER TO MASTER_HOST='192.168.107.135',
MASTER_USER='slave',
MASTER_PASSWORD='12345678',
MASTER_LOG_FILE='mysql-bin.mysql-bin.000005',MASTER_LOG_POS=1254;

#在130 复制 129 
CHANGE MASTER TO MASTER_HOST='192.168.107.129',
MASTER_USER='slave',
MASTER_PASSWORD='12345678',
MASTER_LOG_FILE='mysql-bin.mysql-bin.000001',MASTER_LOG_POS=6271;

两台从服务器均执行 start slave ; 然后查看slave状态,均为YES则表示设置成功。


mycat的使用_第14张图片
image.png
mycat的使用_第15张图片
image.png

两主互相复制

# 129 复制 135
CHANGE MASTER TO MASTER_HOST='192.168.107.135',
MASTER_USER='slave',
MASTER_PASSWORD='12345678',
MASTER_LOG_FILE='mysql-bin.mysql-bin.000005',MASTER_LOG_POS=1254;

# 135复制 129 
CHANGE MASTER TO MASTER_HOST='192.168.107.129',
MASTER_USER='slave',
MASTER_PASSWORD='12345678',
MASTER_LOG_FILE='mysql-bin.mysql-bin.000001',MASTER_LOG_POS=6271;

现在可以在主库中 建库 建表 建数据,验证双主双从是否配置成功了。

配置mycat双主双从读写分离

相比之前的一主一从配置,我们这里需要增加一对host主从配置





        

        
        

        
                select user()
                
                
                        
                        
                

                
                        
                        
                

        

相关配置解释

# balance="1": 全部的readHost与stand by writeHost参与select语句的负载均衡。
#writeType="0": 所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个
#writeType="1",所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐
#writeHost,重新启动后以切换后的为准,切换记录在配置文件中:dnindex.properties 。
 #switchType="1": 1 默认值,自动切换。   -1 表示不自动切换,  2 基于 MySQL 主从同步的状态决定是否切换。

验证mycat读写分离,登录mycat,从查询结果可以看出来,mycat查询是在2从一备份主之一执行查询的。
mysql -umycat -p123456 -P 8066 -h 192.168.107.135


mycat的使用_第16张图片
image.png

验证可用性:任意关闭一台主mysql服务器,数据依然可以插入,依然可以查询数据,主服务器重启后,该台服务器依然会同步最新数据,只是现在这个服务器的角色肯定是主写服务器的备用服务器,不管它之前是主写服务器还是主服务器的备用服务器。

这种配置有一个问题,当任意一台主服务器挂掉的时候(比如m2),那么对于mycat来说 其实就等于挂掉两台服务器,因为s2也相当于也挂了,因为s2 找不到它的m2。 这毫无疑问肯定是不合理的。这里如果对s2自动做主从切换就好了 就是redis哨兵一样。

垂直拆分-分库

一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面。
要明确的是:在mysql的join操作中,如果两个表位于不同主机上的库中,那么join操作是不可执行的,如果位于同一个主机的不同库中 join是可以执行的, 我们这里后续指的 "分库","不同库" 等概念, 指的就是位于不同主机上的库,分库的总体原则是有业务关联关系的表应该放在一个库里,相互没有关联关系的表放在不同库里。
这里我们准备四张表 customer 客户表,orders 订单表,orders_detail 订单详情表,dict_order_type 订单字典表,我们客户表放一个库里,其他的表放一个库里。
mycat 配置如下




        
        
                
select user() select user()

我们现在先在135和129服务器上新建orders数据库

CREATE DATABASE orders;

启动mycat 并访问mycat

/usr/local/mycat/bin/mycat console
mysql -umycat -p123456 -P 8066 -h 192.168.107.135

在mycat命令行下执行建库操作,建库语句如下

#客户表 
CREATE TABLE customer(
 id INT AUTO_INCREMENT,
 NAME VARCHAR(200),
 PRIMARY KEY(id)
);
#订单表 
CREATE TABLE orders(
 id INT AUTO_INCREMENT,
 order_type INT,
 customer_id INT,
 amount DECIMAL(10,2),
 PRIMARY KEY(id) 
); 
#订单详细表 
CREATE TABLE orders_detail(
 id INT AUTO_INCREMENT,
 detail VARCHAR(2000),
 order_id INT,
 PRIMARY KEY(id)
);
#订单状态字典表 
CREATE TABLE dict_order_type(
 id INT AUTO_INCREMENT,
 order_type VARCHAR(200),
 PRIMARY KEY(id)
);

然后切换到各自mysql控制台,可以看到相应的表已经建立在相应的数据上了,并且可以在相应的数据库上新增数据 ,查询数据等等,就这样分库成功了。

水平拆分-分表

MySQL单表存储数据条数是有瓶颈的,单表达到1000万条数据就达到了瓶颈,会影响查询效率,需要进行水平拆分(分表)进行优化。我们以orders表为例子来分表。

关于分表策略: 对于我们orders表来说 如果以id来分表的话,查询订单注重时效,历史订单被查询的次数少,如此分片会造成一个节点访问多,一个访问少,不平均。如果以customer_id来分的话,根据客户id去分,两个节点访问平均,一个客户的所有订单都在同一个节点,数据相对平均。其他典型分片策略有根据时间来分等等,总之分片是很重要的东西。

修改schema.xml,增加以下配置

 
  

schema.xml全配置文件如下




        
        
                
select user() select user()

修改配置文件rule.xml,在rule配置文件里新增分片规则mod_rule,并指定规则适用字段为customer_id,还要选择分片算法mod-long(对字段求模运算),customer_id对两个节点求模,根据结果分片

        
                
                        customer_id
                        mod-long
                
        
  
        
            2
        

现在我们在数据节点dn2上建orders表,因为之前只有dn1有orders表。在mycat命令行插入数据(字段不能省略)

-- 这里必须指明字段列(id,order_type,customer_id,amount) 不能省略,否则mycat不能识别
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(1,101,100,100100);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);

在两个库分别查看数据


mycat的使用_第17张图片
image.png

mycat的使用_第18张图片
image.png

在mycat中查询数据


mycat的使用_第19张图片
image.png

因为数据是从两个库中来的,所以顺序和我们之前的单库不一样,我们制定排序字段就可以了。到此分表已经成功。
mycat join查询

上面的orders表已经分开了,那么与其相关的orders_detail表怎么办呢?假设我们现在执行一条关联sql(如下)。那会不会有问题?答案是肯定有问题,mycat经过分片分析在dn1和dn2节点都进行join查询。因为其中一个节点没有orders_detail 表,所以必然报错,最终结果也就是报错。

SELECT  * FROM orders t1 LEFT JOIN orders_detail t2 WHERE  t1.id = t2.order_id

mycat 解决上面问题的方法是 使用ER表。Mycat 借鉴了 NewSQL 领域的新秀 Foundation DB 的设计思路,Foundation DB 创新性的提出了 Table Group 的概念,其将子表的存储位置依赖于主表,并且物理上紧邻存放,因此彻底解决了JION 的效率和性能问 题,根据这一思路,提出了基于 E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上。
更改schema.xml配置文件 将orders_detail的主表设置为orders,根据orders的id与orders_detail的id的关系来分配。

                
                

在dn2上新建orders_detail表,然后在mycat上插入数据

INSERT INTO orders_detail(id,detail,order_id) values(1,'detail1',1);
INSERT INTO orders_detail(id,detail,order_id) VALUES(2,'detail1',2);
INSERT INTO orders_detail(id,detail,order_id) VALUES(3,'detail1',3);
INSERT INTO orders_detail(id,detail,order_id) VALUES(4,'detail1',4);
INSERT INTO orders_detail(id,detail,order_id) VALUES(5,'detail1',5);
INSERT INTO orders_detail(id,detail,order_id) VALUES(6,'detail1',6);

在mycat、dn1、dn2中运行两个表join语句,能看到对应的结果,其中mycat的结果是结果总集,是各个节点的数据总和。

Select o.*,od.detail from orders o inner join orders_detail od on o.id=od.order_id;

mycat 全局表

在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较 棘手的问题,考虑到字典表具有以下几个特性:
① 变动不频繁
② 数据量总体变化不大
③ 数据规模不大,很少有超过数十万条记录
鉴于此,Mycat 定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:
① 全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
② 全局表的查询操作,只从一个节点获取
③ 全局表可以跟任何一个表进行 JOIN 操作
修改 schema.xml

在 dn2 创建 dict_order_type 表。 在Mycat、dn1、dn2中查询表数据

常用的分片规则

1 取模:此规则为对分片字段求摸运算。也是水平分表最常用规则。
2 分片枚举
通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务
需要按照省份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则。
修改schema.xml配置文件

修改rule.xml配置文件

        
                
                         areacode
                        hash-int
                
        

    
        partition-hash-int.txt
        1
        0
    

mapFile 表示 算法对应文件
type 表示文件数据类型,0表示整数类型,其他表示String
defaultNode表示 默认节点:小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点,设置默认节点如果碰到不识别的枚举值,就让它路由到默认节点,如不设置不识别就报错
修改partition-hash-int.txt配置文件

110=0
120=1

在mycat上建表,并且插入数据


CREATE TABLE orders_ware_info
(
 `id` INT AUTO_INCREMENT comment '编号',
 `order_id` INT comment '订单编号',
 `address` VARCHAR(200) comment '地址',
`areacode` VARCHAR(20) comment '区域编号',
PRIMARY KEY(id)
);

INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (1,1,'北京','110');
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (2,2,'天津','120');
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (3,2,'上海','130');

在Mycat、dn1、dn2可以看到数据分片效果
3 范围约定
此分片适用于,提前规划好分片字段某个范围属于哪个分片。
修改schema.xml配置文件

修改rule.xml配置文件


     
         order_id
         rang-long
     



    autopartition-long.txt
    0

修改autopartition-long.txt配置文件,以order_id区分,0-100的数据放0节点,101-200放1节点

0-100=0
101-200=1

mycat下,建表 插入数据 验证即可

CREATE TABLE payment_info
(
 `id` INT AUTO_INCREMENT comment '编号',
 `order_id` INT comment '订单编号',
 `payment_status` INT comment '支付状态',
 PRIMARY KEY(id)
);

INSERT INTO payment_info (id,order_id,payment_status) VALUES (1,101,0);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (2,102,1);
INSERT INTO payment_info (id,order_id ,payment_status) VALUES (3,103,0);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (4,104,1);

4 按日期分片
此规则为按天分片。设定时间格式、范围
修改schema.xml配置文件

修改rule.xml配置文件


     
         login_date
         shardingByDate
     



     yyyy-MM-dd
     2019-01-01
     2019-01-04
     2 

columns:分片字段,algorithm:分片函数
dateFormat :日期格式
sBeginDate :开始日期
sEndDate:结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入
sPartionDay :分区天数,即默认从开始日期算起,分隔 2 天一个分区

重启mycat 建表 加数据 ,验证

CREATE TABLE login_info
(
 `id` INT AUTO_INCREMENT comment '编号',
 `user_id` INT comment '用户编号',
 `login_date` date comment '登录日期',
 PRIMARY KEY(id)
); 

INSERT INTO login_info(id,user_id,login_date) VALUES (1,101,'2019-01-01');
INSERT INTO login_info(id,user_id,login_date) VALUES (2,102,'2019-01-02');
INSERT INTO login_info(id,user_id,login_date) VALUES (3,103,'2019-01-03');
INSERT INTO login_info(id,user_id,login_date) VALUES (4,104,'2019-01-04');
INSERT INTO login_info(id,user_id,login_date) VALUES (5,103,'2019-01-05');
INSERT INTO login_info(id,user_id,login_date) VALUES (6,104,'2019-01-06');

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

  • 方式1 本地文件
    此方式 Mycat 将 sequence 配置到文件中,当使用到 sequence 中的配置后,Mycat 会更下classpath 中的 sequence_conf.properties 文件中 sequence 当前的值。
    ① 优点:本地加载,读取速度较快
    ② 缺点:抗风险能力差,Mycat 所在主机宕机后,无法读取本地文件。
  • 方式2 数据库方式
    利用数据库一个表 来进行计数累加。但是并不是每次生成序列都读写数据库,这样效率太低。Mycat 会预加载一部分号段到 Mycat 的内存中,这样大部分读写序列都是在内存中完成的。如果内存中的号段用完了 Mycat 会再向数据库要一次。
  • 方式3 时间戳方式
    全局序列ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) 换算成十进制为 18 位数的long 类型,每毫秒可以并发 12 位二进制的累加。
    ① 优点:配置简单
    ② 缺点:18 位 ID 过长
  • 方式4 自主生成全局序列 推荐这种方式比较好
    ① 根据业务逻辑组合
    ② 可以利用 redis 的单线程原子性 incr 来生成序列。

你可能感兴趣的:(mycat的使用)