Redis缓存热点引发的思考

01—背景

一开始并没有打算梳理redis的相关内容,因为在一篇文章中看到关于热点问题的处理,心中有一些疑惑,内容如下:

缓存热点:

对于特别热的数据,如果大部分甚至所有的业务都命中同一份缓存数据,则这份数据所在缓存服务器压力就很大,例如,某明星微博发布“我们”来宣告恋爱了,则短时间内有成千上万的用户都来围观。

缓存热点解决方案:

就是复制多份缓存,将请求分散到多个缓存服务器上,减轻缓存热点导致的单台缓存服务器压力,以新浪微博为例,对于粉丝超过100万的明星,每一条微博都可以生成100分缓存,缓存的数据都是一样的,通过缓存key里面加编号进行分区,每次读缓存都随机读取其中某份缓存。

解决思路是没有问题的,是通过备份相同的数据到多台缓存服务器中,缓解分散单台服务器的压力,我的疑惑是"通过缓存key里面加编号进行分区"这种方式是怎么确保使需要保存的数据都分散到不同的服务器呢?redis集群根据key进行hash散列算法,最终映射的是槽位,不同的缓存服务器分别管理的是一些列表槽位,怎么保证通过key计算出来的槽位值正好不在同一台机器上呢?

带着这个疑问点,开始了redis复制,哨兵,集群内容的整理。

02—内容梳理

复制

在分布式系统中,为了解决单点问题,通常会把数据复制多个副本部署到其他的机器,满足故障恢复和负载均衡的需求。哨兵和集群模式都是在复制的基础上实现的高可用。

图片
image.gif

redis复制拓扑极其应用

拓扑结构分为一主一从,一主多从,树状多从结构。

1. 一主一从结构

图片
image.gif

应用场景:主节点出现宕机时,从节点提供故障转移支持

当写命令并发量较高并且需要持久化时,可以在从节点开启AOF,这样既保证了数据的安全也避免了持久化对于主节点的影响。

注意:

当主节点关闭持久化功能,在从节点实现的时候,如果主节点脱机要避免主节点自动重启,因为主节点没有开启持久化功能,自动重启后数据集为空,这时如果从节点继续复制主节点会导致从节点数据也被清空。

解决方案:

在从节点执行slaveof no one断开与主节点的复制关系,再重启主节点避免这种情况。

  1. 一主多从结构
图片
image.gif

利用多个节点实现读写分离,适用于读占比大的场景,把读命令发送到从节点来分担主节点压力。

日常开发中,执行一些比较耗时的读命令,可以在其中的一个从节点上进行,防止慢查询对主节点造成阻塞

3. 树状主从结构

图片
image.gif

从节点不但可以复制主节点的数据,同时可以作为其他从节点的主节点继续向下层复制。

有效的降低主节点的负载和需要传送给从节点的数据量,降低主节点压力。

复制模式的注意事项:

1. 复制的数据流是单向的,只能从主节点复制到从节点。

  1. 从节点默认使用slave-read-only=yes配置为只读模式,对于从节点的任何修改主节点是无感知的,修改从节点会造成主从数据不一致,所以建议不要修改从节点的只读模式。

3. 读写分离面临的问题

图片
image.gif

对读占比高的场景,把一部分读流量分摊到从节点来减轻主节点压力,永远只对主节点执行写操作。

当使用从节点影响请求时,业务端可能面临的问题:

复制数据延迟

刚在主节点写入数据后立刻在从节点读取可能读取不到,需要业务场景允许短时间内数据的延迟。

当无法容忍延迟场景,可以编写外部监控程序,监听主从节点的复制偏移量,当延迟较大时触发报警或者通知客户端避免读取延迟过高的从节点。

图片
image.gif

监控程序定期检查主从节点偏移量,当延迟字节过高时,例如超过10MB,监控程序触发报警通知客户端从节点延迟过高,客户端接收到从节点过高延迟通知后,修改读命令路由到其他的从节点或者主节点上,当延迟恢复后,再次通知客户端,恢复从节点的读命令请求。

读到过期数据

Redis内部维护过期数据删除策略,删除策略一共有两种,惰性删除和定期删除。

图片
image.gif

