第 17 章 数据库中间件 Mycat2

作者: Brinnatt 分类: mysql 经验专题 发布时间: 2023-01-13 16:37

17.1、Mycat2 概述

如今随着互联网的发展,数据的量级也是成指数的增长,从 GB 到 TB 到 PB。对数据的各种操作也是愈加的困难,传统的关系型数据库已经无法满足快速查询与插入数据的需求。

这个时候 NoSQL 的出现暂时解决了这一危机。它通过降低数据的安全性,减少对事务的支持,减少对复杂查询的支持,来获取性能上的提升。

但是,在有些场合 NoSQL 一些折衷是无法满足使用场景的,就比如有些使用场景是绝对要有事务与安全指标的。这个时候 NoSQL 肯定是无法满足的,所以还是需要使用关系型数据库。

如何使用关系型数据库解决海量存储的问题呢?此时就需要做数据库集群,为了提高查询性能将一个数据库的数据分散到不同的数据库中存储。

17.1.1、基本介绍

Mycat2 官网:http://www.mycat.org.cn/

Mycat2 Github:https://github.com/MyCATApache/Mycat2

Mycat2 是 数据库分库分表 中间件。

mysql_mycat

所谓中间件,是一类连接软件组件的中间桥梁,以便于软件各部件之间的沟通。例如 Tomcat 是常用的 web 中间件。Mycat 就是当下流行的数据库中间件,除此之外,还有下面这些数据库中间件:

Cobar:属于阿里 B2B 事业群,始于 2008 年,在阿里服役 3 年多,接管 3000+ 个 MySQL 数据库的 schema,集群日处理在线 SQL 请求 50 亿次以上。由于 Cobar 发起人的离职,Cobar 停止维护。

Mycat:是开源社区在阿里 cobar 基础上进行二次开发,解决了 cobar 存在的问题,并且加入了许多新的功能在其中。青出于蓝而胜于蓝。

OneProxy:基于 MySQL 官方的 proxy 思想利用 c 语言进行开发的,OneProxy 是一款商业收费的中间件。舍弃了一些功能,专注在性能和稳定性上。

kingshard:由小团队用 go 语言开发,还需要发展,需要不断完善。

Vitess:是 Youtube 生产在使用,架构很复杂。不支持 MySQL 原生协议,使用需要大量改造成本。

Atlas:是 360 团队基于 mysql proxy 改写,功能还需完善,高并发下不稳定。

MaxScale:是 mariadb(MySQL 原作者维护的一个版本)研发的中间件。

MySQLRoute:是 MySQL 官方 Oracle 公司发布的中间件。

17.1.2、Mycat2 作用

  1. 读写分离

    mysql_write_read_split

    mysql_double_master_slave

  2. 数据分片

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

    shujufenpian

    mysql_v_h_cut

  3. 多数据源整合

    mysql_duoshujuyuanzhenghe

    Mycat 支持的数据库:

    mysql_others

17.1.3、原理

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

mysql_mycat_split

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

整体过程可以概括为:拦截 --> 分发 --> 响应

17.2、环境准备

  1. 准备 5 台 CentOS 虚拟机。
  2. 4 台虚拟机上需要安装好 MySQL(这里用的是 MySQL v8.0.31),1 台安装 mycat2。

说明:前面我们讲过如何克隆一台 CentOS。大家可以在一台 CentOS 上安装好 MySQL,进而通过克隆的方式复制出 1 台包含 MySQL 的虚拟机。

注意:克隆的方式需要修改新克隆出来主机的。

  • MAC 地址
  • IP 地址
  • hostname
  • UUID

此外,克隆的方式生成的虚拟机(包含 MySQL Server),则克隆的虚拟机 MySQL Server 的 UUID 相同,必须修改,否则在有些场景会报错。比如:show slave status\G,报如下的错误:

Last_IO_Error: Fatal error: The slave I/O thread stops because master and slave have
equal MySQL server UUIDs; these UUIDs must be different for replication to work.

修改 MySQL Server 的 UUID 方式:

vim /var/lib/mysql/auto.cnf

systemctl restart mysqld

17.3、安装启动

17.3.1、安装

安装 mycat2 之前要安装 java jdk 运行环境,mycat2 是基于 java 1.8 开发的,如下安装:

[root@master1 mycat]# yum install java-1.8.0-openjdk -y

先下载一个运行环境框架包:

[root@master1 ~]# wget http://dl.mycat.org.cn/2.0/install-template/mycat2-install-template-1.20.zip

然后可以下载 MyCat2 的运行包:

[root@master1 ~]# wget http://dl.mycat.org.cn/2.0/1.21-release/mycat2-1.21-release-jar-with-dependencies-2022-5-9.jar

下载下来后,运行框架包解压,将 MyCat2 的运行包移到 mycat/lib 目录下:

[root@master1 ~]# unzip mycat2-install-template-1.20.zip
[root@master1 ~]# mv mycat2-1.21-release-jar-with-dependencies-2022-5-9.jar mycat/lib/

把整合好的文件夹拷贝到 linux 下 /usr/local/:

[root@master1 ~]# mv mycat /usr/local/
[root@master1 ~]# ll /usr/local/mycat/
total 8
drwxr-xr-x 2 root root 4096 Mar  5  2021 bin
drwxr-xr-x 9 root root  275 Mar  5  2021 conf
drwxr-xr-x 2 root root 4096 Dec 29 06:12 lib
drwxr-xr-x 2 root root    6 Mar  5  2021 logs

将 bin 目录下的可执行文件增加执行权限:

