Loading

[09] Mycat

概述

是什么?

  • Mycat 是数据库中间件,前身是阿里的 Cobar。
    • 中间件:是一类连接软件组件和应用的计算机软件,以便于软件各部件之间的沟通。如:Tomcat
    • 数据库中间件:连接 Java 应用程序和数据库
  • 为什么要用 Mycat?
    • Java 与数据库紧耦合
    • 高访问量高并发对数据库的压力
    • 读写请求数据不一致

能干什么?

读写分离

数据分片

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

多数据源整合

数据库中间件对比

  1. Cobar 属于阿里 B2B 事业群,始于 2008 年,在阿里服役 3 年多,接管 3000+ 个 MySQL 数据库的 schema,集群日处理在线 SQL 请求 50 亿次以上。由于 Cobar 发起人的离职,Cobar 停止维护。
  2. Mycat 是开源社区在阿里 Cobar 基础上进行二次开发,解决了 Cobar 存在的问题,并且加入了许多新的功能在其中,青出于蓝而胜于蓝。
  3. OneProxy 基于 MySQL 官方的 Proxy 思想利用 C 进行开发的,OneProxy 是一款商业收费的中间件。舍弃了一些功能,专注在性能和稳定性上。
  4. kingshard 由小团队用 Go 语言开发,还需要发展,需要不断完善。
  5. Vitess 是 Youtube 生产在使用,架构很复杂。不支持 MySQL 原生协议,使用需要大量改造成本。
  6. Atlas 是 360 团队基于 MySQL Proxy 改写,功能还需完善,高并发下不稳定。
  7. MaxScale 是 Mariadb(MySQL 原作者维护的一个版本) 研发的中间件。
  8. MySQLRoute 是 MySQL 官方 Oracle 公司发布的中间件。

Mycat 原理

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

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

安装启动

安装

1. 解压缩文件拷贝到 Linux 的 /usr/local/ 下,解压后即可使用

2. 三个配置文件

  • schema.xml:定义逻辑库,表、分片节点等内容
  • rule.xml:定义分片规则
  • server.xml:定义用户以及系统相关变量,如端口等

配置

1. 修改 server.xml

2. 修改 schema.xml

删除 <schema> 标签间的表信息,<dataNode> 标签只留一个,<dataHost> 标签只留一个,<writeHost><readHost> 只留一对。

3. 验证数据库访问情况

Mycat 作为数据库中间件要和数据库部署在不同机器上,所以要验证远程访问情况。

如果没登进去,我的报错是错误次数太多,使用命令:mysqladmin -u root -p flush-hosts

启动

  • 控制台启动:在 mycat/bin 目录下 ./mycat console
  • 后台启动:去 mycat/bin 目录下 ./mycat start

登录

  • 后台管理窗口:mysql -umycat -p123456 -P9066 -h192.168.206.129
  • 数据窗口:mysql -umycat -p123456 -P8066 -h192.168.206.129

搭建读写分离

通过 Mycat 和 MySQL 的主从复制配合搭建数据库的读写分离,实现 MySQL 的高可用性。

以下将搭建:一主一从、双主双从两种读写分离模式。

一主一从

一个主机用于处理所有写请求,一台从机负责所有读请求,架构图如下。

主从复制

