MySQL单表膨胀优化之MyCat分库分表

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: MySQL单表膨胀优化之MyCat分库分表

MySQL的单表达到多少量级时性能会下降?宽表在千万量级,窄表要好一点在1200W左右。但是MySQL单表达到1500W时性能开始急剧下降!


事实上MySQL单表可以存储10亿级数据,只是这时候性能比较差,业界公认MySQL单表容量在1KW以下是最佳状态,因为这时它的BTREE索引树高在3~5之间。


既然一张表无法搞定,那么就想办法将数据放到多个地方,目前比较普遍的方案有3个:分区;分库分表;NoSQL/NewSQL。NoSQL比较具有代表性的是MongoDB,es。NewSQL比较具有代表性的是TiDB。



那么为什么不采用分区?


分区表是由多个相关的底层表实现,这些底层表也是由句柄对象表示,所以我们也可以直接访问各个分区,存储引擎管理分区的各个底层表和管理普通表一样(所有的底层表都必须使用相同的存储引擎),分区表的索引只是在各个底层表上各自加上一个相同的索引,从存储引擎的角度来看,底层表和一个普通表没有任何不同,存储引擎也无须知道这是一个普通表还是一个分区表的一部分。


事实上,这个方案也不错,它对用户屏蔽了sharding的细节,即使查询条件没有sharding column,它也能正常工作(只是这时候性能一般)。不过它的缺点很明显:很多的资源都受到单机的限制,例如连接数,网络吞吐等!从而导致它的并发能力非常一般,远远达不到互联网高并发的要求!另外其还不支持外键、全文索引。


介绍分库分表前,这里先抛出一道面试题:

Q1:一亿的用户表,怎么做优化?
Q2:两个实体,用户和订单,数据量都过亿
要求:
1.从用户角度快速查询订单
2.从订单角度快速查用户
怎么设计表?


【1】常见数据库中间件

分布式数据库中间件有TDDL、Sharding-JDBC 、Amoeba、Cobar以及MyCAT。目前MyCAT社区十分活跃。Mycat就是介于数据库与应用之间,进行数据处理与交互的中间服务。对数据进行分片处理之后,从原有的一个库,被切分为多个分片数据库,所有的分片数据库集群构成了整个完整的数据库存储。


用户可以把它看做是一个数据库代理,可以用MySQL客户端工具和命令行访问,其核心功能是分库分表,即将一个大表分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。理解MyCat与MySQL关系,可以联想一下Nginx和Tomcat关系。


分库分表中间件全部可以归结为两大类型:


CLIENT模式;

PROXY模式;

CLIENT模式代表有阿里的TDDL,京东金融的sharding-jdbc。架构如下:


PROXY模式代表有阿里的cobar,民间组织的MyCAT。架构如下:

但是,无论是CLIENT模式,还是PROXY模式。几个核心的步骤是一样的:SQL解析,重写,路由,执行,结果归并。


① 常见的数据库中间件对比

功能 Sharding-JDBC TDDL Amoeba Cobar MyCat
基于客户端还是服务端 客户端 客户端 服务端 服务端 服务端
分库分表
MySQL交互协议 JDBC Driver JDBC Driver 前端用NIO,后端用JDBC Driver 前端用NIO,后端用BIO 前后端均用NIO
支持的数据库 任意 任意 任意 MySQL 任意

② 架构图

1、Sharding-JDBC

2、TDDL

3、Amoeba


4、Cobar

5、MyCat


③ 对比分析

TDDL 不同于其它几款产品,并非独立的中间件,只能算作中间层,是以 Jar 包方式提供给应用调用。属于JDBC Shard 的思想,网上也有很多其它类似产品。


Amoeba 是作为一个真正的独立中间件提供服务,即应用去连接 Amoeba 操作 MySQL 集群,就像操作 单个 MySQL 一样。从架构中可以看来,Amoeba 算中间件中的早期产品,后端还在使用 JDBC Driver。


