从容应对亿级QPS访问,Redis还缺少什么?no.29

众所周知,Redis 在线上实际运行时,面对海量数据、高并发访问,会遇到不少问题,需要进行针对性扩展及优化。本课时,我会结合微博在使用 Redis 中遇到的问题,来分析如何在生产环境下对 Redis 进行扩展改造,以应对百万级 QPS。

功能扩展

对于线上较大流量的业务,单个 Redis 实例的内存占用很容易达到数 G 的容量,对应的 aof 会占用数十 G 的空间。即便每天流量低峰时间,对 Redis 进行 rewriteaof,减少数据冗余,但由于业务数据多,写操作多,aof 文件仍然会达到 10G 以上。

此时,在 Redis 需要升级版本或修复 bug 时,如果直接重启变更,由于需要数据恢复,这个过程需要近 10 分钟的时间,时间过长,会严重影响系统的可用性。面对这种问题,可以对 Redis 扩展热升级功能,从而在毫秒级完成升级操作,完全不影响业务访问。

在这里插入图片描述

热升级方案如下,首先构建一个 Redis 壳程序,将 redisServer 的所有属性(包括redisDb、client等)保存为全局变量。然后将 Redis 的处理逻辑代码全部封装到动态连接库 so 文件中。Redis 第一次启动,从磁盘加载恢复数据,在后续升级时,通过指令,壳程序重新加载 Redis 新的 so 文件,即可完成功能升级,毫秒级完成 Redis 的版本升级。而且整个过程中,所有 Client 连接仍然保留,在升级成功后,原有 Client 可以继续进行读写操作,整个过程对业务完全透明。

在 Redis 使用中,也经常会遇到一些特殊业务场景,是当前 Redis 的数据结构无法很好满足的。此时可以对 Redis 进行定制化扩展。可以根据业务数据特点,扩展新的数据结构,甚至扩展新的 Redis 存储模型,来提升 Redis 的内存效率和处理性能。

在这里插入图片描述
在微博中,有个业务类型是关注列表。关注列表存储的是一个用户所有关注的用户 uid。关注列表可以用来验证关注关系,也可以用关注列表,进一步获取所有关注人的微博列表等。由于用户数量过于庞大,存储关注列表的 Redis 是作为一个缓存使用的,即不活跃的关注列表会很快被踢出 Redis。在再次需要这个用户的关注列表时,重新从 DB 加载,并写回 Redis。关注列表的元素全部 long,最初使用 set 存储,回种 set 时,使用 sadd 进行批量添加。线上发现,对于关注数比较多的关注列表,比如关注数有数千上万个用户,需要 sadd 上成千上万个 uid,即便分几次进行批量添加,每次也会消耗较多时间,数据回种效率较低,而且会导致 Redis 卡顿。另外,用 set 存关注列表,内存效率也比较低。

于是,我们对 Redis 扩展了 longset 数据结构。longset 本质上是一个 long 型的一维开放数组。可以采用 double-hash 进行寻址。

在这里插入图片描述

从 DB 加载到用户的关注列表,准备写入 Redis 前。Client 首先根据关注的 uid 列表,构建成 long 数组的二进制格式,然后通过扩展的 lsset 指令写入 Redis。Redis 接收到指令后,直接将 Client 发来的二进制格式的 long 数组作为 value 值进行存储。

longset 中的 long 数组,采用 double-hash 进行寻址,即对每个 long 值采用 2 个哈希函数计算,然后按 (h1 + n*h2)% 数组长度 的方式,确定 long 值的位置。n 从 0 开始计算,如果出现哈希冲突,即计算的哈希位置,已经有其他元素,则 n 加 1,继续向前推进计算,最大计算次数是数组的长度。

在向 longset 数据结构不断增加 long 值元素的过程中,当数组的填充率超过阀值,Redis 则返回 longset 过满的异常。此时 Client 会根据最新全量数据,构建一个容量加倍的一维 long 数组,再次 lsset 回 Redis 中。

