ITPub博客

首页 > 数据库 > 国内数据库 > 一文读懂 GoldenDB 的分片与分区

一文读懂 GoldenDB 的分片与分区

原创 国内数据库 作者:圣彼得堡的村长 时间:2026-10-10 09:27:03 0 删除 编辑

一文读懂 GoldenDB 的分片与分区

1. 分库分表 VS 分布式数据库

    谈到分布式数据库,最核心的诉求是将数据分散存储于多个数据库实例中,这也是传统 Mycat 等中间件的核心能力。然而, GoldenDB 与 Mycat 有着本质区别: GoldenDB 并非简单地将分片路由逻辑叠加在 MySQL 之上,而是将计算层(CN)与存储层(DN)进行了深度融合。

概念 Mycat 传统中间件 GoldenDB 分布式
架构 Proxy 层分片 + 普通 MySQL 实例 计算节点+ 数据节点原生融合
分片单位 逻辑库和逻辑表 物理分片组
后端存储 普通 MySQL MySQL + 主从复制 + 水位
事务 对业务侵入大且性能损耗高 GTM 全局事务
分区→分片 中间件路由,如 order_id % 4 元数据
分区对应 MySQL 实例 DBGroup(1 主 N 备)

   从上表可以看出, Mycat 的本质是“拼装”,底层的 MySQL 只是一个机械的执行者,不知道自己身处分布式集群中,也不知道兄弟节点存放了什么;而 GoldenDB 的本质是“融合”。当我们将分区表的单个分区对应到一个物理分片(DBGroup)时,计算节点在解析 SQL 的瞬间就能完成 “分布式分片裁剪 + 节点内分区裁剪”。

  • 分片( DISTRIBUTED BY) 解决的是 横向扩展(Scale-out) 问题,决定数据分散到哪一个 DBGroup。
  • 分区( PARTITION BY) 解决的是 单机纵向性能(Scale-up) 问题,在 DBGroup 内部通过时间或范围再细分,配合 分区裁剪 能够大幅减少单次 SQL 扫描的数据量。

2. 分片

     数据分片(Sharding):即通过分片键,决定一条数据去往哪一个物理存储组( DBGroup)。数据分片具有以下特性:

  • 无限扩展:数据分片水平切分扩展到多个物理节点,理论上支持无限扩展;
  • 性能提升:数据分片以后单个数据节点上上的数据集变小,数据库查询的压力变小、查询更快,性能更好;同时水平切分以后查询可以并发执行,提升了系统的吞吐量;
  • 高可用:部分分片节点宕机只会影响该部分分片的服务,不会影响整个系统的可用性。

在 GoldenDB 中支持的分片规则包括:Range 分片、Hash 分片、List 分片、复制表以及多级分片。

2.1 Range 分片

  • 机制:按照数据段的连续范围(如时间区间、递增ID)将数据划分到不同的物理分片上。
  • 适用场景:按月/按天切分的历史流水表、日志表、报表数据。
  • 优点:集群扩容时候只需要添加节点即可,不需要对分片数据进行迁移;对于范围查找时,可以通过分片数据快速查询,有效避免跨分片的查询问题。对顺序扫描、排序以及聚合分析也非常友好。
  • 痛点:容易产生 冷热数据集中与热点倾斜。比如永远只有最新时间段的物理节点在疯狂承载写压力(热点),而历史数据节点处于闲置状态(冷点)。
CREATE TABLE tb_order_range (
    order_id   INT          NOT NULL,
    user_id    INT          NOT NULL,
    amount     DECIMAL(10,2) DEFAULT NULL,
    create_time DATETIME    DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (order_id)
) DISTRIBUTED BY RANGE (order_id) (
    g1 VALUES LESS THAN (10000),
    g2 VALUES LESS THAN (20000),
    g4 VALUES LESS THAN MAXVALUE
);
goldendb_client> SELECT 'g1_count' as shard, COUNT(*) FROM tb_order_range STORAGEDB g1;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g1_count |     9999 |
+----------+----------+
1 row in set (2.10 sec)
goldendb_client> 
goldendb_client> SELECT 'g1_count' as shard, COUNT(*) FROM tb_order_range STORAGEDB g2;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g1_count |    10000 |
+----------+----------+
1 row in set (2.64 sec)
goldendb_client> SELECT 'g1_count' as shard, COUNT(*) FROM tb_order_range STORAGEDB g4;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g1_count |    20001 |
+----------+----------+
1 row in set (2.78 sec)

2.2 Hash 分片

  • 机制:通过对分片键进行哈希(或取模)计算,将数据随机且均匀地映射到不同的节点或物理子表中。 GoldenDB 的分布式层采用的是 一致性 HASH 算法,能大幅减少了扩容时的数据迁移量。
  • 适用场景:流水表、客户信息表、高并发 OLTP 核心业务。
  • 优点:数据分布均衡,能有效规避单点热点瓶颈。
  • 痛点:对范围查询不友好,因为 HASH 会打散数据,范围查询通常会引发多节点并发扫描。
