南大通用GBase8s数据库分片表及索引分片策略-01

原文链接:https://www.gbase.cn/community/post/4128
更多精彩内容尽在南大通用GBase技术社区,南大通用致力于成为用户最信赖的数据库产品供应商。

在数据库的浩瀚宇宙中,数据的存储与检索效率是衡量其性能的关键指标。GBase 8s通过其独特的分片技术,为数据的高效管理和快速访问提供了强有力的支持。本文将带您深入了解GBase 8s的分片概念、作用以及分片策略,探索如何通过分片技术提升数据库性能,实现数据的优化存储。

1、分片的概念和作用

分片的概念

  • 是数据分散存储的一种方法
  • 只针对表或索引
  • 把数据或索引根据一定的规则分散存储于不同的位置

分片的作用

  • 唯一目标:提升性能
  • 充分利用引擎的PDQ和并行I/O
  • 利用分片忽略,减少访问的数据的量
  • 有效处理大表
  • 易于管理大表

2、分片忽略的概念

上面提到了分片忽略,这里简单介绍一下,分片忽略是指基于SQL 语句中的查询条件(query predicates),只访问符合条件的表分片与索引分片, 避免全表扫描。对于表达式分片有效,比较简单;对于表和索引的分片都有效。

以下通过gbase8s示例了解分片忽略:

CREATE TABLE tab0 (col1 integer, col2 date)
FRAGMENT BY expression
col1 >= 0 and col1 < 100 in datadbs1,
col1 >= 100 and col1 < 200 in datadbs2,
col1 >= 200 in datadbs3;
--查看其执行计划,可以看到只访问了datadbs2。
set explain on;
select * from tab0 where col1 between 105 and 190;  #  仅访问datadbs2
--如下为执行计划的输出片段

  1) gbasedbt.tab0: SEQUENTIAL SCAN  (Serial, fragments: 1)
         Fragments Scanned: (1) datadbs2
         Filters: (gbasedbt.tab0.col1 <= 190 AND gbasedbt.tab0.col1 >= 105 )

3、DATASKIP简单介绍

文中提到了dataskip这个功能,这里简单介绍一下。DATASKIP 配置参数允许您指定当数据库空间由于磁盘故障不可用时查询可以跳过哪些数据库空间(如果有)。可以列出特定的数据库空间并为所有的数据库空间打开或关闭跳过数据,这有助于提高数据的可用性。

DATASKIP参数可以设置为ON、OFF或ALL,也可以不设置。

设置为ON时,需要指定至少一个数据库空间,如DATASKIP ON dbspace1;

设置为ALL时,表示跳过所有不可用的片段。

设置为OFF时,表示不跳过,不设置时,默认为OFF。

示例如下:

CREATE TABLE tab1 (col1 int, col2 varchar(10))
FRAGMENT BY EXPRESSION
col1 >= 0 and col1 < 100 in datadbs1,
col1 >= 100 and col1 < 200 in datadbs2,
col1 >= 200 and col1 < 300 in datadbsbd;
insert into tab1 values(50,'a');
insert into tab1 values(60,'b');
insert into tab1 values(101,'c');
insert into tab1 values(290,'d');
insert into tab1 values(280,'e');

进行人为破坏,mv datadbsbd datadbsbd1

设置DATASKIP ON datadbsbd 

重新启动实例,启动过程摘要如下:

Opening primary chunks...oninit: Cannot open chunk '/home/xfj/aee350/storage/datadbsbd'. errno = 2
succeeded
Validating chunks...succeeded
Initialize Async Log Flusher...succeeded
......
Starting scheduling system...succeeded
Verbose output complete: mode = 5

--进入数据库中查询原先datadbsbd 中的数据,查询数据为空,不报错
> select * from tab1 where col1 >= 200 and col1 < 300;

       col1 col2

No rows found.
如果不设置DATASKIP或者设置DATASKIP为off,查询原先datadbsbd 中的数据,会报错
> select * from tab1 where col1 >= 200 and col1 < 300;

       col1 col2

  243: Could not position within a table (root.tab1).

  155: ISAM error: Primary and Mirror chunks are bad
