泛读笔记:《DEX: Scalable Range Indexing on Disaggregated Memory》
文章发布时间:
最后更新时间:
文章总字数:
预计阅读时间:
页面浏览: 加载中...
最后更新时间:
文章总字数:
2.4k
预计阅读时间:
8 分钟
页面浏览: 加载中...
1. 摘要
内存分离有可能让内存优化的范围索引(如B+树)在超出单一机器的范围内扩展,同时实现高硬件利用率和低成本。然而,在内存分离架构上设计可扩展的索引是具有挑战性的,主要是因为基础缓存、不规范的卸载以及服务器之间的一致性过度问题。
本文提出了DEX,一种新的用于内存分离的可扩展B+树。DEX包括一组减少远程访问的技术:逻辑分区、轻量级缓存和成本感知卸载。我们的评估显示,DEX可以比现有技术提高到1.7到56.3倍的性能,并且在不同的设置下,如缓存大小和数据倾斜,依然存在优势。
2. 背景
- 单机内存数据库:
- 内存容量有限
- 只有负载接近满载时高成本才能得到回报
- 内存分离架构:
- 将内存和计算分开到各自的服务器池中,并通过快速网络将二者互联
- 灵活地独立扩展计算线程和内存大小,实现高利用率和低成本
- 缓存和计算下推:
- 远程内存访问延迟较高,简单实现从根节点到叶子节点每层都需要一次RDMA操作
- 计算服务器有少量本地内存 => 计算服务器缓存
- 内存服务器有少量计算资源 => 简单计算可以下推(卸载)到内存服务器
3. 面临挑战
- 缓存问题
- 软件开销更加明显:
- 传统的许多研究集中在如何提高缓存命中率上,本地和远程内存的访问延迟差距(10倍)比存储器之间的差距小(SSD1000倍),因此与缓存维护和同步相关的软件开销变得更加明显
- 计算服务器不应假设一定数量的本地内存:
- 如果本地内存严重受限,非树形索引的通用缓存机制可能表现不佳
- 软件开销更加明显:
- 卸载策略
- 卸载利用了内存服务器上有限的计算资源,以减少RDMA通信
- 应当避免在内存服务器上占用有限的计算资源,决策(什么、何时以及多少进行卸载)对于避免过载并保证可扩展的性能至关重要
- 一致性问题
- 内存分离带来了处理不同来源的数据不一致的挑战,但现有的基于RDMA的分布式同步锁定成本高昂
- 如果在计算服务器上实现缓存,则必须确保所有计算端缓存之间的一致性,这种复杂性随着计算服务器数量的增加而增长
- 如果考虑到计算端缓存和内存端卸载,一致性问题将更加严重