回顾 MySQL 主从复制原理

  1. 修改主机配置文件:%MySQL_HOME%/my.ini
    # 服务ID:保证整个集群环境中唯一
    server-id=1
    # 启用二进制日志,配置binlog日志存储路径和文件名
    log-bin=U:/mysql/mysqlbin
    # 错误日志,默认已经开启
    # log-err
    # 是否只读:1代表只读,0代表读写
    read-only=0
    # 忽略的数据, 指不需要同步(复制)的数据库
    binlog-ignore-db=mysql
    # 指定同步(复制)的数据库
    binlog-do-db=mydb
    # 设置 logbin 格式,就用默认的 MIXED
    # binlog_format=STATEMENT
    
  2. 修改从机配置文件:vim /etc/my.cnf
    log_bin=mysqlbin # 开启 binlog 日志,日志的文件前缀
    binlog_format=MIXED # 配置二进制日志的格式
    
  3. 主从机都重启 MySQL 服务、关闭防火墙
  4. 主机上创建用户,给从机做主从复制的权限
    # 授权给Slave
    GRANT REPLICATION SLAVE ON *.* TO 'salve1101'@'%' IDENTIFIED BY '1101';
    
    # 刷新权限列表
    FLUSH PRIVILEGES;
    
    # 查看Master状态,这时候就不要再做操作了,防止接入点变化
    SHOW MASTER STATUS;
    
  5. 在从机上配置需要复制的主机
    # 在从机上配置需要复制的主机:指定当前从库对应的主库的IP地址
    # 用户名,密码,从哪个日志文件开始的那个位置开始同步推送日志
    CHANGE MASTER TO
        MASTER_HOST='192.168.206.1',
        MASTER_USER='salve1101',
        MASTER_PASSWORD='1101',
        MASTER_LOG_FILE='mysqlbin.000001',
        MASTER_LOG_POS=107;
    
    START SLAVE; # 启用从服务器复制功能
    
    SHOW SLAVE STATUS\G; # 查看从机状态
    
  6. 测试:创建指定要同步的数据库

读写分离

验证读写分离:

  1. 在写主机插入:INSERT INTO testMS VALUES (1, @@hostname); 造成主从机数据不一致
  2. 在 Mycat 里查询:SELECT * FROM testMS;

schema.xml

修改 <dataHost> 的 balance 属性,通过此属性配置读写分离的类型,目前的取值有 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 不负担读压力。

为了能看到读写分离的效果,把 balance 设置成 2,会在两个主机间切换查询:

双主双从

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

主从复制

修改对应机器上的 /etc/my.cnf 文件

(1) 双主机配置

① 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 配置

server-id=3
log-bin=mysql-bin
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
binlog-do-db=mydb
binlog_format=STATEMENT
log-slave-updates
auto-increment-increment=2
auto-increment-offset=2

(2) 双从机配置

① Slave1 配置

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

② Slave2 配置

server-id=4
relay-log=mysql-relay

(3) 双主机、双从机重启 MySQL 服务

(4) 主机从机都关闭防火墙

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

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

#查询 Master1、Master2 的状态,分别记录下 File 和 Position 的值
SHOW MASTER STATUS;

(6) 在从机上配置需要复制的主机:Slava1 复制 Master1,Slava2 复制 Master2

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

(7) 启动两台从服务器复制功能

# 启动两台从服务器复制功能
START SLAVE;

#查看从服务器状态
SHOW SLAVE STATUS\G;

# 下面两个参数都是Yes,则说明主从配置成功!
# Slave_IO_Running: Yes
# Slave_SQL_Running: Yes

(8) 两个主机互相复制:Master2 复制 Master1,Master1 复制 Master2

(9) Master1 主机新建库、新建表、insert 记录,Master2 和从机复制

读写分离

修改 Mycat 的配置文件 schema.xml,修改 <dataHost> 的 balance 属性,通过此属性配置读写分离的类型。

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

1. 验证读写分离:在写主机 Master1 数据库表 mytbl 中插入带系统变量数据,造成主从数据不一致。

2. 停止数据库 Master1,在 Mycat 里插入数据依然成功,Master2 自动切换为写主机。

垂直拆分——分库

如何划分表

系统被切分成了用户、订单交易、支付几个模块。

在两台主机上的两个数据库中的表,不可以关联查询!→【分库原则】有紧密关联关系的表应该在一个库里,相互没有关联关系的表可以分到不同的库里。

# 客户表 rows:20万
CREATE TABLE customer(
 id INT AUTO_INCREMENT,
 NAME VARCHAR(200),
 PRIMARY KEY(id)
);

# 订单表 rows:600万
CREATE TABLE orders(
 id INT AUTO_INCREMENT,
 order_type INT,
 customer_id INT,
 amount DECIMAL(10,2),
 PRIMARY KEY(id)
);