Error in line 1
Near character position 50

4、GBase8s的gbase模式索引及分片策略

分片表本身只有定义,没有存储,数据存储在DbSpace 中。使用 FRAGMENT BY 子句创建分片表并指定它的存储分布方案。也可以使用 PARTITION BY 关键字,它是FRAGMENT BY 的同义词。

GBase8s的gbase模式分片方式概括起来分为二大类:

  • 轮转法分片(Round-Robin);
  • 基于表达式的方式(Expression-Based),包括多种具体的形式,如基本表达式、list 等。

GBase8s的索引分为两种:

  • attached索引(GBase8s默认行为是采用attached索引)

attached即数据与索引存放在相同的dbspace上或者采用相同的分片策略。严格的讲attached索引是指每个分片的数据都有对应的索引独立存在

  • detached索引

分离索引是具有单独分片策略的索引。

4.1 gbase模式的分片表介绍

轮转法分片(Round-robin)
  • 采用GBase 8s内部提供的哈希算法进行数据的均匀分布,不需要了解数据的分片情况。
  • 轮转法只能应用于表,不能对索引采用该方法进行分片,因为这样会降低系统性能。
  • 轮转法不能利用分片表的忽略分片的特性减少查询对磁盘的扫描,但对于需要扫描大部分数据应用的情况,使用轮转法分片方法,可以利用PDQ启用多线程并行扫描,可以提高查询性能。
  • 轮转法有一个缺陷,在进行一个查询时,要扫描所有的数据分片 。因此,轮转法方案不支持 DATASKIP。如果不支持DATASKIP,同时又有一个数据分片出现错误,那么整个查询将会失败,因为不能确定出现故障的数据分片上是否有符合条件的数据记录存在。
  • 总的来说,轮转法方案适用的情况是:用户需要快速加载数据,用户预先不知道数据访问的方式。用户的数据经常更新,或者是用户对于数据分布方式未知。

下面是轮转法分片表及对应的索引分片的举例:

database testdb;
CREATE TABLE frag_rb_tab(
sale_time datetime year to second,
product_id int,
product_time datetime year to second,
price float,
sale_amount int
)FRAGMENT BY ROUND ROBIN IN
datadbs1, datadbs2, datadbs3;
--建立两个索引,一个普通索引,1个唯一索引(需显示的指定索引位置)
CREATE INDEX idx1_frag_rb_tab ON frag_rb_tab(product_time,product_id);
CREATE unique INDEX idx2_frag_rb_tab ON frag_rb_tab(sale_time,product_id) in
datadbs3;
--轮转分片表的索引 idx1_frag_rb_tab 和 idx2_frag_rb_tab 的数据存放在哪里?下面可以通过
oncheck查看到索引的存储 dbspace。
!oncheck -ci testdb:frag_rb_tab
Validating indexes for testdb:root.frag_rb_tab...
Index idx1_frag_rb_tab
Index fragment partition datadbs1 in DBspace datadbs1
Index fragment partition datadbs2 in DBspace datadbs2
Index fragment partition datadbs3 in DBspace datadbs3
Index idx2_frag_rb_tab
Index fragment partition datadbs3 in DBspace datadbs3
可以看到索引 idx1_frag_rb_tab 是 attached 索引。默认情况下,GBase8s对轮转分片表所创建的非
唯一索引都采用 attached 索引;而唯一索引idx_frag_exp_tab2 为 detached 索引。
注意:轮转分片表建立唯一索引时,必须显示的指定索引位置(上述定义中指定的in datadbs3),也就是
说,对于轮转法则只能创建unique 的 detached索引,否则报“-872”错误。
drop INDEX idx2_frag_rb_tab;
CREATE unique INDEX idx2_frag_rb_tab ON frag_rb_tab(sale_time,product_id);
872: Invalid fragment strategy or expression for the unique index.
如果在轮转分片的表上建一个主键,GBase8s又是如何处理的?
alter table frag_rb_tab add constraint primary key(product_id) constraint
pk1_frag_rb_tab;
!oncheck -ci testdb:frag_rb_tab
Validating indexes for testdb:root.frag_rb_tab...
Index idx1_frag_rb_tab
Index fragment partition datadbs1 in DBspace datadbs1Index fragment partition datadbs2 in DBspace datadbs2
Index fragment partition datadbs3 in DBspace datadbs3
Index 104_2
Index fragment partition rootdbs in DBspace rootdbs
可以看到,默认情况下,GBase8s对分片表上创建的主键自动创建一个 dettached 索引;
基于表达式分片(Expression-based)
  • 基于表达式分片方法是我们在实际数据库设计中使用最多的方式。
  • 需要用户对数据分布有一定的了解,按照用户定义的表达式把数据分布存储在预定的表空间上,数据的分布方式完全由用户自行根据数据的特征和访问情况进行指定。
  • 基于表达式的分片方式可以基于一列或者多列构建表达式。
  • 为分片忽略和性能提升提供可能。
  • 既可以用于表也可以用于索引。
  • 基于表达式的分片方案,可以包含关系操作符,如 >,<,>=,<=,=还可以包含一些逻辑操作符,比如AND等。

