Page 430 - 《软件学报》2025年第5期
P. 430
2330 软件学报 2025 年第 36 卷第 5 期
个写线程可以向目标 block 和溢出缓存中写入数据. 写操作完成后, 使用 CAS 指令原子地增加块元数据中版本字
段 version 并释放锁. 读线程无需获取锁, 它首先读取版本锁的快照, 并检查锁是否被并发写入线程占用. 如果是的
话, 读线程会等待直到写线程释放锁. 当读操作完成后, 读线程会再次读取版本信息, 以判断在读取过程中数据是
否被其他写线程修改. 如果两次读取的版本不匹配, 则会重新尝试整个操作. 锁存储在 DRAM 中, 避免了 PM 访
问, 从而节约 PM 带宽, 提高了索引并发性能.
PLTree 在指向叶节点的槽位中设置了叶节点锁 lock. 当叶节点进行扩展时获取锁, 此时写入操作会受阻但读
操作不受影响. 接着, 采用写时复制 (copy-on-write) 策略更新叶节点: 首先, 在内存中创建叶节点的副本, 将原叶节
点中的数据排序后写入新创建的叶节点. 当新的叶节点创建完成后, 通过原子操作修改内部节点槽位中的指针
child 指向新的叶节点, 随后释放锁. 为了避免额外的 PM 访问并保证崩溃一致性, 在更新叶节点链表时采取了无
日志方法. 具体做法是, 在叶节点扩展完成后, 将前一个叶节点的备用指针指向新的兄弟节点, 并使用 PM 原子操
作修改其中的 flag 字段来切换指针. 如果此时系统发生崩溃, 则前一个叶节点指向的下一个兄弟节点只会是旧节
点或已扩展的新节点. 若崩溃发生在切换指针之后, 可能会暴露中间状态, 出现槽位中字段 child 指向的旧叶节点
的情况. 但由于新叶节点存储了指向内部节点的指针 fptr, 因此在系统恢复时, 可以通过检查并更新 fptr 指向槽位
中字段 child 来确保崩溃一致性.
4 实验分析
4.1 实验设置
4.1.1 实验环境和对比方案
本文实验在 Linux 服务器上进行, 该服务器操作系统为 64 位 Ubuntu 20.04.5 LTS, 内核版本为 5.4.0. 该服务
器配备 512 GB DRAM 和 4 048 GB 持久化内存, 采用 Intel(R) Xeon(R) Gold 6348 CPU @ 2.60 GHz 双处理器, 每
个处理器拥有 28 个物理核心和 42 MB 三级缓存. 傲腾持久化内存以 App Direct 模式配置, 并通过创建 ext4-DAX
文件系统将其挂载到文件系统进行管理. 为避免 NUMA 效应 [11] , 所有线程均固定到 NUMA 节点 0, 并且只能访问
该节点上的 DRAM 和 PM.
本文选择了 5 个最近的持久化内存索引进行性能对比, 包括最近的持久化学习索引 APEX 以及 4 个基于 B+
树结构的持久化内存索引 (FPTree, uTree, NBTree 和 DPTree). 由于 APLI 未提供源码, PLIN 为纯 PM 架构索引且
实验 [24] 显示 PLIN 性能要低于混合架构的 APEX. 因此本文未将它们加入对比. 与 PLTree 相同, 参加对比的所有
索引均为 DRAM/PM 混合架构. 其中 FPTree, uTree, NBTree 将内部节点和元数据存储在 DRAM 中. DPTree 采用
分层结构, 在 DRAM 中存储缓存树, 当缓存树的大小达到一定阈值后, 使用后台线程将 DRAM 中的缓存树与持久
化树进行合并. APEX 将指纹和锁等元数据存储在 DRAM 中, 内部节点和叶节点均存储在 PM 中. 以上索引均使
用 PMDK 进行持久化内存的管理, 并在都在 ADR 模式 [40] 下运行, 采用 CLWB 和 SFENCE 指令 [39] 确保数据持久
化. 所有索引使用 C++实现并采用-O3 优化编译. 在索引构建时, APEX 和 PLTree 的叶节点初始数据填充率设置
为 50%, 以支持之后的插入操作. 参加对比索引的其他参数均采用原论文中默认配置.
4.1.2 数据集和工作负载
为了测试持久化学习型索引在合成数据和真实数据上的性能表现, 实验中选取了 1 个合成数据集 uniform 和
4 个真实数据集 face [26] , osm [26] , planet [41] , genome [41] . 这些数据集在现有的学习型索引研究中得到广泛使用, 每个数
据集由 2 亿个非重复的 8 B 无符号整数组成. 其中 uniform 数据集由均匀分布的无符号整数组成, face 数据集采样
自 Facebook 用户 ID 数据, osm 和 planet 数据集采样自公开地图 OpenStreetMap 的经纬度数据和 planet ID 数据,
genome 数据集包含人类染色体中的位点对数据. 其中, 真实数据集更难以拟合, 因此相较于合成数据集, 真实数据
集具有更大的挑战性, 适用于评估持久化学习型索引的性能表现.
本文采用常见的工作负载来测试索引性能, 包括只读、读写混合、只写、范围查找和删除操作. 其中, 读写混
合包括读重负载 (20% 写, 80% 读)、读写平衡负载 (50% 写, 50% 读) 和写重负载 (80% 写, 20% 读). 上述工作负载