# 订单详细表 rows:600万
CREATE TABLE orders_detail(
 id INT AUTO_INCREMENT,
 detail VARCHAR(2000),
 order_id INT,
 PRIMARY KEY(id)
);

# 订单状态字典表 rows:20
CREATE TABLE dict_order_type(
 id INT AUTO_INCREMENT,
 order_type VARCHAR(200),
 PRIMARY KEY(id)
);

以上四个表如何分库?客户表分在一个数据库,另外三张都需要关联查询,分在另外一个数据库。

配置分库

1. 修改 schema 配置文件

2. 新增两个空白库:分库操作不是在原来的老数据库上进行操作,而是需要准备两台机器分别安装新的数据库。

# 在数据节点 dn1、dn2 上分别创建数据库 orders
CREATE DATABASE orders;

3. 启动 Mycat, 访问 Mycat 创建 4 张表,查看效果

水平拆分——分表

相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图:

实现分表

1. 选择要拆分的表

MySQL 单表存储数据条数是有瓶颈的,单表达到 1000 万条数据就达到了瓶颈,会影响查询效率,需要进行水平拆分(分表)进行优化。例如:示例中的 orders、orders_detail 都已经达到 600 万行数据,需要进行分表优化。

2. 分表字段

# 订单表 rows:600万
CREATE TABLE orders(
    id INT AUTO_INCREMENT,
    order_type INT,
    customer_id INT,
    amount DECIMAL(10,2),
    PRIMARY KEY(id)
);

以 orders 表为例,可以根据不同字段进行分表。

3. 修改配置文件 schema.xml

4. 修改配置文件 rule.xml

<!-- 在 rule 配置文件里新增分片规则 mod_rule -->
<tableRule name="mod_rule">
    <rule>
        <!-- 指定规则适用字段为 customer_id -->
        <columns>customer_id</columns>
        <!-- 选择分片算法 mod-long (对字段求模运算) -->
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

...

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <!-- how many data nodes -->
    <!--
    就两个节点,故配置算法 mod-long 参数 count 为 2
    customer_id 对两个节点求模,根据结果分片
     -->
    <property name="count">2</property>
</function>

5. 在数据节点 dn2 上建 orders 表

6. 重启 Mycat,让配置生效

7. 访问 Mycat 实现分片

# 在 Mycat 里向 orders 表插入数据,INSERT 后声明的字段列表不能省略!!!
# ERROR 1064 (HY000): partition table, insert must provide ColumnList
# ···········································································
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);

分片 Join

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

# 订单表 rows:600万
CREATE TABLE orders(
    id INT AUTO_INCREMENT,
    order_type INT,
    customer_id INT,
    amount DECIMAL(10,2),
    PRIMARY KEY(id)
);

# 订单详细表 rows:600万
CREATE TABLE orders_detail(
    id INT AUTO_INCREMENT,
    detail VARCHAR(2000),
    order_id INT,
    PRIMARY KEY(id)
);

我们要对 orders_detail 也要进行分片操作。Join 的原理如下图:

E-R 表

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

1. 修改 schema.xml 配置文件

2. 在 dn2 创建 orders_detail 表

3. 重启 Mycat

4. 访问 Mycat 向 orders_detail 表插入数据

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);

5. 在 Mycat、dn1、dn2 中运行两个表 Join 语句

全局表

在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题。考虑到字典表具有以下几个特性:

  1. 变动不频繁
  2. 数据量总体变化不大
  3. 数据规模不大,很少有超过数十万条记录

鉴于此,Mycat 定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:

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

将字典表或者符合字典表特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据 JOIN 的难题(实质是通过数据冗余,达到分片的效果)。通过全局表+基于 E-R 关系的分片策略,Mycat 可以满足 80% 以上的企业应用开发。


1. 修改 schema.xml 配置文件

2. 在 dn2 创建 dict_order_type 表

3. 重启 Mycat

4. 访问 Mycat 向 dict_order_type 表插入数据

INSERT INTO dict_order_type(id,order_type) VALUES(101,'type1');
INSERT INTO dict_order_type(id,order_type) VALUES(102,'type2');