下面是基于表达式分片表的举例:

database testdb;
CREATE TABLE frag_exp_tab(
sale_time datetime year to second,
product_id int,
product_time datetime year to second,
price float,
sale_amount int
) FRAGMENT BY EXPRESSION
sale_time < '2012-01-01 00:00:00' AND sale_time>= '2011-01-01 00:00:00'
IN datadbs1,
sale_time < '2011-01-01 00:00:00' AND sale_time>= '2010-01-01 00:00:00' IN
datadbs2,
sale_time < '2010-01-01 00:00:00' AND sale_time>= '2009-01-01 00:00:00' IN
datadbs3;
CREATE UNIQUE INDEX idx1_frag_exp_tab ON frag_exp_tab(sale_time,product_id);
CREATE INDEX idx2_frag_exp_tab ON frag_exp_tab(product_time,product_id);
--表达式分片表的索引 idx1_frag_exp_tab 和 idx2_frag_exp_tab 的数据存放在哪里?下面可以通
过 oncheck查看到索引的存储 dbspace。
!oncheck -ci testdb:frag_exp_tab
Validating indexes for testdb:root.frag_exp_tab...
Index idx1_frag_exp_tab
Index fragment partition datadbs1 in DBspace datadbs1
Index fragment partition datadbs2 in DBspace datadbs2
Index fragment partition datadbs3 in DBspace datadbs3
Index idx2_frag_exp_tab
Index fragment partition datadbs1 in DBspace datadbs1
Index fragment partition datadbs2 in DBspace datadbs2
Index fragment partition datadbs3 in DBspace datadbs3
可以看到索引 idx1_frag_exp_tab 和idx2_frag_exp_tab默认采用的都是attached 索引。
需要注意的是,分片表上(除轮转分片表),如果要创建一个 unique 的attached 索引,则必须在索引中
包含分片的 key 字段,否则报“-872”错误。
drop INDEX idx1_frag_exp_tab;
CREATE UNIQUE INDEX idx1_frag_exp_tab ON frag_exp_tab(product_id);
872: Invalid fragment strategy or expression for the unique index.

分片表达式的顺序是非常重要的,行数据是根据第一个匹配的表达式来分配存储位置的。可以使用remainder 分片来存放不能满足任何一个分片表达式的数据,如果一个 remainder 分片已存在,就不能添加它。如果存在remainder 分片时,再添加一个新的分片,数据库服务器将检索并重新计算remainder 分片中的所有记录;一些记录将可能移动到新的分片中。remainder fragment 必须放在分片表达式的最后部分,否则语法不满足。 871: Remainder fragment must be specified last,下面是使用举例:

CREATE TABLE tab1 (col1 int, col2 varchar(10))
FRAGMENT BY EXPRESSION
col1 >= 0 and col1 < 100 in datadbs1,
col1 >= 100 and col1 < 200 in datadbs2,
remainder in datadbs3;
insert into tab1 values(50,'a');
insert into tab1 values(60,'b');
insert into tab1 values(101,'c');
insert into tab1 values(301,'d');