[root@master1 ~]# chmod +x /usr/local/mycat/bin/*
[root@master1 ~]# ll /usr/local/mycat/bin/*
-rwxr-xr-x 1 root root  15666 Mar  5  2021 /usr/local/mycat/bin/mycat
-rwxr-xr-x 1 root root   3916 Mar  5  2021 /usr/local/mycat/bin/mycat.bat
-rwxr-xr-x 1 root root 281540 Mar  5  2021 /usr/local/mycat/bin/wrapper-aix-ppc-32
-rwxr-xr-x 1 root root 319397 Mar  5  2021 /usr/local/mycat/bin/wrapper-aix-ppc-64
-rwxr-xr-x 1 root root 253808 Mar  5  2021 /usr/local/mycat/bin/wrapper-hpux-parisc-64
-rwxr-xr-x 1 root root 140198 Mar  5  2021 /usr/local/mycat/bin/wrapper-linux-ppc-64
-rwxr-xr-x 1 root root  99401 Mar  5  2021 /usr/local/mycat/bin/wrapper-linux-x86-32
-rwxr-xr-x 1 root root 111027 Mar  5  2021 /usr/local/mycat/bin/wrapper-linux-x86-64
-rwxr-xr-x 1 root root 114052 Mar  5  2021 /usr/local/mycat/bin/wrapper-macosx-ppc-32
-rwxr-xr-x 1 root root 233604 Mar  5  2021 /usr/local/mycat/bin/wrapper-macosx-universal-32
-rwxr-xr-x 1 root root 253432 Mar  5  2021 /usr/local/mycat/bin/wrapper-macosx-universal-64
-rwxr-xr-x 1 root root 112536 Mar  5  2021 /usr/local/mycat/bin/wrapper-solaris-sparc-32
-rwxr-xr-x 1 root root 148512 Mar  5  2021 /usr/local/mycat/bin/wrapper-solaris-sparc-64
-rwxr-xr-x 1 root root 110992 Mar  5  2021 /usr/local/mycat/bin/wrapper-solaris-x86-32
-rwxr-xr-x 1 root root 204800 Mar  5  2021 /usr/local/mycat/bin/wrapper-windows-x86-32.exe
-rwxr-xr-x 1 root root 220672 Mar  5  2021 /usr/local/mycat/bin/wrapper-windows-x86-64.exe

17.3.2、启动

  1. 在 mycat 连接的 mysql 数据库里添加用户,用户名为 mycat,密码为 WelC0me168!,赋相应权限,如下:

    mysql> CREATE USER 'mycat'@'%' IDENTIFIED BY 'WelC0me168!';
    Query OK, 0 rows affected (0.01 sec)
    
    --必须要赋的权限mysql8才有的
    mysql> GRANT XA_RECOVER_ADMIN ON *.* TO 'root'@'%';
    Query OK, 0 rows affected (0.00 sec)
    
    --视情况赋权限
    mysql> GRANT ALL PRIVILEGES ON *.* TO 'mycat'@'%';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> flush privileges;
    Query OK, 0 rows affected (0.00 sec)
  2. 修改 mycat 的 prototype 元数据信息。

    这是 MyCat 运行所需要的基本信息。配置文件在 config/datasource/prototypeDs.datasource.json。

    主要是通过 url,user,password 三个属性指向一个 MySQL 服务。如果指向的 MySQL 服务无法连接,那么 MyCat2 在启动阶段就会报错。

    [root@master1 ~]# cat /usr/local/mycat/conf/datasources/prototypeDs.datasource.json
    {
           "dbType":"mysql",
           "idleTimeout":60000,
           "initSqls":[],
           "initSqlsGetConnection":true,
           "instanceType":"READ_WRITE",
           "maxCon":1000,
           "maxConnectTimeout":3000,
           "maxRetryCount":5,
           "minCon":1,
           "name":"prototypeDs",
           "password":"WelC0me168!",
           "type":"JDBC",
           "url":"jdbc:mysql://localhost:3306/mysql?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8",
           "user":"mycat",
           "weight":0
    }
  3. 验证物理数据库访问情况

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

    [root@master1 ~]# mysql -umycat -pWelC0me168! -h 192.168.95.101 -P 3306
    mysql: [Warning] Using a password on the command line interface can be insecure.
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 16
    Server version: 8.0.31 MySQL Community Server - GPL
    
    Copyright (c) 2000, 2022, Oracle and/or its affiliates.
    
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    
    mysql>

    如果拒绝访问,请参考网上的一些资料解决 mysql8.0 的远程访问问题。

  4. 启动 mycat

    [root@master1 mycat]# bin/mycat start
    Starting mycat2...
    [root@master1 mycat]# bin/mycat status
    mycat2 is running (3197).
  5. 登录数据窗口

    [root@master1 mycat]# mysql -uroot -P 8066 -p123456 -h127.0.0.1
    mysql: [Warning] Using a password on the command line interface can be insecure.
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 3
    Server version: 5.7.33-mycat-2.0 MySQL Community Server - GPL
    
    Copyright (c) 2000, 2022, Oracle and/or its affiliates.
    
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    
    mysql> show databases;

注意:show databases;指令看到的是 MyCat2 服务当中的逻辑库,跟之前 prototypeDs.datasource.json 配置文件中指定的数据库中的 databases 会有不同。

17.4、Mycat2 相关概念

