新闻中心新闻详情
惊呆了老铁!通过删数据实现DBProxy数据库扩容是什么操作?
2022-06-15 | 内容资讯

大家都知道,数据库集群一旦需要扩容,十之八九,都会涉及到数据迁移。只有少部分情况,由于对数据库使用的特殊性,或者数据库代理的特性,可以做到无数据迁移。而通过删数据来实现扩容的操作,想必对于大部分人来说闻所未闻,而实际上业内已经有两家知名的互联网公司持续多年使用“删、删、删”原则对数据库进行扩容,其中一家还将自己“删”成了中国头部出海游戏品牌。其实上述两家企业使用的数据库本身没什么特别,均为常用的 MySQL 或者 MySQL 的兼容版本,例如 AWS 的 Aurora。删数据扩容的关键也并不在数据库,而是数据库的代理。大家耳熟能详的老牌数据库QiHoo Atlas以及美团DBProxy,其实也都可以通过删除数据的方式来实现扩容,只是很少有人想过或者尝试过如此操作。

那么究竟如何使用“删、删、删”原则进行数据库扩容?该原则又适用于哪类数据库呢?接下来我们就以云上曲率自研的DBProxy(FPNN)为例,针对可停服的业务模块,为大家详细的分享具体的操作步骤。

首先,DBProxy和其他常见的主流数据库代理一样,是全对等代理。这就意味着,DBProxy集群内,不存在中心节点,DBProxy按需部署和启动,需要多少起多少。同时DBProxy也支持hash取模和按区段划分两种分库分表的方式。无论是哪种形式,均适用“删、删、删”原则。
其次,说到数据库扩容的方式无非是垂直扩容和水平扩容两种。其中水平扩容可以再分为按区段分片的扩容,以及按hash分片的扩容。其中最简单的是垂直扩容,最难的是按hash分片的扩容。遵循着由易入难的原则,我们先来介绍一下DBProxy的垂直扩容。


垂直扩容

垂直扩容其实是最简单的,因为只需要移动整个模块对应的数据库就行。

假设扩容前,我们有两个 MySQL 实例,分别是 MySQL Instance 01和MySQL Instance 02。MySQL Instance 01上部署了四个模块对应的数据库,MySQL Instance 02 上面也部署了另外4个模块的数据库。如下图所示:

假设MySQL Instance 01压力偏大,MySQL Instance 02正常。此时我们仅需要单扩MySQL Instance 01即可。根据“删、删、删”的原则,我们采取以下步骤:

1. 用 MySQL Instance 01 做一个镜像,并用镜像启动新的 MySQL 实例,作为 MySQL Instance 03;

2. 修改 DBProxy 的配置库:

  • 配置表 server_info 中增加 MySQL Instance 03 的信息;
  • 在配置表 split_table_info 中,将数据库 Module-C、Model-D 中所有的表的 server_id 字段更新为 MySQL Instance 03 的 server_id;
  • 通知 DBProxy 重新加载配置库;

3. 在MySQL Instance 01上直接删除数据库 Module-C、Model-D,在MySQL Instance 03 上直接删除数据库 Module-A、Model-B。

完毕!

                                                                     MySQL Instance 01 扩容后示意图

如果 MySQL instance 02 也需要同步扩容,那只需要将 MySQL instance 01 的扩容操作对 MySQL instance 02 再做一遍即可。


                                                 MySQL instance 01、02 同步扩容后示意图


以上就是DBProxy对垂直扩容时的具体操作。

按区段分片的水平扩容

按区段划分的水平扩容又分为两种:增加段区 & 特定区段扩容。首先,我们来看 DBProxy 对于增加区段是如何操作的。


增加段区

假设我们同样有两个 MySQL 实例,分别是 MySQL Instance 01 和 MySQL Instance 02。MySQL Instance 01上部署着01-04区段的数据库,MySQL Instance 02 上面部署着05-08区段的数据库。如下图所示:

根据“删、删、删”的原则对于增加新的区段,我们采取以下步骤:


1. 启动新的 MySQL 实例作为 MySQL Instance 03 并部署区段09-12;

2. 修改 DBProxy 的配置库:

  • 配置表 server_info 中增加 MySQL Instance 03 的信息
  • 在配置表 split_range_info 中,增加区段 09-12 的信息
  • 通知 DBProxy 重新加载配置库


                                                                                   新增区段扩容后,如下图所示:

