引言
在redis里面,我们经常会涉及到一个严重的问题,就是缓存的击穿,雪崩和穿透
缓存击穿:当key值过期的一瞬间,同一个时间有大量的请求访问一个相同的数据,导致所有请求都落在了数据库上面,数据库崩溃
缓存穿透:大量的key值同时间失效,导致请求全部都落在了数据库上面
缓存雪崩:查询不存在的数据,导致大量请求落在了数据库上面
为了解决这个问题,我们引进了SingleFlight机制,这个机制的主要目的是防止重复的昂贵操作,比如访问数据库,其核心思想就是合并重复的请求,只允许有一个请求访问数据资源。
代码实现
我们要一个Func,这个函数的目的就是把传进来的请求转化成需要的数据返回
using Result = std::optional<ByteView>; using Func = std::function<Result()>;这是一个线程间通信的结构体,涉及到两个,这两个类一般是同时间出现的,一个是promise,一个是future,promise是把结果从一个线程传递到另一个线程,而future是负责等待并且获取结果。那么我们获取结果,如果是future.get(),只可以获取一次,而我们的目的是对于多个相同的请求只获取第一个请求,但是这并不意味不回应其他的请求了,所以我们需要对一个相同的数据读取很多次,然后返回给那些客户端
struct Call { /* std::promise 是 C++11 提供的一个工具,用来把结果从一个线程传给另一个线程。 它通常和 std::future 配合使用 promise:负责设置结果。 future:负责等待并获取结果。 一个 promise 通常只能设置一次结果,不适合反复传数据 future.get() 只能调用一次;需要多次读取可以用 std::shared_future 如果 promise 销毁时还没提供结果,接收方的 get() 会抛出 broken_promise 对应的 std::future_error */ std::promise<Result> prom; // get_future():从 promise 拿到接收结果的 future share():把这个 future 转成可以共享的 shared_future std::shared_future<Result> fut = prom.get_future().share(); };最后就是一个核心的调用接口DO,我们只需要传入键值还有怎么找到这个数据的函数,就可以返回我们需要的结果。这个map里面存储的是都是正在进行调用的call,因为当收到了多个相同的请求之后,我们只会处理第一个请求,那么当一个key到来的时候,我们会存储这个key值对应的call,当同样的key来的时候,我们会查map,如果发现key值存在,就说明我们的call还在调用,这个时候就不需要调用我们传进来的func函数,而是直接等待future的结果,也就是前面的请求处理之后的结果。那如果这是第一个请求,那么先要把key存储进去,然后调用函数,当得到结果之后再删除key值。注意一下子我们锁的释放和加锁的地方,我们只在访问map的地方加锁。
然后我们可以理解一下promise和future的使用,call在这里是一个智能指针,多个线程都可以访问这个智能指针,第一个线程得到结果之后设置promise的值,其他的线程就可以通过future来得到这个。而多线程之间怎么共享这个map,其实我们之后会涉及到一个内存组的概念,这个组之间可以共享一个map
Result Do(const std::string& key, Func func) { std::unique_lock<std::mutex> lock(mutex_); // 检查是否有进行中的调用 if (map_.find(key) != map_.end()) { auto existing_call = map_[key]; lock.unlock(); // 释放组锁,避免阻塞其他键的处理 // 直接等待 future 的结果 auto result = existing_call->fut.get(); return result; } auto new_call = std::make_shared<Call>(); map_[key] = new_call; lock.unlock(); // 执行用户函数并设置promise Result val = func(); new_call->prom.set_value(val); // 确保从映射中删除条目 std::lock_guard<std::mutex> lock_(mutex_); map_.erase(key); return val; }总结
本篇文章到这里就结束了!!!希望可以帮助大家理解~~~