美团大规模KV存储挑战与架构实践--图文分析

news2024/11/24 20:53:07

美团大规模KV存储挑战与架构实践–图文分析

原作者:美团技术团队

原文链接:https://tech.meituan.com/2024/03/15/kv-squirrel-cellar.html

1 美团 KV 存储发展历程

第一代:使用Memcached

img

什么是一致性哈希?

哈希:hash,可以通过Key存储Value,也可以通过Key取得Value. 简单说就是Value存储的位置是通过Hash算法计算出来的

(实现,如HashMap)。

随着客户端发起的请求数量越来越高,为了加快响应速度,在客户端与数据库之间增加了缓存层,把数据库中的热点数据存入缓存,这样客户端可以直接从缓存取得热点数据,无需每次都访问数据库,即加快了响应时间,又降低了数据库层的压力。

在这里插入图片描述

hash取模运算

随着业务规模不断扩大,数据量也跟着增大,缓存数量飙增,这样就需要考据搭建缓存集群,把大量的缓存分散到多台缓存服务器中进行存储

取模算法hash(key)%N,即:对缓存数据的key进行hash运算后取模,N是机器的数量;运算后的结果映射对应集群中的节点。具体如下图所示:

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=F%3A%2FBaiduNetdiskDownload%2FDevOps%E7%AC%94%E8%AE%B0-MarkDown%E5%8E%9F%E7%89%88%2FDevOps%2FPictures%2Fimage-20240607182832191.png&pos_id=img-VgEmEbVn-1717929022871在这里插入图片描述

问题,如上图,三个node,所以N为3,此时key通过公式hash(key)%3存入指定节点,之后扩容成4个节点,N为4,公式也变成了hash(key)%4,之前n为3的值取不出来了。

一致性哈希

一致性哈希算法将整个哈希值空间映射成一个虚拟的圆环。整个哈希空间的取值范围为02^32-1,按顺时针方向开始从0232-1排列,最后的节点232-1在0开始位置重合,形成一个虚拟的圆环。

对服务器IP地址进行哈希计算,哈希计算后的结果对232取模,结果一定是一个0到232-1之间的整数。最后将这个整数映射在哈希环上,整数的值就代表了一个服务器节点的在哈希环上的位置。即:hash(服务器ip)% 2^32。

当服务器接收到数据请求时,首先需要计算请求Key的哈希值;然后将计算的哈希值映射到哈希环上的具体位置;接下来,从这个位置沿着哈希环顺时针查找,遇到的第一个节点就是key对应的节点

在这里插入图片描述

服务器扩容

D数据本来应该会落在node1中,由于扩容节点node4,D数据落在了node4中

在这里插入图片描述

服务器缩容

node4节点宕机或者移除后,数据D继续顺时针找到node1节点存储。

在这里插入图片描述

数据倾斜与虚拟节点

数据abcd都落在node1中,node2,node3没事干,node1压力山大。

在这里插入图片描述

解决方案,使用虚拟节点

hash(服务器ip)% 2^32 变更为 hash(服务器ip+随机数)% 2^32

例如:Hash(“114.211.11.141”);

变更为:

Hash(“114.211.11.141#1”);

Hash(“114.211.11.141#2”);

注意,虚拟节点只是帮助真实节点扩大获取数据的范围,并不会保存数据,所获取的数据最终还是要存储在真实节点中。

在这里插入图片描述

memcached的问题

传统的memcached是不支持内存数据的持久化操作,因为它将数据存储在内存当中的,当服务器宕机重启,数据会丢失。

不适合存储1M以上的数据。

适用场景:缓存一些很小但是被频繁访问的,即便你丢失也不会影响系统以及系统业务正常执行的数据。如:新闻热点缓存,即便日后数据丢失,热点已经降温,数据不重要。

第二代:使用Redis

img

优点:支持持久化,服务宕机后数据不会丢失,可以通过哨兵机制进行故障恢复(failover)

宕机数据不丢失策略

RDB每5分钟一次生成快照,AOF每秒通过一个后台的线程持久化操作,最多丢一秒的数据。单独用RDB会丢失很多数据,单独用AOF数据恢复没RDB快,所以解决方案是第一时间用RDB恢复,然后AOF做数据补全

缺点:扩缩容丢失数据。

一致性hash只是用来减少扩缩容时数据迁移量,无法对整个集群进行统一管理(缺乏类似提供心跳检测等服务的中心管理层,数据迁徙的时候,节点无法感知整个集群的状态,数据从A节点中迁徙出去了,但接收的B节点处于网络卡顿状态导致无法接受全部迁徙数据等问题)

第三代:阿里巴巴的 Tair

Tair 开源版本的架构主要是三部分:最下边的是存储节点,存储节点会上报心跳到它的中心节点,中心节点内部设有两个配置管理节点,会监控所有的存储节点。如果有任何存储节点宕机或者扩容之类的行为,它会做集群拓扑的重新构建,拥有数据迁移机制来保证数据的完整性。

img

缺点:

1.中心节点是高可用的,但是会发生脑裂。

脑裂:选举bug,多个leader,多个从节点被选举成了主节点,不干从节点的活了导致数据丢失。

2.和redis的数据类型不兼容

第四代:

基于Tair的Cellar ,基于Redis Cluster的Squirrel

img

这两个存储其实都是 KV 存储领域的解决方案。实际应用上,如果业务的数据量小,对延迟敏感,建议用 Squirrel ;如果数据量大,对延迟不是特别敏感,我们建议用成本更低的 Cellar 。

2 大规模 KV 存储的挑战

1.集群的规模越来越大,节点增加困难(比如带宽饱和)

2.部分业务场景带来的木桶效应以及长尾延迟(例如:mget

3.如何保证集群可用性不会随着规模的变大而有所降低

名词解释:

mget:

可以一次性读取redis中多个值:

redis 127.0.0.1:6379> SET key1 "hello"
OK
redis 127.0.0.1:6379> SET key2 "world"
OK
redis 127.0.0.1:6379> MGET key1 key2 someOtherKey
1) "Hello"
2) "World"
3) (nil)