在这里插入图片描述
在移动社交平台中,庞大的用户群体,相互之间会关注、订阅,用户自己会持续分享各种状态,另外这些状体数据会被其他用户阅读、评论、扩散及点赞。这样,在用户维度,就有关注数、粉丝数、各种状态行为数,然后用户每发表的一条 feed、状态,还有阅读数、评论数、转发数、表态数等。一方面会有海量 key 需要进行计数,另外一方面,一个 key 会有 N 个计数。在日常访问中,一次查询,不仅需要查询大量的 key,而且对每个 key 需要查询多个计数。

以微博为例,历史计数高达千亿级,而且随着每日新增数亿条 feed 记录,每条记录会产生 4~8 种计数,如果采用 Redis 的计数,仅仅单副本存储,历史数据需要占用 5~6T 以上的内存,每日新增 50G 以上,如果再考虑多 IDC、每个 IDC 部署 1 主多从,占用内存还要再提升一个数量级。由于微博计数,所有的 key 都是随时间递增的 long 型值,于是我们改造了 Redis 的存储结构。

首先采用 cdb 分段存储计数器,通过预先分配的内存数组 Table 存储计数,并且采用 double hash 解决冲突,避免 Redis 实现中的大量指针开销。 然后,通过 Schema 策略支持多列,一个 key id 对应的多个计数可以作为一条计数记录,还支持动态增减计数列,每列的计数内存使用精简到 bit。而且,由于 feed 计数冷热区分明显,我们进行冷热数据分离存储方案,根据时间维度,近期的热数据放在内存,之前的冷数据放在磁盘, 降低机器成本。

关于计数器服务的扩展,后面的案例分析课时,我会进一步深入介绍改造方案。

在这里插入图片描述
线上 Redis 使用,不管是最初的 sync 机制,还是后来的 psync 和 psync2,主从复制都会受限于复制积压缓冲。如果 slave 断开复制连接的时间较长,或者 master 某段时间写入量过大,而 slave 的复制延迟较大,slave 的复制偏移量落在 master 的复制积压缓冲之外,则会导致全量复制。

完全增量复制

于是,微博整合 Redis 的 rdb 和 aof 策略,构建了完全增量复制方案。
在这里插入图片描述
在完全增量方案中,aof 文件不再只有一个,而是按后缀 id 进行递增,如 aof.00001、aof.00002,当 aof 文件超过阀值,则创建下一个 id 加 1 的文件,从而滚动存储最新的写指令。在 bgsave 构建 rdb 时,rdb 文件除了记录当前的内存数据快照,还会记录 rdb 构建时间,对应 aof 文件的 id 及位置。这样 rdb 文件和其记录 aof 文件位置之后的写指令,就构成一份完整的最新数据记录。

主从复制时,master 通过独立的复制线程向 slave 同步数据。每个 slave 会创建一个复制线程。第一次复制是全量复制,之后的复制,不管 slave 断开复制连接有多久,只要 aof 文件没有被删除,都是增量复制。

第一次全量复制时,复制线程首先将 rdb 发给 slave,然后再将 rdb 记录的 aof 文件位置之后的所有数据,也发送给 slave,即可完成。整个过程不用重新构建 rdb。

在这里插入图片描述
后续同步时,slave 首先传递之前复制的 aof 文件的 id 及位置。master 的复制线程根据这个信息,读取对应 aof 文件位置之后的所有内容,发送给 slave,即可完成数据同步。

由于整个复制过程,master 在独立复制线程中进行,所以复制过程不影响用户的正常请求。为了减轻 master 的复制压力,全增量复制方案仍然支持 slave 嵌套,即可以在 slave 后继续挂载多个 slave,从而把复制压力分散到多个不同的 Redis 实例。

集群管理

前面讲到,Redis-Cluster 的数据存储和集群逻辑耦合,代码逻辑复杂易错,存储 slot 和 key 的映射需要额外占用较多内存,对小 value 业务影响特别明显,而且迁移效率低,迁移大 value 容易导致阻塞,另外,Cluster 复制只支持 slave 挂在 master 下,无法支持 需要较多slave、读 TPS 特别大的业务场景。除此之外,Redis 当前还只是个存储组件,线上运行中,集群管理、日常维护、状态监控报警等这些功能,要么没有支持,要么支持不便。

