每个子查询都会先尝试获取缓存中的数据 , 此时有两种结果:
解析
缓存未命中
如果子查询结果在缓存中不存在 , 即 cache miss 。只需要将调用 DruidBorker 获取数据 , 异步写入缓存中 , 同时该子查询缓存的修改的时间即可 。
缓存命中
在谈论命中之前 , 首先引入一个概念「阈值时间(threshold_time)」 。它表示缓存更新前的一段时间(一般为10min) 。我们默认缓存中的数据是不被信任的 , 因为可能因为数据积压等情况导致一部分数据延迟入库 。
如果子查询命中了缓存 , 则存在两种情况:「缓存部分命中」和「缓存完全命中」 。其中部分命中如下图所示 。
- 缓存部分被命中:
文章插图
- 缓存完全命中:
- 而缓存完全命中则是种理想形式:end_time > cache_update_time - threshold_time 。这种情况说明了缓存被完全命中 , 缓存中的数据都可以被相信 , 这种情况下直接拿出来就可以了 。
文章插图
经过上述分析不难看出:对于距离现在超过一天的查询 , 只需要查询一次 , 之后就无需访问 DruidBroker 了 , 可以直接从缓存中获取 。
而对于一些实时热数据 , 其实只是查询了
cache_update_time-threshold_time 到 end_time 这一小段的时间 。在实际应用里 , 这段查询时间的跨度基本上在 20min 内 , 而 15min 内的数据由 Druid 实时节点提供 。
3.2.2 维度组合子查询设计
维度枚举查询和时间序列查询不一样的是:每一分钟 , 每个维度的量都不一样 。而维度枚举拿到的是各个维度组合在任意时间的总量 , 因此基于上述时间序列的缓存方法无法使用 。在这里 , 核心思路依然是打散查询和缓存 。对此 , 微信团队使用了如下方案:
缓存的设计采用了多级冗余模式 , 即每天的数据会根据不同时间粒度:天级、4小时级、1 小时级存多份 , 从而适应各种粒度的查询 , 也同时尽量减少和 Redis 的 IO 次数 。
每个查询都会被分解为 N 个子查询 , 跨度不同时间 , 这个过程的粗略示意图如下:
文章插图
举个例子:例如 04-15 13:23 ~ 04-17 08:20 的查询 , 会被分解为以下 10 个子查询:
04-15 14:00 ~ 04-15 15:00
04-15 15:00 ~ 04-15 16:00
04-15 16:00 ~ 04-15 20:00
04-15 20:00 ~ 04-16 00:00
04-16 00:00 ~ 04-17 00:00
04-17 00:00 ~ 04-17 04:00
04-17 00:00 ~ 04-17 04:00
04-17 04:00 ~ 04-17 08:00
04-17 08:00 ~ 04-17 08:20
这里可以发现 , 查询 1 和查询 10 , 绝对不可能出现在缓存中 。因此这两个查询一定会被转发到 Druid 去进行 。2~9 查询 , 则是先尝试访问缓存 。如果缓存中不存在 , 才会访问 DruidBroker , 在完成一次访问后将数据异步回写到 Redis 中 。
维度枚举查询和时间序列一样 , 同时也用了 update_time 作为数据可信度的保障 。因为最细粒度为小时 , 在理想状况下一个时间跨越很长的请求 , 实际上访问 Druid 的最多只有跨越 2h 内的两个首尾部查询而已 。
3.3 更进一步-子维度表
通过子查询缓存方案 , 我们已经限制了 I/O 次数 , 并且保障 90% 的请求都来自于缓存 。但是维度组合复杂的协议 , 即 Segments 过大的协议 , 仍然会消耗大量时间用于检索数据 。
推荐阅读
- 杨紫|杨紫微信名曝光,对好友有求必应,是个既善良也幽默的姑娘
- 微信锁定功能怎么用 微信新增锁定功能使用教程[多图]
- 热搜!微信新增锁定功能!网友热议......
- 教你如何打开微信隐藏的拍照功能?打开后照片清晰10倍,简单实用
- 微信视频通话也能开美颜了,附开启方法
- 微信视频聊天怎样才能美颜?
- iPhone “应用锁”来了,可以给微信等 App“上锁”,操作简单
- 微信昵称特殊符号太阳怎么设置 微信昵称特殊符号不让用
- 微信这个开关要尽快清理,不然微信被别人登录了,你可能都不知道
- 微信关掉哪些功能节约内存 关闭微信的这三项功能