5. 在 Mycat、dn1、dn2 中查询表数据

常用分片规则

取模

此规则为对分片字段取模运算。也是水平分表最常用规则。#1.1 配置分表中,orders 表采用了此规则。

分片枚举

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

1. 修改 schema.xml 配置文件

2. 修改 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>
    <!-- 0 为 int 型、!0 为 String -->
    <property name="type">1</property>
    <!-- 默认节点:<0 表示不设置默认节点,>=0 表示设置默认节点 -->
    <!-- 若设置默认节点,碰到不识别的枚举值,就让它路由到默认节点;若不设置,碰到不识别的就报错 -->
    <property name="defaultNode">0</property>
</function>

3. 修改 partition-hash-int.txt 配置文件(与 schema.xml 同目录)

110=0
120=1

0 表示 dn1,1 表示 dn2

4. 重启 Mycat

5. 访问 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)
);

6. 插入数据

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');

7. 查询 Mycat、dn1、dn2 可以看到数据分片效果

范围约定

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

1. 修改 schema.xml 配置文件

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 创建表

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

6. 插入数据

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);

7. 查询 Mycat、dn1、dn2 可以看到数据分片效果

按日期分片

此规则为按“天”分片,设定时间格式、范围。

1. 修改 schema.xml 配置文件

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>
    <!-- 分区天数,即默认从开始日期算起,分隔 2 天一个分区 -->
    <property name="sPartionDay">2</property>
</function>

11-01、11-02 插入 dn1;11-03、11-04 插入 dn2;如果不设置结束日期,11-05、11-06 就会去找 dn3 插入,我这就俩节点,肯定会报错。所以,要设置结束日期,让他再重新回到开始日期对应的节点进行插入,构成“循环插入”。

3. 重启 Mycat

4. 访问 Mycat 创建表

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

5. 插入数据

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');

6. 查询 Mycat、dn1、dn2 可以看到数据分片效果

全局序列

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

本地文件

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

优点:本地加载,读取速度较快。

缺点:抗风险能力差,Mycat 所在主机宕机后,无法读取本地文件。

数据库方式

概述:

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

详述:

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

sequence 获取步骤:

  1. 第一次使用该 sequence 时,根据传入的 sequence 名称,从数据库这张表中读取 current_value 和 increment 到 MyCat 中,并将数据库中的 current_value 设置为原 current_value 值 + increment 值(实现方式是基于后续的存储函数)。
  2. MyCat 将读取到 current_value + increment 作为本次要使用的 sequence 值,下次使用时,自动加 1,当使用increment 次后,执行步骤 1 相同的操作。

MyCat 负责维护这张表,用到哪些 sequence,只需要在这张表中插入一条记录即可。若某次读取的 sequence 没有用完,系统就停掉了,则这次读取的 sequence 剩余值不会再使用。当 Mycat 重新启动后会向数据库申请新的号段,原有号段会弃用。也就是说,如果 Mycat 重启,那么损失是当前的号段没用完的号码,因此不会出现主键重复。


1. 数据库配置

# (1) 在 dn1 上创建全局序列表
CREATE TABLE MYCAT_SEQUENCE(
    NAME VARCHAR(50) NOT NULL, # sequence名称
    current_value INT NOT NULL, # 当前value
    increment INT NOT NULL DEFAULT 100, # 增长步长,mycat在数据库中一次读取多少个sequence
    PRIMARY KEY(NAME)
) ENGINE=INNODB;

# (2) 创建全局序列所需的存储函数
# a. 获取当前 sequence 的值(返回当前值,增量)
DROP FUNCTION IF EXISTS mycat_seq_currval;
DELIMITER $$
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50))
RETURNS VARCHAR(64) DETERMINISTIC
BEGIN
    DECLARE retval VARCHAR(64);
    SET retval="-999999999,null";
    SELECT CONCAT(CAST(current_value AS CHAR),",",CAST(increment AS CHAR))
            INTO retval FROM MYCAT_SEQUENCE WHERE NAME = seq_name;
    RETURN retval;