因此我们也基于 Redis 构建了集群存储体系。首先将 Redis 的集群功能剥离到独立系统,Redis 只关注存储,不再维护 slot 等相关的信息。通过新构建的 clusterManager 组件,负责 slot 维护,数据迁移,服务状态管理。

Redis 集群访问可以由 proxy 或 smart client 进行。对性能特别敏感的业务,可以通过 smart client 访问,避免访问多一跳。而一般业务,可以通过 Proxy 访问 Redis。

业务资源的部署、Proxy 的访问,都通过配置中心进行获取及协调。clusterManager 向配置中心注册业务资源部署,并持续探测服务状态,根据服务状态进行故障转移,切主、上下线 slave 等。proxy 和 smart client 从配置中心获取配置信息,并持续订阅服务状态的变化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/841010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法金 | Dask,一个超强的 python 库

本文来源公众号“算法金”,仅用于学术分享,侵权删,干货满满。 原文链接:Dask,一个超强的 python 库 1 Dask 概览 在数据科学和大数据处理的领域,高效处理海量数据一直是一项挑战。 为了应对这一挑战&am…

滑动菜单栏

效果如下&#xff1a; NavigationView 新建menu布局,表示菜单栏的选项 <menu xmlns:android"http://schemas.android.com/apk/res/android"> <group android:checkableBehavior"single"> <item android:id"id/navCall" android…

海外CDN加速方式

随着全球化经济的进一步推进和互联网时代的到来&#xff0c;给对外贸易行业带来了巨大的商机&#xff0c;众多传统的贸易公司都纷纷建立起自已的外贸网站或服务站点等各种信息化平台&#xff0c; 相当多的贸易公司也从他们所构建的平台中得到了很高的利益&#xff0c;然而由于当…

医疗科技:UWB模块为智能医疗设备带来的变革

随着医疗科技的不断发展和人们健康意识的提高&#xff0c;智能医疗设备的应用越来越广泛。超宽带&#xff08;UWB&#xff09;技术作为一种新兴的定位技术&#xff0c;正在引领着智能医疗设备的变革。UWB模块作为UWB技术的核心组成部分&#xff0c;在智能医疗设备中发挥着越来越…

抖音运营_打造高流量的抖音账号

目录 一 账号定位 行业定位 用户定位 内容定位 二 账号人设 我是谁? 我的优势 我的差异化 三 创建账号 名字 头像 简介 四 抖音养号 为什么要养号&#xff1f; 抖音快速养号 正确注册抖音账号 一机一卡一号 实名认证 正确填写账号信息 养号期间的操作 五…

韵搜坊 -- Elastic Stack快速入门

文章目录 现有问题Elastic Stack介绍&#xff08;一套技术栈&#xff09;安装ES安装KibanaElasticsearch概念倒排索引Mapping分词器IK分词器&#xff08;ES插件&#xff09;打分机制 ES的几种调用方式restful api调用&#xff08;http 请求&#xff09;kibana devtools客户端调…

程序员做推广?我劝你别干

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 这是卢松松会员专区&#xff0c;一位会员朋友的咨询&#xff0c;如果你也有自研产品&#xff0c;但不知道如何推广&#xff0c;一定要阅读本文!强烈建议收藏关注&#xff0c;因为你关注的人&#xff0c;决定你看到的…

【机器学习300问】98、卷积神经网络中的卷积核到底有什么用?以边缘检测为例说明其意义。

卷积核是用于从输入数据中提取特征的关键工具。卷积核的设计直接关系到网络能够识别和学习的特征类型。本文让我以边缘检测为例&#xff0c;带大家深入理解卷积核的作用。 一、卷积核的作用 卷积核&#xff0c;又称为过滤器&#xff0c;本质上是一个小的矩阵&#xff0c;其元素…