17.4.1、概念描述

  1. 分库分表

    按照一定规则把数据库中的表拆分为多个带有数据库实例,物理库,物理表访问路径的分表。

    分库:一个电商项目,分为用户库、订单库等等。

    分表:一张订单表数据量超百万,达到 MySQL 单表瓶颈,分到多个数据库中的多张表。

  2. 逻辑库

    数据库代理中的数据库,它可以包含多个逻辑表。

    Mycat 里定义的库,在逻辑上存在,物理上在 MySQL 里并不存在。有可能是多个 MySQL 数据库共同组成一个逻辑库。类似多个小孩叠罗汉穿上外套,扮演一个大人。

  3. 逻辑表

    数据库代理中的表,它可以映射代理连接的数据库中的表(物理表)。

    Mycat 里定义的表,在逻辑上存在,可以映射真实的 MySQL 数据库的表。可以一对一,也可以一对多。

  4. 物理库

    数据库代理连接的数据库中的库,MySQL 真实的数据库。

  5. 物理表

    数据库代理连接的数据库中的表,MySQL 真实的数据库中的真实数据表。

  6. 拆分键

    即分片键,描述拆分逻辑表的数据规则的字段,比如订单表可以按照归属的用户 id 拆分,用户 id 就是拆分键。

  7. 物理分表

    指已经进行数据拆分的,在数据库上面的物理表,是分片表的一个分区。

    多个物理分表里的数据汇总就是逻辑表的全部数据。

  8. 物理分库

    一般指包含多个物理分表的库,参与数据分片的实际数据库。

  9. 分库

    一般指通过多个数据库拆分分片表,每个数据库一个物理分表,物理分库名字相同。

    解读:分库是个动作,需要多个数据库参与。就像多个数据库是多个盘子,分库就是把一串数据葡萄,分到各个盘子里,而查询数据时,所有盘子的葡萄又通过 Mycat2 组成了完整的一串葡萄。

  10. 分片表,水平分片表

    按照一定规则把数据拆分成多个分区的表,在分库分表语境下,它属于逻辑表的一种。

  11. 单表

    没有分片,没有数据冗余的表,没有拆分数据,也没有复制数据到别的库的表。

  12. 全局表,广播表

    每个数据库实例都冗余全量数据的逻辑表。它通过表数据冗余,使分片表的分区与该表的数据在同一个数据库实例里,达到 join 运算能够直接在该数据库实例里执行。它的数据一致性一般是通过数据库代理分发 SQL 实现,也有基于集群日志实现。

    例如系统中翻译字段的字典表,每个分片表都需要完整的字典数据翻译字段。

  13. ER 表

    狭义指父子表中的子表,它的分片键指向父表的分片键,而且两表的分片算法相同;广义指具有相同数据分布的一组表。

    关联别的表的子表,例如:订单详情表就是订单表的 ER 表。

  14. 集群

    多个数据节点组成的逻辑节点在 mycat2 里,它是把对多个数据源地址视为一个数据源地址(名称),并提供自动故障恢复、转移,即实现高可用,负载均衡的组件。

    集群就是高可用、负载均衡的代名词。

  15. 数据源

    连接后端数据库的组件,它是数据库代理中连接后端数据库的客户端。

    Mycat 通过数据源连接 MySQL 数据库。

  16. 原型库(prototype)

    原型库是 Mycat2 后面的数据库,比如 mysql 库。

    原型库就是存储数据的真实数据库,配置数据源时必须指定原型库。

17.4.2、配置文件

MyCat2 定位是一个数据库中间件,他并不存储数据。MyCat2 所有的功能都可以理解为通过一系列配置文件定制一系列业务规则,通过与其他数据库(目前主要是MySQL)协作,提供具体的业务功能。所以,目前 MyCat2 的所有功能都体现在他的配置文件当中。

我们先来了解一下 MyCat2 的主要配置文件。

[root@master1 mycat]# ll conf/
total 32
drwxr-xr-x 2 root root   36 Jun 28  2021 clusters       # 逻辑集群配置
drwxr-xr-x 2 root root   41 Dec 29 07:20 datasources    # 数据源配置
-rw-r--r-- 1 root root 3338 Mar  5  2021 dbseq.sql
-rw-r--r-- 1 root root  316 Nov  2  2021 logback.xml
-rw-r--r-- 1 root root    0 Mar  5  2021 mycat.lock
drwxr-xr-x 2 root root   31 Jun 28  2021 schemas        # 逻辑库配置
drwxr-xr-x 2 root root    6 Jun 28  2021 sequences
-rw-r--r-- 1 root root  776 Dec 28  2021 server.json
-rw-r--r-- 1 root root 1643 Mar  5  2021 simplelogger.properties
drwxr-xr-x 2 root root  233 Jun 28  2021 sql
drwxr-xr-x 2 root root    6 Jun 28  2021 sqlcaches
-rw-r--r-- 1 root root   49 Mar  5  2021 state.json
drwxr-xr-x 2 root root   28 Dec 29 07:49 users          # mycat2用户配置
-rw-r--r-- 1 root root  211 Mar  5  2021 version.txt
-rw-r--r-- 1 root root 4165 Jan 13  2022 wrapper.conf

1、用户 users

目录:mycat/conf/users

命名规则:{用户名}.user.json

主要配置内容:

[root@master1 mycat]# cat conf/users/root.user.json
{
        "dialect":"mysql",
        "ip":null,
        "password":"123456",
        "transactionType":"xa",
        "username":"root"
}

主要字段含义:

  • ip:客户端访问 ip,建议为空,填写后会对客户端的 ip 进行限制。

  • username:MyCat2 登录的用户名。

  • password:MyCat2 登录的密码。

  • dialect:数据库类型。目前就 MySQL。

  • transactionType:事务类型。

    • proxy:本地事务,兼容性最好,但是分布式场景事务控制不严格。

    • XA:分布式事务,需要后端数据库能够支持 XA。

    # 可以通过语句查询
    mysql> select @@transaction_policy;
    +----------------------+
    | @@transaction_policy |
    +----------------------+
    | xa                   |
    +----------------------+
    1 row in set (0.01 sec)
    
    # 这是在mycat里面,可以通过语句实现切换
    # set transaction_policy = 'xa'
    # set transaction_policy = 'proxy'

2、数据源 datasource

目录:mycat/conf/datasources

命名规则:{数据源名字}.datasource.json

主要配置内容:

[root@master1 mycat]# cat conf/datasources/prototypeDs.datasource.json
{
        "dbType":"mysql",
        "idleTimeout":60000,
        "initSqls":[],
        "initSqlsGetConnection":true,
        "instanceType":"READ_WRITE",
        "maxCon":1000,
        "maxConnectTimeout":3000,
        "maxRetryCount":5,
        "minCon":1,
        "name":"prototypeDs",
        "password":"WelC0me168!",
        "type":"JDBC",
        "url":"jdbc:mysql://localhost:3306/mysql?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8",
        "user":"mycat",
        "weight":0
}