长尾效应:

红色区域量大响应时间短,黄色区域量小耗时长。比如使用了上面的mget批量读取就有大概率造成此类问题。

在这里插入图片描述

3 内存 KV Squirrel 挑战和架构实践

img

上图是美团的 Squirrel 架构。中间部分跟 Redis 社区集群是一致的。它有主从的结构,Redis 实例之间通过 Gossip 协议去通信。右边添加了一个集群调度平台管理整个集群,把管理结果作为元数据更新到 ZooKeeper。我们的客户端会订阅 ZooKeeper 上的元数据变更,实时获取到集群的拓扑状态,直接对 Redis 集群节点进行读写操作。

难点补充:

Gossip 原理:

节点1更新数据,告知了相邻节点2,4,7;相邻节点再去告诉自己的相邻节点。

在这里插入图片描述

优点:去中心化(多个缓存中间件采用,区块链采用)

缺点:当节点特别多的时候消耗性能,节点更新后相邻节点可能会重复更新,非常损耗性能。

3.2 Gossip优化

为了解决上述的扩展性问题,我们对社区的 Gossip 方案进行了优化。首先针对 Gossip 传输的消息,我们通过 Merkle Tree 对其做了一个摘要,把集群 Gossip 通信的数据量减少了90%以上。服务端节点仅需要对比 Hash 值即可判断元数据是否有更新对于存在更新的情况也能快速判断出更新的部分并仅对此部分元数据进行获取、更新,大幅降低了 Gossip 消息处理的资源消耗

原文中的默克尔树(Merkle Tree)

img

补全图:

在这里插入图片描述

类似平衡二叉树,通过对比上层节点的hash是否一致即可得知是否已经更新,可双向判断:判断更新以及找到更新位置。难度:之前为O(N)现在为O Log(N)

3.3 Squirrel 垂直扩展的挑战

节点过大,Fork生成RDB影响性能。(执行RDB的主要三种场景:save,bgsave,主从复制)

Redis备份数据做持久化操作有AOF和RDB两种,为了不影响主线程,会通过内置的Fork系统启动一个子线程执行,但Fork子进程过程中如果节点过大,依然会占用大量资源导致阻塞。可以简单理解为,Redis会在数据增长到一定程度或者执行主从复制的时候,备份自己的数据,生成一个RDB文件(快照),保存当时Redis中的所有数据用于以后的数据恢复,但这个RDB文件尽管通过子线程生成,但每次生成一个大文件在保存还是耗费资源。

3.4 forkless RDB

解决方案就是采用复制队列的方式,由原来的一次拷贝一个大的(越大越慢,会造成阻塞),改成一个key一个key的往队列拷贝生成的方式。

拷贝期间数据变化怎么办:把变化过程也拷贝到队列里。

拷贝期间发生rehash导致里面的key顺序重排怎么办:暂停rehash.

img

这里的rehash 又是什么意思呢?

redis中有一个全局Hash表(HashTable),表里有一个一个的哈希桶(Bucket),桶里装着Entry(存放key,value的指针),数据量特别大的时候会发生Hash冲突行成链表,如下图的Bucket2.

在这里插入图片描述

解决方案:rehash,扩容。

创建一个比原来还大的HashTable,向其中传递原来HashTable的数据,并对存在Hash冲突的地方进行哈希桶的重新分配。

在这里插入图片描述

这就是为什么美团的forkless RDB执行的时候需要停止rehash了,因为要一个一个的拷贝key,并且有游标指向key的位置,但rehash发生后,key的位置就会发生变化。

