引言
数据库构架设计中主要有 Shared Everthting、Shared Nothing 和 Shared Disk:
-
Shared Everthting:一般是针对单个主机,完全透明共享 CPU/MEMORY/IO,并行处理能力是最差的,例如 Oracle 的单机模式。
-
Shared Disk:各个处理单元使用自己的私有 CPU和 Memory,共享磁盘系统。典型的代表 Oracle RAC, 它是数据共享,可通过增加节点来提高并行处理的能力,扩展能力较好。其类似于 SMP(对称多处理)模式,但是当存储器接口达到饱和的时候,增加节点并不能获得更高的性能 。
-
Shared Nothing:各个处理单元都有自己私有的 CPU / 内存 / 硬盘等,不存在共享资源,类似于 MPP(大规模并行处理)模式,各处理单元之间通过协议通信,并行处理和扩展能力更好。 各节点相互独立,各自处理自己的数据,处理后的结果可能向上层汇总或在节点间流转。
我们常说的 Sharding 其实就是 Share Nothing 架构,它是把某个表从物理存储上被水平分割,并分配给多台服务器(或多个实例),每台服务器可以独立工作,具备共同的 schema,只需增加服务器数就可以增加处理能力和容量。
Oracle Sharding 简介
Oracle Sharding 是 Oracle 12.2 版本推出的新功能,也称为数据分片,适用于 online transaction processing (OLTP)。Oracle Sharding 基于表分区技术,是一种在数据层将数据水平分区存储到不同的数据库的技术。Sharding 可以实现将一个分区表的不同分区存储在不同的数据库中,每个数据库位于不同的服务器,每一个数据库都称为shard,这些 shard 组成一个逻辑数据库,称为 sharded database (SDB)。这个 table 也称为 sharded table,每个 shard 数据库中保存该表的不同数据集(按照 sharding key 分区),但是他们有相同的列 (columns)。
Shard 是一种 shared-nothing 技术,每个 shard 数据库使用独立的服务器硬件 (CPU、内存等)。Shard 可以运行在单机数据库或者 DATAGUARD / ADG 数据库。
Sharding 其实需要解决三个问题:
-
数据的路由
数据路由是数据库告诉应用程序,你让我查的数据目前在哪个分片上,这条路怎么走过去。
-
数据的分片
数据分片就是实际数据的存放地点,往往每个分片就是一台单独的服务器(含存储)。
-
分片的元数据信息保存
由于分片的数据实际是被切割放在不同的机器上,那么需要有个集中的地点存放数据分片的信息,即分片元数据的信息。
Oracle Sharding 组成
Oracle Sharding 主要包括下面组件:
-
Sharded database (SDB):逻辑上 SDB 是一个数据库,但是物理上SDB包括多个物理独立的数据库,SDB 类似一个数据库池 (pool),数据库池 (pool) 中包括多个数据库 (Shard)。目前版本最大支持1000个 shard。
-
Shards:SDB 包括多个物理独立的数据库,每一个数据库都称为shard,每个 shard 数据库位于不同的服务器,他们不共享 CPU、内存、存储等资源。每个shard 数据库中保存表的不同数据集, 但是每个 shard 中都有相同的列(columns)。Shard 数据库可以是 Dataguard / ADG,提供高可用性,Shard 数据库(单机或者 ADG)可以通过 GSM deploy 来自动创建,也可以将一个已经通过 dbca 创建好的数据库 add 到 SDB。
-
Shard catalog:是一个 Oracle 数据库,用于集中存储管理 SDB 配置信息,是 SDB 的核心。SDB 配置变化,比如添加/删除 shard,Globalservice 等等,都记录在 Shard catalog。如果应用查询多个 shard 中的数据,那么由 Shard catalog统一协调分配。我们推荐将 Shard catalog 配置为 dataguard 环境,这样可以提供 HA 高可用。如果 Shard catalog 无法访问,那么只会影响一些维护操作和跨shard访问,而不会影响单独的 shard 操作(通过 sharding key 的查询 /DML)。
-
Shard directors:Global Data Service (GDS) 实现对 Sharding 的集中部署和管理。GSM 是 GDS 的核心组件。GSM 作为 Shard director。GSM 类似于监听器,将客户端对 SDB 的请求路由到对应的 shard,负载均衡客户端的访问。
-
Shardgroup:在逻辑上,将一组相同复制属性的 shard 称作 shard group。如有8台主机,其中4台是 shard node 的 primary,另外4台是 shard node 的 standby。那么,我们可以把4台 primary 定义成一个shardgroup,叫 primary_shardgroup,另外4台 standby 定义成另外一个shardgroup,叫 standby_shardgroup。
注:一个 shardgroup 通常是在一个 datacenter 内。
Dataguard 可以级联,那么我们可以把 primary 做为一个 shardgroup1,第一级的 dataguard 叫 shardgroup2,第二级的 dataguard 叫 shardgroup2。如下:
-
-
shardspace shardspace 的概念伴随着综合性分片的分片方法(compositesharding method)出现的;如果是系统管理分片方法(system-managed shardingmethod),只有一个默认的 shardspace。所以只有在 compositesharding 下,才有可能出现多个 shardspace。
在 composite sharding 下,数据首先根据 list 或者 range,分成若干个shardspace。然后再根据一致性 hash 进行分片。
注:每个 shardspace 包含一个或者多个复制 shard for HA/DR。
我们可以按照服务级别来划分 shardspace,如硬件好的,作为 shardspace_gold,硬件差一些的,划做 shardspace_silver:
Oracle Sharding 方法
Oracle Sharding 支持3种方法 shard / 分片方法:
-
System-Managed Sharding:这种方法用户不用指定数据存放在哪个 shard 中。Sharding 通过一致性哈希 (CONSISTENT HASH) 方法将数据分区 (partitioning),并自动分布在不同的 Shard。System-managed sharding 只能有一个shardspace。
-
Composite Sharding:这种方法用户创建多个 shardspaces ,每个 shardspaces 中存放一定范围 (range) 或者列表 (list) 的数据。一般情况下,Shardspace 按照区域来划分,比如美国区域的 shard 属于 shardspace cust_america,欧洲的 shard 属于 shardspace cust_europe。
-
Subpartitions with Sharding:Sharding 基于表分区,因此子分区 (Subpartitions) 技术同样适用于 Sharding
Oracle Sharding 对象
被 Shard/分片的表我们成为 shardedtable,这些 sharded table 的集合称为表家族(TableFamily)。
所谓表家族(Table Family)就是指 sharded table 之间是父-子关系,一个表家族(Table Family)中没有任何父表的表叫做根表(root table),每个表家族中只能有一个根表。
在12.2,在一个 SDB 中只支持一个表家族。
在表家族(Table Family)中的所有 sharded table 都按照相同的 sharding key (主键)来分片,主要是由 root table 的 shardingkey 决定的。表家族(Table Family)中有相同 shardingkey 的数据存储在同一个 Chunk 中,这样方便以后的数据移动。
比如: 用户表 – 订单表 – 订单明细表 就是一个表家族,其中用户表是 root table,订单表和订单明细表分别是子表,他们都按照 sharding key (CustNo ) 分区。
可以通过如下两种方式建立 table family:
通过CONSTRAINT [FK_name] FOREIGN KEY(FK_column) REFERENCES [R_table_name]([R_table_column]) —这种关系可以有级联关系。
SQL> CREATE SHARDED TABLE Customers 2 ( 3 CustId VARCHAR2(60) NOT NULL, 4 FirstName VARCHAR2(60), 5 LastName VARCHAR2(60), 6 Class VARCHAR2(10), 7 Geo VARCHAR2(8), 8 CustProfile VARCHAR2(4000), 9 Passwd RAW(60), 10 CONSTRAINT pk_customers PRIMARY KEY (CustId), 11 CONSTRAINT json_customers CHECK (CustProfile IS JSON) 12 ) TABLESPACE SET TSP_SET_1 13 PARTITION BY CONSISTENT HASH (CustId) PARTITIONS AUTO; |
可以看到上面根表(root table)是 customer 表,主键是 CustId,partition 是根据 CONSISTENT HASH,对 CustId 进行分区;
下一级的表是 order 表,主键是 CustId+OrderId,外键是 CustId 且 references Customers 表,partition 是参考外键;
SQL> CREATE SHARDED TABLE Orders 2 ( 3 OrderId INTEGER NOT NULL, 4 CustId VARCHAR2(60) NOT NULL, 5 OrderDate TIMESTAMP NOT NULL, 6 SumTotal NUMBER(19,4), 7 Status CHAR(4), 8 constraint pk_orders primary key (CustId, OrderId), 9 constraint fk_orders_parent foreign key (CustId) 10 references Customers on delete cascade 11 ) partition by reference (fk_orders_parent); |
再下一级表是 LineItems 表,主键是 CustId+OrderId+ProductId,外键是 CustId+OrderId,即上一层表达主键,partition 是参考外键
SQL> CREATE SHARDED TABLE LineItems 2 ( 3 OrderId INTEGER NOT NULL, 4 CustId VARCHAR2(60) NOT NULL, 5 ProductId INTEGER NOT NULL, 6 Price NUMBER(19,4), 7 Qty NUMBER, 8 constraint pk_items primary key (CustId, OrderId, ProductId), 9 constraint fk_items_parent foreign key (CustId, OrderId) 10 references Orders on delete cascade 11 ) partition by reference (fk_items_parent); |
方法2:同关键字 PARENT 来显式的说明父子关系。这种关系只有父子一层关系,不能级联。
SQL> CREATE SHARDED TABLE Customers 2 ( CustNo NUMBER NOT NULL 3 , Name VARCHAR2(50) 4 , Address VARCHAR2(250) 5 , region VARCHAR2(20) 6 , class VARCHAR2(3) 7 , signup DATE 8 ) 9 PARTITION BY CONSISTENT HASH (CustNo) 10 TABLESPACE SET ts1 11 PARTITIONS AUTO 12 ; |
SQL> CREATE SHARDED TABLE Orders 2 ( OrderNo NUMBER 3 , CustNo NUMBER 4 , OrderDate DATE 5 ) 6 PARENT Customers 7 PARTITION BY CONSISTENT HASH (CustNo) 8 TABLESPACE SET ts1 9 PARTITIONS AUTO 10 ;
SQL> CREATE SHARDED TABLE LineItems 2 ( LineNo NUMBER 3 , OrderNo NUMBER 4 , CustNo NUMBER 5 , StockNo NUMBER 6 , Quantity NUMBER 7 ) 8 PARENT Customers 9 PARTITION BY CONSISTENT HASH (CustNo) 10 TABLESPACE SET ts1 11 PARTITIONS AUTO 12 ; |
-
注意上面的 order 表和 LineItems 表,都是属于同一个父表,即 customers 表。
-
另外,也注意上面的 CustNo 字段,在每个表中都是有的。而上面说的第一种的级联关系的 tablefamily,可以不在每个表中都存在 CustNo 字段。
上面创建在表,都是 sharded table,即表的各个分区,可以分布在不同的 shard node 上。各个 shard node 上的分区,是不同的。即整个表的内容,是被切割成片,分配在不同的机器上的。
而 duplicatedtable,是整个表达同样内容,在各个机器上是一样的。duplicate table 在各个 shard node上,是以 read only mv 的方式呈现:在 shardcat 中,存在 mast table;在各个shard中,存在 read only mv。duplicated table 的同步:以物化视图的方式同步。
chunk
chunk 的概念和 table family 密不可分。因为 family 之间的各个表都是有关系的,我们把某个 table family 的一组分区称作一个 chunk。
如 customers 表中的1号~100万号客户信息在一个分区中;在 order 表中,也有1号~100万号的客户的 order 信息,也在一个分区中;另外 LineItems 表中的1号~100万号客户的明细信息,也在一个分区中,我们希望这些相关的分区,都是在一个 shard node 中,避免 cross shard join。所以,我们把这些在同一个 table family 内,相关的分区叫做 chunk。在进行 re-sharding 的时候,是以 chunk 为单位进行移动。因此可以避免 cross shard join。
注:chunk 的数量在 CREATESHARDCATALOG 的指定,如果不指定,默认值是每个shard 120个chunk
chunk move
chunk move 的条件:
-
re-sharding 发生,即当 shard 的数量发生改变的时候,会发生 chunk move。
注:re-sharding 之后,chunk 的数虽然平均,但并不连续。如:原来是2个 shard,1~6号 chunk在 shard 1,7~12号 chunk 在 shard2。加多一个 shard 后,1~4号 chunk 在 shard 1,7~10号 chunk 在 shard 2,那么5~6,11~12号 chunk 在 shard 3 上。即:总是挪已经存在的 shard node 上的后面部分 chunk。
-
DBA 手工发起:move chunk-chunk 7 -source sh2 -target sh1。将 chunk 7 从 shard node sh2 上,挪到 shard node sh1 上。
chunk move 的过程:
在 chunk migration 的时候,chunk 大部分时间是 online 的,但是期间会有几秒钟的时间 chunk 中的 data 处于 read-only 状态。
chunk migration 的过程就是综合利用 rman 增量备份和 TTS 的过程:
level 0 备份源 chunk 相关的 TS,还原到新 shard->开始 FAN(等待几秒)->将源 chunk 相关的 TS 置于 read-only->level 1 备份还原->chunk up(更新 routing table 连新 shard)->chunk down(更新 routing table 断开源 shard)->结束 FAN(等待几秒)->删除原 shard 上的老 chunk
Oracle Sharding 路由选择(Routing)
--直接路由
应用程序初始化时,在应用层/中间件层建立连接池,连接池获取所有 shard 节点的sharding key 范围,并且保存在连接池中,形成 shard topologycache(拓扑缓存),Cache 提供了一个快速的方法直接将请求路由到具体的 shard。
客户端请求时指定 shard key,直接从连接池获取连接,这种情况下不经过 shard director / catalog 数据库,直接连接到对应的 shard。
--代理路由
如果客户端执行 select 或者DML 时不指定 shard key 或者执行聚合操作(比如groupby),那么请求发送到 Catalog 数据库,根据 matadata 信息,SQL 编译器决定访问哪些 shards。
为了实现 sharding,Oracle 在连接池和驱动方面都做了增强,提供了新的 API(UCP, JDBC, OCI 等等)在连接创建时来传递 sharding keys。
Sharding 技术的优点和缺点
-
增加了 SQL 的复杂性
因为开发人员必须要写更复杂的 SQL 来处理 sharding 的逻辑。
-
Sharding 本身带来的复杂性
sharding 软件需要照顾分区,数据平衡, 访问协调,数据完整性
-
单点故障
一个 shard 损坏可能导致整张表不可访问。失效接管服务器也更复杂。因为负责失效接管的服务器必须拥有任何可能损坏的 shard 上的数据。
-
备份也更复杂
多个 shard 可能都需要同时备份。
-
维护也更复杂
比如增加删除索引,增减删除字段,修改表定义等等,都变得更困难
-
商业 sharding 技术随着节点数的增加,Licensing 费用也会大规模增加
在 Oracle12.2 Sharding 中都无需担心,Oracle 作为一个商业数据库,已通过底层开发规避了这些问题。
单点故障问题可以通过 RAC+Sharding,ADG+Sharding 来规避,但成本也会增加。
Oracle Sharding 技术提供线性扩展和失败隔离的优点
-
线性扩展
因为每个 shard 是一个独立的数据库,通过增加新的 Shard 节点,来线性扩展性能。自动 rebalance 数据。
-
失败隔离
由于 Shard 是一种 shared-nothing 技术,每个 shard 使用独立的硬件,因此一个 shard 节点出现故障,只会影响到这个 shard 存放的数据,而不会影响到其他shard。
-
按照地理位置分布数据
可以选择根据地理位置不同,将数据存储在不同的 shard。
-
滚动升级
选择不同时间升级不同的 shard。比如同一时间只升级一个或一部分 shard,那么只有这些升级的 shard 中存储的数据受到影响,其他的 shard 不受到影响,可以继续提供服务。
-
云部署
Shard 非常适合部署在 cloud