主要字段含义:

  • dbType:数据库类型 mysql。

  • name:在 MyCat2 中定义的数据源名字。

  • user,password,url:实际数据库的 JDBC 属性。

  • instanceType:配置实例只读还是读写。可选值有 READ_WRITE、READ、WRITE。

  • type:数据源类型,默认 JDBC。

  • weight:负载均衡权重。

  • idleTimeout:空闲连接超时时间。

  • initSqls:初始化sql。

  • initSqlsGetConnection:对于 jdbc 每次获取连接是否都执行 initSqls。

3、集群 clusters

目录:mycat/conf/clusters

命名规则:{集群名字}.cluster.json

主要配置内容:

[root@master1 mycat]# cat conf/clusters/prototype.cluster.json
{
        "clusterType":"MASTER_SLAVE",
        "heartbeat":{
                "heartbeatTimeout":1000,
                "maxRetry":3,
                "minSwitchTimeInterval":300,
                "slaveThreshold":0
        },
        "masters":[
                "prototypeDs"
        ],
        "maxCon":200,
        "name":"prototype",
        "readBalanceType":"BALANCE_ALL",
        "switchType":"SWITCH"
}

主要字段含义:

  • clusterType:集群类型。

    • SINGLE_NODE:单一节点;
    • MASTER_SLAVE:普通主从;
    • 另外针对高可用集群还支持 MHA 和 MGR。
  • readBalanceType:查询负载均衡策略,可选值:

    • BALANCE_ALL(默认值):获取集群中所有数据源;
    • BALANCE_ALL_READ:获取集群中允许读的数据源;
    • BALANCE_READ_WRITE:获取集群中允许读写的数据源,但允许读的数据源优先;
    • BALANCE_NONE:获取集群中允许写数据源,即主节点中选择。
  • switchType:切换类型。

    • NOT_SWITCH:不进行主从切换;
    • SWITCH:进行主从切换。
  • masters:主从集群中的主库名字。从库用 replicas 配置。

4、逻辑库 schema

目录:mycat/conf/schemas

命名规则:{库名}.schema.json

主要配置内容:

[root@master1 mycat]# cat conf/schemas/mysql.schema.json
{
    "customTables":{},
    "globalTables":{},
    "normalProcedures":{},
    "normalTables":{},
    "schemaName":"wrdb",
    "shardingTables":{},
    "targetName":"",
    "views":{}
}

主要字段含义:

  • schema:逻辑库名。
  • targetName:目的真实数据源或集群。
  • normalTables:常规表。如果物理表已经存在或者在每次启动服务时需要加载表定义,就可以写在这里。

17.5、实现读写分离

我们通过 Mycat 和 MySQL 的主从复制配合搭建数据库的读写分离,实现 MySQL 的高可用性。我们将搭建:一主一从、双主双从两种读写分离模式。

读写分离的基本原理是 MyCat2 将 Update、Delete、Insert 这一类写数据的请求转发到 MySQL 集群中的主节点,然后将 Select 类的读数据的请求转发到 MySQL 集群中的从节点。而 MySQL 的主节点与从节点之间,通常会使用 MySQL 的主从同步机制来进行数据同步,从而保证读请求能够读取到最新的数据结果。

17.5.1、搭建一主一从

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

mysql_master_slave_midware1

编号 角色 IP 地址 机器名
1 Master1 192.168.95.101 master1
2 Slave1 192.168.95.201 slave1

搭建过程这里不再赘述,参见第 15 章。

17.5.2、配置 mycat2 一主一从读写分离

17.5.2.1、配置真实数据源

登录 MyCat2 后,可以使用 MyCat2 提供的注解方式配置真实数据源。

# 创建数据源
# 创建dbw写库,指向集群中的master服务
/*+ mycat:createDataSource{"name":"dbW","url":"jdbc:mysql://192.168.95.101:3306/mysql?useSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true","user":"root", "password":"WelC0me168!" } */;

# 创建dbR读库,指向集群中的slave服务。如果有多个从库,依次继续创建
/*+ mycat:createDataSource{ "name":"dbR","url":"jdbc:mysql://192.168.95.201:3306/mysql?useSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true","user":"root", "password":"WelC0me168!" } */;

# 查询配置数据源结果
/*+ mycat:showDataSources{} */\G

1、真实数据源也就是实际存储数据的数据源,例如之前在 prototypeDs.datasource.json 文件中配置的 prototypeDs 也是一个真实数据源。

2、创建了两个数据库后,进入 MyCat 的部署目录,在 conf/datasource 目录下,可以看到对应的数据库配置文件 dbR.datasource.json 和
dbW.datasource.json。这是 MyCat2 自动创建的配置文件,里面有关于这个数据源的详细配置信息。

17.5.2.2、配置 MySQL 集群

接下来需要在 MyCat2 中配置一个包含真实数据源的 MySQL 集群,通过集群来实现读写分离。

# 更新集群信息
/*! mycat:createCluster{"name":"WRSplitCluster","masters":["dbW"],"replicas":["dbR"]} */;

# 查看集群配置信息
/*+ mycat:showClusters{} */;

1、这样就创建一个名为 WRSplitCluster 的集群,masters 指定主库,replicas 指定从库。其中,replicas 从库可以配置多个,表示有多个从库。masters 也可以配置多个,第一个表示主库,后面的表示备用库。

2、配置集群后,MyCat2 会在 conf/cluster 目录下创建对应的集群配置文件,WRSplitCluster.cluster.json。里面包含了关于这个集群的详细配置信息。另外,也可以看到,MyCat2 默认情况下也提供了一个名为 prototype 的集群。