惰性删除:主节点每次处理读请求命令时,都会检查键是否超时,如果超时,则执行del命令删除键对象,之后del命令也会异步发送给从节点,为了保证数据一致性,从节点本身永远不会主动删除超时数据。

如果在从节点读取数据,则获取到的数据就有可能是已经过期的数据了,因为此时读请求没有发送到主节点,主节点不会检查键是否过期也不会发送del命令给从节点。

图片
image.gif

定期删除:Redis主节点内部定时任务会循环采样一定数量的键,当发现采样的键过期时执行del命令,之后同步给从节点。

如果此时数据大量超时,主节点采样的速度跟不上过期的速度且主节点没有读取过期键操作,那么从节点将无法收到del命令,这时从节点可能读取到已经超时的数据。

定时删除和惰性删除命令都会有读取到过期数据的问题,在Rdis3.2版本之后,从节点读取数据之前会检查键的过期时间来决定是否返回数据,规避了读取超时数据的问题。

从节点故障

对于从节点故障,需要在客户端维护可用从节点列表,当从节点故障时立刻切换到其他从节点或主节点上,类似于延迟过高的监控处理。

哨兵

redis主从复制模式下,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点****,同时还要通知应用方更新主节点地址,为了解决人工处理问题,Redis Sentinel(哨兵)架构解决了这个问题。

图片
image.gif

Sentinel是Redis的高可用的解决方案,由一个或多个Sentinel实例组成Sentinel系统可以监视任意多个主服务器以及这些主服务器下的所有从服务器,并且在被监视的主服务器进入下线状态时,自动将下线主服务器下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。

当主节点出现故障时,Redis Sentinel能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。

高可用读写分离思路:

从节点的作用:

第一,当主节点出现故障时,作为主节点的后备"顶"上来,实现故障转移,Redis Sentinel实现了该功能的自动化,实现了真正的高可用。第二,扩展主节点的读能力,尤其是在读多写少的场景,非常适用。

图片
image.gif

上述的模型中,从节点不是高可用的,如果slave-1节点出现故障,首先客户端client-1将与其失联,其次Sentinel节点只会对该节点做主观下线,因为Redis Sentinel的故障转移时针对主节点的。

很多时候Redis Sentinel中的从节点仅仅是作为主节点的一个热备,不让它参与客户端的读操作,就是为了保证整体的高可用,但实际上这种使用方法还是有一些浪费,尤其是在有很多从节点或者确实需要读写分离的场景,所以如何实现从节点的高可用是非常有必要的。

图片
image.gif

设计Redis Sentinel从节点的高可用,只要能够掌握所有的从节点的状态,把所有的从节点看做是一个资源池,无论是上线还是下线一个从节点,客户端都能及时感知到(将其从资源池中添加或者删除),这样就实现了从节点的高可用

集群

Redis cluster是Redis的分布式解决方案,当遇到单机内存,并发,流量等瓶颈时,可以采用Cluster架构方案达到负载均衡的目的。

数据分布理论

分布式数据库首要解决的是把整个数据集按照分区规则映射到多个节点的问题。把数据集划分到多个节点,每个节点负责整个数据的一个子集。

图片
image.gif

数据分区规则有两种:哈希分区和顺序分区两种。

哈希分区:离散度好,数据分布业务无关,无法顺序访问。

顺序分区:离散度易倾斜,数据分布业务相关,可顺序访问。

Redis Cluster使用的是哈希分区规则,哈希分区规则有以下几种:

节点取余分区

使用特定的数据,如Redis的键或用户ID,再根据节点数量N使用公式:

hash(key) % N 计算出哈希值,用来决定数据映射到哪一个节点。

问题:

当节点数量发送变化时,如扩容或收缩节点,数据节点映射关系需要重新计算,会导致数据的重新迁移。

使用场景:

常用于数据库的分库分表规则,一般采用预分区的方式,提前根据数据量规划好分区数,比如划分512或1024张表,保证支撑未来一段时间的数据量,再根据负载情况将表迁移到其他的数据库中。

扩容时通常采用翻倍扩容,避免数据映射全部打乱导致全量迁移的情况。

图片
image.gif

一致性哈希分区

