目录
简介
Dragonfly 是一款高性能的缓存中间件,与 Redis 和 Memcached API 完全兼容,无缝对接(意思就是开发人员直接改一下配置文件的链接地址即可,把他当redis用)。Dragonfly 在多线程、无共享架构之上实现了新颖的算法(2Q算法)和数据结构(Dash稳定哈希结构)。因此,与 Redis 相比,Dragonfly 的性能达到了 x25,并且在单个实例上支持数百万 QPS。
在redis官方得知dragonfly可能威胁到统治地位存在的时候,为了扳回一成,redis官方测试,在相同的集群环境下redis的吞吐量比Dragonfly高18%
Dragonfly 的核心特性使其成为经济高效、高性能且易于使用的 Redis 替代品。
官网地址:Dragonfly
短短75天在github关注突飞,突破10k github star
安装
建议在 linux 5.11 或更高版本上运行它,但您也可以在旧内核上运行 Dragonfly。
1.安装docker
如果您的机器上没有 docker,请先安装 Docker,然后再继续。
2.直接运行
docker run --network=host --ulimit memlock=-1 docker.dragonflydb.io/dragonflydb/dragonfly
docker容器启动后,默认端口6379
您可以使用redis-cli
连接localhost:6379
或打开浏览器并访问http://localhost:6379
redis-cli
127.0.0.1:6379> set hello world2022
OK
127.0.0.1:6379> keys *
1) "hello"
127.0.0.1:6379> get hello
"world2022"
127.0.0.1:6379>
底层原理
LRU
顾名思义,最近最少使用 (LRU) 缓存策略驱逐最近最少使用的项目。它之所以这样工作,是因为缓存算法努力优化命中率,或未来访问其项目的概率。
如果缓存已满,则需要腾出项目以腾出空间来添加新内容。假设最近最少使用的项目也是最不值钱的,缓存通过删除最不有价值的项目来为新添加的项目释放空间。
这个假设是合理的,但不幸的是,如果上面的假设不成立,这个算法的表现就会很差。例如,考虑具有长尾分布的访问模式。这里,y 轴表示缓存中项目的归一化访问频率,x 轴表示从最高频率到最低频率排序的项目。
LRU 执行效率
LRU 是一种可以高效实现的简单算法。实际上,它维护了一个双链表中的所有项目。当一个项目被访问时,LRU 将它移动到列表的头部。为了腾出 LRU 项目,它从列表的尾部弹出。见上图。所有操作都在 中完成O(1)
,每个项目的内存开销是 2 个指针,即 64 位架构上的 16 个字节。
Redis 中的 LRU
Redis 实现了一些驱逐策略启发式。其中一些被描述为“近似 LRU”。为什么要近似?因为 Redis 不像经典 LRU 那样在其项目之间保持精确的全局顺序。相反,它将最后一次访问时间戳存储在每个条目中。
当需要驱逐一个项目时,Redis 对整个键空间进行随机抽样并选择 K 个候选者。然后它在这 K 个候选者中选择最近最少使用时间戳的项目并将其腾出。这样,Redis 为在一个全局订单中排序项目所需的每个条目节省了 16 个字节。这种启发式是 LRU 的非常粗略的近似。Redis 维护人员最近讨论了通过实施具有全局顺序的经典 LRU 策略向 Redis 添加额外启发式的可能性,但最终决定反对。
Dragonfly缓存
实现缓存:
- 与 LRU 不同,它可以抵抗近期流量的波动。
- 不需要像 Redis 中那样的随机抽样或其他近似值。
- 每个项目的内存开销为零。
- 具有非常小
O(1)
的运行时开销。
这是一种新颖的缓存设计方法,以前在学术研究中没有提出过。
Dragonfly Cache(dash 缓存)基于1994年的另一篇著名的缓存策略——“2Q: A Low Overhead High Performance Buffer Management Replacement Algorithm”。
2Q 通过引入两个独立的缓冲区来解决 LRU 的问题。2Q 不仅将新近度作为一个因素考虑,还考虑了每个项目的访问频率。它首先将最近的项目纳入所谓的试用缓冲区。这个缓冲区只保存了缓存空间的一小部分,比如不到 10%。所有新添加的项目在此缓冲区内相互竞争。
2Q实施
Dragonfly 扩展了上述想法。一个简单的解决方案是将哈希表条目划分为两个缓冲区:一个具有 FIFO 排序的试用缓冲区,以及一个使用 LRU 链表的受保护缓冲区。这可行,但需要使用额外的元数据并浪费宝贵的内存。
相反,Dragonfly 利用了Dashtable的独特设计,并利用其弱排序特性为其优势。
为了解释 2Q 如何与 Dashtable 一起工作,我们需要解释我们如何在那里定义试用和受保护的缓冲区,我们如何将试用项目提升到受保护的缓冲区以及我们如何从缓存中驱逐项目。
我们在原始 Dashtable 上覆盖了以下语义:
-
存储桶中的插槽现在具有等级或优先级。左边的槽位最高
(0)
,右边的最后一个槽位最低(9)
。 -
段内的存储桶用作试用缓冲区。当一个新项目被添加到整个段时,它被添加到插槽 0 的存储桶中。桶中的所有其他项目都向右移动,并且桶中的最后一个项目被驱逐。这样,存储桶就充当了试用项目的 FIFO 队列。
-
每个缓存命中“提升”其项目:
-
如果该项目在存储桶中,它会立即移动到其主存储桶中的最后一个插槽。
-
如果它在 slot 的 home 存储桶中
i
,它会被 slot 的一个项目换掉i-1
。 -
插槽中的项目
0
保持在同一个位置。
-
-
当一个试用项目被提升到受保护的存储桶时,它会被移动到那里的最后一个插槽。之前存在的项目被降级回试用期。
基本上,Dash-Cache 驱逐策略由(2)描述的“驱逐”步骤和(3)描述的正强化步骤组成。
而已。不需要额外的元数据。高质量的物品会很快出现在他们的主存储桶中的高级插槽中,而新添加的项目会在存储/试用存储桶中相互竞争。在我们的实现中,每个存储桶有 14 个插槽,这意味着每个试用项目在被从缓存中驱逐之前可以移动 14 次,除非它证明了它的有用性并被提升。每个段有 56 个常规桶和 4 个存储桶;因此,我们6.7%
为试用缓冲区分配了总空间。在被驱逐之前捕获高质量的物品就足够了。