END $$
DELIMITER ;

# b. 设置 sequence 值
DROP FUNCTION IF EXISTS mycat_seq_setval;
DELIMITER $$
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50), VALUE INTEGER)
RETURNS VARCHAR(64) DETERMINISTIC
BEGIN
    UPDATE MYCAT_SEQUENCE
    SET current_value = VALUE
    WHERE NAME = seq_name;
    RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;

# c. 获取下一个 sequence 值
DROP FUNCTION IF EXISTS mycat_seq_nextval;
DELIMITER $$
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50))
RETURNS VARCHAR(64) DETERMINISTIC
BEGIN
    UPDATE MYCAT_SEQUENCE
    SET current_value = current_value + increment
    WHERE NAME = seq_name;
    RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;

# (3) 初始化序列表记录
INSERT INTO MYCAT_SEQUENCE(NAME,current_value,increment) VALUES ('ORDERS', 400000, 100);

2. Mycat 配置

1) 修改 sequence_db_conf.properties

# ORDERS 这个序列在 dn1 节点上,具体 dn1 节点是哪台机子,请参考 schema.xml
ORDERS=dn1

2) 修改 server.xml

3) 重启 Mycat

3. 验证全局序列

1) 插入数据,查询数据

INSERT INTO orders(id,amount,customer_id,order_type)
VALUES (next value for MYCATSEQ_ORDERS, 1000, 101, 102);

2) 重启 Mycat,插入数据,查询数据

时间戳方式

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

优点:配置简单

缺点:18 位 ID 过长

自主生成全局序列

可在 Java 项目里自己生成全局序列:① 根据业务逻辑组合;② 利用 Redis 的单线程原子性 incr 来生成序列。

但是,自主生成需要单独在工程中用 Java 代码实现(耦合),还是推荐使用 Mycat 自带全局序列。

基于 HA 机制的 Mycat 高可用

在实际项目中,Mycat 服务也需要考虑高可用性,如果 Mycat 所在服务器出现宕机,或 Mycat 服务故障,需要有备机提供服务,需要考虑 Mycat 集群。

我们可以使用 HAProxy + Keepalived 配合两台 Mycat 搭起 Mycat 集群,实现高可用性。HAProxy 实现了 MyCat 多节点的集群高可用和负载均衡,而 HAProxy 自身的高可用则可以通过 Keepalived 来实现。

Mycat 安全设置

权限配置

user 标签

目前 Mycat 对于中间件的连接控制并没有做太复杂的控制,目前只做了中间件逻辑库级别的读写权限控制。是通过 server.xml 的 <user> 标签进行配置。

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

配置说明


测试案例一:使用 user 用户,权限为只读(readOnly:true),验证是否可以查询出数据,验证是否可以写入数据。

  1. 用 user 用户登录:mysql -uuser -puser -h 192.168.140.128 -P8066
  2. 切换到 TESTDB 数据库,查询 orders 表数据
  3. 执行插入数据 SQL,可看到运行结果:插入失败,只有只读权限

测试案例二:使用mycat用户,权限为可读写(readOnly:false),验证是否可以查询出数据,验证是否可以写入数据。

  1. 用 mycat 用户登录,运行命令如下:mysql -umycat -p123456 -h 192.168.140.128 -P8066
  2. 切换到 TESTDB 数据库,查询 orders 表数据
  3. 执行插入数据 SQL,可看到运行结果:插入成功

privileges 标签

<user> 标签下的 <privileges> 标签可以对逻辑库(schema)、表(table)进行精细化的 DML 权限控制。

<privileges> 标签下的 check 属性,如为 true 开启权限检查,为 false 不开启,默认为 false。由于 Mycat 一个用户的 schemas 属性可配置多个逻辑库(schema) ,所以 <privileges> 的下级节点 schema 节点同样可配置多个,对多库多表进行细粒度的 DML 权限控制。

<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>
        </schema>
    </privileges>
</user>

配置说明


