数据库的数据量达到一定程度之后,为避免带来系统性能上的瓶颈。需要进行数据的处理,采用的手段是分区、分片、分库、分表。

1 分片 Distribution

分片是把数据库横向扩展(Scale Out)到多个物理节点上的一种有效的方式,每一个分区包含数据库的某一部分,称为一个片(segment)。其主要目的是为突破单节点数据库服务器的 I/O 能力限制,解决数据库扩展性问题。

  1. 垂直(纵向)拆分:是指按功能模块拆分,以解决表与表之间的io竞争。比如分为订单库、商品库、用户库…这种方式多个数据库之间的表结构不同。
  2. 水平(横向)拆分:将同一个表的数据进行分块保存到不同的数据库中,来解决单表中数据量增长出现的压力。

例如在分布式系统Greenplum中,创建表时需要指定分布键将数据平均分布到各个分片。选择分布键非常重要,选择错了会导致数据不唯一,更严重的是会造成SQL性能急剧下降。

有两种分布策略:

hash分布

Greenplum默认使用hash分布策略。该策略可选一个或者多个列作为分布键(distribution key,简称DK)。分布键做hash算法来确认数据存放到对应的segment上。相同分布键值会hash到相同的segment上。表上最好有唯一键或者主键,这样能保证数据均衡分不到各个segment上,若选择的分布列值分布不均匀,则可能导致数据倾斜。如果创建表时未提供DISTRIBUTED子句,则将PRIMARY KEY或表的第一个合格列用作分布键,若没有则退化为随机分布策略。

CREATE TABLE TEST(
    id INT, 
    data INT
)
distribute by id;

randomly分布

数据会被随机分不到segment上,相同记录可能会存放在不同的segment上。随机分布可以保证数据平均,但是Greenplum没有跨节点的唯一键约束数据,所以无法保证数据唯一。

CREATE TABLE TEST(
    id INT, 
    data INT
)
distribute by random;

分布键选择

分布键必须是约束列的左子集,且列的顺序正确
默认分布键策略

  • 存在hash分区
  • 如果不存在唯一性约束,可以直接使用hash分区的字段作为分布键字段
  • 如果存在唯一性约束,检查hash分区字段是否是唯一约束字段的左子集(不要求顺序),若是则截取唯一约束中的hash分区字段作为分布键,顺序和唯一约束保持一致。如下所示唯一约束字段为(“NAME”, “VALUE”, “CONTENT”),hash分区字段为 value、name,是唯一约束的左子集,则截取"NAME"、 "VALUE"作为分布键。
CREATE TABLE "FOO"(
	"NAME" VARCHAR(10), 
	"VALUE" VARCHAR(10), 
	"CONTENT" VARCHAR(10), 
	UNIQUE("NAME", "VALUE", "CONTENT"))
partition by hash(value, name)(partition par1, partition par2)
  • 不存在hash分区
  • 存在唯一键,使用唯一键字段作为分布键
  • 不存在唯一键,使用数据库定义的第一个字段作为分布键

2 分区 Partition

分区是通过 PARTITION BY子句完成的,它允许将一个大表划分为多个子表。分区与分片的根本区别在于:分片是将数据存储在不同的物理机器或数据库,而分区是在一个数据库内进行的划分。

对大表进行分区,可以提高查询性能并简化数据库的维护任务,例如将旧数据滚动移除出数据库。但是创建过多的分区可能会拖慢管理和维护的速度,例如清理,恢复segment,扩展集群,检查磁盘使用情况等等。

有如下几种分区类型:

2.1 range分区。

分区表达式的值位于一个给定的连续区间内。一般使用这种分区方式大都是对连续的值进行分区,常见的如:按年份,日期进行分区。

CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',)
partition BY range(to_days(create_time))
	(PARTITION p201701 VALUES LESS THAN (TO_DAYS('2017-02-01')) ENGINE = InnoDB,
	 PARTITION p201702 VALUES LESS THAN (TO_DAYS('2017-03-01')) ENGINE = InnoDB,
	 PARTITION p2018 VALUES LESS THAN MAXVALUE ENGINE = InnoDB );

2.2 List分区

列值在一个离散值集合中的某个值来进行选择。
例如下面store_id在1,5,6,9,17这些值中时候会被分配到pNorth这个分区中

CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',)
PARTITION BY LIST(store_id)
	PARTITION pNorth VALUES IN (1,5,6,9,17),
	PARTITION pEast VALUES IN (2,4,10,11,19,20),
	PARTITION pWest VALUES IN (3,12,13,14,18),
	PARTITION pCentral VALUES IN (7,8,15,16));

分区键的选择:如果同时存在唯一性约束(unique和primary)和分区键,则唯一性约束必须包含分区键的所有字段

2.3 Hash分区

对分区键应用一个散列函数,得出数据应该放在n个分区中的哪一个分区。hash分区支持两种散列函数(分区方式):取模算法(默认hash分区方式)和线性的2的幂的运算法则(liner hash 分区)。
如下所示为线性hash分区

CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
PARTITION BY LINEAR HASH(id)
PARTITIONS 5;

2.4 Key分区

Key分区和Hash分区相似,但hash分区只支持整数分区,key分区支持除了blob或text类型之外的其他数据类型分区。创建key分区表的时候,可以不指定分区键,默认会选择使用主键/唯一键作为分区键

CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
email VARCHAR(30) NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
PARTITION BY LINEAR Key(email)
PARTITIONS 5;

** 分区与分表 **

分表是把一张表分成多个小表。分区只不过把存放数据的文件分成了许多小块,分区后的表逻辑上还是一张表。分表与分区的根本不同在于:分表是对存储的数据进行物理划分,而分区则是逻辑划分