11
表分区
KingbaseES
支持基本的表划分。本小节介绍为何以及怎样把划分实现为数据库设计的一部分。
11.1
概述
划分指的是将逻辑上的一个大表分成一些小的物理上的片。划分有很多益处:
•
在某些情况下查询性能能够显著提升,特别是当那些访问压力大的行在一个分区或者少数几个分区时。
划分可以取代索引的主导列、减小索引尺寸以及使索引中访问压力大的部分更有可能被放在内存中。
•
当查询或更新访问一个分区的大部分行时,可以通过该分区上的一个顺序扫描来取代分散到整个表上
的索引和随机访问,这样可以改善性能。
•
如果批量操作的需求是在分区设计时就规划好的,则批量装载和删除可以通过增加或者去除分区来完
成。执行
ALTER TABLE DETACH PARTITION
或者使用
DROP TABLE
删除一个分区远快于批量操
作。这些命令也完全避免了批量
DELETE
导致的
VACUUM
开销。
•
很少使用的数据可以被迁移到便宜且较慢的存储介质上。
当一个表非常大时,划分所带来的好处是非常值得的。一个表何种情况下会从划分获益取决于应用,一个经
验法则是当表的尺寸超过了数据库服务器物理内存时,划分会为表带来好处。
KingbaseES
对下列分区形式提供了内建支持:
范围划分
表被根据一个关键列或一组列划分为“范围”,不同的分区的范围之间没有重叠。例如,我们可以
根据日期范围划分,或者根据特定业务对象的标识符划分。
列表划分
通过显式地列出每一个分区中出现的键值来划分表。
哈希分区
通过为每个分区指定模数和余数来对表进行分区。每个分区所持有的行都满足:分区键的值除以
为其指定的模数将产生为其指定的余数。
如果你的应用需要使用上面所列之外的分区形式,可以使用诸如继承和
UNION ALL
视图之类的替代方法。
这些方法很灵活,但是却缺少内建声明式分区的一些性能优势。
11.2
声明式划分
KingbaseES
提供了一种方法指定如何把一个表划分成称为分区的片段。被划分的表被称作
分区表
。这种说明
由
分区方法
以及要被用作
分区键
的列或者表达式列表组成。
所有被插入到分区表的行将被基于分区键的值路由到
分区
中。每个分区都有一个由其
分区边界
定义的数据子
集。当前支持的分区方法是范围、列表以及哈希。
分区本身也可能被定义为分区表,这种用法被称为
子分区
。分区可以有自己的与其他分区不同的索引、约束
以及默认值。创建分区表及分区的更多细节请见
CREATE TABLE
。
无法把一个常规表转换成分区表,反之亦然。不过,可以把一个包含数据的常规表或者分区表作为分区加入
到另一个分区表,或者从分区表中移走一个分区并且把它变成一个独立的表。有关
ATTACH PARTITION
和
DETACH PARTITION
子命令的内容请见
ALTER TABLE
。
个体分区在内部以继承的方式链接到分区表,不过无法对声明式分区表或其分区使用继承的某些一般特性
(下文讨论)。例如,分区不能有除其所属分区表之外的父表,一个常规表也不能从分区表继承使得后者成为
其父表。这意味着分区表及其分区不会参与到与常规表的继承关系中。由于分区表及其分区组成的分区层次
仍然是一种继承层次,所有
继承
中所述的继承的普通规则也适用,不过有一些例外,尤其是:
•
分区表的
CHECK
约束和
NOT NULL
约束总是会被其所有的分区所继承。不允许在分区表上创建标记
为
NO INHERIT
的
CHECK
约束。
•
只要分区表中不存在分区,则支持使用
ONLY
仅在分区表上增加或者删除约束。一旦分区存在,那样
做就会导致错误,因为当分区存在时是不支持仅在分区表上增加或删除约束的。不过,分区表本身上
的约束可以被增加(如果它们不出现在父表中)和删除。
•
由于分区表并不直接拥有任何数据,尝试在分区表上使用
TRUNCATE ONLY
将总是返回错误。
•
分区不能有在父表中不存在的列。在使用
CREATE TABLE
创建分区时不能指定列,在事后使用
ALTER TABLE
时也不能为分区增加列。只有当表的列正好匹配父表时(包括任何
oid
列),才能使
用
ALTER TABLE ... ATTACH PARTITION
。
•
如果
NOT NULL
约束在父表中存在,那么就不能删除分区的列上的对应的
NOT NULL
约束。
分区也可以是外部表,不过它们有一些普通表没有的限制,详情请见
CREATE FOREIGN TABLE
。
更新行的分区键可能导致它满足另一个不同的分区的分区边界,进而被移动到那个分区中。
11.2.1
例子
假定我们正在为一个大型的冰激凌公司构建数据库。该公司每天测量最高温度以及每个区域的冰激凌销售情
况。概念上,我们想要一个这样的表:
CREATE TABLE measurement (
city_id
int
not
null,
logdate
date
not
null,
peaktemp
int
,
unitsales
int
);
我们知道大部分查询只会访问上周的、上月的或者上季度的数据,因为这个表的主要用途是为管理层准备在
线报告。为了减少需要被存放的旧数据量,我们决定只保留最近
3
年的数据。在每个月的开始我们将去除掉
最早的那个月的数据。在这种情况下我们可以使用分区技术来帮助我们满足对
measurement
表的所有不同需
求。
要在这种情况下使用声明式分区,可采用下面的步骤:
1.
通过指定
PARTITION BY
子句把
measurement
表创建为分区表,该子句包括分区方法(这个例子
中是
RANGE
)以及用作分区键的列列表。
CREATE TABLE measurement (
city_id
int
not
null,
logdate
date
not
null,
peaktemp
int
,
unitsales
int
) PARTITION BY RANGE (logdate);
你可能需要决定在分区键中使用多列进行范围分区。当然,这通常会导致较大数量的分区,其中每一
个个体都比较小。另一方面,使用较少的列可能会导致粗粒度的分区策略得到较少数量的分区。如果
条件涉及这些列中的一部分或者全部,访问分区表的查询将不得不扫描较少的分区。例如,考虑一个
使用列
lastname
和
firstname
(按照这样的顺序)作为分区键进行范围分区的表。
2.
创建分区。每个分区的定义必须指定对应于父表的分区方法和分区键的边界。注意,如果指定的边界
使得新分区的值会与已有分区中的值重叠,则会导致错误。向父表中插入无法映射到任何现有分区的
数据将会导致错误,这种情况下应该手工增加一个合适的分区。
分区以普通
KingbaseES
表(或者可能是外部表)的方式创建。可以为每个分区单独指定表空间和存储
参数。
没有必要创建表约束来描述分区的分区边界条件。相反,只要需要引用分区约束时,分区约束会自动
地隐式地从分区边界说明中生成。
CREATE TABLE measurement_y2006m02 PARTITION OF measurement
FOR VALUES FROM (
'2006-02-01'
) TO (
'2006-03-01'
);
CREATE TABLE measurement_y2006m03 PARTITION OF measurement
FOR VALUES FROM (
'2006-03-01'
) TO (
'2006-04-01'
);
...
CREATE TABLE measurement_y2007m11 PARTITION OF measurement
FOR VALUES FROM (
'2007-11-01'