另外,也可以自己定义分区名,用例如下:

CREATE TABLE tab2 (col1 int, col2 varchar(10))
FRAGMENT BY EXPRESSION
partition p1(col1 >= 0 and col1 < 100) in datadbs1,
partition p2(col1 >= 100 and col1 < 200) in datadbs2,
partition p3 remainder in datadbs3;

表达式分片的原则总结:

  • 基于表达式的分片的数据是根据第一个匹配的表达式来分配存储位置的,可以在表达式的最后使用REMAINDER分片,用于保存不满足之前任何表达式的记录。
  • 尽量创建连续的没有交叉区域的分片,帮助分片忽略的使用。
  • 分片表达式必须尽可能简单,因为执行较复杂的表达式将加重CPU的负荷。
  • 把不同分片的数据尽量分配到不同的设备上,如果大量的查询只对表数据中很小的区域进行访问, 建议用表达式将那些被频繁访问的数据分片到多个设备上,尽管这样的分片也许数据量并不均衡。
  • 注意表达式的顺序,同一个表达式中限制性最强的部分放在前面。选择性高的放在前面,选择性低的放在后面,常用的放在前面。
  • 对表达式进行修正是为了减少表达式的计算量,减少每次访问的数据量,从而最终减轻CPU的负 荷。建议查看执行计划并根据需要进行调整。
  • 下面是GBase8s提供的基于表达式的其他分片方式的一些使用示例。
MOD in expression
--MOD为求余函数,以下示例把 id_num 除以3,余数为1、2和整除的数据进行存储在不同数据空间。
database testdb;
CREATE TABLE employee (
id_num integer,
name char(50),
salary integer)
FRAGMENT BY EXPRESSION
MOD(id_num, 3) = 0 IN datadbs1,
MOD(id_num, 3) = 1 IN datadbs2,
MOD(id_num, 3) = 2 IN datadbs3;
CREATE INDEX idx_empoyee ON employee(id_num);
--查看其执行计划,我们看出 Mod 运算表达式可以正确地做到分片效果。
set explain on;
select * from employee where id_num=2;
--如下为执行计划的输出片段
select * from employee where id_num=2
Estimated Cost: 1
Estimated # of Rows Returned: 1
1) root.employee: INDEX PATH
(1) Index Name: root.idx_empoyee
Index Keys: id_num (Serial, fragments: 2)
Fragments Scanned: (2) datadbs3c
Lower Index Filter: root.employee.id_num  = 2
List

List 方式实际上是对 or 和 in 运算的改进,可以认为是基于表达式的特例。在表达式计算上更具有灵活性和效率。示例如下:

create table customer(cust_id integer,name varchar(128),street varchar(255),state
char(2),zipcode char(5),phone char(15))fragment by list(state)
PARTITION p0 values('RS','IL') in datadbs1,
PARTITION p1 values('CA','OR') in datadbs2,
PARTITION p2 values(NULL) in datadbs3;
Interval 固定间隔

我们在采用分片表的过程中,经常会遇到按时间范围分片,但是时间总是变化的情况,如某表的分片设计按时间分片,每 10 天分一个分片存储,随着时间的变化,分片的表达式需要不断地修改(attach 操作新增分片),为了更好地支持固定间隔变化的情况,可采用基于 Interval 的分片策略,分片将根据Insert 记录的情况自动扩展分片,从而提供更为灵活的方式。示例如下:

--创建一个基于Interval的分片,每10天一个分片,所有数据分布到datadbs1—datadbs3的 3 个dbspace 上。
create table sales(amount int, id int,data_time datetime year to second)
fragment by range(data_time) interval(10 units day)
store in ( datadbs1 ,datadbs2,datadbs3)
partition p_sales0 values < '2011-01-01 00:00:00' in datadbs4;
create unique index idx_sales on sales(data_time,id);
--Insert 测试数据记录的时间为 2011-01-01~2011-03-11,按照分片情况,记录将分散到3个dbspace 上,并自动扩展出 4个分片,循环使用 3个 dbspace。