CREATE TABLE tb_order_hash (
    order_id   INT          NOT NULL,
	user_id    INT          NOT NULL,
	amount     DECIMAL(10,2) DEFAULT NULL,
	create_time DATETIME    DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (user_id, order_id)
)
DISTRIBUTED BY HASH(user_id) (g1, g2, g4);
goldendb_client> SELECT 'g1_count' as shard, COUNT(*) FROM tb_order_hash STORAGEDB g1;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g1_count |    13354 |
+----------+----------+
1 row in set (5.98 sec)
goldendb_client> SELECT 'g1_count' as shard, COUNT(*) FROM tb_order_hash STORAGEDB g2;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g1_count |    13436 |
+----------+----------+
1 row in set (2.98 sec)
goldendb_client> SELECT 'g1_count' as shard, COUNT(*) FROM tb_order_hash STORAGEDB g4;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g1_count |    13210 |
+----------+----------+
1 row in set (2.16 sec)

2.3 LIST 分片

  • 机制:基于离散枚举值(如行政区划、法人机构代码、渠道标识等)进行显式映射,将数据精准路由至指定分片。
  • 适用场景:多法人银行核心系统(按法人机构隔离)、跨国/跨省业务(按地理编码分片),适用于数据归属边界清晰的业务模型。
  • 优点:具备明确的物理与业务边界,支持人为干预数据分布。
  • 痛点:强依赖业务数据的实际分布规律。若特定枚举值对应的数据量远超其他值,极易引发严重的 数据倾斜,导致单点存储与性能瓶颈。
CREATE TABLE tb_order_list (
    order_id    INT             NOT NULL,
    user_id     INT             NOT NULL,
    region_code VARCHAR(10)    NOT NULL,
    amount      DECIMAL(10,2)   DEFAULT NULL,
    create_time DATETIME        DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (order_id)
) DISTRIBUTED BY LIST (region_code) (
    g1 VALUES IN ('BJ', 'TJ', 'HEB'),
    g2 VALUES IN ('SH', 'HZ', 'NJ', 'WH'),
    g4 VALUES IN ('GZ', 'SZ', 'XA', 'CD')
);
goldendb_client> SELECT COUNT(*), COUNT(DISTINCT region_code) FROM tb_order_list;
+----------+-----------------------------+
| COUNT(*) | COUNT(DISTINCT REGION_CODE) |
+----------+-----------------------------+
|    30000 |                          11 |
+----------+-----------------------------+
1 row in set (0.20 sec)
goldendb_client>  SELECT 'g1_count' as shard, COUNT(*) FROM tb_order_list STORAGEDB g1;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g1_count |     8131 |
+----------+----------+
1 row in set (4.06 sec)
goldendb_client> SELECT 'g2_count' as shard, COUNT(*) FROM tb_order_list STORAGEDB g2;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g2_count |    10875 |
+----------+----------+
1 row in set (2.51 sec)
goldendb_client> SELECT 'g4_count' as shard, COUNT(*) FROM tb_order_list STORAGEDB g4;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g4_count |    10994 |
+----------+----------+
1 row in set (2.48 sec)

2.4 复制表

  • 机制:采用全量广播策略,每个数据节点均保留该表的完整数据副本,本地即可完成关联查询。
  • 适用场景:数据量较小、读多写少的公共基础表,如参数配置表、字典表、机构码表等。
  • 优点:将高频访问的公共表下沉至各数据节点本地,彻底消除跨节点网络访问,避免分布式事务开销,显著提升 JOIN 及单表查询性能。
  • 痛点:同一份数据在多个节点冗余存储,随着节点规模扩大,存储开销线性增长;同时写操作需同步至所有副本,写入性能与一致性维护成本随之上升。
CREATE TABLE tb_region_dict (
    region_code VARCHAR(10)  NOT NULL,
    region_name VARCHAR(50)  NOT NULL,
    status      TINYINT      DEFAULT 1,
    update_time DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (region_code)
) distributed by duplicate(g1,g2,g4);
INSERT INTO tb_region_dict (region_code, region_name, status) VALUES
('BJ', '北京', 1),
('TJ', '天津', 1),
('HEB', '河北', 1),
('SH', '上海', 1),
('HZ', '杭州', 1),
('NJ', '南京', 1),
('WH', '武汉', 1),
('GZ', '广州', 1),
('SZ', '深圳', 1),
('XA', '西安', 1),
('CD', '成都', 1);
goldendb_client> SELECT 'g1_count' as shard, COUNT(*) FROM tb_region_dict STORAGEDB g1;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g1_count |       11 |
+----------+----------+
1 row in set (3.52 sec)
goldendb_client> SELECT 'g2_count' as shard, COUNT(*) FROM tb_region_dict STORAGEDB g2;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g2_count |       11 |
+----------+----------+
1 row in set (5.89 sec)
goldendb_client> SELECT 'g4_count' as shard, COUNT(*) FROM tb_region_dict STORAGEDB g4;
+----------+----------+
| SHARD    | COUNT(*) |
+----------+----------+
| g4_count |       11 |
+----------+----------+
1 row in set (2.89 sec)

2.5 多级分片

    多级分片是通过多维属性对数据分片进行精确控制,通常是通过多个字段进行多层次分片,一级分片 用 DISTRIBUTED BY 语句,二级分片用 SUBDISTRIBUTED BY...  语句。

CREATE TABLE t1 (a INT, b INT, c INT)
DISTRIBUTED BY CASE c
    WHEN 1 THEN CASE
                  WHEN b < 100 THEN SUBDISTRIBUTED BY HASH(a) (g1);
                  ELSE SUBDISTRIBUTED BY HASH(a) (g2);
                END CASE;
    WHEN 2 THEN SUBDISTRIBUTED BY HASH(a) (g3);
    ELSE SUBDISTRIBUTED BY HASH(a) (g4);