测试案例一:使用 mycat 用户,privileges 配置 orders 表权限为禁止增删改查(dml="0000"),验证是否可以查询出数据,验证是否可以写入数据。

  1. 重启 mycat,用 mycat 用户登录:mysql -umycat -p123456 -h 192.168.140.128 -P8066
  2. 切换到 TESTDB 数据库,查询 orders 表数据,运行结果:禁止该用户查询数据
  3. 执行插入数据sql,同理,禁止该用户插入数据

测试案例二:使用 mycat 用户,privileges 配置 orders 表权限为可以增删改查(dml="1111"),验证是否可以查询出数据,验证是否可以写入数据。

  1. 重启 mycat,用 mycat 用户登录:mysql -umycat -p123456 -h 192.168.140.128 -P8066
  2. 切换到 TESTDB 数据库,查询 orders 表数据,查询成功
  3. 执行插入数据sql,插入成功

SQL 拦截

server.xml 中的 <firewall> 标签用来定义防火墙,<firewall><whitehost> 标签用来定义 IP 白名单 ,blacklist 用来定义 SQL 黑名单。

白名单

可以通过设置白名单,实现某主机某用户可以访问 Mycat,而其他主机用户禁止访问。

# 配置只有 192.168.140.128 主机可以通过 mycat 用户访问
<firewall>
    <whitehost>
        <host host="192.168.140.128" user="mycat"/>
    </whitehost>
</firewall>

黑名单

可以通过设置黑名单,实现 Mycat 对具体 SQL 操作的拦截,如增删改查等操作的拦截。

# 配置禁止 mycat 用户进行删除操作
<firewall>
    <whitehost>
        <host host="192.168.140.128" user="mycat"/>
    </whitehost>
    <blacklist check="true">
        <property name="deleteAllow">false</property>
    </blacklist>
</firewall>

测试删除操作:

可以设置的黑名单 SQL 拦截功能列表:

Mycat 监控平台

Mycat-web 简介

Mycat-web 是 Mycat 可视化运维的管理和监控平台,弥补了 Mycat 在监控上的空白。帮 Mycat 分担统计任务和配置管理任务。Mycat-web 引入了 ZooKeeper 作为配置中心,可以管理多个节点。Mycat-web 主要管理和监控 Mycat 的流量、连接、活动线程和内存等,具备 IP 白名单、邮件告警等模块,还可以统计 SQL 并分析慢 SQL 和高频 SQL 等,为优化 SQL 提供依据。

Mycat-web 配置使用

ZooKeeper 安装

  1. 下载安装包 http://zookeeper.apache.org/
  2. 安装包拷贝到 Linux 系统 /opt 目录下,并解压:tar -zxvf zookeeper-3.4.11.tar.gz
  3. 进入 ZooKeeper 解压后的配置目录(conf),复制配置文件并改名:cp zoo_sample.cfg zoo.cfg
  4. 进入 ZooKeeper 的命令目录(bin),运行启动命令:./zkServer.sh start
  5. ZooKeeper 服务端口为 2181,查看服务:netstat -ant | grep 2181

Mycat-web 安装

  1. 下载安装包 http://www.mycat.io/
  2. 安装包拷贝到 Linux 系统 /opt 目录下,并解压:tar -zxvf Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz
  3. 拷贝 mycat-web 文件夹到 /usr/local 目录下:cp -r mycat-web /usr/local
  4. 进入 mycat-web 的目录下运行启动命令:./start.sh &
  5. Mycat-web 服务端口为 8082,查看服务:netstat -ant | grep 8082
  6. 通过地址访问服务:http://192.168.140.127:8082/mycat/

Mycat-web 配置

  1. 先在注册中心配置 ZooKeeper 地址,配置后刷新页面
  2. 新增 Mycat 监控实例
  3. 保存成功

Mycat 性能监控指标

在 Mycat-web 上可以进行 Mycat 性能监控,例如:内存分享、流量分析、连接分析、活动线程分析等等。

posted @ 2020-11-15 11:16  tree6x7  阅读(80)  评论(0编辑  收藏  举报