谈谈表分区

表分区并不是 Rocket Science (火箭科学),没有那么多神秘的计算或者多么强大的功能。它能给数据系统带来的益处,如果将它比喻成古代藩王制度的话,也就那么 2 个:

  1. 分区自成一格:有自己的存储空间,地盘大小全靠你的数据量。管理也靠自己,有自己的独立索引。因为存储空间相比整张大表小了很多数据量级,故建立的索引更加有效,命中率更高。在表 有分区的设计下,要处理的一个问题是,假如本区的数据损坏,如何保证本区的完整性和安全性?就像藩王的兵部队在战争中,作战兵力急剧减少,面临“亡藩”的危险。该怎么办呢,我们之后再讲。

  2. 分区并行处理:一个即将检索大量数据的查询,如果能被有效的切分成几个细小的查询,那么查询的速度就会被显著提高,这是不言而喻的。其中的原因,就是利用多方计算资源分散压力。这在概念上很好理解,但是在实际操作过程中,我们还得多理解一点,就是硬盘驱动的多核化。假如我们的数据都存放在一块硬盘上,那就不谈分区并行处理的优越性了。再怎么分区,一块硬盘就那么几个盘面的磁头在工作,任务(查询数据)还是要排队。但如果我们把分区架构在不同的硬盘之上,那么任务(查询数据)就可以由不同的硬盘上的磁头完成,并行实实在在的提高了查询速度。

只是单台机器,提供的CPU计算能力有限,如何将查询分拆成不同的磁盘寻址,会消耗总 CPU 资源,造成压力,进而影响其他进程的工作效率,因此分布式查询应运而生,即由多机分摊计算。就像遇着六国抗秦一样,本质上都是在分摊压力和提高效率。假如都是由中央发配作战部队,地方早被收割,完成自我独立了。

在上面的讲述中,我们谈到了单台计算机和多台计算机集群的分区。每种架构的实现方法各异,但功能最终都是一样的,有效的实现分摊压力。

在商业数据库环境下,实现分区总有一定的套路。

SQL Server

创建一个分区表大致的方法如下:先建立一个分区函数(partition function), 在建立分区表的时候,以分区字段作为调用分区函数时用的分区参数。这里有必要谈下分区函数用到的分区字段,此字段只允许依照分区字段的离散值来分区。

再建立分区 scheme(partition scheme), 按照某一个字段(比如时间字段,按照月份作分区)建立 scheme, 比如从 2010年 1月份开始建立到 2020年12月份的分区 scheme。scheme的作用就是将分区函数指定的分区值区间对应到不同的文件组 file group (文件组)上。这里很重要的一点是 file group 的配置。

file group 的配置决定了分区的 2 个益处,是不是被有效利用起来了。假如只有一块硬盘,那么分再多的区,除了能够完成分区的第一个使命外,第二个必杀技就被浪费了。所以分区必须是基于多块硬盘或者 RAID 集群上的。其中的原因是基于容错冗余和并行查询。

以 RAID (Redundant Array of Independent Disks)来举例:

这里写图片描述

RAID 0 将不同的数据块分别存储于不同的磁盘上,提高读写速度,但不安全。一旦其中一块磁盘故障脱机,数据损失。

RAID 1 将数据复制出 2 个副本,确保了安全性。但读写速度就慢下来了。就像刚才战争中,军队需要编制一样,多编制战斗力一样的军队,有效保证作战效率。

RAID 5 数据块分布式存储,在其他磁盘上保存副本。读写效率与安全性双赢。

file group 必须分配在这种存储架构上,方能利用好分区的 2 个益处。当然如果你把分区都放在同一个file group 里面也不能尽显优势,更好的打散数据分布,就应该将分区分到不同的 file group 里面。具体的细节就不展开了,好多文章都会详细解释配置。

讲到这里,我们可以看一下,怎么将 1 个包含 2 亿条数据的表,直接 1 秒装载到另外一张表里头去了。一个未分区的表,本质上是一个大分区。这个大分区在物理磁盘上就是一个地址。我们通过将这个地址交给另外一位分区的大表,即完成了数据切换。如果表有分区,一样的原理,一样的操作。聚合完成的历史数据,装载到最终存储的归档表,通过切换分区即可。

Oracle

分区本质一样,都是对大数据的分而治之,分区的目的是不同的 tablespace. 当然放在一个 tablespace 也是没问题的。放于不同的 tablespace, 目的就是为了提高并行效率,也更加安全的保障了数据分区的冗余和容错,即使数据在一个分区丢了,其他分区的数据依然有效。

但 oracle 不愧是世界第一的商用数据库。它提供的分区方式,比 SQL Server (仅有 Range Paritioning ) 丰富多了:

Range Partitioning

Hash Partitioning

List Partitioning

Composite Partitioning

前面三个都好理解,Range Partitioning 就是范围分区, Hash Partitioning 哈希分区, List Partitioning 列表分区。最后一个才是个重量级的分区方法,Composite Partitioning, 组合分区。