END CASE;

2.6 本地分区 + 分片 组合表

     分区(Partition BY) 即纵向切分。表经过 DISTRIBUTED BY 横向分片后,数据已落入各 DBGroup;在此基础上, GoldenDB 允许在单个 DBGroup 内部通过 PARTITION BY 按指定分区键进一步切分数据。分区发生在本地 MySQL 引擎层,对上层透明,主要作用是缩小单节点扫描范围、提升时间范围类查询的效率。

CREATE TABLE tb_orders_hash_par_range (
    order_id     BIGINT        NOT NULL,
    user_id      BIGINT        NOT NULL,
    region_code  VARCHAR(10)   NOT NULL,
    amount       DECIMAL(10,2) DEFAULT NULL,
    create_time  DATETIME      NOT NULL,
    ym           INT           NOT NULL,
    PRIMARY KEY (order_id, ym)
)
PARTITION BY RANGE (ym) (
    PARTITION p202609 VALUES LESS THAN (202610),
    PARTITION p202610 VALUES LESS THAN (202611),
    PARTITION p202611 VALUES LESS THAN (202612),
    PARTITION p202612 VALUES LESS THAN (202701),
    PARTITION pmax    VALUES LESS THAN MAXVALUE
)
DISTRIBUTED BY HASH (order_id) (g1, g2, g4);
SELECT ym, COUNT(*) AS cnt
FROM tb_orders_hash_par_range
GROUP BY ym
ORDER BY ym;
+--------+------+
| YM     | CNT  |
+--------+------+
| 202601 | 2993 |
| 202602 | 3053 |
| 202603 | 2927 |
| 202604 | 2925 |
| 202605 | 3152 |
| 202606 | 3004 |
| 202607 | 2873 |
| 202608 | 3016 |
| 202609 | 3030 |
| 202610 | 3027 |
+--------+------+

通过分区建查询:

goldendb_client>  EXPLAIN SELECT * FROM tb_orders_hash_par_range WHERE ym = 202610 \G
*************************** 2. row ***************************
           ID: 10001
  SELECT_TYPE: SQLNode
        TABLE: 1
   PARTITIONS: 
         TYPE: 
POSSIBLE_KEYS:  SELECT `test`.`tb_orders_hash_par_range`.`order_id`,`test`.`tb_orders_hash_par_range`.`user_id`,`test`.`tb_orders_hash_par_range`.`region_code`,`test`.`tb_orders_hash_par_range`.`amount`,`test`.`tb_orders_hash_par_range`.`create_time`,`test`.`tb_orders_hash_par_range`.`ym`,`test`.`tb_orders_hash_par_range`.`gtid` as `gtid1` FROM `test`.`tb_orders_hash_par_range`  where (`ym` = 202610) 
          KEY: Cluster1,g1,g2,g4
      KEY_LEN: 
          REF: Parent=NULL,Child=NULL,Next=NULL
         ROWS: 
     FILTERED: 
        EXTRA: cr, test.tb_orders_hash_par_range=hash,digest=d1abd2d84dd534050ff121f24fbb81a3
*************************** 2. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: tb_orders_hash_par_range
   PARTITIONS: p202610
         TYPE: ALL
POSSIBLE_KEYS: NULL
          KEY: NULL
      KEY_LEN: NULL
          REF: NULL
         ROWS: 985
     FILTERED: 10.00
        EXTRA: Using where, g1
*************************** 3. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: tb_orders_hash_par_range
   PARTITIONS: p202610
         TYPE: ALL
POSSIBLE_KEYS: NULL
          KEY: NULL
      KEY_LEN: NULL
          REF: NULL
         ROWS: 1030
     FILTERED: 10.00
        EXTRA: Using where, g2
*************************** 4. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: tb_orders_hash_par_range
   PARTITIONS: p202610
         TYPE: ALL
POSSIBLE_KEYS: NULL
          KEY: NULL
      KEY_LEN: NULL
          REF: NULL
         ROWS: 1012
     FILTERED: 10.00
        EXTRA: Using where, g4
4 rows in set (0.64 sec)

发现使用分区裁剪验证,执行计划中 partitions 列只出现 p202610,而不是全部分区。但是扫描了三个分片。再带上 orders_id 字段构成主键查询:

goldendb_client>  EXPLAIN SELECT * FROM tb_orders_hash_par_range WHERE order_id = 15 AND ym = 202610 \G
*************************** 2. row ***************************
           ID: 10001
  SELECT_TYPE: SQLNode
        TABLE: 1
   PARTITIONS: 
         TYPE: 
POSSIBLE_KEYS:  SELECT `test`.`tb_orders_hash_par_range`.`order_id`,`test`.`tb_orders_hash_par_range`.`user_id`,`test`.`tb_orders_hash_par_range`.`region_code`,`test`.`tb_orders_hash_par_range`.`amount`,`test`.`tb_orders_hash_par_range`.`create_time`,`test`.`tb_orders_hash_par_range`.`ym`,`test`.`tb_orders_hash_par_range`.`gtid` as `gtid1` FROM `test`.`tb_orders_hash_par_range`  where ((`order_id` = 15) and (`ym` = 202610))  
          KEY: Cluster1,g4
      KEY_LEN: 
          REF: Parent=NULL,Child=NULL,Next=NULL
         ROWS: 
     FILTERED: 
        EXTRA: cr, test.tb_orders_hash_par_range=hash,digest=277702f4e80ac20b5bc8531ca9ed9148
