实践polardb X

云原生分布式数据库PolarDB-X_DRDS_分库分表_数据库-阿里云 (aliyun.com)是由阿里巴巴自主研发的云原生分布式数据库,具备水平扩容、金融级高可用、HTAP混合负载、兼容 MySQL 等重要特性,专注解决超高并发吞吐、海量数据存储、大表瓶颈等数据库瓶颈难题,历经各届天猫双11及阿里云各行业客户业务的考验,助力企业加速完成业务数字化转型。分布式数据库PolarDB-X开源版 (polardbx.com)

整体架构

适用场景

高负载低延时交易

大峰谷差流量

数据集中存储

分布式快速改造

数据库国产化替换

混合负载访问

名词解释

  • 分区键:分区表中用于进行水平切分的一个列或多个列。
  • 分区列:水平切分后,参与分区路由及计算的列,它通常是分区键的一部分,一个分区键可以含有一个或多个分区列。
  • 向量分区键:由一个或多个分区列组成的分区键。
  • 单列分区键:由一个分区列组成的分区键。
  • 前缀分区列:若一个向量分区键由N(N>1)个分区列组成,它的前K(1<=K<=N)个分区列便组成这个向量分区键的前缀分区列。
  • 分区函数:将分区列作为一个函数的输入参数,并将该函数的输出结果作为原始值参与路由计算,该函数被称为分区函数。
  • 分区裁剪:根据分区定义查询条件,最大限度地过滤不需要扫描的分区的查询优化手段。
  • 热点分裂:当向量分区键的前缀分区列存在访问热点或分布不均衡时,允许使用下一个分区列对热点分区进行分裂,以达到负载均衡效果【key(向量分区键)、range columns(向量分区键)支持】。
  • 自动分区表:不指定partition by等相关参数
  • 手动分区表:指定partition by等相关参数
  • 回表

操作

数据库

AUTO模式数据库与DRDS模式数据库 (aliyun.com)

  • 创建AUTO模式数据库必须在CREATE DATABASE语法中显式指定MODE='AUTO'
  • 如果在CREATE DATABASE语法中不指定MODE参数的值,默认创建DRDS模式的数据库。
  • AUTO模式数据库下不支持使用DRDS分库分表的语法创建表,仅支持创建分区表。
  • DRDS模式数据库下不支持使用分区表的语法创建表,仅支持创建分库分表。

AUTO模式数据库

1
CREATE DATABASE auto_db MODE='AUTO';

DRDS模式数据库

1
CREATE DATABASE drds_db MODE='DRDS';

查看

1
show create dababase auto_db;

分区表

auto模式下,默认创建分区表

CREATE TABLE `single_tbl` (
	`id` int(11) NOT NULL,
	`name` varchar(30) DEFAULT NULL,
	PRIMARY KEY (`id`)
)

show full create table single_tbl;

CREATE PARTITION TABLE `single_tbl` (
	`id` int(11) NOT NULL,
	`name` varchar(30) DEFAULT NULL,
	PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`id`)
PARTITIONS 24
/* tablegroup = `tg1` */

单表

不进行任何分区

如果一张表的数据,数据量预估在1000万条以下,且没有与其他拆分表进行关联查询的需求,建议将其设置为单表类型。

1
2
3
4
5
6
CREATE TABLE single_tbl(
 id bigint not null auto_increment, 
 bid int, 
 name varchar(30), 
 primary key(id)
) SINGLE;

广播表

在所有DN节点上有一份数据完全相同的拷贝。

在业务数据库中,存在一些数据量不大,更新频度低,但常常需要用来做关联查询的表。

为了支持这类表与拆分表进行JOIN操作,可以采用广播表,具有以下特点:

  • 广播表在各DN中数据一致。数据插入、更新与删除会实时在每一个DN中执行一次。
  • 对广播表的查询,仅在一个DN中执行。
  • 任何表都可以与广播表进行JOIN操作。
CREATE TABLE broadcast_tbl(
 id bigint not null auto_increment, 
 bid int, 
 name varchar(30), 
 primary key(id)
) BROADCAST;

索引

本地索引(local index)

auto模式下,主键默认会创建本地索引

