分布式存储----缓存组

发布时间:2026/10/14 9:47:39
分布式存储----缓存组 缓存组这个是分布式缓存里面的核心设计具有本地缓存远程结点访问数据加载的能力具体来说就是当有数据被访问之后会存在本地通过lru算法如果在本地没有访问找到数据就会向其他结点发出请求查询是否有请求的数据如果还是没有找到就会在数据库里面加载数据所以这个设计允许结点之间相互访问数据同时保持本地的高效访问一个group里面的的核心是PeerPicker我们客户要查询数据的时候会传递组的名字所以我们会直接锁定我们要查询的组然后会在本地缓存里面查询数据如果没有查询到数据就会通过一致哈希性尝试找到我们需要的虚拟结点但是负责的结点不一定有这个数据的缓存如果没有找到那么需要加载数据从数据库或者指定的文件路径把这个数据放到该结点里面。这里面一定要注意一个结点会有多个缓存组这也说明多个结点会存同一个数据当本地结点没有数据的时候会通过一致性哈希找到确定的结点而多个结点存同一个数据的目的就是为了当客户端连接到结点时候可以不用访问其他结点而是直接从本地缓存取出数据即可每一个CacheGroup都代表了一个独立的缓存空间使用LruCache作为本地缓存通过singleFlight防止缓存击穿每一个结点即使客户端也是服务器端InvalidateFromPeer这个函数很好的验证了这一点我们客户端连接的时候是随意连接这个组里面的某一个结点的所以当通过一致性哈希发现这个结点在其他地方的时候我们会通过gRPC向远程结点请求数据如果就是当前这个结点那么就直接调用getter。所以当一个结点收到其他结点的请求的时候会把这个数据发送给目标节点那么目标结点也会存同样一个数据。这就会导致多个结点有着同一份数据当用户需要修改数据的时候会通知其中的一个结点为了防止其他有着相同数据的结点存放着失效的数据我们会用广播的形式通知那些存放数据的结点删除原来失效的数据、然后我们每一个结点里面的缓存组存储数据也是LRUclass CacheGroup { public: CacheGroup() default; CacheGroup(std::string_view name, int64_t bytes, DataGetter getter) : cache_(std::make_uniqueLRUCache(bytes)), name_(name), getter_(getter) {} CacheGroup(const CacheGroup) delete; auto operator(const CacheGroup other) - CacheGroup delete; CacheGroup(CacheGroup other) { cache_ std::move(other.cache_); name_ std::move(other.name_); getter_ std::move(other.getter_); } auto operator(CacheGroup other) - CacheGroup { cache_ std::move(other.cache_); name_ std::move(other.name_); getter_ std::move(other.getter_); return *this; } auto Get(const std::string_view key) - ByteViewOptional; bool Set(const std::string_view key, ByteView data); bool Delete(const std::string_view key); // 处理来自其他结点失效的请求 bool InvalidateFromPeer(const std::string_view key); private: auto Load(const std::string_view key) - ByteViewOptional; auto LoadData(const std::string_view key) - ByteViewOptional; std::unique_ptrLRUCache cache_; // 本地缓存 std::string_view name_; std::atomicbool is_close_{false}; DataGetter getter_; SingleFlight loader_; GroupStatus status_; };对于这一个类我们对外提供了两个接口一个是创建缓存组一个是得到缓存组我们一般不会调用其构造函数。我们请求结点的时候通过gRPCgRPC Server就会接收到请求之后找到结点得到或者创建缓存组auto MakeCacheGroup (const std::string_view name, int64_t bytes, DataGetter getter) - CacheGroup; auto GetCacheGroup (const std::string_view name) - CacheGroup*;auto MakeCacheGroup(const std::string_view name, int64_t bytes, DataGetter getter) - CacheGroup { if (getter nullptr) { std::exit(1); } std::lock_guardstd::mutex lock(mutex_); cache_groups[name] std::move(CacheGroup{name, bytes, getter}); return cache_groups[name]; } auto GetCacheGroup(const std::string_view name) - CacheGroup* { std::lock_guardstd::mutex lock(mutex_); if (cache_groups.find(name) cache_groups.end()) { return nullptr; } return cache_groups[name]; }这一个是加载数据加载数据从数据库里面加载用到了SingleFlight机制用lambda表达式把我们需要的函数传递进去得到数据之后我们还需要更新缓存这个getter_是一个function传递进去的是std::string_view返回ByteViewOptional我们初始化getter_的初始化是在CacheGroup的构造函数里面传递进去auto CacheGroup::Load(const std::string_view key) - ByteViewOptional { auto ret loader_.Do(key.data(), [] {return LoadData(key);}); if (!ret) { return std::nullopt; } // 更新本地缓存 cache_-Set(key, ret.value()); return ret; } auto CacheGroup::LoadData(const std::string_view key) - ByteViewOptional { auto val getter_(key); if (!val) { return std::nullopt; } status_.local_hits; // 本地缓存次数加1 return val; }得到数据就是从cache里面通过LRU算法取数据如果没有就调用load从本地缓存加载数据我们这里set数据之后会同步数据但是这个环节还不在这一块完成在后面会一一实现auto CacheGroup::Get(const std::string_view key) - ByteViewOptional { if (is_close_) { return std::nullopt; } if (key ) { return std::nullopt; } // 先从本地缓存中获取 auto ret cache_-Get(key); if (ret) { status_.local_hits; // 本地缓存命中1 return ret; } status_.local_misses; // 本地缓存未命中1 return Load(key); } bool CacheGroup::Set(const std::string_view key, ByteView data) { if (is_close_) { return false; } if (key.empty()) { return false; } cache_-Set(key, data); return true; } bool CacheGroup::Delete(const std::string_view key) { if (is_close_) { return false; } if (key.empty()) { return false; } cache_-Delete(key); return true; } bool CacheGroup::InvalidateFromPeer(const std::string_view key) { if (is_close_) { return false; } if (key.empty()) { return false; } // 其他节点通知当前节点这个 key 的缓存可能已经过期不能继续用了所以要删除本地这份缓存避免后续返回旧数据 /* A 的缓存用户名字 张三 B 修改了数据用户名字 李四 ↓ B 向 A 发送失效请求 ↓ A 删除本地缓存中的 张三 ↓ 下次查询时缓存未命中再走正常加载流程获取新数据 */ cache_-Delete(key); return true; }总结本篇文章到这里结束了希望可以帮助大家理解~~~~

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询