17.5.2.3、配置逻辑库

有了集群后,还需要在 MyCat2 中配置一个逻辑库,指向这个集群。这样,客户端就只需要操作这个逻辑库,由 MyCat2 去完成真实的读写分离逻辑。

-- 1、在mycat2的服务中声明一个逻辑库。
create database wrdb;

-- 2、在mycat2的部署目录下,找到对应的配置文件 conf/schema/wrdb.schema.json。在其中增加targetName:WRSplitCluster属性。指向真实的集群。
{
    "customTables":{},
    "globalTables":{},
    "normalProcedures":{},
    "normalTables":{},
    "schemaName":"wrdb",
    "targetName":"WRSplitCluster",
    "shardingTables":{},
    "views":{}
}
-- 3、手动修改配置文件之后,需要重启MyCat2服务,让配置文件生效。

1、逻辑库即客户端直接操作的库。后续在库中建表,建视图、存储过程等,最终都会体现在对应的 schema.json 中。

2、Mycat2 可以有很多集群,每个集群对应不同的物理数据源和读写逻辑,所以逻辑库文件 conf/schema/xxx.schema.json 的 targetName 属性指向哪个集群变得至关重要,因为客户端只在乎如何操作逻辑库,不关心背后复杂的物理数据库。

重启 mycat2:

[root@master1 mycat]# bin/mycat restart
Stopping mycat2...
Stopped mycat2.
Starting mycat2...

注意:如果 mycat2 配置完成后不能重启服务,请检查 mysql 数据源的用户名和密码以及是否可远程连接,特别是 mysql8.0 以后的版本。

17.5.2.4、读写分离测试

这样就完成了 MyCat2 的读写分离配置。接下来在 MySQL 主从集群的基础上测试 MyCat2 的读写分离。

# 在mycat2上操作
mysql> USE wrdb;
mysql> CREATE TABLE t1(id int, name VARCHAR(10));
mysql> insert into t1 values(1,"Darry");

# 因为主从会同步,为了演示效果,手动在从节点插入一条数据
mysql> insert into t1 values(2,"Tom");

# 在mycat2上查询,有Tom的是从节点
mysql> select * from t1;
+------+-------+
| id   | name  |
+------+-------+
|    1 | Darry |
+------+-------+
1 row in set (0.00 sec)

mysql> select * from t1;
+------+-------+
| id   | name  |
+------+-------+
|    1 | Darry |
+------+-------+
1 row in set (0.00 sec)

mysql> select * from t1;
+------+-------+
| id   | name  |
+------+-------+
|    1 | Darry |
|    2 | Tom   |
+------+-------+
2 rows in set (0.00 sec)

当前使用的集群配置 readBalanceType":"BALANCE_ALL,效果如上所示,可以修改成别的选项继续测试。

17.5.3、搭建双主双从

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

mysql_double_master_slave1

编号 角色 IP 地址 机器名
1 Master1 192.168.95.101 master1
2 Slave1 192.168.95.201 slave1
3 Master2 192.168.95.102 master2
4 Slave2 192.168.95.202 slave2
5 mycat 192.168.95.88 guest

搭建过程这里不再赘述,参见第 15 章。

17.5.4、配置 mycat2 多主多从读写分离

17.5.4.1、配置真实数据源

登录 MyCat2 后,可以使用 MyCat2 提供的注解方式配置真实数据源。注意,这里只是添加数据源,读写分离逻辑在后面的集群配置中体现。

# 创建数据源
# 创建dbW1写库,指向集群中的master1服务
/*+ mycat:createDataSource{"name":"dbW1","url":"jdbc:mysql://192.168.95.101:3306/mysql?useSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true","user":"root", "password":"WelC0me168!" } */;
# 创建dbW2写库,指向集群中的master2服务
/*+ mycat:createDataSource{"name":"dbW2","url":"jdbc:mysql://192.168.95.102:3306/mysql?useSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true","user":"root", "password":"WelC0me168!" } */;

# 创建dbR1读库,指向集群中的slave1服务。如果有多个从库,依次继续创建。
/*+ mycat:createDataSource{ "name":"dbR1","url":"jdbc:mysql://192.168.95.201:3306/mysql?useSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true","user":"root", "password":"WelC0me168!" } */;
# 创建dbR2读库,指向集群中的slave2服务。
/*+ mycat:createDataSource{ "name":"dbR2","url":"jdbc:mysql://192.168.95.202:3306/mysql?useSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true","user":"root", "password":"WelC0me168!" } */;

# 查询配置数据源结果
/*+ mycat:showDataSources{} */\G

17.5.4.2、配置 MySQL 集群

接下来需要在 MyCat2 中配置一个包含真实数据源的 MySQL 集群,通过集群来实现读写分离。

# 更新集群信息
/*! mycat:createCluster{"name":"WRSplitCluster","masters":["dbW1","dbW2"],"replicas":["dbR1","dbR2","dbW2"]} */;

# 查看集群配置信息
/*+ mycat:showClusters{} */;

我们看一眼自动生成的集群配置文件,注意我们上面的每一步都是通过注解命令的方式执行的,mycat2 根据这些命令自动生成集群配置文件。如果在生产环境下,想要在已有集群的基础上增加数据源的话,可以手动修改配置文件。

[root@guest mycat]# cat conf/clusters/WRSplitCluster.cluster.json 
{
        "clusterType":"MASTER_SLAVE",
        "heartbeat":{
                "heartbeatTimeout":1000,
                "maxRetryCount":3,
                "minSwitchTimeInterval":300,
                "showLog":false,
                "slaveThreshold":0.0
        },
        "masters":[
                "dbW1",
                "dbW2"
        ],
        "maxCon":2000,
        "name":"WRSplitCluster",
        "readBalanceType":"BALANCE_ALL",
        "replicas":[
                "dbR1",
                "dbR2",
                "dbW2"
        ],
        "switchType":"SWITCH"
}