CREATE TABLE auto_part(
 id bigint not null auto_increment,
 bid int,
 name varchar(30),
 primary key(id)
);

show full create table auto_part;

CREATE PARTITION TABLE `auto_part` (
	`id` bigint(20) NOT NULL AUTO_INCREMENT,
	`bid` int(11) DEFAULT NULL,
	`name` varchar(30) DEFAULT NULL,
	PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`id`)
PARTITIONS 24
/* tablegroup = `tg6` */

查询本地索引

show index from auto_part;

1
2
Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment	Index_comment
auto_part_equb_00000	0	PRIMARY	1	id	A	0				BTREE		

单独创建本地索引

1
create local index `i_order_id` on t_order10(`order_id`);

全局二级索引(global secondary index)

  • 创建表的同时创建全局索引
CREATE TABLE t_order13 (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `order_id` varchar(20) DEFAULT NULL,
  `buyer_id` varchar(20) DEFAULT NULL,
  `seller_id` varchar(20) DEFAULT NULL,
  `order_snapshot` longtext DEFAULT NULL,
  `order_detail` longtext DEFAULT NULL,
  PRIMARY KEY (`id`),
	GLOBAL index g_i_seller_id(`seller_id`)  partition by hash(`seller_id`)
) partition by hash(`buyer_id`);
  • 单独创建全局索引
CREATE GLOBAL INDEX `g_i_seller` ON t_order9 (`seller_id`) covering (`buyer_id`,`order_id`) partition by hash(`seller_id`) partitions 16;
  • 当分区键为主键时(自动分区表),创建普通索引默认会同时创建全局索引
CREATE TABLE auto_part_tbl(
 id bigint not null auto_increment,
 bid int,
 name varchar(30),
 primary key(id),
 index idx_name (name)
);

show full CREATE TABLE auto_part_tbl;