insert into sales values(100,1,'2011-01-01 00:00:00');
insert into sales values(100,1,'2011-01-02 00:00:00');
insert into sales values(102,2,'2011-01-11 00:00:00');
insert into sales values(100,1,'2011-01-11 01:00:00');
insert into sales values(100,2,'2011-01-21 01:00:00');
insert into sales values(100,1,'2011-01-21 02:00:00');
insert into sales values(100,2,'2011-02-01 01:00:00');
insert into sales values(100,1,'2011-02-01 02:00:00');
insert into sales values(100,2,'2011-02-11 01:00:00');
insert into sales values(100,1,'2011-02-11 02:00:00');
insert into sales values(100,2,'2011-02-21 01:00:00');
insert into sales values(100,1,'2011-02-21 02:00:00');
insert into sales values(100,2,'2011-03-01 01:00:00');
insert into sales values(100,1,'2011-03-01 02:00:00');
insert into sales values(100,2,'2011-03-11 01:00:00');
insert into sales values(100,1,'2011-03-11 02:00:00');
select count(*)from sales;
--通过 oncheck 检查分片的自动扩展情况,将自动为表及对应的索引的分片进行扩展。
!oncheck -pt testdb:sales |grep DB
Table fragment partition p_sales0 in DBspace datadbs4
Table fragment partition sys_p1 in DBspace datadbs2
Table fragment partition sys_p2 in DBspace datadbs3
Table fragment partition sys_p3 in DBspace datadbs1
Table fragment partition sys_p4 in DBspace datadbs2
Table fragment partition sys_p5 in DBspace datadbs3
Table fragment partition sys_p6 in DBspace datadbs1
Table fragment partition sys_p7 in DBspace datadbs2
Index idx_sales fragment partition p_sales0 in DBspace datadbs4
Index idx_sales fragment partition sys_p1 in DBspace datadbs2
Index idx_sales fragment partition sys_p2 in DBspace datadbs3
Index idx_sales fragment partition sys_p3 in DBspace datadbs1
Index idx_sales fragment partition sys_p4 in DBspace datadbs2
Index idx_sales fragment partition sys_p5 in DBspace datadbs3
Index idx_sales fragment partition sys_p6 in DBspace datadbs1
Index idx_sales fragment partition sys_p7 in DBspace datadbs2
--也可以通过表所在的数据库中的系统表:sysfragments 查询分片的具体信息:
select t.tabname,f.dbspace,f.partition,f.exprtext
from systables t, sysfragments f
where t.tabid=f.tabid and f.fragtype='T'
and t.tabname='sales';

4.2 gbase模式的索引分片

从上面的基于轮转法分片和基于表达式分片的列子看到,当索引没有FRAGMENT BY子句时,索引继承表的分片(Attach or detach)。

  • 默认情况下,GBase8s对分片表(轮转法、基于表达式法)所创建的索引都采用attached 索引;
  • 默认情况下,对分片表上创建的主键自动创建一个unique 的 dettached 索引;外键、唯一约束默认情况下也是自动创建一个unique 的 dettached 索引,这里就不再举例。

索引也可以自己指定FRAGMENT BY子句,创建attach 或者 detach索引。

attach索引举例

attached即数据与索引存放在相同的dbspace上或者采用相同的分片策略

举例1:索引分片处理(存放在相同的dbspace上,但是不同于表的分片策略)

CREATE TABLE tab1 (col1 int, col2 date)
FRAGMENT BY round robin in  datadbs1, datadbs2;
CREATE INDEX idx1 on tab1 (col1)
FRAGMENT BY EXPRESSION
col1 >= 0 and col1 < 100 in  datadbs1,
col1 >= 100 and col1 < 200 in  datadbs2;

举例2:索引分片处理(与表的分片策略相同,但是存储位置不同)

CREATE TABLE tab2 (col1 int, col2 date)
       FRAGMENT BY EXPRESSION
        col1 >= 0 and col1 < 100 in  datadbs1,
        col1 >= 100 and col1 < 200 in  datadbs2;