3.5 工作多线程

原生Redis:IO多线程,工作线程为单线程。

修改为:IO多线程,工作线程也是多线程,通过加锁解决线程安全问题。

img

3.6 Squirrel可用性的挑战

只有两个机房,A选B,B选A,选不出主或者造成脑裂,必须部署3机房,但通常两个副本足够用了,没必要部署3个副本。

3.7 两机房容灾

img

参考 Google Spanner(可伸缩分布式数据库)的见证者投票方式。

简单说就是:

还是三个机房,但保存两个副本就行了,另外一个只投票,不保存副本。

3.8 跨地域容灾

img

3.9 双向同步冲突自动解决

img

简单说就是谁的时间新就保存谁的。

服务器发生了时间回退怎么办:保存数据时使用时间戳,发生回退了依然使用时间戳来防止数据跟着回退。

两个值的更新时间一样怎么办:比较集群ID,保存集群ID大的那一个。(个人理解类似足球比赛积分净胜球等都相同,靠抽签决定谁进入下一轮)

由复制操作改为复制变更后的数据:只考虑操作后的数据,而不是复制操作。

链图片转存中…(img-qMRzytbP-1717929022879)]

3.9 双向同步冲突自动解决

[外链图片转存中…(img-CDljEhDy-1717929022880)]

简单说就是谁的时间新就保存谁的。

服务器发生了时间回退怎么办:保存数据时使用时间戳,发生回退了依然使用时间戳来防止数据跟着回退。

两个值的更新时间一样怎么办:比较集群ID,保存集群ID大的那一个。(个人理解类似足球比赛积分净胜球等都相同,靠抽签决定谁进入下一轮)

由复制操作改为复制变更后的数据:只考虑操作后的数据,而不是复制操作。

保存最近删除的 Key:简单说就是B集群同步复制A集群数据的时候,对于复制过来一个已经不存在的数据,先不要创建,而是到删除库中看一看这个数据是不是已经被删除了。

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

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

相关文章

[Mdfs] lc3067. 在带权树网络中统计可连接服务器对数目(邻接表+图操作基础+技巧+好题)

文章目录 1. 题目来源2. 题目解析 1. 题目来源 链接:3067. 在带权树网络中统计可连接服务器对数目 2. 题目解析 挺有意思的一道题目,重点是要能够读懂题目,然后结合几个图相关的处理技巧即可拿下。 图存储:邻接表即可。无向无…

【Redis】什么是Redis缓存 雪崩、穿透、击穿?(一篇文章就够了)

目录 什么是Redis? Redis的正常存储流程? 什么是Redis缓存雪崩? 缓存雪崩 缓存预热 缓存失效时间的随机性 什么是Redis缓存穿透? 缓存穿透 缓存空对象 BloomFilter(布隆过滤器) 什么是Redis缓存击穿&#…

LabVIEW 用于 MES 系统和卡钳上位机检测

LabVIEW 确实可以用于制造执行系统(MES)的开发以及卡钳上位机检测。以下是详细说明: 使用 LabVIEW 开发 MES 系统 数据采集与处理:LabVIEW 擅长实时数据采集和处理,可以连接多种传感器和设备,获取生产线上…

Spark作业运行异常慢的问题定位和分析思路

一直很慢 🐢 运行中状态、卡住了,可以从以下两种方式入手: 如果 Spark UI 上,有正在运行的 Job/Stage/Task,看 Executor 相关信息就好。💻 第一步,如果发现卡住了,直接找到对应的…

YoloV9改进策略:主干网络篇|MobileNetV4主干替换YoloV9的BackBone(独家原创)

摘要 今年,轻量级王者MobileNetV4闪亮登场!在我们这篇文章里,我们把MobileNetV4加入到了YoloV9中,对MobileNetV4的层数和卷积层核做了适当的修改,然后替换原有的BackBone。哈哈,你猜怎么着?效果…

整除及求余运算符、数字的提取、顺序结构程序

1.运算符 在有余数的除法运算中,如果要知道商和余数分别是多少,可以用/和%这两个运算符号来得到。 (1)/(整除),当被除数和除数均为整数时,结果也为整型,只取商的整数部分。 如:10/25 10/33 5/10 0 (2)%(求余)&…

2024年最详细的Studio One 6.6.1中文破解版图文安装激活指南(附Keygen下载)

Studio One 6是一款非常专业的音乐创作编辑软件。为用户提供了所有一切你所需要创作的功能,包括所有的歌曲、项目、仪表板等动能,而且还自定义添加配置文件,良好的界面交互和丰富的功能板块,再结合优秀的性能,能够满足…

苹果跌穿4500元,反噬国产手机,这算是自作自受吧!

