【存储中心件】Redis核心技术与实战(五):Redis缓存使用问题(BigKey、数 ...

打印 上一主题 下一主题

主题 931|帖子 931|积分 2803


个人主页:道友老李
欢迎加入社区:道友老李的学习社区
Redis缓存使用问题

BigKey

什么是bigkey

bigkey是指key对应的value所占的内存空间比力大,例如一个字符串类型的value可以最大存到512MB,一个列表类型的value最多可以存储23-1个元素。
假如按照数据结构来细分的话,一般分为字符串类型bigkey和非字符串类型bigkey。
字符串类型:体现在单个value值很大,一般认为超过10KB就是bigkey,但这个值和具体的OPS相干。
非字符串类型:哈希、列表、聚集、有序聚集,体现在元素个数过多。
bigkey无论是空间复杂度和时间复杂度都不太友爱,下面我们将先容它的危害。
bigkey的危害

bigkey的危害体现在三个方面:
1、内存空间不均匀.(平衡):例如在Redis Cluster中,bigkey 会造成节点的内存空间使用不均匀。
2、超时阻塞:由于Redis单线程的特性,操作bigkey比力耗时,也就意味着阻塞Redis可能性增大。
3、网络拥塞:每次获取bigkey产生的网络流量较大
假设一个bigkey为1MB,每秒访问量为1000,那么每秒产生1000MB 的流量,对于平凡的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾,而且一般服务器会采取单机多实例的方式来部署,也就是说一个bigkey可能会对其他实例造成影响,其效果不堪设想。
bigkey的存在并不是完全致命的:
假如这个bigkey存在但是几乎不被访问,那么只有内存空间不均匀的问题存在,相对于别的两个问题没有那么紧张紧急,但是假如bigkey是一个热门key(频繁访问),那么其带来的危害不可想象,所以在实际开发和运维时肯定要密切关注bigkey的存在。
发现bigkey

redis-cli --bigkeys可以下令统计bigkey的分布。

但是在生产环境中,开发和运维职员更希望自己可以界说bigkey的巨细,而且更希望找到真正的bigkey都有哪些,如许才可以去定位、解决、优化问题。
判断一个key是否为bigkey,只需要实行debug object key查察serializedlength属性即可,它表示 key对应的value序列化之后的字节数。

假如是要遍历多个,则尽量不要使用keys的下令,可以使用scan的下令来减少压力。
scan

Redis 从2.8版本后,提供了一个新的下令scan,它能有效的解决keys下令存在的问题。和keys下令实行时会遍历全部键不同,scan采取渐进式遍历的方式来解决 keys下令可能带来的阻塞问题,但是要真正实现keys的功能,需要实行多次scan。可以想象成只扫描一个字典中的一部分键,直到将字典中的全部键遍历完毕。scan的使用方法如下:
  1. scan cursor [match pattern] [count number]
复制代码
cursor :是必需参数,实际上cursor是一个游标,第一次遍历从0开始,每次scan遍历完都会返回当前游标的值,直到游标值为0,表示遍历结束。
Match pattern :是可选参数,它的作用的是做模式的匹配,这点和keys的模式匹配很像。
Count number :是可选参数,它的作用是表明每次要遍历的键个数,默认值是10,此参数可以得当增大。

可以看到,第一次实行scan 0,返回效果分为两个部分:
第一个部分9就是下次scan需要的cursor
第二个部分是10个键。接下来继承
直到得到效果cursor变为0,说明全部的键已经被遍历过了。
除了scan 以外,Redis提供了面向哈希类型、聚集类型、有序聚集的扫描遍历下令,解决诸如hgetall、smembers、zrange可能产生的阻塞问题,对应的下令分别是hscan、sscan、zscan,它们的用法和scan基本类似,请自行参考Redis官网。

渐进式遍历可以有效的解决keys下令可能产生的阻塞问题,但是scan并非完美无瑕,假如在scan 的过程中假如有键的变化(增加、删除、修改),那么遍历效果可能会碰到如下问题:新增的键可能没有遍历到,遍历出了重复的键等环境,也就是说scan并不能保证完备的遍历出来全部的键,这些是我们在开发时需要考虑的。
假如键值个数比力多,scan + debug object会比力慢,可以使用Pipeline机制完成。对于元素个数较多的数据结构,debug object实行速度比力慢,存在阻塞Redis的可能,所以假如有从节点,可以考虑在从节点上实行。
解决bigkey

重要思绪为拆分,对 big key 存储的数据 (big value)进行拆分,酿成value1,value2… valueN等等。
例如big value 是个大json 通过 mset 的方式,将这个 key 的内容打散到各个实例中,或者一个hash,每个field代表一个具体属性,通过hget、hmget获取部分value,hset、hmset来更新部分属性。
例如big value 是个大list,可以拆成将list拆成。= list_1, list_2, list3, …listN
其他数据类型同理。
数据倾斜

数据倾斜实在分为访问量倾斜或者数据量倾斜:
1、hotkey出现造成集群访问量倾斜
2、bigkey 造成集群数据量倾斜
解决方案前面已经说过了,这里不再赘述。
Redis脑裂

所谓的脑裂,就是指在有主从集群中,同时有两个主节点,它们都能吸收写请求。而脑裂最直接的影响,就是客户端不知道应该往哪个主节点写入数据,效果就是不同的客户端会往不同的主节点上写入数据。而且,严峻的话,脑裂会进一步导致数据丢失。
哨兵主从集群脑裂

现在假设:有三台服务器一台主服务器,两台从服务器,还有一个哨兵。
基于上边的环境,这时候网络环境发生了波动导致了sentinel没有可以大概心跳感知到master,但是哨兵与slave之间通讯正常。所以通过推举的方式提升了一个salve为新master。假如恰恰此时server1仍然毗连的是旧的master,而server2毗连到了新的master上。数据就不同等了,哨兵规复对老master节点的感知后,会将其降级为slave节点,然后从新maste同步数据(full resynchronization),导致脑裂期间老master写入的数据丢失。
而且基于setNX指令的分布式锁,可能会拿到雷同的锁;基于incr生成的全局唯一id,也可能出现重复。通过配置参数

  1. min-replicas-to-write 2
  2. min-replicas-max-lag 10
复制代码
第一个参数表示最少的salve节点为2个
第二个参数表示数据复制和同步的耽误不能超过10秒
配置了这两个参数:假如发生脑裂:原master会在客户端写入操作的时候拒绝请求。如许可以制止大量数据丢失。
集群脑裂

Redis集群的脑裂一般是不存在的,因为Redis集群中存在着过半推举机制,而且当集群16384个槽任何一个没有指派到节点时整个集群不可用。所以我们在构建Redis集群时,应该让集群 Master 节点个数最少为 3 个,且集群可用节点个数为奇数。
不过脑裂问题不是是可以完全制止,只要是分布式体系,必然就会肯定的几率出现这个问题,CAP的理论就决定了。
多级缓存实例

一个使用了Redis集群和其他多种缓存技术的应用体系架构如图

首先,用户的请求被负载平衡服务分发到Nginx上,此处常用的负载平衡算法是轮询或者同等性哈希,轮询可以使服务器的请求更加平衡,而同等性哈希可以提升Nginx应用的缓存命中率。
接着,Nginx应用服务器读取本地缓存,实现本地缓存的方式可以是Lua Shared Dict,或者面向磁盘或内存的Nginx Proxy Cache,以及本地的Redis实现等,假如本地缓存命中则直接返回。Nginx应用服务器使用本地缓存可以提升团体的吞吐量,降低后端的压力,尤其应对热门数据的反复读取问题非常有效。
假如Nginx应用服务器的本地缓存没有命中,就会进一步读取相应的分布式缓存——Redis分布式缓存的集群,可以考虑使用主从架构来提升性能和吞吐量,假如分布式缓存命中则直接返回相应数据,并回写到Nginx应用服务器的本地缓存中。
假如Redis分布式缓存也没有命中,则会回源到Tomcat集群,在回源到Tomcat集群时也可以使用轮询和同等性哈希作为负载平衡算法。当然,假如Redis分布式缓存没有命中的话,Nginx应用服务器还可以再尝试一次读主Redis集群操作,目的是防止当从 Redis集群有问题时可能发生的流量打击。
在Tomcat集群应用中,首先读取本地平台级缓存,假如平台级缓存命中则直接返回数据,并会同步写到主Redis集群,然后再同步到从Redis集群。此处可能存在多个Tomcat实例同时写主Redis集群的环境,可能会造成数据错乱,需要留意缓存的更新机制和原子化操作。
假如全部缓存都没有命中,体系就只能查询数据库或其他相干服务获取相干数据并返回,当然,我们已经知道数据库也是有缓存的。
团体来看,这是一个使用了多级缓存的体系。Nginx应用服务器的本地缓存解决了热门数据的缓存问题,Redis分布式缓存集群减少了访问回源率,Tomcat应用集群使用的平台级缓存防止了相干缓存失效崩溃之后的打击,数据库缓存提升数据库查询时的效率。正是多级缓存的使用,才能保障体系具备优良的性能。
互联网大厂中的Redis

经过几年演进,携程金融形成了自顶向下的多层次体系架构,如业务层、平台层、底子服务层等,此中用户信息、产品信息、订单信息等底子数据由底子平台等底层体系产生,服务于全部的金融体系,对这部分底子数据我们引入了同一的缓存服务(体系名utag)。

缓存数据有三大特点:全量、准实时、永久有效,在数据实时性要求不高的场景下,业务体系可直接调用同一的缓存查询接口。
在构建此同一缓存服务时候,有三个关键目标:
数据正确性:DB中单条数据的更新肯定要正确同步到缓存服务。
数据完备性:将对应DB表的全量数据进行缓存且永久有效,从而可以替代对应的DB查询。
体系可用性:我们多个产品线的多个核心折务都已经接入,utag的高可用性显得尤为关键。
团体方案


体系在多地都有部署,故缓存服务也做了相应的异地多机房部署,一来可以让不同地区的服务调用本地区服务,无需跨越网络专线,二来也可以作为一种灾备方案,增加可用性。
对于缓存的写入,由于缓存服务是独立部署的,因此需要感知业务DB数据变更然后触发缓存的更新,本着“可以多次更新,但不能漏更新”的原则,计划了多种数据更新触发源:定时任务扫描,业务体系MQ、binlog变更MQ,相互之间作为互补来保证数据不会漏更新。
对于MQ使用携程开源消息中心件QMQ 和 Kafka,在公司内部QMQ和Kafka也做了异地机房的互通。
使用MQ来驱动多地多机房的缓存更新,在不同的触发源触发后,会查询最新的DB数据,然后发出一个缓存更新的MQ消息,不同地区机房的缓存体系同时监听该主题并各自进行缓存的更新。
对于缓存的读取,utag体系提供dubbo协议的缓存查询接口,业务体系可调用本地区的接口,省去了网络专线的耗时(50ms耽误)。在utag内部查询redis数据,并反序列化为对应的业务model,再通过接口返回给业务方。
数据正确性

不同的触发源,对缓存更新过程是一样的,整个更新步调可抽象为4步:
step1:触发更新,查询DB中的新数据,并发送同一的MQ
step2:吸收MQ,查询缓存中的老数据
step3:新老数据对比,判断是否需要更新
step4:若需要,则更新缓存
并发控制

若一条DB数据出现了多次更新,且刚好被不同的触发源触发,更新缓存时候若未加控制,可能出现数据更新错乱,如下图所示:

故需要将第2、3、4步加锁,使得缓存革新操作全部串行化。由于utag本身就依靠了redis,此处我们的分布式锁就基于redis实现。
基于updateTime的更新顺序控制

即使加了锁,也需要进一步判断当前db数据与缓存数据的新老,因为到达缓存更新流程的顺序并不代表数据的真正更新顺序。我们通过对比新老数据的更新时间来实现数据更新顺序的控制。若新数据的更新时间大于老数据的更新时间,则认为当前数据可以直接写入缓存。
我们体系从建立之初就有自己的MySQL规范,每张表都必须有update_time字段,且设置为ON
UPDATE CURRENT_TIMESTAMP,但是并没有约束时间字段的精度,大部分都是秒级别的,因此在同一秒内的多次更新操作就无法识别出数据的新老。
针对同一秒数据的更新策略我们采取的方案是:先进行数据对比,若当前数据与缓存数据不相等,则直接更新,并且发送一条耽误消息,耽误1秒后再次触发更新流程。
举个例子:假设同一秒内同一条数据出现了两次更新,value=1和value=2,期望终极缓存中的数据是value=2。若这两次更新后的数据被先后触发,分两种环境:
case1:若value=1先更新,value=2后更新,(两者都可更新到缓存中,因为固然是同一秒,但是值不相等)则缓存中终极数据为value=2。
case2:若value=2先更新,value=1后更新,则第一轮更新后缓存数据为value=1,不是期望数据,之后对比发现是同一秒数据后会通过消息触发二次更新,重新查询DB数据为value=2,可以更新到缓存中。如下图所示:

数据完备性计划

上述数据正确性是从单条数据更新角度的计划,而我们构建缓存服务的目的是替代对应DB表的查询,因此需要缓存对应DB表的全量数据,而数据的完备性从以下三个方面得到保证:
(1)“把鸡蛋放到多个篮子里”,使用多种触发源(定时任务,业务MQ,binglog MQ)来最大限度降低单条数据更新缺失的可能性。
单一触发源有可能出现问题,比如消息类的触发依靠业务体系、中心件canel、中心件QMQ和Kafka,扫表任务依靠分布式调度平台、MySQL等。中心任何一环都可能出现问题,而这些中心折务同时出概率的可能相对来说就极小了,相互之间可以作为互补。
(2)全量数据革新任务:全表扫描定时任务,每周实行一次来进行兜底,确保缓存数据的全量正确同步。
(3)数据校验任务:监控Redis和DB数据是否同步并进行补偿。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

来自云龙湖轮廓分明的月亮

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表