优点:相比于节点取余的好处在于加入和删除节点只影响哈希环中相邻的节点,对其他的节点没有影响。

问题:

加减节点会造成哈希环中部分数据无法命中,需要手动处理或忽略这部分数据,所以一致性哈希常用于缓存场景。

当使用少量节点时,节点变化将大范围影响哈希环中数据映射。一致性哈希不适合少量数据节点的分布式方案。

普通的一致性哈希分区在增减节点时,需要增加一倍或者减少一半的节点才能够保证数据和负载的均衡。

图片
image.gif

虚拟槽分区

虚拟槽分区巧妙的使用了哈希空间,使用分散度良好的哈希函数把所有的数据映射到一个固定范围的整数集合中****,整数定义为槽(slot),整个范围一般远远大于节点数量,Redis Cluster槽范围是0-163843,槽是集群内数据管理和迁移的基本单位,采用大范围槽的主要目的是为了方便数据拆分和集群扩展。

每一个节点会负责一定数量的槽

图片
image.gif

Redis Cluster使用的虚拟槽分区,所有的键根据哈希函数映射到0-16383整数槽内,计算公式:slot=CRC16(key) & 16383,每一个节点负责维护一部分槽以及槽所映射的键值数据。

图片
image.gif

Redis虚拟槽分区的特点:

解耦数据和节点之间的关系,简化了节点扩容和缩容的难度。

节点自身维护槽的映射关系,不需要客户端或代理服务器维护槽分区元数据。

支持节点,槽,键之间的映射查询,用于数据路由,在线伸缩等场景。

集群功能的限制:

key批量操作支持有限,如mset,mget目前只支持具有相同slot值的key执行批量操作(相同的槽位slot可能会存放多个key,根据公式可知:slot=CRC16(key) & 16383)。

key事务操作支持有限,只支持多key在同一个节点上的事务操作。

key作为数据分区的最小粒度,不能将一个大的键值对象如hash,list等映射到不同的节点。

单机下Redis可以支持16个数据库,集群模式下只能使用一个数据库空间,即db0。

复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。

集群伸缩原理:

在不影响集群对外提供服务的情况下,可以为集群添加节点进行扩容,也可以下线部分节点进行缩容

图片
image.gif

Redis集群对节点灵活上下线控制,其原理可抽象为槽和对应的数据在不同节点之间灵活的移动。

图片
image.gif

三个节点分别维护自己负责的槽和对应的数据,如果希望加入一个节点实现集群扩容时,需要通过相关命令把一部分槽和数据迁移到新节点。

图片
image.gif

图中每一个节点把槽和数据迁移到新节点6385上,每个节点负责的槽和数据相比之前变少了,从而达到了集群扩容的目的。

集群水平伸缩的上层原理:集群伸缩=槽和数据在节点之间的移动。

03—解决方案

再回到开始自己的疑问,我理解我自己的想法是没有错的,不能够完全保证可以通过key来进行分区,因为生成的key通过hash算法最终获取到的是一个槽位,并不能确保新生成的槽位分配在不同的机器上,一般情况下每一个节点对应的是多个槽位。

在已知热点key的情况下推荐使用内存存放热key来解决。

针对这种热key请求,会直接从jvm中获取,避免走redis层,假设此时有10万请求针对同一个key请求过来,如果没有本地缓存,这十万个请求直接到了redis,会将redis服务器弄挂,如果应用服务器有50台,同时热数据在jvm中已经存储了,这十万请求可以平均分散开来,每个机器2000请求,直接从jvm中取值,然后返回数据

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容

  • 如今,市面上的缓存解决方案已经逐步成熟了,今天我将选取其中一些代表性的方案包括Redis、Memcached和Ta...
    Python编程社区阅读 112评论 0 0
  • 关于Redis热点key的一些思考 昨天在和一个已经跳槽的同事聊天时,询问他这段时间面试时碰到的一些问题。自己也想...
    不学无数的程序员阅读 2,187评论 1 5
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,524评论 16 22
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,567评论 0 11
  • 在妖界我有个名头叫胡百晓,无论是何事,只要找到胡百晓即可有解决的办法。因为是只狐狸大家以讹传讹叫我“倾城百晓”,...
    猫九0110阅读 3,266评论 7 3