CREATE INDEX idx2 on tab2 (col1)
FRAGMENT BY EXPRESSION
col1 >= 0 and col1 < 100 in  datadbs3,
col1 >= 100 and col1 < 200 in  datadbs4;

举例3:索引分片处理(自己指定FRAGMENT BY子句,创建 unique 的attached 索引)

能在基于表达式的分片表上创建一个包含分片 key 字段的unique 的 attached 索引(轮转法只能创建一个 unique 的 detached索引)

CREATE TABLE tab3 (col1 int, col2 int)
       FRAGMENT BY EXPRESSION
        col1 >= 0 and col1 < 100 in  datadbs1,
        col1 >= 100 and col1 < 200 in  datadbs2;
CREATE unique INDEX idx3 on tab3 (col1)
FRAGMENT BY EXPRESSION
col1 >= 0 and col1 < 100 in  datadbs3,
col1 >= 100 and col1 < 200 in  datadbs4;

注意:在分片表上,如果要创建一个 unique 的attached 索引,则需要在索引中包含分片的 key 字段否则报“-872”错误

drop INDEX idx3;
CREATE unique INDEX idx3 on tab3 (col2)
FRAGMENT BY EXPRESSION
col1 >= 0 and col1 < 100 in  datadbs3,
col1 >= 100 and col1 < 200 in  datadbs4;
 872: Invalid fragment strategy or expression for the unique index.
dettach索引举例

detached索引是一种具有独立碎片化策略的索引,创建detached索引时,可以使用CREATE INDEX语句,并使用FRAGMENT BY EXPRESSION子句来定义索引的碎片化策略。

举例1: 创建一个detached索引,使用不同的表达式和存储空间

CREATE TABLE tab4 (col1 integer, col2 date)
       FRAGMENT BY EXPRESSION
        col1 >= 0 and col1 < 100 in  datadbs1,
        col1 >= 100 and col1 < 200 in  datadbs2;
-- 创建一个detached索引,使用不同的表达式和存储空间
CREATE INDEX idx4 on tab4 (col1)
FRAGMENT BY EXPRESSION
col1 >= 0 and col1 < 200 in  datadbs3,
col1 >= 200 in  datadbs4;
!oncheck -pt testdb:tab4 |grep DB
                  Table fragment partition datadbs1 in DBspace datadbs1
                  Table fragment partition datadbs2 in DBspace datadbs2
                  Index idx4 fragment partition datadbs3 in DBspace datadbs3
                  Index idx4 fragment partition datadbs4 in DBspace datadbs4

举例2:创建一个 unique 的 detached 索引

CREATE TABLE tab5 (col1 integer, col2 date)
       FRAGMENT BY EXPRESSION
        col1 >= 0 and col1 < 100 in  datadbs1,
        col1 >= 100 and col1 < 200 in  datadbs2;
-- 创建一个detached索引,使用不同的表达式和存储空间
CREATE unique INDEX idx5 on tab5 (col1)
FRAGMENT BY EXPRESSION
col1 >= 0 and col1 < 200 in  datadbs3,
col1 >= 200 in  datadbs4;
!oncheck -pt testdb:tab4 |grep DB
                  Table fragment partition datadbs1 in DBspace datadbs1
                  Table fragment partition datadbs2 in DBspace datadbs2
                  Index idx4 fragment partition datadbs3 in DBspace datadbs3
                  Index idx4 fragment partition datadbs4 in DBspace datadbs4

通过本文的深入解析,我们不仅揭开了GBase 8s分片技术的神秘面纱,更为数据库管理员和开发者提供了实用的策略和技巧。掌握GBase 8s的分片技术,将有助于您在数据库性能优化的道路上更进一步。下篇我们来说说GBase8s分片表管理,敬请期待。

原文链接:https://www.gbase.cn/community/post/4128
更多精彩内容尽在南大通用GBase技术社区,南大通用致力于成为用户最信赖的数据库产品供应商。

  • 17
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值