618大促还在进行中,苹果仍然在降价,iPhone15已是618大促最畅销的手机,国产手机没有谁能超越它了,或许是国产手机眼见着已无法击败苹果,选择涨价,能卖一部算一部,多赚一点钱了吧。 苹果的iPhone1…

工业互联网数字中台建设方案(ppt原件)

工业互联网数字中台解决方案旨在为企业提供全面、高效的数据驱动能力。该方案主要包括以下几个核心部分: 数据中台:作为核心,数据中台负责汇聚、整合、提纯和加工各类工业数据,实现数据资产的标准化、模型化和模块化。通过提供API…

【Oracle】Oracle导入导出dmp文件

文章目录 前言一、什么是dmp?二、imp/impdp、exp/expdp对比及示例1.区别2.imp/impdp对比及示例a. impb. impbp 3.exp/expdp对比及示例a. expb.expdp 3.其他事项 三、执行导入导出前置条件1.创建角色并授权2.创建目录映射 前言 在工作中,经常会遇到需要备…

基于关键词自动采集抖音视频排名及互动数据(点赞、评论、收藏)

在当今的社交媒体时代,抖音作为一个热门短视频平台,吸引了大量用户和内容创作者。对于研究和分析抖音上的热门视频及其互动数据(如点赞、评论、收藏等),自动化的数据采集工具显得尤为重要。本项目旨在开发一个基于关键…

Linux基础I/O

一&#xff0c;系统文件I/O 写文件: #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main() {umask(0);int fd open("myfile", O_WRO…

【Java】解决Java报错:StackOverflowError

文章目录 引言1. 错误详解2. 常见的出错场景2.1 无限递归2.2 递归深度过大2.3 方法调用层次过深 3. 解决方案3.1 优化递归算法3.2 尾递归优化3.3 增加调用栈大小3.4 检查递归终止条件 4. 预防措施4.1 使用迭代替代递归4.2 尾递归优化4.3 合理设计递归算法4.4 调整JVM参数4.5 定…

【Qt】Qt QTreeWidget隐藏列名称(横向表头)

1. 效果 未隐藏 隐藏 2. 方法 方法1 ui->treeWidget->header()->hide();方法2 ui->treeWidget->header()->setVisible(false);

RDK X3(aarch64) 测试手柄

0. 环境 - 亚博智能的ROSMASTER-X3 标准版 - XDK X3 1.0 - 冰原狼等win10免驱的手柄 1. RDK X3 1.0 串口通信 波特率 921600 root/root mobaterm -> Session -> VNC -> 192.168.8.108:5900 -> runrise 2. 测试 ROSMASTER-X3 标准版 配套的手柄 安装 …

【Java SE】字符串常量池详解,什么情况下字符串String对象存在常量池,通过==进行判断,字符串创建及截取后是否同一个对象

复习字符串创建方式 字符串的31种构造方法 public String();创建一个空白字符串&#xff0c; 不含有任何内容public String(char[] array);根据字符数组的内容&#xff0c;来创建对应的字符串public String(byte[] array);根据字节数组的内筒&#xff0c;来创建对应的字符串 …

物联网设计竞赛_8_Jetson Orin Nano安装pytorch与torchvision

我的新板子到了&#xff0c;型号是jetson orin Nano与之前的jetson nano稍有不同我发现库又得从新下载 我的pip3的版本是3.8.10&#xff0c;jetpack版本5.1.1&#xff0c;又得重新开始下载库&#x1f62d; 安装pytorch: 得科学上网&#xff1a; PyTorch for Jetson - Jetson …

U-Net: Convolutional Networks for Biomedical Image Segmentation--论文笔记

U-Net: Convolutional Networks for Biomedical Image Segmentation 资料 1.代码地址 2.论文地址 https://arxiv.org/pdf/1505.04597 3.数据集地址 论文摘要的翻译 人们普遍认为&#xff0c;深度网络的成功训练需要数千个带注释的训练样本。在本文中&#xff0c;我们提出…

nodejs最新某东h5st(4.7.2)参数分析与javascript逆向纯算法还原(含算法源码)(2024-06-09)

一、作者声明&#xff1a; 文章仅供学习交流与参考&#xff01;严禁用于任何商业与非法用途&#xff01;否则由此产生的一切后果均与作者无关&#xff01;如有侵权&#xff0c;请联系作者本人进行删除&#xff01; 二 、写在前面 h5st从4.1一路更新到4.7.2&#xff0c;逐渐vmp…

57.Semaphore信号量

用来限制能同时访问共享资源的线程上限。只是适合限制单机线程数量。 Slf4j public class SemaphoreDemo {public static void main(String[] args) {Semaphore semaphore new Semaphore(3);for (int i 0; i < 10; i) {new Thread(() -> {try {semaphore.acquire();//…