Cobar 是在 Amoeba 基础上进化的版本,一个显著变化是把后端 JDBC Driver 改为原生的 MySQL 通信协议层。后端去掉 JDBC Driver 后,意味着不再支持 JDBC 规范,不能支持 Oracle、PostgreSQL 等数据。但使 用原生通信协议代替 JDBC Driver,后端的功能增加了很多想象力,比如主备切换、读写分离、异步操作等。


MyCat 又是在 Cobar 基础上发展的版本,两个显著点是:

(1)后端由 BIO 改为 NIO,并发量有大幅提高

(2)增加了对Order By、Group By、limit 等聚合功能的支持(虽然 Cobar 也可以支持 Order By、Group By、Limit 语法,但是结果没有进行聚合,只是简单返回给前端,聚合功能还是需要业务系统自己完成)。


【2】MyCat进行分库分表实践

① MyCat的数据处理流程

② MyCat的下载和安装

下载地址:http://dl.mycat.io/

这里我们使用1.6:


不好意思,1.6有个bug,在单库分表中会提示partition size大于datanode size :

Caused by: io.mycat.config.util.ConfigException: Illegal table conf : table [ 
CUSTOMER_ORDER ] rule function [ mod-long ] partition size : 3 > table datanode size : 1,
 please make sure table datanode size = function partition size

不要使用1.6版本,建议使用1.6.5版本!在这里坑我了半个小时!!

  • 目录说明
目录 说明
bin mycat命令,启动、重启、停止等
catlet catlet为mycat的一个扩展功能
conf Mycat配置信息
lib Mycat引用的jar包,MyCat是Java开发的
logs 日志文件,包括MyCat启动的日志和运行的日志
  • Windows下操作MyCat
//进入bin目录进行安装
mycat.bat install
//启动服务
mycat.bat start
//查看状态
mycat.bat status
//停止服务
mycat.bat stop
//登录mycat逻辑数据库
mysql -uroot -p123456 -P8066 -DTESTDB


查看逻辑库和逻辑表:

此时这三个逻辑表并不存在,如下所示使用navicat连接mycat,双击customer将会提示如下:


③ 三个配置文件

MyCat的主要配置其实就在于3(或者说4)个文件的配置


server.xml:mycat最基本的配置,配置连接的数据库和用户

schema.xml:mycat中逻辑表的具体配置

rule.xml : mycat中逻辑表需要分片的分片规则的配置文件

以及一个具体的分片规则txt文件。我这边命名为chen.txt。


Server.xml主要配置mycat服务的参数,比如端口号,myact用户名和密码使用的逻辑数据库以及用户管理。

<property name="serverPort">8066</property> 
<property name="managerPort">9066</property> 
<property name="idleTimeout">300000</property> 
<user name="root" defaultAccount="true">
  <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>

schema.xml文件主要配置数据库的信息,例如逻辑数据库名称,物理上真实的数据源以及表和数据源之间的对应关系和路由策略等。

<mycat:schema xmlns:mycat="http://io.mycat/">
<!--
TESTDB对应Server.xml中的逻辑数据库的名称,item为物理数据库表的名称,id为表主键,
dataNode对应下面的dataNode的name,rule为分片规则,这边配置的是求模分片,如果要
多个分片结合,中间用逗号分隔
-->
  <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
    <!-- customer表在dn1中不使用分库分表 -->
    <table name="customer" dataNode="dn1" />
    <!--item 在dn1 dn2 dn3上 分库-->
    <table name="item" primaryKey="ID" dataNode="dn1,dn2,dn3"   rule="mod-long"/>
    <!-- customer_order在dn3上id做分片 -->
    <table name="customer_order" primaryKey="ID" subTables="customer_order$1-3" dataNode="dn3" rule="mod-long"/>
  </schema>