微信小程序毕业设计-智慧旅游平台系统项目开发实战(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

【算法】二分算法——山脉数组的峰顶索引

该题用二分算法解“山脉数组的峰顶索引”&#xff0c;有需要借鉴即可。 目录 1.题目2.总结 1.题目 题目链接&#xff1a;LINK 暴力求解很简单&#xff0c;这里不再提及。 这个可以根据峰顶值分为两部分&#xff0c;因而具有“二段性”&#xff0c;可以用二分算法&#xff0c…

默认路由实现两个网段互通实验

默认路由实现两个网段互通实验 **默认路由&#xff1a;**是一种特殊的静态路由&#xff0c;当路由表中与数据包目的地址没有匹配的表项时&#xff0c;数据包将根据默认路由条目进行转发。默认路由在某些时候是非常有效的&#xff0c;例如在末梢网络中&#xff0c;默认路由可以…

Postgresql源码(133)优化器动态规划生成连接路径的实例分析

物理算子的生成分为两步&#xff0c;基表的扫描路径生成set_base_rel_pathlists&#xff1b;连接路径生成&#xff08;make_rel_from_joinlist动态规划&#xff09;。本篇简单分析实现。看过代码会发现&#xff0c;“基表的扫描路径生成”其实就是作为连接路径生成dp计算的第一…

【Mac】MWeb Pro(好用的markdown编辑器) v4.5.9中文版安装教程

软件介绍 MWeb Pro for Mac是一款Mac上的Markdown编辑器软件&#xff0c;它支持实时预览&#xff0c;语法高亮&#xff0c;自动保存和备份等功能&#xff0c;并且有多种主题和样式可供选择。此外&#xff0c;MWeb还支持多种导出格式&#xff0c;包括HTML、PDF、Word、ePub等&a…

栈和队列的经典例题,LeetCode 括号匹配问题;栈实现队列;队列实现栈;队列带环问题

1.前序 又有很久没有更新文章了&#xff0c;这次带你们手撕几道基础题&#xff1b;真的就和康纳吃饭一样简单&#xff01;&#xff01;&#xff01; 如果还不会队列和栈的可以去看看之前写的博客&#xff1b; 栈的实现 队列概念以及实现 <- 快速传送 目录 1.前序 …

自定义全局变量3

变量删除 语法 unset var_name演示 自定义常量 介绍 就是变量设置值以后不可以修改的变量叫常量, 也叫只读变量 语法 readonly var_name演示 自定义全局变量 父子Shell环境介绍 例如: 有2个Shell脚本文件 A.sh 和 B.sh 如果 在A.sh脚本文件中执行了B.sh脚本文件, 那么A.…

【Web】CISCN 2024初赛 题解(全)

目录 Simple_php easycms easycms_revenge ezjava mossfern sanic Simple_php 用php -r进行php代码执行 因为ban了引号&#xff0c;考虑hex2bin&#xff0c;将数字转为字符串 php -r eval(hex2bin(16进制)); 注意下面这段报错&#xff0c;因为加不了引号&#xff0c;开…

链表-设计LRU缓存结构

题目描述&#xff1a; 代码实现&#xff1a;这里记录了根据LRU算法原理最直接理解的代码实现。 import java.util.*;//存储输入内容&#xff0c;记录访问权值 class CounterInfo {int key;int value;int times;//代表key对应的权值&#xff0c;值越小优先级越高public Counter…

【第2章】SpringBoot配置文件

文章目录 前言一、编写配置信息1. properties2. yml 二、获取配置信息1.直接获取2.配置类形式 总结 前言 SpringBoot工程创建后&#xff0c;会为我们提供一个默认的配置文件(application.properties)&#xff0c;配置文件主要用于那些可能发生变化且经常改变的属性值。 一、编…

Autodesk 3DS Max v2025 解锁版安装教程 (3D 建模软件)

前言 Autodesk 3ds Max 是一款功能强大的 3D 建模和动画解决方案&#xff0c;游戏开发人员、视觉效果艺术家和平面设计师使用它来创建庞大的世界、令人惊叹的场景和引人入胜的虚拟现实 (VR) 体验。 Autodesk 3DS MAX是业界使用最广泛的3D建模和动画软件程序之一&#xff0c;它…

盲盒小程序开发,数字化发展下的优势

近年来&#xff0c;盲盒经济得到了快速发展&#xff0c;不少人开始加入到盲盒大军中&#xff0c;盲盒市场规模不断扩大。 盲盒最大的特点就是能够给消费者带来拆盒的刺激性和惊喜感。盲盒商品大多是动漫手办、周边等&#xff0c;具有较大的收藏价值&#xff0c;因此深深吸引着…