Oracle 11g 之前,组合分区只有两种 Range-Hash Partitioning 和 Range-List Partitioning. Oracle 11g 之后,组合分区又多加了四种:

Range-Range

List-Range

List-Hash

List-List

除了这些恰到好处的好处之外,还加上了在线分区和分区压缩。在线分区的概念,就是在分区还有 DML 的操作情况下,继续分区;分区压缩就是对分区数据进行压缩,减少数据存储体积,从而增加查询的速度。

《Designing Data-Intensive Applications》 一书中也给出了分区的介绍。本书给出的参考书目有很多,所以分区实现的方法也讲解了大片的篇幅。通读下来,我主要想讲讲,书中带给我的思考。因为这些书中提到的这些问题,虽然没有给出确切的答案,但是引起我对分区架构问题的思索,是很重要的。

上面主讲的是商业数据库的分区,而在这本书中,除了对商业数据库做了分区知识的总结 ,主要是专门讲解分布式的分区架构。总的来说,分布式分区需要解决的问题有这些:

  1. 不要让数据聚集在一台计算机上运行,要均衡分布。比如按照时间分区,按月来分区,那么一个月占一个区,假如查询是查最新的一个月,性能就没什么提高,如果是查询最近6个月,那么一个分一个月是有效分摊服务器资源的。所以看具体应用场景。假如有 10 台 node, 结果查询还是落在了其中一台计算机上,那么这种情况称为 skewed, 即数据倾斜,这台计算机被称为 Hot Spot. 第一大问题就是不要认为分区是提高性能的万能钥匙。分区策略是依据我们查询分析的判断条件,而随时可能变化的。

  2. 分区的路由在哪里计算以及如何计算:Java的 Object.hashCode(), Ruby的 Object#hash 等函数都是可以将key转换成 Hash 的。但是在不同的进程中,这些函数将同一个Key转换出来的 hash 值是可能不一样的。因此不能用来做 分布式存储的 hash 函数。所以应该采用更普适性的hash函数,比如 MD5, FowlerNoll-Vo函数。hash 分区,存储的不是 key 值,而是 给定的hash 值。这倒是很值得思考的。为什么不是key值,而是要计算出来的hash值。

  3. 分区组合:如果能将 Hash 分区, Range 分区结合起来用,是再好不过了。Cassandra 就是如此的。试想在社交媒体的后台设计中,如果用user id , updated timestamp 结合起来, user id 用来做hasn 分区的标准, updated timestamp 用来做分区标准,那么 一个用户更新的数据,通过先定位partition, 然后再根据 updated timestamp 分区扫描,就比任何一种单纯的分区策略好很多。

  4. 分布式分区的索引可以有分区:

    4.1 local index: 可以在数据各分区下面建立索引。索引所包含的数据指针都是指向本分区的

    4.2 global index: 可以建立一个完整的索引,包含对每个分区数据的指针,但是按照term分配到不同的分区上。当然global index 的优点很明显,就是可以免去 scatter/gather 的操作,增加了分区命中率,但是也有缺点,多字段的文档,在新建或者修改,删除的时候,需要同时往很多global index 分区中更新索引片段

  5. 分区重分配:Rebalancing Partitions:

    5.1. 如果新的节点增加了,原先的平衡被打破,那么怎么建立新的分区平衡

    5.2. 如果有新的分区要增加,怎么安排新分区在集群中的分布?

    5.3. 如果所有分区的数据量,远远大于分区节点的数量,怎么安排

    5.4. 如果有节点退出了,原先的分区平衡被打破,怎么建立新的分区平衡?

  6. 分区的复制:Partition Replication

假如每个分区需要 3 个副本,总共有 4 个分区,那复制集群架构所需要的机器数量是, 4 台。 一台上的分区, 将 其他 3 个分区的 2 个充当其副本。

这里写图片描述

7 分区路由:保证前端传入的数据,被正确发送到了应该去的那个分区。

这在业界有个术语, service discovery。涉及到任何的HA(High Availability)都有这问题。有开源的 service discovery 可以参考, jasonwilder.com 上有篇文章, open-source service discovery.

针对 service discovery,我们可以有几种解决方法:

7.1 随机访问集群中的任意一台。如果碰巧这台有我们需要的数据,那么直接给出答案。如果没有,这台节点将请求转发给其他有我们需要数据的节点,让这台节点处理。那么问题是,他们之间是怎么配置通信,以达到相互识别的目的。

7.2 通过访问一层路由层 ,由路由层帮转发请求。这层有点类似于 hadoop 的 NameNode, 知晓每个节点的数据分布。通过各个子节点汇报数据元结构给namenode,namenode掌握了所有的节点元数据。问题是 zookeeper 与 Hadoop 自带的 Name Node 之间是如何协同?

7.3 由client客户端直接记录每台节点数据,直接发送对应的请求到相应处理节点上。这里就是要hard code 每台节点的元数据。

这里写图片描述


欢迎关注【有关SQL】,入群讨论技术

这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dbLenis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值