引言
上期我们聊到redis的AKF拆分原则关于沿着X轴扩展的一些问题
X轴扩展只是解决了单点故障问题,未解决容量有限问题,本期我们就来探讨一下使用AKF时沿着Y轴、Z轴扩展时遇到的问题。Y轴进行功能点划分数据就比如你们的系统有用户基本信息,邮件、短信等通知信息,商品信息,订单信息,库存信息等等,将这些不同功能点的数据放到不同的redis服务器中;
Z轴进行业务数据再分区在拆分,解决访问压力的问题。
一、Y轴Z轴扩展问题
有个前置知识需要和大家同步一下,我们知道redis是使用内存作为存储空间的,一台服务器的内存空间是有限的,不可能全部分配给redis做存储使用,redis还有持久化的进程和数据需要内存空间和物理空间,再加上服务器上还有其他进程需要处理,那么一台服务器不可能将所有内存都分配给redis使用,所以一般情况下是给redis分配几个G的内存空间使用,这样做使得redis在做持久化等服务进程的时候很轻盈,很快速的就完成了,假如你有一台几百G的服务器给redis实例分配了70、80G内存空间,redis在做持久化的时候很吃力,管理那么大的存储空间就会很慢,不切合redis作者的初衷,redis就是要快。基于轻盈的redis特点,当有大数据量来临的时候怎么存放,就是本期要讨论的重点,一台redis实例肯定是存储不了了,那么就需要把数据进行划分,也就是按照Y轴和Z轴进行划分,将数据分配到不同的redis实例中且每台redis实例中的数据是不一样的,怎么分配就是Y轴和Z轴带来的问题。
二、客户端实现划分逻辑,数据可按业务划分
当数据量不够大,可以进行业务功能进行划分时,我们可以采用一定的逻辑,将不同功能的业务数据存放到不同的redis实例中。
三、客户端实现划分逻辑,数据量特别大已经不能在拆分
前提是已经按照功能点进行业务拆分了,现在某一项数据比如商品数据非常大,拆了后空间都不够,那就只能在拆,就不能用直接逻辑的方法去归类拆分了,这个时代就进入了shrding分片时代,数据根据对应规则被划分到不同的机器上,有三种拆分逻辑,我们一起来看一下。
1、通过取模算法MODULA将数据分治
由我们客户端自己实现算法,将数据划分到不同的redis实例上,算法可以是hash+取模。
这种实现方式也有自己的弊端,hash取模算法的模数值是固定的,一开始就要确定到底是模3、4还是5,还是10,会影响分布式下的扩展性,比如公司业务升级了,数据量也升级了,新加了机器,数据按照之前的模数去取就可能取不到新的数据。
2、通过随机算法RANDOM将数据分治
说到随机,不管后面有几台redis实例,客户端直接随机到后面某台实例,这样一来可能客户端自己都不知道数据去了哪一台redis实例,感觉这种实现就没有实用价值了,其实不然,这种随机也是有使用场景的。比如我们存放一个list数据类型的key,并且key的开头是按照我们的规则设定(比如ooxx),让它随机到2台实例上,这样就将数据分片了并且降低了一台实例的压力,我们客户端使用lpush ooxx...放进去,数据是散列在2台redis,但是我们在客户端连接读取redis使用rpop ooxx..就能够将整个list数据类型取出,而这样一个场景就像是消息队列,ooxx就像是一个topic,这2台redis就像是partition,这种架构的成熟中间件就是kafka。
3、通过一致性哈希算法KETAMA将数据分治
前面我们说过了hash取模算法有弊端不利于扩展,取模数固定,一致性hash算法没有取模,它的算法是结合了数据data和部署的节点node一起计算而分配到不同的redis实例上的。代码实现有千万种写法,关于这个算法不展开叙述,大致理一下思路。
a、规划一个hash环
这个圆环上放着一个个的虚拟点,可以是从0到2的32次方这么多个。
image.png
b、规划节点到hash环位置
不管你目前有几台redis实例,还是后面又有增加实例,我们可以按照某个规则比如取redis实例所在机器的Ip和端口号经过hash运算,映射到某个点上,这个物理节点就可以确定位置,比如node01经过hash计算落到了A点上,机器落的点是物理的其他的点是虚拟的。
c、数据data来临寻找存放位置
当data数据来临时,也通过一个hash计算得到一个位子,这个点是虚拟的,在经过在环上比较比它大的物理节点是哪个,比如这里算出来就在node01和node02间,比这个位置大的最近的物理节点是Node02,就找到了node02这个节点,并且将数据放到node02上。
d、物理节点增加
假如这个时候增加了一台机器node03,算出来的位置恰巧就在node01和node02之间,刚刚我们存放的那个数据是在node02,这个时候会带来什么问题?之前客户端压过来的请求经过计算是在node01到node02之间,和node02到node01之间,都能够最终在2或1上取到数据,现在03节点进来,不会影响到03节点到02节点间的数据访问,但会影响01到03节点间的数据访问,请求压过来在03节点上找不到数据,可能会带来缓存穿透,压到数据库给数据库造成压力,但是增加了03节点,对于后面新增的数据有一部分会分配到03节点,势必是为02节点降低了压力,并且这种增加方式不会像取模算法那样让所有数据重新分配,不会造成数据的全局洗牌。
关于增加一个节点03带来的问题,我们可以采取折中方案,取离得计算节点最近的2个节点,增加系统复杂度,就是假设在03上取不到,就再去02节点取一次,最近的这2个节点都找不到的时候,才去数据库mysql中查询,返回且回写到最近的节点。当然可能又有伙伴会说假设又增加一个节点04刚好落在了03和02之间,那取最近的2个节点不就取不到了吗?那把流程再设计的复杂点,取3次取4次,不用这么做,这个点的取舍在于我们怎么去设计了,要是真取3、4次,那多次tcp的消耗就够多了,所以这里就需要根据实际场景去计算和推算。另外刚刚说道增加了03节点,之前放在02节点的部分数据就不能够访问,数据是不是就一直在那放着占用存储空间,这个时候我们就可以使用过期淘汰策略来清除一些非热点数据,因而redis更是倾向于作为缓存使用而不是作为数据库使用。
e、数据倾斜
刚刚我们谈到2个redis实例,经过计算落在了2个点上,也有可能所有数据经过hash计算最后就只存放在了node02节点上,node01节点没有存放到任意1笔数据,这样就会造成数据倾斜。为了解决数据倾斜的问题,我们可以再增加一个操作,刚刚我们假设是使用ip地址,这时我们在ip地址后面拼接10个数,假如就是ip1,ip2,ip3...参与hash运算,就会产生10个点分布在hash还上,2个redis实例就是20个点,不管你有多少数据,总会计算到2台节点上,不会造成数据倾斜。
总结
本期我们讨论了AKF拆分沿着YZ轴带来的一些问题,也给出了再客户端解决数据分片的问题,数据如何拆分同样也可以由第三方模块或者代理来实现数据分片,我们下期接着聊,欢迎持续关注!