17.5.4.3、配置逻辑库

有了集群后,还需要在 MyCat2 中配置一个逻辑库,指向这个集群。这样,客户端就只需要操作这个逻辑库,由 MyCat2 去完成真实的读写分离逻辑。

-- 1、在mycat2的服务中声明一个逻辑库。
create database wrdb;

-- 2、在mycat2的部署目录下,找到对应的配置文件 conf/schema/wrdb.schema.json。在其中增加targetName:WRSplitCluster属性。指向真实的集群。
{
    "customTables":{},
    "globalTables":{},
    "normalProcedures":{},
    "normalTables":{},
    "schemaName":"wrdb",
    "targetName":"WRSplitCluster",
    "shardingTables":{},
    "views":{}
}
-- 3、手动修改配置文件之后,需要重启MyCat2服务,让配置文件生效。

重启 mycat2,重启之前确保所有 mysql 都可以远程登陆:

mysql> use mysql;
mysql> update user set host='%' where user='root';
mysql> flush privileges;

[root@guest mycat]# bin/mycat restart 
Stopping mycat2...
Stopped mycat2.
Starting mycat2...

重启失败,报错:

Caused by: com.mysql.cj.exceptions.UnableToConnectException: Public Key Retrieval is not allowed

解决方法:

# 手动给物理数据源的url加allowPublicKeyRetrieval=true参数,如下所有的数据源都加。
[root@guest mycat]# grep "allowPublicKeyRetrieval=true" conf/datasources/db*
conf/datasources/dbR1.datasource.json:  "url":"jdbc:mysql://192.168.95.201:3306/mysql?useJDBCCompliantTimezoneShift=true&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true",
conf/datasources/dbR2.datasource.json:  "url":"jdbc:mysql://192.168.95.202:3306/mysql?useJDBCCompliantTimezoneShift=true&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true",
conf/datasources/dbW1.datasource.json:  "url":"jdbc:mysql://192.168.95.101:3306/mysql?useJDBCCompliantTimezoneShift=true&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true",
conf/datasources/dbW2.datasource.json:  "url":"jdbc:mysql://192.168.95.102:3306/mysql?useJDBCCompliantTimezoneShift=true&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true",

17.5.4.4、读写分离测试

这样就完成了 MyCat2 的读写分离配置。接下来在 MySQL 双主双从集群的基础上测试 MyCat2 的读写分离。

# 在mycat2上操作
mysql> USE wrdb;
Database changed
mysql> CREATE TABLE t1(id int, name VARCHAR(10));
Query OK, 0 rows affected (0.49 sec)

# 因为我们的mysql服务器binlog格式都是ROW格式,所以@@hostname都会变现成master1,主从数据一致
mysql> INSERT INTO t1 VALUES (1, @@hostname);
Query OK, 1 row affected (1.68 sec)

# 下面我们把binlog格式改成STATEMENT,@@hostname不会变现,仅仅记录完整SQL语句,主从数据不一致,
mysql> SET GLOBAL binlog_format='STATEMENT';
!!!换成STATEMENT实验失败

经过实验,双主双从与mycat2,binlog_format 只能是 ROW 或 MIXED 格式,STATEMENT 格式主从数据根本就不会复制,数据错乱,主从服务崩溃。

# 手动在双主双从上插入@@hostname,只是为了实验,看到效果而已
mysql> SELECT * FROM t1;
+------+---------+
| id   | name    |
+------+---------+
|    1 | master2 |
|    1 | master1 |
+------+---------+
2 rows in set (0.02 sec)

mysql> SELECT * FROM t1;
+------+---------+
| id   | name    |
+------+---------+
|    1 | slave1  |
|    1 | master1 |
|    1 | master2 |
+------+---------+
3 rows in set (0.01 sec)

mysql> SELECT * FROM t1;
+------+---------+
| id   | name    |
+------+---------+
|    1 | slave2  |
|    1 | master2 |
|    1 | master1 |
+------+---------+
3 rows in set (0.00 sec)

当前使用的集群配置 readBalanceType":"BALANCE_ALL,效果如上所示,可以修改成别的选项继续测试。

17.6、分库分表

前面无论是一主一从还是双主双从,都演示的是读写分离,根据集群配置的负载算法进行读写。接下来要讲的分库分表是对数据进行切片,分散到多个集群当中去,所以我们实验用两个独立的一主一从集群。

编号 角色 IP 地址 机器名
1 Master1 192.168.95.101 master1
2 Slave1 192.168.95.201 slave1
3 Master2 192.168.95.102 master2
4 Slave2 192.168.95.202 slave2
5 mycat 192.168.95.88 guest

搭建过程这里不再赘述,参见第 15 章。

一个数据库由很多表构成,每个表对应着不同的业务,垂直切分是指按照业务不同将表进行分类,分散到不同的数据库上面,这样也就将数据或者说压力分担到不同的数据库上面,如下图:

shujufenpian

17.6.1、如何分库

分库原则:有紧密关联关系的表应该在一个库里,没有关联关系的表可以分到不同的库里。

# 客户表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)
);

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

17.6.2、如何分表

  1. 选择要拆分的表

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

    例如:例子中的 orders、orders_detail 都已经达到 600 万行数据,需要进行分表优化。

  2. 分表字段

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

    编号 分表字段 效果
    1 id(主键、或创建时间) 查询订单注重时效,历史订单被查询的次数少,如此分片会造成一个节点访问多,一个访问少,不平均。
    2 customer_id(客户 id) 根据客户 id 去分,两个节点访问平均,一个客户的所有订单都在同一个节点。

17.6.3、实现分库分表

Mycat2 的一大优势就是可以在终端直接创建数据源、集群、库表,并在创建时指定分库、分表。与 1.6 版本比大大简化了分库分表的操作。

17.6.3.1、配置数据源