<!-- 设置节点对应的数据库以及物理连接主机-->
  <dataNode name="dn1" dataHost="localhost1" database="mycat01" />
  <dataNode name="dn2" dataHost="localhost1" database="mycat02" />
  <dataNode name="dn3" dataHost="localhost1" database="mycat03" />
  <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
        writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
    <heartbeat>select user()</heartbeat>
    <!-- can have multi write hosts -->
    <writeHost host="hostM1" url="localhost:3306" user="root"
           password="123456">
      <!-- can have multi read hosts -->
      <readHost host="hostS2" url="localhost:3306" user="root" password="123456" />
    </writeHost>
  </dataHost>
</mycat:schema>

rule.xml主要配置路由策略,主要有分片的片键,拆分的策略(取模还是按区间划分等)。

如下所示定义一个rule,name为"mod-long",算法实现为mod-long

<tableRule name="mod-long">
  <rule>
    <columns>id</columns>
    <algorithm>mod-long</algorithm>
  </rule>
</tableRule>

每一个tableRule都对应一个function节点,class为具体实现类:

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
  <!-- how many data nodes -->
  <property name="count">3</property>
</function>

【3】创建库和表进行测试

customer表不做分库分表,item表做分库操作,customer_order表在dn3-mycat03中做水平分表操作。

① 分别创建三个库mycat01,mycat02和mycat03。


② 在TESTDB中创建表

  • 创建customer表(只在dn1-mycat01中存在,未做分库分表)
CREATE TABLE customer (
id int not null auto_increment PRIMARY key,
name varchar(20) default '' ,
phone varchar(12) default '',
create_time datetime  DEFAULT NULL,
update_time datetime  DEFAULT NULL 
)ENGINE=INNODB DEFAULT charset=utf8


此时将会在物理库mycat01中创建表customer:

  • 在TESTDB创建中执行脚本创建item表,mycat01 mycat02 mycat03都会创建
CREATE TABLE item (
id int not null auto_increment PRIMARY key,
value int default 0 ,
create_time datetime  DEFAULT NULL,
update_time datetime  DEFAULT NULL 
)ENGINE=INNODB DEFAULT charset=utf8

  • 在mycat03中执行脚本创建三个表:
CREATE TABLE customer_order1 (
id int not null auto_increment PRIMARY key,
amount int default 0 ,
create_time datetime  DEFAULT NULL,
update_time datetime  DEFAULT NULL 
)ENGINE=INNODB DEFAULT charset=utf8;
CREATE TABLE customer_order2 (
id int not null auto_increment PRIMARY key,
amount int default 0 ,
create_time datetime  DEFAULT NULL,
update_time datetime  DEFAULT NULL 
)ENGINE=INNODB DEFAULT charset=utf8;
CREATE TABLE customer_order3 (
id int not null auto_increment PRIMARY key,
amount int default 0 ,
create_time datetime  DEFAULT NULL,
update_time datetime  DEFAULT NULL 
)ENGINE=INNODB DEFAULT charset=utf8;


③ 测试customer插入和查询

插入:

INSERT into customer(name,phone,create_time,update_time)
VALUES ('jane','15369689516',SYSDATE(),SYSDATE());


查询:

select * from customer;


使用explain分析如下所示,会自动从对应节点中获取数据。

可能会疑惑,默认进行了limit 100 ,这个从哪里来?回顾Schema.xml配置:


④ 测试item插入和查询

插入语句:

INSERT into item(id,value,create_time,update_time) VALUES(1,1,SYSDATE(),SYSDATE());
INSERT into item(id,value,create_time,update_time) VALUES(2,2,SYSDATE(),SYSDATE());
INSERT into item(id,value,create_time,update_time) VALUES(3,3,SYSDATE(),SYSDATE());

根据id 进行mod然后插入到三个库中:

查询分析如下所示:


⑤ 测试custom_order插入和查询

如下图所示:

⑥ 插入数据id不能省略


如果你的规则是根据id进行mod,那么在SQL插入的时候id一列必须赋值,不能使用MySQL的自动增长。



【4】SpringBoot整合MyBatis中使用Mycat

