浅谈数据分区、分布式集群
01
容量瓶颈驱动数据分区
数据库的使用过程中,经常会遇到各种各样的瓶颈。例如CPU、内存、网络带宽、磁盘等等,今天我们主要看磁盘容量这个方面。
传统的数据库适合的场景比较有限,以redis为例,通常单个实例控制在5G以内比较合适。如果容量太大的话,例如40G容量,在执行bgsave或者bgrewriteaof的时候,fork操作会产生阻塞1s以上的时间,这对于某些低延时的应用程序来讲是无法忍受的。
在这种情况下,我们就需要对Redis实例的数据进行分区。
02
数据分区方法
通常来讲,数据分区分为两种类型的方案:
其一:业务侧数据分区。
顾名思义,就是业务侧对数据进行拆分,例如将一个30GB的Redis实例,拆分成3个10GB的Redis实例,然后不同的业务场景,使用不同的Redis实例。
它的优点:分区逻辑业务可控
它的缺点:需要业务自己处理数据路由,故障转移等问题。
其二:数据库代理方案
这个方案中,客户端不做任何修改,而数据库层面利用集群架构来满足业务需求。
它的优点:简化客户端分布式逻辑
它的缺点:加重了服务部署的复杂度和性能损耗(由于有proxy的存在)
03
数据分区理论
一个完整的分布式数据库架构,必须包含数据分区方法、集群搭建方法、节点通信方式、集群动态扩缩容、请求路由配置、故障转移方法等模块。
这其中,最重要的,要属数据分区方法,而传统的数据分区方法包含顺序分区和哈希分区。
顺序分区比较容易理解,它就是计算出来数据值的范围,然后根据实例个数,顺序的将数据落在每个实例上;
哈希分区利用一种算法,将数据值进行计算,然后取模,让数据落在某一个范围的hash区间内,最终实现数据分区。
顺序分区计算方法简单,容易操作,支持范围查询;但是从离散性上来讲,不如hash分区,如果写入的数据连续,容易出现热数据片段;
hash分区显然离散程度更好,写入更加均匀;但是在进行查询的时候,不支持范围查询,而且计算相对复杂,还需要处理hash碰撞问题。
一般需要根据具体的业务场景来确定,例如要支持范围查询,则使用顺序分区,例如要保证写入离散,数据分布更均匀,则建议使用hash分区。
04
常见hash分区规则
顺序分区相对比较简单,这里不做分析。在hash分区中,不同的hash规则,关注点也不同。常见的hash规则如下:
1、节点取余分区
要想让数据落在[1,N]这个区间,需要使用下面的公式:
hash(key)%N
取余即可,其中hash是采用的hash函数。
这种方案最简单,还可以提前预分区,但是它的一个缺点是扩容或者缩容的时候(N需要改变),节点需要重新进行hash运算,涉及了数据迁移。
2、一致性hash分区
它的设计理念是为每一个节点分配一个ID,ID的区间比较大。
这些ID组成一个环形区域,数据读写操作的时候,先根据key计算hash值,然后在这个环中找到第一个大于或者等于这个hash值的ID值,将数据写入到这个ID的节点中。
如下图,有4个ID,分别是1、2、3、4
每个key计算完hash值之后,根据hash函数的最终结果,确定落在哪一个节点上。
这种方式的优点是:加入或者删除节点,只影响哈希环中的相邻节点,不影响其他节点。
缺点:节点数量较少的时候,容易出现某个节点宕机,导致大部分数据失效;另外,加减节点可能造成部分数据无法命中。
具体实现可以参考MC的一致性hash环。
3、虚拟槽分区
提前固定一个区间,例如[0~1023], 这个区间的每一个数,都称之为一个槽。槽的范围一般远远大于节点个数。
将所有的key值都通过某个hash函数映射到槽中,槽是集群内数据管理和迁移的基本单位,然后每个数据节点负责一定的槽数量。
这种大范围的槽分布,可以方便数据拆分和集群扩展。具体实现,可以参考Redis Cluster或者Codis