关于特定区段的扩容,DBProxy 同样有两种方式可选择:奇偶分库 or 将原始逻辑库拆分,然后按照垂直分库处理。奇偶分库是 DBProxy 为了兼容特定历史遗留项目而开发的特需功能,对于任一区段,只能实现一次扩容。对任意区段更通用的扩容方式建议使用“拆分原始逻辑库”的方式。


奇偶分库

假设我们需要对区段 02 进行奇偶分库,区段 02 位于MySQL Instance 01 上,对应数据库 Range-02。如下图所示:

根据“删、删、删”的原则对于需要奇偶扩容的区段,我们采取以下步骤:

1. 使用 MySQL Instance 01做一个镜像,并用镜像启动新的 MySQL 实例,作为MySQL Instance 01-2;

2. 修改 DBProxy 的配置库:

  • 配置表 server_info 中增加 MySQL Instance 01-2 的信息;
  • 在配置表 split_range_info 中:
  1. 将区段02对应的信息的 index_type 字段从0改为1
  2. 将区段02的对应信息复制然后新加一行,index_type 改为 2,并将该行server_id 字段更新为MySQL Instance 01-2 的 server_id;
  • 通知 DBProxy 重新加载配置库;

3. 在 MySQL Instance 01上,直接删除数据库 Range-02 中分表键为偶数的数据;在 MySQL Instance 01-2,直接删除数据库 Range-02 中分表键为奇数的数据,并删除 MySQL Instance 01-2 上,并不需要的 Range-01、Range-03、Range-04 三个区段的数据。

完毕!

                                          区段 02 进行奇偶分库后示意图

拆分原始逻辑库

假设我们需要对区段 N 进行拆分扩容。区段 N 位于 MySQL Instance XX 上,对应数据库 DB-xx。内部有 Table-A、Table-B、Table-C、Table- D 四个业务表。如下图所示:

根据“删、删、删”的原则,对于需要奇偶扩容的区段,我们采取以下步骤:

1.用 MySQL Instance XX 做一个镜像,并用镜像启动新的 MySQL 实例,作为 MySQL Instance XX-1;

2.修改 DBProxy 的配置库:

  • 配置表 server_info 中增加 MySQL Instance XX-1 的信息
  • 配置表 table_info 中,将 Table- C、Table-D 对应的 database_category 字段改为一个新的名字(逻辑拆库):假设原始字段值为 bizDB,新值为 bizDB-2
  • 在配置表 split_range_info 中:
  1. 对所有区段复制一份,并将复制后的database_category 字段的值从 bizDB 改为 bizDB-2;
  2. 将区段 N 对应的数据中,字段 database_category 值为 bizDB-2 的一行数据,server_id 字段更新为 MySQL Instance XX-1 的 server_id;
  3. 通知 DBProxy 重新加载配置库;

3.在 MySQL Instance XX上,直接删除数据库 DB-xx 中的数据表 Table-C、Table-D;在 MySQL Instance XX-1上,直接删除数据库DB-xx中的数据表 Table-A、Table-B。

完毕!

                                                    区段N进行拆分逻辑库后示意图


按哈希分片的水平扩容

按哈希分片的数据,如果以2的倍数进行的分库分表,则水平扩容其实分为:结构拆分 & 数据拆分两个阶段。其中结构拆分又分为:实例拆分与库拆分两个子阶段。

如果在建表时,就创建了足够的分表(2的次方),那大部分的扩容,都无需走到数据拆分阶段。只有分表不够用的情况下,才需要进行数据拆分。


实例拆分

我们先来看一下实例拆分,假设我们现在有两个 MySQL 实例:MySQL Insgtance 01 和 MySQL Insgtance 02,上面部署着 DB-01 至 DB-08, 8个分库,如下图所示:


扩容时,我们将按照2的倍数进行扩容。假设当前计划扩容一倍,根据“删、删、删”的原则,我们采取以下步骤:

1.用 MySQL Instance 01 与 MySQL Instance02 分别各做一个镜像,并用镜像启动对应的新的 MySQL 实例,分别作为 MySQL Instance 03 和 MySQL Instance 04;

2.修改 DBProxy 的配置库:

  • 配置表 server_info 中增加 MySQL Instance 03 和 MySQL Instance 04 的信息;
  • 在配置表 split_table_info 中:
  1. 将所有database_name 字段为 DB-03 和 DB-04的数据,对应的 server_id 改为 MySQL Instance 03 的 server_id;
  2. 将所有database_name 字段为 DB-07 和 DB-08的数据,对应的 server_id 改为 MySQL Instance 04 的 server_id;
  • 通知 DBProxy 重新加载配置库;