*************************** 2. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: NULL
   PARTITIONS: NULL
         TYPE: NULL
POSSIBLE_KEYS: NULL
          KEY: NULL
      KEY_LEN: NULL
          REF: NULL
         ROWS: NULL
     FILTERED: NULL
        EXTRA: no matching row in const table, g4
2 rows in set (0.85 sec)

发现使用了 分区裁剪 + 分片路由,只扫描了一个分片和分区。

3. 分区

     GoldenDB 支持只建分区表、不写 DISTRIBUTED BY    —— 此时它是一张 非分片表(单 DBGroup 表),整张表落在一个 DBGroup 内,但内部按 PARTITION BY 做本地分区。

3.1 Range 分区

如果建表时不指定 DISTRIBUTED BY,且集群配置了多个 DBGroup, 默认会创建为 DUPLICATE(复制表),数据全量拷贝到列出的所有组。

  1. 创建 Range 分区表,不指定 DISTRIBUTED BY 语句。
goldendb_client> CREATE TABLE orders_part_range (
    order_id     BIGINT        NOT NULL,
    user_id      BIGINT        NOT NULL,
    region_code  VARCHAR(10)   NOT NULL,
    amount       DECIMAL(10,2) DEFAULT NULL,
    create_time  DATETIME      NOT NULL,
    ym           INT           NOT NULL,          -- 分区键:YYYYMM
    PRIMARY KEY (order_id, ym)                  -- 主键必须含分区键
)
PARTITION BY RANGE (ym) (
    PARTITION p202608 VALUES LESS THAN (202609),
    PARTITION p202609 VALUES LESS THAN (202610),
    PARTITION p202610 VALUES LESS THAN (202611),
    PARTITION p202611 VALUES LESS THAN (202612),
    PARTITION p202612 VALUES LESS THAN (202701),
    PARTITION pmax    VALUES LESS THAN MAXVALUE
);
  1. 创建完发现,该表实际上是复制表,每个分片组都有一个完整的副本。
goldendb_client>  show create table orders_part_range\G
*************************** 1. row ***************************
       TABLE: orders_part_range