CREATE PARTITION TABLE `auto_part_tbl` (
	`id` bigint(20) NOT NULL AUTO_INCREMENT,
	`bid` int(11) DEFAULT NULL,
	`name` varchar(30) DEFAULT NULL,
	PRIMARY KEY (`id`),
	GLOBAL INDEX /* idx_name_$5159 */ `idx_name` (`name`) PARTITION BY KEY (`name`, `id`) PARTITIONS 24,
	LOCAL KEY `_local_idx_name` (`name`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`id`)
PARTITIONS 24
/* tablegroup = `tg6` */

查询本地索引

show index from auto_part_tbl;

1
2
3
Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment	Index_comment
auto_part_tbl_f88v_00000	0	PRIMARY	1	id	A	0				BTREE		
auto_part_tbl_f88v_00000	1	idx_name	1	name	A	0			YES	BTREE		

查询全局索引

show GLOBAL index from auto_part_tbl;

1
2
SCHEMA	TABLE	NON_UNIQUE	KEY_NAME	INDEX_NAMES	COVERING_NAMES	INDEX_TYPE	DB_PARTITION_KEY	DB_PARTITION_POLICY	DB_PARTITION_COUNT	TB_PARTITION_KEY	TB_PARTITION_POLICY	TB_PARTITION_COUNT	STATUS
testdb1	auto_part_tbl	1	idx_name_$5159	name	id								PUBLIC
  • 后创建普通索引,也会同时创建全局索引
CREATE TABLE auto_part_tbl4(
 id bigint not null auto_increment,
 bid int,
 name varchar(30),
 primary key(id)
);

create index i_name on auto_part_tbl4 (`name`);

show full create table auto_part_tbl4;

CREATE PARTITION TABLE `auto_part_tbl4` (
	`id` bigint(20) NOT NULL AUTO_INCREMENT,
	`bid` int(11) DEFAULT NULL,
	`name` varchar(30) DEFAULT NULL,
	PRIMARY KEY (`id`),
	GLOBAL INDEX /* i_name_$543e */ `i_name` (`name`) PARTITION BY KEY (`name`, `id`) PARTITIONS 24,
	LOCAL KEY `_local_i_name` (`name`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`id`)
PARTITIONS 24
/* tablegroup = `tg6` */
  • 当分区键不包含主键时(手动分区表),创建普通索引则是本地索引
CREATE TABLE t_order11 (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `order_id` varchar(20) DEFAULT NULL,
  `buyer_id` varchar(20) DEFAULT NULL,
  `seller_id` varchar(20) DEFAULT NULL,
  `order_snapshot` longtext DEFAULT NULL,
  `order_detail` longtext DEFAULT NULL,
  PRIMARY KEY (`id`),
	index i_seller_id(`seller_id`)
) partition by hash(`buyer_id`);

show full create table t_order11;

CREATE TABLE `t_order11` (
	`id` bigint(11) NOT NULL AUTO_INCREMENT,
	`order_id` varchar(20) DEFAULT NULL,
	`buyer_id` varchar(20) DEFAULT NULL,
	`seller_id` varchar(20) DEFAULT NULL,
	`order_snapshot` longtext,
	`order_detail` longtext,
	PRIMARY KEY (`id`),
	LOCAL KEY `i_seller_id` (`seller_id`),
	LOCAL KEY `auto_shard_key_buyer_id` USING BTREE (`buyer_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`buyer_id`)
PARTITIONS 24
/* tablegroup = `tg9` */
  • 默认覆盖列包含主键和主表的分区键
CREATE TABLE t_order13 (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `order_id` varchar(20) DEFAULT NULL,
  `buyer_id` varchar(20) DEFAULT NULL,
  `seller_id` varchar(20) DEFAULT NULL,
  `order_snapshot` longtext DEFAULT NULL,
  `order_detail` longtext DEFAULT NULL,
  PRIMARY KEY (`id`),
	GLOBAL index g_i_seller_id(`seller_id`)  partition by hash(`seller_id`)
) partition by hash(`buyer_id`);

show full create table t_order13;

CREATE TABLE `t_order13` (
	`id` bigint(11) NOT NULL AUTO_INCREMENT,
	`order_id` varchar(20) DEFAULT NULL,
	`buyer_id` varchar(20) DEFAULT NULL,
	`seller_id` varchar(20) DEFAULT NULL,
	`order_snapshot` longtext,
	`order_detail` longtext,
	PRIMARY KEY (`id`),
	GLOBAL INDEX /* g_i_seller_id_$93ea */ `g_i_seller_id` (`seller_id`) COVERING (`buyer_id`) PARTITION BY KEY (`seller_id`) PARTITIONS 24,
	LOCAL KEY `auto_shard_key_buyer_id` USING BTREE (`buyer_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`buyer_id`)
PARTITIONS 24
/* tablegroup = `tg9` */

show global index from t_order13;

1
2
SCHEMA	TABLE	NON_UNIQUE	KEY_NAME	INDEX_NAMES	COVERING_NAMES	INDEX_TYPE	DB_PARTITION_KEY	DB_PARTITION_POLICY	DB_PARTITION_COUNT	TB_PARTITION_KEY	TB_PARTITION_POLICY	TB_PARTITION_COUNT	STATUS
testdb1	t_order13	1	g_i_seller_id_$93ea	seller_id	id, buyer_id								PUBLIC
  • 后创建普通索引(手动分区表),也是本地索引
CREATE TABLE t_order14 (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `order_id` varchar(20) DEFAULT NULL,
  `buyer_id` varchar(20) DEFAULT NULL,
  `seller_id` varchar(20) DEFAULT NULL,
  `order_snapshot` longtext DEFAULT NULL,
  `order_detail` longtext DEFAULT NULL,
  PRIMARY KEY (`id`)
) partition by hash(`buyer_id`);

create index i_seller_id on t_order14(`seller_id`);

show full create table t_order14;

CREATE TABLE `t_order14` (
	`id` bigint(11) NOT NULL AUTO_INCREMENT,
	`order_id` varchar(20) DEFAULT NULL,
	`buyer_id` varchar(20) DEFAULT NULL,
	`seller_id` varchar(20) DEFAULT NULL,
	`order_snapshot` longtext,
	`order_detail` longtext,
	PRIMARY KEY (`id`),
	LOCAL KEY `auto_shard_key_buyer_id` USING BTREE (`buyer_id`),
	LOCAL KEY `i_seller_id` (`seller_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`buyer_id`)
PARTITIONS 24
/* tablegroup = `tg9` */
  • 分区列默认会创建本地索引
CREATE TABLE t_order8 (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `order_id` varchar(20) DEFAULT NULL,
  `buyer_id` varchar(20) DEFAULT NULL,
  `seller_id` varchar(20) DEFAULT NULL,
  `order_snapshot` longtext DEFAULT NULL,
  `order_detail` longtext DEFAULT NULL,
  PRIMARY KEY (`id`)
) partition by hash(`buyer_id`);

show full create table t_order8;

CREATE TABLE `t_order8` (
	`id` bigint(11) NOT NULL AUTO_INCREMENT,
	`order_id` varchar(20) DEFAULT NULL,
	`buyer_id` varchar(20) DEFAULT NULL,
	`seller_id` varchar(20) DEFAULT NULL,
	`order_snapshot` longtext,
	`order_detail` longtext,
	PRIMARY KEY (`id`),
	LOCAL KEY `auto_shard_key_buyer_id` USING BTREE (`buyer_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`buyer_id`)
PARTITIONS 24
/* tablegroup = `tg9` */
  • 全局索引默认以索引列主键列作为分区键并进行自动分区
create GLOBAL index g_i_bid on auto_part_tbl3(`bid`);

show full create table auto_part_tbl3;

CREATE PARTITION TABLE `auto_part_tbl3` (
	`id` bigint(20) NOT NULL AUTO_INCREMENT,
	`bid` int(11) DEFAULT NULL,
	`name` varchar(30) DEFAULT NULL,
	PRIMARY KEY (`id`),
	GLOBAL INDEX /* g_i_bid_$fa03 */ `g_i_bid` (`bid`) PARTITION BY KEY (`bid`, `id`) PARTITIONS 24,
	GLOBAL INDEX /* idx_name_$d0e9 */ `idx_name` (`name`) PARTITION BY KEY (`name`, `id`) PARTITIONS 24,
	LOCAL KEY `_local_idx_name` (`name`),
	LOCAL KEY `_local_g_i_bid` (`bid`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`id`)
PARTITIONS 24
/* tablegroup = `tg6` */

聚簇索引

PolarDB-X新增支持聚簇索引功能,用于自动维护全局二级索引(GSI)中的覆盖列,保证聚簇索引表和主表的实时同步,所有查询均不用回表,避免因回表带来的额外开销。本文介绍如何创建并使用聚簇索引。

  • 聚簇索引是一种特殊的全局二级索引,相关行为和限制请参考全局二级索引
  • 聚簇索引的覆盖列默认包含主表的所有列,并在主表的列发生变更时,自动同步修改聚簇索引表,保证聚簇索引表和主表的实时同步。
  • 聚簇索引表也会和主表的本地索引保持同步。

调优

执行计划

EXPLAIN select * from dic_sceneorder where ProductSysNo = 103315;

算子

含义 物理算子
下发给DN的算子 LogicalView,LogicalModifyView,PhyTableOperation, IndexScan
连接(Join) BKAJoin,NLJoin,HashJoin,SortMergeJoin,HashSemiJoin,SortMergeSemiJoin,MaterializedSemiJoin
排序 MemSort,TopN, MergeSort
聚合(Group By) HashAgg,SortAgg
数据重分布或者聚合 Exchange Gather
过滤 Filter
投影 Project
求并集 Union
设置结果集输出行数(Limit/Offset…Fetch) Limit
窗口函数 OverWindow

当涉及到多分区操作时,单纯DN无法做到,CN层面的处理必不可少,比如跨分区排序,跨分区group,跨分区过滤(having)

查询改写

通过explain optimizer + sql可以看到查询改写的具体过程。

1
EXPLAIN optimizer select * from dic_sceneorder where CreateTime = '2017-06-09 19:10:06.000';

职能索引推荐

EXPLAIN advisor select * from dic_sceneorder where ProductSysNo = 103315;

代价分析

1
explain cost

查询执行器

执行模式

1
explain physical

获取物理SQL在DN上的执行情况,比如是否命中了DN的局部索引

1
explain execute