3.删!

  • 在MySQL Instance 01上,直接删除数据库DB-03 和 DB-04;
  • 在MySQL Instance 02上,直接删除数据库DB-07 和 DB-08;
  • 在MySQL Instance 03上,直接删除数据库DB-01 和 DB-02;
  • 在MySQL Instance 04上,直接删除数据库DB-05 和 DB-06。


完毕!

                                                               2倍实例拆分后示意图

如此下去,可扩至单实例单库。如下图所示:

到目前为止,仅为实例和库的处理。只要一个库内还有多张表,单实例单库,并不是 DBProxy 2倍快速扩容的尽头。


库拆分

当实例拆分达到尽头,已处于单实例单库的阶段时,再次扩容就进入了库拆分的阶段。我们以 MySQL Instance 01 和 DB-01 为例,讲述如何进行库的拆分。

假设经多次扩容后,MySQL Instance 01 上仅有唯一个库 DB-01,DB-01 中有四个数据表:Table-01 至 Table-04,如下图所示:


此时,我们需要对该实例继续进行2倍扩容。根据“删、删、删”的原则,我们采取以下步骤:

1.用 MySQL Instance 01 制作一个镜像,并用镜像启动新的 MySQL 实例,作为 MySQL Instance 01-2;

2.修改 DBProxy 的配置库:

  • 配置表 server_info 中增加 MySQL Instance 1-2 的信息;
  • 配置表split_table_info 中,将 Table-03 和Table-04 对应的 server_id 改为 MySQL Instance 01-2 的 server_id;
  • 通知 DBProxy 重新加载配置库;

3.删!

  • 在MySQL Instance 01的DB-01中,直接删除数据表Table-03 和 Table-04;
  • 在MySQL Instance 01-2的DB-01中,直接删除数据库Table-01 和 Table-02。


完毕!

                                                      2倍扩容拆分后示意图

如此下去,可扩至单实例单库单表。如下图所示:

当扩展到单实例单库单表时,2倍扩容的结构拆分阶段达到尽头,之后的扩容,将直接进入数据拆分阶段。


数据拆分

当结构拆分来到尽头,已处于单实例单库单表的阶段时,再次扩容就进入了数据拆分的阶段。此时我们继续以上文的情况为例。上文已扩展到单实例单库单表,如下图所示:


此时我们继续进行2倍扩容。

根据“删、删、删”的原则,我们采取以下步骤:

1.MySQL Instance 01、MySQL Instance01-2、MySQL Instance01-3、MySQL Instance01-4 分别各做一个镜像,并用镜像启动对应的新的 MySQL实例,分别作为MySQL Instance 01-5、MySQL Instance01-6、MySQL Instance01-7、MySQL Instance01-8;

2.修改 DBProxy 的配置库:

  • 配置表 server_info 中增加 MySQL Instance 01-5 至 MySQL Instance 01-8 的信息;
  • 修改配置表 table_info中Table 对应的 table_count,将count值从N改为2N(原值2倍);
  • 在配置表split_table_info 中,增加 Table-N-1 至 Table N + 4 的信息,其 server_id 分别对应 MySQL Instance 01-5、MySQL Instance01-6、MySQL Instance01-7、MySQL Instance01-8 的 server_id;
  • 通知 DBProxy 重新加载配置库;

3.删!

  • 在 Table-01 中,删除 key 为 N 对应的数据;
  • 在 Table-02 中,删除 key 为 N+1 对应的数据;
  • 在 Table-03 中,删除 key 为 N+2 对应的数据;
  • 在 Table-04 中,删除 key 为 N+3 对应的数据;
  • 在 Table-N+1 中,删除 key 为 0 对应的数据;
  • 在 Table-N+2 中,删除 key 为 1 对应的数据;
  • 在 Table-N+3 中,删除 key 为 2 对应的数据;
  • 在 Table-N+4 中,删除 key 为 3 对应的数据;


完毕!

                                                                             2倍扩容并进行数据拆分后示意图


此后,对于后续的扩容需求,继续按照数据拆分扩容即可。


结语

以上便是在常见的分库分表情形下,如何通过删数据的方式进行数据库扩容的具体流程。根据多年的经验积累,拥有千万级用户的社交网络及全球互联的大型游戏企业,后端数据库扩容一般不超过40分钟。其中80%以上的时间消耗在数据库镜像生成和启动上,实际扩容时间仅需要5分钟左右。

微信公众号
更多资讯干货
敬请关注:
云上曲率公众号
Telephone consultation:
13820629335
Wechat
Contact sales