分布式数据库中间件 MyCat - 分库分表实践

Posted by 彭超 on 2020-08-20
Estimated Reading Time 8 Minutes
Words 2.1k In Total
Viewed Times

MyCat 简介

MyCat 是一个功能强大的分布式数据库中间件,是一个实现了 MySQL 协议的 Server,前端人员可以把它看做是一个数据库代理中间件,用 MySQL 客户端工具和命令行访问;而后端人员可以用 MySQL 原生协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信。可以用作 读写分离分库分表(分片)容灾备份多租户应用开发大数据基础设施,使底层数据架构具备很强的适应性和灵活性。

MyCat 的智能优化模块可以使系统的数据访问瓶颈和热点一目了然,并且可以将这些统计分析数据自动或手工调整后端存储,将不同的表映射到不同存储引擎上,而整个应用的代码可以一行也不用变。

具体应用场景

  1. 读写分离:支持读写分离,主从切换,此配置最简单;
  2. 分库分表:对于超过 1000 万的表进行分片,最大支持 1000 亿的单表分片;
  3. 多租户应用:每个应用一个库,但应用程序只连接 MyCat,使程序不用改造本身,实现多租户化;
  4. 替代 Hbase:用于分析大数据;
  5. 报表系统:借助 MyCat 的分表能力,处理大规模报表的统计;
  6. 海量数据查询:比如 10 亿条频繁查询的记录需要在 3 秒内查询出来结果,除了基于主键的查询,还可能存在范围查询或其他属性查询,此时 MyCat 可能是最简单有效的选择。

本文主要实践核心功能 分库分表

分库分表原理剖析

指通过某种特定的条件,将存放在同一个数据库中的数据分散存放到多个数据库上面,以达到分散单台设备负载的效果。

根据切分规则的类型可以分为以下两种切分模式。

  • 垂直切分:最大特点是规则简单,适合各业务之间的的耦合度非常低、相互影响小、业务逻辑非常清晰的系统。在这种系统中,可以很容易 将不同业务模块所使用的的表切到不同的数据库中

  • 水平切分:相对来说复杂一些,因为要 将同一个表中的不同数据切分到不同的数据库中,后期的数据维护也更为复杂一些。

垂直切分

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

一个架构设计好的系统其总体功能通常是由多个功能模块所组成的,而每一个功能模块的数据对应到数据库中就是一个或多个表。而在架构设计中,各个功能模块相互之间的交互点越少和越统一,系统的耦合度就越低,系统各个模块的维护性以及扩展性也就越好,这样的系统也就越容易实现垂直切分。

但是往往系统中有些表难以做到完全的独立,存在跨库 join 的情况,对于这类分库,可以共用一个数据源,业务之间通过接口来调用。

优点:规则明确、业务清晰、更易于整合和扩展、维护简单。

缺点:部分业务表无法 join,需要通过业务接口方式解决,提高系统复杂度;各业务存在单库性能瓶颈,不易于数据扩展和性能提高;事务处理复杂问题。

由于垂直切分是将表按照业务分类切分到不同的单库中,所有导致某些业务表过于庞大,存在单库读写与存储瓶颈,则需要水平切分来解决。

水平切分

水平切分不是将表按照业务分类,而是按照某个字段的某种规则分散到多个库中,每个表中包含一部分数据,如图。

拆分数据需要定义分片规则,拆分的第一原则是找到 拆分维度。比如:从会员的角度来分析,需要查询会员某天某月某个订单,那么就需要按照日期来拆分,不同的数据按照会员 ID 做分组。

优点:拆分规则抽象好;不存在单库数据瓶颈问题;提高系统稳定性和负载能力。

缺点:事务一致性难以解决;数据扩展和维护的难度极大;跨库 join 性能差。

附:多数据源管理方案

  • 第一种:客户端模式,在每个应用程序中配置管理自己需要的一个或多个数据源,直接访问各个数据库。
  • 第二种:通过中间代理层来统一管理所有的数据源。

搭建环境

均采用 Docker Compose 搭建服务

部署 3 台 MySQL 容器