4. 本文方法
- 计算端采用逻辑分区
- 每个计算服务器逻辑上“拥有”一组键范围,而内存服务器仍然提供全局可寻址的共享空间
- 减少了计算服务器之间的缓存一致性成本以及RDMA远程内存访问同步开销
- 易于负载均衡和增删计算服务器:逻辑分区只需调整路由,而不需要物理重新划分数据
- 轻量级路径感知缓存
- 提出了一种基于随机采样的轻量级缓存替换策略,减少了竞争并实现了高可扩展性
- 为了减少缓存未命中的情况,利用应用层信息进行路径感知缓存,将频繁访问的索引路径保留在缓存中。
- 子B+树节点只能在其父节点被缓存后才能被加入计算端缓存中,父节点在其所有子节点被逐出之前不会被逐出。这不仅提高了缓存效率(因为靠近根节点的节点更热),还使得计算和内存服务器之间的卸载更加有效,降低了一致性开销。
- 成本感知卸载
- 在运行时跟踪内存服务器的资源可用性
- 仅在卸载成本更低时才进行卸载
5. 计算服务器的逻辑分区
- 每个计算服务器都拥有一个逻辑分区
- 可以使用各种范围分区方案
- 例如等宽或工作负载感知
- 要求每个叶节点仅由一个分区(即一个计算服务器)拥有
- 分区的边界使用最底层内部节点中的键
- 跨服务器同步和缓存一致性的需求限制在仅内部节点上(内部节点更新频率低)
- 重新分区非常快
算法:
- 如果某节点缓存未命中
- 要么将剩余索引操作卸载到内存服务器
- 要么将节点从内存服务器读取到计算服务器(共享节点使用乐观锁)
- 内部节点过时使其搜索到错误的子节点时,才从远程内存中引入内部节点的新副本
- 如果在过时的内部节点能找到正确的叶子节点,就没有必要从远程内存引入
6. 计算端缓存
- 使用一张hash表(mapping table)和多个FIFO队列,节点根据指针hash值放入不同FIFO队列中
- 热节点的父亲指向该节点使用本地指针,冷节点父亲指向该节点使用全局指针,通过最高位区分
- 当有FIFO队列满的时候,随机挑选队列中一组(本文实现是两个)节点,将其脏数据刷回内存池,将其变为冷节点,它会成为被驱逐的候选者,如果冷节点被再次访问到,就把它变成热节点
- 路径感知:如果中间节点需要变冷,且它有孩子是热节点,那么冷却命令会传递给它的一个热孩子,它本身还是热节点
- “I/O” flag(乐观锁):对于正在remote read的节点,会先在mapping table插入一个“I/O” flag来避免重复的RDMA读取,其他线程遇到该标志会从头重新开始
- 延迟接纳(可选特性):节点在远程读之后有一定概率留在cache中,文中设置内部节点概率为1,叶子节点留存概率为0.1
- 实验对比:FIFO在并行度高时争用大,扩展性不佳,Cooling Map扩展性好
7. 成本感知卸载
- 因为路径感知的缓存策略,当一个节点未命中时,其子节点也可能会未命中,所以卸载是有利的
- 如果1)节点不专属于该分区 或者 2)节点高度L>M,则不会卸载
- 确保卸载仅限于一个计算服务器和一个内存服务器
- 当卸载报告会在内存服务器上触发结构修改操作(SMO)时,将退回到正常处理路径
- 因为SMO(例如叶子节点分裂)可以向上传播到第𝑀 + 1层或更高层的节点
- 根据最近的样本估算代价
- 正确卸载需满足两个条件(卸载的一致性问题):
- 问题简化:
- 卸载仅发生在一对计算服务器和内存服务器之间
- 如果推送线程操作某子树,那么子树所有节点要么已经被驱逐,要么处于冷却状态
- 在卸载进行时,没有其他计算线程正在操作子树在缓存中的节点或使用RDMA检索子树中缺失的任何节点
- 在卸载操作开始之前,会将子树根的父节点固定在热状态,以防止其被驱逐
- 在卸载操作开始之前,会将映射表中子树根节点值设置“I/O”flag,以防止其被其他节点检索或操作
- 在卸载过程中对节点的任何更新都必须传递回计算节点,以使相应的缓存副本失效
- 内存侧的卸载线程在成功时返回更新节点的全局地址(或在遇到任何SMO时返回失败状态)
- 卸载请求返回后,计算侧工作线程会检查映射表,确定是否有更新节点的缓存副本,如果有,则通过从映射表中删除其引用来使其失效
- 最后取消子树根的父节点固定,并从映射表移除子树根节点
- 问题简化:
8. 索引操作:插入
- 插入操作可能会导致节点分裂
- 对于独属于该分区的节点,使用一种积极的分裂策略,即在自顶向下遍历过程中遇到的任何满的节点都会立即分裂
- 当一个节点𝑁在当前分区中已满,而它的父节点𝑃是共享的:
- 获取𝑃的全局锁,并从内存池中检索其最新版本
- 如果满足两个条件:1)缓存的𝑃是最新的,且2)𝑃不是满的,则进行当前节点的分裂并完成
- 如果不满足条件:
- 放弃正在进行的节点分裂,并从远程根节点触发缓存刷新
- 对于路径中的共享节点,如果它们是满的,则立即进行节点分裂
- 最后重试插入操作