对分库分表的一些想法
经历过几家公司从小到大的成长,数据量也会跟着业务量和访问量剧增。最初的系统架构完全无法支持大数据的到来,期间做过多次架构升级,包括数据库主从读写分离,系统soa化等等。那么就针对系统最重要的一块 数据来说吧。
说到数据大家都会想到数据存储和读取,还会联想到关系型数据库和非关系型数据库,当然随着互联网的发展,非关系性数据库越来越火,但是不能说明非关系型数据库完全能取代关系型数据库,至少目前不行。那么,关系型数据库的性能,是让人头疼的一个问题。目前最主流的方案是分库分表。
先说分表吧,可以分为纵向拆分和横向拆分,纵向拆分就是根据时间或者业务分表,或者拆分表结构,这些都需要改变表结构。但是数据量暴增,纵向分表最终还是无法解决问题,最终还是要考虑到横向拆分。
横向拆分也可以说是水平拆分,就是按照一定规则进行分表,不改变表结构。那么水平拆分的依据也是需要斟酌的。要保证数据能基本平均分配到不同的分表中,那么分表的依据就是重复性不能太高。那么首先考虑的就是主键。根据主键按照一定的策略进行分表。我想到的的有按区间分表,取模分表。
先说按区间分表,按区间分表有局限性,就是主键一定要保证是一个有序的数字,而且是不执行或很少执行delete的。但是好处是能保证表的数据量,也好维护。分表后的数据如下表:
取模分表,很简单就是对某个数值取余,然后分配到不同的表里。比如对4取余。那么数据分布如下:
取模分表没有对数据有苛刻要求,但是需要提前确定好取模因子(被取余数 也可以看做分表的数量)
看似取模分表比较合适,但是如果分表后数据量增长,当前分表已经无法支撑的时候怎么办呢,增加表,再取模? 那么同步数据将会是很头疼的事情。因为每张表都要再从新分配数据。那么我们能不能借鉴一致性hash来进行分库分表呢?
一致性hash也可以看做是按区间分表,在0-2^32之间创建几个节点,节点可以看做是表,同时增加虚拟节点(对0-2^32分成多个区间段,然后多个区间段分别指定到几个表中)来保证各表的数据基本均衡,如果出现数据分配不均衡,就增加节点来分流数据命中大的节点。这样增加表的时候只同步数据量最大的那张表即可。但是0-2^32是一个很大的范围,怎么分区保证数据平均将是很复杂的事情。如果分段比较粗粒度那么不能保证数据的均衡,细粒度的话则需要维护一个范围段的数据,增加运算和维护成本。粒度越细运算和维护成本越高。那么有没有更好的方案呢?
能不能用二叉树的结构来进行分表呢?统一对2取模,left节点库存放可整除的数据,right存放不可被2整除的数据。如果某个节点压力较大则对该节点继续二叉,同时对分库指标加固定前缀或后缀,再hash对2取模。这样的话就可以避免添加表的时候全部数据要从新分配,也节省了维护成本(只维护一个二叉树即可)。
比如:分表字段为一个uuid,值为b9a6fd18-8734-45c4-ad81-57a98ada8304,hashcode = 2039422118(可以被2整除), 那么该数据存放在left节点, 如果left节点不是最终节点(再分表),则uuid+后缀 如:b9a6fd18-8734-45c4-ad81-57a98ada8304_EXT , 则该值的hashcode为 -1376741656(可以被2整除),则该数据存放再二级二叉树的left节点。
节点内容存放表名称,如果该节点有子节点,则按照规则加前缀或后缀,再hash,按照取模原则找下一节点,直到节点没有子节点的时候,获取表名称。
先分析添加表,比如某个节点表压力较大需要分表,则分流这个节点即可,最糟糕的情况是多个节点同时分表,那么逐个分表即可,各个节点互不影响。这样比取模分表扩容的时候要方便的多。
从维护的角度看,根据二叉树的原理分表,可以避免数据迁移的麻烦,同时系统只要维护一个二叉树即可,也节省了维护成本。
以上只是个人的一些想法,难免有一些不合理或者错误的地方,请大家指出批评并一块讨论改进。