CREATE TABLE: CREATE TABLE `orders_part_range` (
  `order_id` bigint NOT NULL,
  `user_id` bigint NOT NULL,
  `region_code` varchar(10) COLLATE utf8mb4_bin NOT NULL,
  `amount` decimal(10,2) DEFAULT NULL,
  `create_time` datetime NOT NULL,
  `ym` int NOT NULL,
  PRIMARY KEY (`order_id`,`ym`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
/*!50100 PARTITION BY RANGE ("ym")
(PARTITION p202608 VALUES LESS THAN (202609) ENGINE = InnoDB,
 PARTITION p202609 VALUES LESS THAN (202610) ENGINE = InnoDB,
 PARTITION p202610 VALUES LESS THAN (202611) ENGINE = InnoDB,
 PARTITION p202611 VALUES LESS THAN (202612) ENGINE = InnoDB,
 PARTITION p202612 VALUES LESS THAN (202701) ENGINE = InnoDB,
 PARTITION pmax VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
 DISTRIBUTED BY DUPLICATE(g1,g2,g4)
1 row in set (0.01 sec)
goldendb_client> INSERT INTO orders_part_range SELECT * FROM tb_orders_hash_par_range limit 1000;
goldendb_client> select count(*) from orders_part_range storagedb g1;
+----------+
| COUNT(*) |
+----------+
|     1000 |
+----------+
1 row in set (0.17 sec)
goldendb_client> select count(*) from orders_part_range storagedb g2;
+----------+
| COUNT(*) |
+----------+
|     1000 |
+----------+
1 row in set (0.19 sec)
goldendb_client> select count(*) from orders_part_range storagedb g4;
+----------+
| COUNT(*) |
+----------+
|     1000 |
+----------+
1 row in set (0.10 sec)
  1. 查询一个分区只会扫描一个分片组,但是每次可能扫的分片组不同。
goldendb_client> EXPLAIN SELECT * FROM orders_part_range WHERE  ym = 202610 \G
*************************** 1. row ***************************
           ID: 10001
  SELECT_TYPE: SQLNode
        TABLE: 1
   PARTITIONS: 
         TYPE: 
POSSIBLE_KEYS:  SELECT `test`.`orders_part_range`.`order_id`,`test`.`orders_part_range`.`user_id`,`test`.`orders_part_range`.`region_code`,`test`.`orders_part_range`.`amount`,`test`.`orders_part_range`.`create_time`,`test`.`orders_part_range`.`ym`,`test`.`orders_part_range`.`gtid` as `gtid1` FROM `test`.`orders_part_range`  where (`ym` = 202610)  
          KEY: Cluster1,g1
      KEY_LEN: 
          REF: Parent=NULL,Child=NULL,Next=NULL
         ROWS: 
     FILTERED: 
        EXTRA: cr, test.orders_part_range=rep,digest=61479bf21572913a6d1e742a646c58bb
*************************** 2. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: orders_part_range
   PARTITIONS: p202610
         TYPE: ALL
POSSIBLE_KEYS: NULL
          KEY: NULL
      KEY_LEN: NULL
          REF: NULL
         ROWS: 1
     FILTERED: 100.00
        EXTRA: Using where, g1
2 rows in set (0.00 sec)
  1. 如果想创建 "真正的单组表",需要显式指定存放在一个组
goldendb_client> CREATE TABLE orders_single_group (
    order_id     BIGINT        NOT NULL,
    user_id      BIGINT        NOT NULL,
    region_code  VARCHAR(10)   NOT NULL,
    amount       DECIMAL(10,2) DEFAULT NULL,
    create_time  DATETIME      NOT NULL,
    ym           INT           NOT NULL,
    PRIMARY KEY (order_id, ym)
)
PARTITION BY RANGE (ym) (
    PARTITION p202608 VALUES LESS THAN (202609),
    PARTITION p202609 VALUES LESS THAN (202610),
    PARTITION p202610 VALUES LESS THAN (202611),
    PARTITION p202611 VALUES LESS THAN (202612),
    PARTITION p202612 VALUES LESS THAN (202701),
    PARTITION pmax    VALUES LESS THAN MAXVALUE
)
DISTRIBUTED BY DUPLICATE(g1);   -- 只放 g1,等价于"单组表"
goldendb_client> INSERT INTO orders_single_group SELECT * FROM tb_orders_hash_par_range;
-- 数据只存放在 g1 分片组上:
goldendb_client> select count(*) from orders_single_group storagedb g1;
+----------+
| COUNT(*) |
+----------+
|    30000 |
+----------+
1 row in set (0.10 sec)
goldendb_client> select count(*) from orders_single_group storagedb g2;
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
goldendb_client> select count(*) from orders_single_group storagedb g4;
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.01 sec)
  1. 查看表结构,发现确实只存放在 g1 分片组中。
goldendb_client> show create table orders_single_group\G
*************************** 1. row ***************************
       TABLE: orders_single_group
CREATE TABLE: CREATE TABLE `orders_single_group` (
  `order_id` bigint NOT NULL,
  `user_id` bigint NOT NULL,
  `region_code` varchar(10) COLLATE utf8mb4_bin NOT NULL,
  `amount` decimal(10,2) DEFAULT NULL,
  `create_time` datetime NOT NULL,
  `ym` int NOT NULL,
  PRIMARY KEY (`order_id`,`ym`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
/*!50100 PARTITION BY RANGE ("ym")
(PARTITION p202608 VALUES LESS THAN (202609) ENGINE = InnoDB,
 PARTITION p202609 VALUES LESS THAN (202610) ENGINE = InnoDB,
 PARTITION p202610 VALUES LESS THAN (202611) ENGINE = InnoDB,
 PARTITION p202611 VALUES LESS THAN (202612) ENGINE = InnoDB,
 PARTITION p202612 VALUES LESS THAN (202701) ENGINE = InnoDB,
 PARTITION pmax VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
 DISTRIBUTED BY DUPLICATE(g1)
1 row in set (0.03 sec)
  1. 收集统计信息并查看分区子表情况:
goldendb_client> analyze table orders_single_group;
+--------------------------+---------+----------+----------+
| TABLE                    | OP      | MSG_TYPE | MSG_TEXT |
+--------------------------+---------+----------+----------+
| test.orders_single_group | analyze | status   | OK       |
+--------------------------+---------+----------+----------+
1 row in set (0.23 sec)
goldendb_client> SELECT PARTITION_NAME,        PARTITION_METHOD,        PARTITION_EXPRESSION,        PARTITION_DESCRIPTION,        TABLE_ROWS FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE()   AND TABLE_NAME   = 'orders_single_group'   AND PARTITION_NAME IS NOT NULL ORDER BY PARTITION_ORDINAL_POSITION;
+----------------+------------------+----------------------+-----------------------+------------+
| PARTITION_NAME | PARTITION_METHOD | PARTITION_EXPRESSION | PARTITION_DESCRIPTION | TABLE_ROWS |
+----------------+------------------+----------------------+-----------------------+------------+
| p202608        | RANGE            | `ym`                 | 202609                |      22063 |
| p202609        | RANGE            | `ym`                 | 202610                |       3030 |
| p202610        | RANGE            | `ym`                 | 202611                |       3027 |
| p202611        | RANGE            | `ym`                 | 202612                |          0 |
| p202612        | RANGE            | `ym`                 | 202701                |          0 |
| pmax           | RANGE            | `ym`                 | MAXVALUE              |          0 |
+----------------+------------------+----------------------+-----------------------+------------+
6 rows in set (0.01 sec)
  1. 利用分区键进行过滤查询:
goldendb_client>  EXPLAIN SELECT * FROM  orders_single_group  WHERE  ym = 202610 \G
*************************** 1. row ***************************
           ID: 10001
  SELECT_TYPE: SQLNode
        TABLE: 1
   PARTITIONS: 
         TYPE: 
POSSIBLE_KEYS:  SELECT `test`.`orders_single_group`.`order_id`,`test`.`orders_single_group`.`user_id`,`test`.`orders_single_group`.`region_code`,`test`.`orders_single_group`.`amount`,`test`.`orders_single_group`.`create_time`,`test`.`orders_single_group`.`ym`,`test`.`orders_single_group`.`gtid` as `gtid1` FROM `test`.`orders_single_group`  where (`ym` = 202610)  
          KEY: Cluster1,g1
      KEY_LEN: 
          REF: Parent=NULL,Child=NULL,Next=NULL
         ROWS: 
     FILTERED: 
        EXTRA: cr, test.orders_single_group=rep,digest=df55d9c07dd1f57d13cb4817e1d9495b
*************************** 2. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: orders_single_group
   PARTITIONS: p202610
         TYPE: ALL
POSSIBLE_KEYS: NULL
          KEY: NULL
      KEY_LEN: NULL
          REF: NULL
         ROWS: 3027
     FILTERED: 10.00
        EXTRA: Using where, g1
2 rows in set (0.00 sec)

PARTITIONS: p202610  代表使用了分区裁剪,并只扫描了 g1 分片组。

3.2 Hash 分区

  1. 创建 Hash 分区表,此时不指定 HASH 分区的数量,指定落在 g2 分片组上。
DROP TABLE IF EXISTS orders_part_hash;
CREATE TABLE orders_part_hash (
    order_id   INT          NOT NULL,
	user_id    INT          NOT NULL,
	amount     DECIMAL(10,2) DEFAULT NULL,
	create_time DATETIME    DEFAULT CURRENT_TIMESTAMP,
	PRIMARY KEY (user_id, order_id)
)
Partition BY HASH(user_id) 
DISTRIBUTED BY DUPLICATE(g2);
goldendb_client> INSERT INTO orders_part_hash SELECT * FROM tb_order_hash;
goldendb_client> COMMIT;
goldendb_client> select count(*) from orders_part_hash storagedb g1;
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
goldendb_client> select count(*) from orders_part_hash storagedb g2;
+----------+
| COUNT(*) |
+----------+
|    40000 |
+----------+
1 row in set (0.20 sec)
goldendb_client> select count(*) from orders_part_hash storagedb g4;
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.01 sec)
  1. 收集统计信息并查看分区子表情况,发现只有一个分区:p0
goldendb_client> analyze table orders_part_hash;
+-----------------------+---------+----------+----------+
| TABLE                 | OP      | MSG_TYPE | MSG_TEXT |
+-----------------------+---------+----------+----------+
| test.orders_part_hash | analyze | status   | OK       |
+-----------------------+---------+----------+----------+
1 row in set (0.20 sec)
-- 实际上只有一个分区
goldendb_client> SELECT PARTITION_NAME,
       PARTITION_METHOD,
       PARTITION_EXPRESSION,
       PARTITION_DESCRIPTION,
       TABLE_ROWS
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME   = 'orders_part_hash'
  AND PARTITION_NAME IS NOT NULL
ORDER BY PARTITION_ORDINAL_POSITION;
+----------------+------------------+----------------------+-----------------------+------------+
| PARTITION_NAME | PARTITION_METHOD | PARTITION_EXPRESSION | PARTITION_DESCRIPTION | TABLE_ROWS |
+----------------+------------------+----------------------+-----------------------+------------+
| p0             | HASH             | `user_id`            | NULL                  |      40152 |
+----------------+------------------+----------------------+-----------------------+------------+
1 row in set (0.01 sec)
  1. 重新建表,指定 HASH 分区数量为 4,同样落在 g2 分片组上。
DROP TABLE IF EXISTS orders_part_hash;
CREATE TABLE orders_part_hash (
    order_id   INT          NOT NULL,
	user_id    INT          NOT NULL,
	amount     DECIMAL(10,2) DEFAULT NULL,
	create_time DATETIME    DEFAULT CURRENT_TIMESTAMP,
	PRIMARY KEY (user_id, order_id)
)
Partition BY HASH(user_id) PARTITIONS 4
DISTRIBUTED BY DUPLICATE(g2);
goldendb_client> INSERT INTO orders_part_hash SELECT * FROM tb_order_hash;
goldendb_client> COMMIT;
  1. 收集统计信息并查看分区子表情况,发现有 4 个分区
goldendb_client> analyze table orders_part_hash;
+-----------------------+---------+----------+----------+
| TABLE                 | OP      | MSG_TYPE | MSG_TEXT |
+-----------------------+---------+----------+----------+
| test.orders_part_hash | analyze | status   | OK       |
+-----------------------+---------+----------+----------+
1 row in set (0.06 sec)
goldendb_client> SELECT PARTITION_NAME,
       PARTITION_METHOD,
       PARTITION_EXPRESSION,
       PARTITION_DESCRIPTION,
       TABLE_ROWS
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME   = 'orders_part_hash'
  AND PARTITION_NAME IS NOT NULL
ORDER BY PARTITION_ORDINAL_POSITION;
+----------------+------------------+----------------------+-----------------------+------------+
| PARTITION_NAME | PARTITION_METHOD | PARTITION_EXPRESSION | PARTITION_DESCRIPTION | TABLE_ROWS |
+----------------+------------------+----------------------+-----------------------+------------+
| p0             | HASH             | `user_id`            | NULL                  |       9979 |
| p1             | HASH             | `user_id`            | NULL                  |      10055 |
| p2             | HASH             | `user_id`            | NULL                  |      10025 |
| p3             | HASH             | `user_id`            | NULL                  |       9941 |
+----------------+------------------+----------------------+-----------------------+------------+
4 rows in set (0.01 sec)
  1. 利用分区键进行过滤查询:
goldendb_client>  EXPLAIN SELECT * FROM orders_part_hash WHERE user_id = 1000\G
*************************** 1. row ***************************
           ID: 10001
  SELECT_TYPE: SQLNode
        TABLE: 1
   PARTITIONS: 
         TYPE: 
POSSIBLE_KEYS:  SELECT `test`.`orders_part_hash`.`order_id`,`test`.`orders_part_hash`.`user_id`,`test`.`orders_part_hash`.`amount`,`test`.`orders_part_hash`.`create_time`,`test`.`orders_part_hash`.`gtid` as `gtid1` FROM `test`.`orders_part_hash`  where (`user_id` = 1000)  
          KEY: Cluster1,g2
      KEY_LEN: 
          REF: Parent=NULL,Child=NULL,Next=NULL
         ROWS: 
     FILTERED: 
        EXTRA: cr, test.orders_part_hash=rep,digest=cef8764fed61cc1abfd00932c917df73
*************************** 2. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: orders_part_hash
   PARTITIONS: p0
         TYPE: ref
POSSIBLE_KEYS: PRIMARY
          KEY: PRIMARY
      KEY_LEN: 4
          REF: const
         ROWS: 1
     FILTERED: 100.00
        EXTRA: NULL, g2
2 rows in set (0.00 sec)
goldendb_client> EXPLAIN SELECT * FROM orders_part_hash WHERE user_id = 1001\G
*************************** 1. row ***************************
           ID: 10001
  SELECT_TYPE: SQLNode
        TABLE: 1
   PARTITIONS: 
         TYPE: 
POSSIBLE_KEYS:  SELECT `test`.`orders_part_hash`.`order_id`,`test`.`orders_part_hash`.`user_id`,`test`.`orders_part_hash`.`amount`,`test`.`orders_part_hash`.`create_time`,`test`.`orders_part_hash`.`gtid` as `gtid1` FROM `test`.`orders_part_hash`  where (`user_id` = 1001)  
          KEY: Cluster1,g2
      KEY_LEN: 
          REF: Parent=NULL,Child=NULL,Next=NULL
         ROWS: 
     FILTERED: 
        EXTRA: cr, test.orders_part_hash=rep,digest=cef8764fed61cc1abfd00932c917df73
*************************** 2. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: orders_part_hash
   PARTITIONS: p1
         TYPE: ref
POSSIBLE_KEYS: PRIMARY
          KEY: PRIMARY
      KEY_LEN: 4
          REF: const
         ROWS: 1
     FILTERED: 100.00
        EXTRA: NULL, g2
2 rows in set (0.01 sec)
goldendb_client> -- partitions: p1
goldendb_client> 
goldendb_client> EXPLAIN SELECT * FROM orders_part_hash WHERE user_id = 1002\G
*************************** 1. row ***************************
           ID: 10001
  SELECT_TYPE: SQLNode
        TABLE: 1
   PARTITIONS: 
         TYPE: 
POSSIBLE_KEYS:  SELECT `test`.`orders_part_hash`.`order_id`,`test`.`orders_part_hash`.`user_id`,`test`.`orders_part_hash`.`amount`,`test`.`orders_part_hash`.`create_time`,`test`.`orders_part_hash`.`gtid` as `gtid1` FROM `test`.`orders_part_hash`  where (`user_id` = 1002)  
          KEY: Cluster1,g2
      KEY_LEN: 
          REF: Parent=NULL,Child=NULL,Next=NULL
         ROWS: 
     FILTERED: 
        EXTRA: cr, test.orders_part_hash=rep,digest=cef8764fed61cc1abfd00932c917df73
*************************** 2. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: orders_part_hash
   PARTITIONS: p2
         TYPE: ref
POSSIBLE_KEYS: PRIMARY
          KEY: PRIMARY
      KEY_LEN: 4
          REF: const
         ROWS: 1
     FILTERED: 100.00
        EXTRA: NULL, g2
2 rows in set (0.01 sec)

user id 为 1000 时查询是 p0 分区; userid 为 1001 时查询是 p1 分区; user_id 为 1002 时查询是 p2 分区,

因为 user_id 经过哈希运算:

WHERE user_id = 1000  → 1000 % 4 = 0 → p0

WHERE user_id = 1001  → 1 → p1

WHERE user_id = 1002  → 2 → p2

3.3 List 分区

  1. 创建 List 分区表,指定落在 g4 分片上。
CREATE TABLE orders_part_list (
    order_id    INT             NOT NULL,
    user_id     INT             NOT NULL,
    region_code VARCHAR(10)    NOT NULL,
    amount      DECIMAL(10,2)   DEFAULT NULL,
    create_time DATETIME        DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (order_id,region_code)
) Partition BY LIST (region_code) (
   PARTITION p_huabei  VALUES IN ('BJ','TJ','HEB'),
   PARTITION p_huadong VALUES IN ('SH','HZ','NJ','WH'),
   PARTITION p_huanan  VALUES IN ('GZ','SZ','XA','CD')
)
DISTRIBUTED BY DUPLICATE(g4);
goldendb_client> insert into orders_part_list select *  from tb_order_list;
goldendb_client> commit;
goldendb_client> select count(*) from orders_part_list storagedb g1;
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
goldendb_client> select count(*) from orders_part_list storagedb g2;
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
goldendb_client> select count(*) from orders_part_list storagedb g4;
+----------+
| COUNT(*) |
+----------+
|    30000 |
+----------+
1 row in set (0.13 sec)
  1. 收集统计信息,查看分区子表,发现按定义生成了 3 个子分区。
goldendb_client> analyze table orders_part_list;
+-----------------------+---------+----------+----------+
| TABLE                 | OP      | MSG_TYPE | MSG_TEXT |
+-----------------------+---------+----------+----------+
| test.orders_part_list | analyze | status   | OK       |
+-----------------------+---------+----------+----------+
goldendb_client> SELECT PARTITION_NAME,
       PARTITION_METHOD,
       PARTITION_EXPRESSION,
       PARTITION_DESCRIPTION,
       TABLE_ROWS
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME   = 'orders_part_list'
  AND PARTITION_NAME IS NOT NULL
ORDER BY PARTITION_ORDINAL_POSITION;
+----------------+------------------+----------------------+-----------------------+------------+
| PARTITION_NAME | PARTITION_METHOD | PARTITION_EXPRESSION | PARTITION_DESCRIPTION | TABLE_ROWS |
+----------------+------------------+----------------------+-----------------------+------------+
| p_huabei       | LIST             | `region_code`        | 'BJ','TJ','HEB'       |       8070 |
| p_huadong      | LIST             | `region_code`        | 'SH','HZ','NJ','WH'   |      10827 |
| p_huanan       | LIST             | `region_code`        | 'GZ','SZ','XA','CD'   |      11103 |
+----------------+------------------+----------------------+-----------------------+------------+
3 rows in set (0.00 sec)
  1. 利用分区键过滤查询:
goldendb_client> EXPLAIN SELECT * FROM orders_part_list WHERE region_code = 'BJ'\G
*************************** 1. row ***************************
           ID: 10001
  SELECT_TYPE: SQLNode
        TABLE: 1
   PARTITIONS: 
         TYPE: 
POSSIBLE_KEYS:  SELECT `test`.`orders_part_list`.`order_id`,`test`.`orders_part_list`.`user_id`,`test`.`orders_part_list`.`region_code`,`test`.`orders_part_list`.`amount`,`test`.`orders_part_list`.`create_time`,`test`.`orders_part_list`.`gtid` as `gtid1` FROM `test`.`orders_part_list`  where (`region_code` = 'BJ')  
          KEY: Cluster1,g4
      KEY_LEN: 
          REF: Parent=NULL,Child=NULL,Next=NULL
         ROWS: 
     FILTERED: 
        EXTRA: cr, test.orders_part_list=rep,digest=a11c25763f3cae4a6542465d743661ac
*************************** 2. row ***************************
           ID: 1
  SELECT_TYPE: SIMPLE
        TABLE: orders_part_list
   PARTITIONS: p_huabei
         TYPE: ALL
POSSIBLE_KEYS: NULL
          KEY: NULL
      KEY_LEN: NULL
          REF: NULL
         ROWS: 8070
     FILTERED: 10.00
        EXTRA: Using where, g4
2 rows in set (0.01 sec)

PARTITIONS: p_huabei  发现可以使用分区裁剪。

4. 总结

   本文首先介绍了 MyCat 传统中间件 + MySQL 数据库 与 GoldenDB 分布式数据库的主要区别,传统 MyCat 架构属于“拼装”逻辑,中间件与底层 MySQL 相互割裂;而 GoldenDB 实现了计算层(CN)与存储层(DN)的 融合。它支持 将逻辑分区直接映射到物理分片组( DBGroup),在 SQL 解析的瞬间即可完成“分布式分片裁剪 + 节点内分区裁剪”的双重加速。

其次,介绍了 HASH、RANGE、LIST、DUPLICATE 及 组合分片 五大规则( DISTRIBUTED BY)和 RANGE、LIST、HASH三种分区( PARTITION BY)方式。

  • 分片( DISTRIBUTED BY)—— 水平维度:决定数据分散到哪一个 DBGroup(物理主备集群);
  • 分区( PARTITION BY)  —— 垂直维度:在单个 DBGroup 的存储引擎层,按规则将数据文件进一步切分为多个物理段,实现单节点内的数据隔离与裁剪优化。

最后,生产中需注意以下三点:

  • 建表时不写 DISTRIBUTED BY 默认为复制表:每个分片组上存放一份完整的数据;对于海量流水表,漏写分片键会导致磁盘空间暴涨。
  • HASH 分区需要提前定义子分区个数:若漏写,底层默认只会创建一个 p0 分区文件。
  • 统计信息刷新机制:海量数据刚 COMMIT 时,元数据视图中的 TABLE_ROWS 往往会显示为 0 或失真。必须在跑批或执行计划调优前,显式执行 ANALYZE TABLE 。

     GoldenDB 的分片和分区介绍到这里。这里是《实战派K8S&DB》,更多干货敬请关注公众号。如果本文对你有所帮助,欢迎点赞、推荐和转发,也欢迎关注后续文章,一起考证、一起学习 GoldenDB 分布式数据库。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31444449/viewspace-3184719/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2018-02-12

  • 博文量
    25
  • 访问量
    20658