在【3】中我们所有插入和查询测试都是从TESTDB链接中进行查询的,所以SpringBoot整合MyBatis中使用Mycat十分简单,只需要替换一下URL!

spring:
  datasource:
    # 单库单表
    url: jdbc:mysql://localhost:3306/test
    # 分库分表
    url:jdbc:mysql://localhost:8066/TESTDB
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: true
    testOnReturn: false
    poolPreparedStatements: true
#   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

多数据源

另外一个问题是,不可能把所有的表都配置进Schema.xml中让mycat管理。那么就可以配置两个数据源!使用另外一个数据源来管理剩余的表。


【5】回到面试题

① 中间表user_order

维护一个中间表user_order来保持用户和订单的关系,如果中间表膨胀,对中间关系表进行分表。但是此时逻辑就变得更复杂了。如果要求从userId,orderId,shopId。。等维度做分表,那么中间表的数据就会急剧上升Cn2


② 对user_order关系表根据userId做分表

此时根据userId可以很好命中数据,但是如果根据orderId呢?


③ 对user_oder表分表根据userId和orderId进行分表

也就是将一份数据根据不同规则保存两份,以空间换时间!

那么在具体操作上是不是分别插入两遍呢?当然不是!


为了保证数据准确性,数据只需要写入一份。另一份分表的数据通过binlog进行回放(binlog记录所有的对表的insert,update和delete操作,回放就是将这些对数据库的更新操作,再执行一遍)。


④ 为什么不是Sharing-JDBC呢?

Sharing-JDBC是需要修改代码的,而mycat只需要修改一下URL!


【6】一主多备


在实际的应用中,绝大部分情况都是读远大于写。Mysql提供了读写分离的机制,所有的写操作都必须对应到Master,读操作可以在Master和Slave机器上进行,Slave与Master的结构完全一样,一个Master可以有多个Slave,甚至Slave下还可以挂Slave,通过此方式可以有效的提高DB集群的QPS.


所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。


此外,可以看出Master是集群的瓶颈,当写操作过多,会严重影响到Master的稳定性,如果Master挂掉,整个集群都将不能正常工作。


所以,1.当读压力很大的时候,可以考虑添加Slave机器的分式解决,但是当Slave机器达到一定的数量就得考虑分库了。2.当写压力很大的时候,就必须得进行分库操作。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
5天前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
42 9
|
1月前
|
SQL 关系型数据库 MySQL
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
MySQL慢查询优化、索引优化,是必知必备,大厂面试高频,本文深入详解,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验分享。
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
|
9天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
50 18
|
8天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
17 7
|
7天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化与慢查询优化:原理与实践
通过本文的介绍,希望您能够深入理解MySQL索引优化与慢查询优化的原理和实践方法,并在实际项目中灵活运用这些技术,提升数据库的整体性能。
27 5
|
1月前
|
SQL 关系型数据库 MySQL
MySQL慢查询优化、索引优化、以及表等优化详解
本文详细介绍了MySQL优化方案,包括索引优化、SQL慢查询优化和数据库表优化,帮助提升数据库性能。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
MySQL慢查询优化、索引优化、以及表等优化详解
|
27天前
|
关系型数据库 MySQL Java
MySQL索引优化与Java应用实践
【11月更文挑战第25天】在大数据量和高并发的业务场景下,MySQL数据库的索引优化是提升查询性能的关键。本文将深入探讨MySQL索引的多种类型、优化策略及其在Java应用中的实践,通过历史背景、业务场景、底层原理的介绍,并结合Java示例代码,帮助Java架构师更好地理解并应用这些技术。
26 2
|
1月前
|
缓存 监控 关系型数据库
如何优化MySQL查询速度?
如何优化MySQL查询速度?【10月更文挑战第31天】
81 3
|
1月前
|
缓存 关系型数据库 MySQL
如何优化 MySQL 数据库的性能?
【10月更文挑战第28天】
107 1
|
1月前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
250 1

推荐镜像

更多
下一篇
DataWorks