很多网上的资料都没有加 url 参数,我实验时经常遇到各种参数问题,所以我在做实验的时候,加上了这些参数,避免报错:

# 第一个写库
/*+ mycat:createDataSource{"name":"dw0","url":"jdbc:mysql://192.168.95.101:3306/mysql?useJDBCCompliantTimezoneShift=true&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true","user":"root","password":"WelC0me168!"} */;

# 第一个读库
/*+ mycat:createDataSource{"name":"dr0","url":"jdbc:mysql://192.168.95.201:3306/mysql?useJDBCCompliantTimezoneShift=true&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true","user":"root","password":"WelC0me168!"} */;

# 第二个写库
/*+ mycat:createDataSource{"name":"dw1","url":"jdbc:mysql://192.168.95.102:3306/mysql?useJDBCCompliantTimezoneShift=true&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true","user":"root","password":"WelC0me168!"} */;

# 第二个读库
/*+ mycat:createDataSource{"name":"dr1","url":"jdbc:mysql://192.168.95.202:3306/mysql?useJDBCCompliantTimezoneShift=true&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true","user":"root","password":"WelC0me168!"} */;

生成下面 4 个数据源文件:

[root@guest mycat]# ll conf/datasources/d*
-rw-r--r-- 1 root root 519 Dec 31 04:09 conf/datasources/dr0.datasource.json
-rw-r--r-- 1 root root 519 Dec 31 04:09 conf/datasources/dr1.datasource.json
-rw-r--r-- 1 root root 519 Dec 31 04:08 conf/datasources/dw0.datasource.json
-rw-r--r-- 1 root root 519 Dec 31 04:09 conf/datasources/dw1.datasource.json

17.6.3.2、配置集群

# 在mycat2终端输入
# 配置第一组集群
/*! mycat:createCluster{"name":"c0","masters":["dw0"],"replicas":["dr0"]}*/;

# 配置第二组集群
/*! mycat:createCluster{"name":"c1","masters":["dw1"],"replicas":["dr1"]}*/;

生成下面 2 个集群文件:

[root@guest mycat]# ll conf/clusters/c*
-rw-r--r-- 1 root root 312 Dec 31 04:23 conf/clusters/c0.cluster.json
-rw-r--r-- 1 root root 312 Dec 31 04:23 conf/clusters/c1.cluster.json

这里集群名 c0,c1 是 MyCat2 默认支持的分片集群名字。不建议修改集群名字。按照使用习惯,接下来要配置逻辑库,然后逻辑库文件 xxx.schema.json 中指定目标集群。但是 mycat2 为了简化配置,在实现分库分表时,直接创建逻辑库和表的时候,指定规则,最终逻辑库文件会自动找到 c0、c1、...... 集群进行配置。

17.6.3.3、配置全局表

全局表表示在所有数据分片上都相同的表。比如字典表。

# 添加数据库db1
CREATE DATABASE db1;

# 在建表语句中加上关键字 BROADCAST(广播,即为全局表)
CREATE TABLE db1.`travelrecord` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `user_id` varchar(100) DEFAULT NULL,
    `traveldate` date DEFAULT NULL,
    `fee` decimal(10,0) DEFAULT NULL,
    `days` int DEFAULT NULL,
    `blob` longblob,
    PRIMARY KEY (`id`),
    KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 BROADCAST;

配置完成后的全局表会写入到 mycat/conf/schemas/db1.schema.json 文件中。这样下次启动服务时就能够初始化表结构。

[root@guest mycat]# cat conf/schemas/db1.schema.json 
{
        "customTables":{},
        "globalTables":{
                "travelrecord":{
                        "broadcast":[
                                {
                                        "targetName":"c0"
                                },
                                {
                                        "targetName":"c1"
                                }
                        ],
                        "createTableSQL":"CREATE TABLE db1.`travelrecord` (\n\t`id` bigint NOT NULL AUTO_INCREMENT,\n\t`user_id` varchar(100) DEFAULT NULL,\n\t`traveldate` date DEFAULT NULL,\n\t`fee` decimal(10, 0) DEFAULT NULL,\n\t`days` int DEFAULT NULL,\n\t`blob` longblob,\n\tPRIMARY KEY (`id`),\n\tKEY `id` (`id`)\n) BROADCAST ENGINE = InnoDB CHARSET = utf8"
                }
        },
        "normalProcedures":{},
        "normalTables":{},
        "schemaName":"db1",
        "shardingTables":{},
        "views":{}
}

然后可以插入几条数据进行测试:

INSERT INTO db1.`travelrecord` VALUES (1, "202212310001", CURDATE(),10,10,'ABC');
INSERT INTO db1.`travelrecord` VALUES (2, "202212310002", CURDATE(),10,10,'ABC');
INSERT INTO db1.`travelrecord` VALUES (3, "202212310003", CURDATE(),10,10,'ABC');
INSERT INTO db1.`travelrecord` VALUES (4, "202212310004", CURDATE(),10,10,'ABC');
INSERT INTO db1.`travelrecord` VALUES (5, "202212310005", CURDATE(),10,10,'ABC');
INSERT INTO db1.`travelrecord` VALUES (6, "202212310006", CURDATE(),10,10,'ABC');

经测试,2 套一主一从集群中,每个 mysql 服务都有 db1.travelrecord 表,并且有完整的 6 条测试数据。

17.6.3.4、创建分片表(分库分表)

分片表表示逻辑表中的数据会分散保存到多个数据分片中。

# 在 Mycat 终端直接运行建表语句进行数据分片。
# 数据库分片规则,表分片规则,以及各分多少片。
# 核心就是后面的分片规则。 dbpartition表示分库规则,tbpartition表示分表规则。而mod_hash表示按照customer_id字段取模进行分片。
# 意思是切2份到两个数据库集群,每个数据库集群切1份表(当然也可以切成多张表)
CREATE TABLE db1.orders(
    id BIGINT NOT NULL AUTO_INCREMENT,
    order_type INT,
    customer_id INT,
    amount DECIMAL(10,2),
    PRIMARY KEY(id),
    KEY `id` (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8 dbpartition BY mod_hash(customer_id) tbpartition BY mod_hash(customer_id) tbpartitions 1 dbpartitions 2;

接下来可以往测试表中插入部分测试数据进行验证。

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

mysql> SELECT * FROM orders;
+----+------------+-------------+-----------+
| id | order_type | customer_id | amount    |
+----+------------+-------------+-----------+
|  1 |        101 |         100 | 100100.00 |
|  2 |        101 |         100 | 100300.00 |
|  6 |        102 |         100 | 100020.00 |
|  3 |        101 |         101 | 120000.00 |
|  4 |        101 |         101 | 103000.00 |
|  5 |        102 |         101 | 100400.00 |
+----+------------+-------------+-----------+
6 rows in set (0.09 sec)

此时,可以看一下自动生成的 schema 信息:

[root@guest mycat]# cat conf/schemas/db1.schema.json
{
    "customTables":{},
    "globalTables":{
        "travelrecord":{
            "broadcast":[
                {
                    "targetName":"c0"
                },
                {
                    "targetName":"c1"
                }
            ],
            "createTableSQL":"CREATE TABLE db1.`travelrecord` (\n\t`id` bigint NOT NULL AUTO_INCREMENT,\n\t`user_id` varchar(100) DEFAULT NULL,\n\t`traveldate` date DEFAULT NULL,\n\t`fee` decimal(10, 0) DEFAULT NULL,\n\t`days` int DEFAULT NULL,\n\t`blob` longblob,\n\tPRIMARY KEY (`id`),\n\tKEY `id` (`id`)\n) BROADCAST ENGINE = InnoDB CHARSET = utf8"
        }
    },
    "normalProcedures":{},
    "normalTables":{},
    "schemaName":"db1",
    "shardingTables":{
        "orders":{
            "createTableSQL":"CREATE TABLE db1.orders (\n\tid BIGINT NOT NULL AUTO_INCREMENT,\n\torder_type INT,\n\tcustomer_id INT,\n\tamount DECIMAL(10, 2),\n\tPRIMARY KEY (id),\n\tKEY `id` (`id`)\n) ENGINE = INNODB CHARSET = utf8\nDBPARTITION BY mod_hash(customer_id) DBPARTITIONS 2\nTBPARTITION BY mod_hash(customer_id) TBPARTITIONS 1",
            "function":{
                "properties":{
                    "dbNum":"2",
                    "mappingFormat":"c${targetIndex}/db1_${dbIndex}/orders_${index}",
                    "tableNum":"1",
                    "tableMethod":"mod_hash(customer_id)",
                    "storeNum":2,
                    "dbMethod":"mod_hash(customer_id)"
                }
            },
            "shardingIndexTables":{}
        }
    },
    "views":{}
}

最后查看一下切分的结果:

mysql_mycat_splitdata

mysql_mycat_splitdata_1

17.6.3.5、创建 ER 表

关联表也称为绑定表或者 ER 表。表示数据逻辑上有关联性的两个或多个表,例如订单和订单详情表。对于关联表,通常希望他们能够有相同的分片规则,这样在进行关联查询时,能够快速定位到同一个数据分片中。

# 在 Mycat 终端直接运行建表语句进行数据分片
CREATE TABLE orders_detail(
    id BIGINT NOT NULL AUTO_INCREMENT,
    detail VARCHAR(2000),
    order_id INT,
    PRIMARY KEY(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8 dbpartition BY mod_hash(order_id) tbpartition BY mod_hash(order_id) tbpartitions 1 dbpartitions 2;

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

对比一下数据,会发现由于我们分片字段不同,导致 ER 分片表条目不对应:

mysql_mycat_splitdata_2

上述两表具有相同的分片算法,但是分片字段不相同,mycat2 在涉及这两个表的 join 分片字段等价关系的时候可以完成 join 的下推。mycat2 无需指定 ER 表,是自动识别的,具体看分片算法的接口。

查看配置的表是否具有 ER 关系,使用:

mysql> /*+ mycat:showErGroup{}*/;
+---------+------------+---------------+
| groupId | schemaName | tableName     |
+---------+------------+---------------+
| 0       | db1        | orders        |
| 0       | db1        | orders_detail |
+---------+------------+---------------+
2 rows in set (0.00 sec)

group_id 表示相同的组,该组中的表具有相同的存储分布,ER 关系暂时不检查分区的目标是否相同,仅使用分片算法判断是否满足 ER 关系。

mysql> SELECT * FROM orders o INNER JOIN orders_detail od ON od.order_id=o.id;
+----+------------+-------------+-----------+-----+---------+----------+
| id | order_type | customer_id | amount    | id0 | detail  | order_id |
+----+------------+-------------+-----------+-----+---------+----------+
|  1 |        101 |         100 | 100100.00 |   1 | detail1 |        1 |
|  2 |        101 |         100 | 100300.00 |   2 | detail1 |        2 |
|  3 |        101 |         101 | 120000.00 |   3 | detail1 |        3 |
|  4 |        101 |         101 | 103000.00 |   4 | detail1 |        4 |
|  5 |        102 |         101 | 100400.00 |   5 | detail1 |        5 |
|  6 |        102 |         100 | 100020.00 |   6 | detail1 |        6 |
+----+------------+-------------+-----------+-----+---------+----------+
6 rows in set (0.08 sec)

17.6.4、小结

学到这里,基本上算是入门了 mycat2 中间件,应对大型的数据库架构设计奠定了一个很好的基础,不过,数据库的性能调优,分库分表都是一个很专业的话题,DBA 的修炼也不是一朝一夕,真到了分库分表的时候,最好多参考官方的一些示例说明,虽然官方文档相当杂乱,但也好过自己手无寸铁。

标签云