分别创建 3 份 docker-compose.yml 文件

  • mysql-01

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    version: '3.1'
    services:
    mysql-1:
    image: mysql
    container_name: mysql-01
    environment:
    MYSQL_ROOT_PASSWORD: 123456
    command:
    --default-authentication-plugin=mysql_native_password
    --character-set-server=utf8mb4
    --collation-server=utf8mb4_general_ci
    --explicit_defaults_for_timestamp=true
    --lower_case_table_names=1
    ports:
    - 3306:3306
    volumes:
    - ./data:/var/lib/mysql
  • mysql-02

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    version: '3.1'
    services:
    mysql-2:
    image: mysql
    container_name: mysql-02
    environment:
    MYSQL_ROOT_PASSWORD: 123456
    command:
    --default-authentication-plugin=mysql_native_password
    --character-set-server=utf8mb4
    --collation-server=utf8mb4_general_ci
    --explicit_defaults_for_timestamp=true
    --lower_case_table_names=1
    ports:
    - 3307:3306
    volumes:
    - ./data:/var/lib/mysql
  • mysql-03

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    version: '3.1'
    services:
    mysql-3:
    image: mysql
    container_name: mysql-03
    environment:
    MYSQL_ROOT_PASSWORD: 123456
    command:
    --default-authentication-plugin=mysql_native_password
    --character-set-server=utf8mb4
    --collation-server=utf8mb4_general_ci
    --explicit_defaults_for_timestamp=true
    --lower_case_table_names=1
    ports:
    - 3308:3306
    volumes:
    - ./data:/var/lib/mysql

分别启动 3 台容器

1
$ docker-compose up -d

部署 MyCat 容器

  • 克隆项目

    1
    $ git clone https://github.com/antoniopeng/docker.mycat.git
  • 构建镜像

    1
    2
    $ cd docker.mycat
    $ docker-compose build
  • 启动容器

    1
    $ docker-compose up -d

配置数据库分片

此处以 水平切分 为例

配置用户名和密码

1
$ vi config/mycat/server.xml

找到第 90 行,参考配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<mycat:server xmlns:mycat="http://io.mycat/">

<user name="root">

<property name="password">123456</property>


<property name="schemas">hellomycat</property>


<property name="usingDecrypt">0</property>
</user>
</mycat:server>

配置数据库节点

1
$ vi config/mycat/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
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

<schema name="hellomycat" checkSQLschema="true" sqlMaxLimit="100">

<table />
</schema>


<dataNode name="dataNode1" dataHost="dataHost1" database="hellomycat_1" />
<dataNode name="dataNode2" dataHost="dataHost2" database="hellomycat_2" />
<dataNode name="dataNode3" dataHost="dataHost3" database="hellomycat_3" />


<dataHost name="dataHost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>

<writeHost
host="192.168.127.130"
url="jdbc:mysql://192.168.127.130:3306?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"
user="root" password="123456">

</writeHost>
</dataHost>
<dataHost name="dataHost2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.127.130"
url="jdbc:mysql://192.168.127.130:3307?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"
user="root" password="123456">

</writeHost>
</dataHost>
<dataHost name="dataHost3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.127.130"
url="jdbc:mysql://192.168.127.130:3308?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"
user="root" password="123456">

</writeHost>
</dataHost>

</mycat:schema>

分片规则配置如下

1
$ vi config/mycat/rule.xml

第 32 行配置

1
2
3
4
5
6
7
8
<tableRule name="auto-sharding-long">
<rule>

<columns>id</columns>

<algorithm>rang-long</algorithm>
</rule>
</tableRule>

第 105 行配置

1
2
3
4
<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">

<property name="mapFile">autopartition-long.txt</property>
</function>

自定义数字范围分片规则

1
$ vi config/mycat/autopartition-long.txt

配置如下:

1
2
3
4
5
6
7
8
# range start-end ,data node index
# K=1000,M=10000.
# ID 0-5000000 保存在 dataNode1
0-500M=0
# ID 5000000-10000000 保存在 dataNode2
500M-1000M=1
# ID 10000000-15000000 保存在 dataNode3
1000M-1500M=2

验证是否成功

使用 MySQL 客户端工具连接 MyCat,默认端口号为 8066

分别在 3 个数据库中创建表,表只需要 ID 一个字段即可:

1
create table sys_user (id int not null primary key);

新增数据:

1
2
3
insert into sys_user(id) values(2000000);
insert into sys_user(id) values(7000000);
insert into sys_user(id) values(12000000);

新增后如果 3 个数据库的表中都出现了一条数据,则说明分片成功。


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !