DPDK CPU Packet Processing - 3. Membership Library
3.1 简介
DPDK 的成员库(Membership Library)为 DPDK 应用提供了一套 API,用于将新成员插入集合、从集合中删除已有成员,或者查询某个成员是否属于某个集合,或者某组集合。对于集合组的情况,该库不仅可以判断一个元素是否曾被插入,还能返回它属于哪一个集合。
成员库是对传统过滤器结构(例如布隆过滤器 [Member-bloom])的扩展与泛化,可用于多种工作负载和应用场景。总体来说,成员库是一种数据结构,提供对集合中成员关系的“集合摘要”(Set-Summary)。相比于存储完整的成员列表,这种集合摘要具有两个主要优势:
- 它比直接存储所有成员元素所需的空间要少得多。
- 它在执行成员检测(或其他操作)时的速度远快于对完整元素列表的查询。
本指南中使用“集合摘要”(Set-Summary)一词,表示这种空间高效、基于概率的成员关系检测结构。对某个元素进行的成员测试将返回它所属的集合,或返回“未找到”,并且具有极高的准确性概率。
集合摘要是一种基础的数据聚合组件,在许多网络(及其他)应用中都十分有用。它是解决多种网络应用中性能和可扩展性问题的重要数据结构,包括:叠加网络(Overlay Networks)、以数据为中心的网络(Data-Centric Networks)、流表摘要、网络统计和流量监测等。
当某些应用需要包含一个元素列表,而完整列表又过于占空间或处理成本过高时,集合摘要是非常实用的方案。它以损耗式的、基于哈希的方式表示成员集合,在引入极小的成员检测误判率的代价下,显著减少空间开销并提升查询性能。
拓展解释:
这段是在介绍 DPDK 里的一个组件叫“成员库”(Membership Library),它的功能和 Bloom Filter(布隆过滤器)很像,但是比 Bloom Filter 更强大。
它是干什么的?
用来判断 “某个元素是不是在某个集合里”——而且非常高效、省内存。
你可以理解为:
- 你有很多包、IP地址、流量标签等
- 你不想一个一个去比对查找(太慢、太耗内存)
- 就可以把它们塞进这个“成员库”
- 然后快速判断一个新的数据是不是在“我们关心的那一堆”里
举个简单的例子:
想象你是一个防火墙,有一份“黑名单 IP 集合”
你可以用成员库:
- 把这些 IP 加进去
- 每收到一个包,提取 IP,扔进去查一下
- 如果它在集合里(match),你就丢掉它
- 否则你就放行
和传统查找相比,它的特点是:
传统方式(比如链表/哈希表) | 成员库(Set-Summary) |
---|---|
一个一个查 | 用位图和哈希函数 |
占内存多 | 非常省内存(压缩结构) |
查找慢 | 查找快(通常是 O(1)) |
结果100%准确 | 会有极小概率误判(误判为“存在”) |
它为什么省内存、快?
因为它 不存每个元素的“原始内容”,而是:
- 把元素通过多个哈希函数处理后,映射到一个 bit 位图
- 查询时也做哈希映射 → 检查那些 bit 是否为 1
- 如果是 → 很大可能这个元素是存在的
- 如果不是 → 肯定不在
这种结构就叫:集合摘要(Set-Summary)
多集合支持(Group of Sets)
这个成员库比普通 Bloom Filter 强大的是:
它不仅能告诉你“元素在不在”,还能告诉你“在哪个集合里”。
比如:
- 有集合 A、B、C(比如不同客户、不同区域、不同服务)
- 某个元素加到 B 里了
- 查询时它会告诉你:“这个元素在 B 集合里”,而不是只是“存在”
应用场景有哪些?
场景 | 用途 |
---|---|
DPI/探针系统 | 判断 IP/域名/流量是不是“已知的恶意内容” |
CDN 缓存系统 | 判断某个资源是否已被缓存 |
网络流表加速 | 不查整个大流表,先通过摘要快速排除不命中 |
DoS 攻击防御 | 检测陌生来源的流量是否属于可疑集合 |
子图(a)
描述的是一个分布式 Web 缓存架构:
- 一组代理服务器共享它们的 Web 缓存(这些缓存内容来源于后端 Web 服务器),
- 它们使用 Membership Library 来共享它们当前缓存了哪些网页/对象的摘要信息。
- 当某个代理接收到 HTTP 请求时,它可以通过查看缓存摘要快速判断:
- 是从附近的其他代理取内容,
- 还是从后端服务器重新获取。
子图(b)
描述 Membership Library 被用于防止路由环路(routing loop):
- 传统做法是使用 TTL 递减来避免环路,TTL 到 0 就丢弃。
- 现在,可以在数据包头部附带一个“已访问节点集合”的摘要(Set-Summary)。
- 每个路由节点收到数据包时,判断自己是不是在这个集合中:
- 如果是 → 说明这个包又绕回来了,就可以检测出环路。
子图(c)
描述一种基于流负载均衡的用法,确保包的顺序性(in-order guarantee):
- Membership Library 被用于判断数据包是属于一个已有流还是一个新流。
- 如果是新流 → 转发到当前负载最轻的线程。
- 如果是老流 → 转发到之前已经绑定的线程,以确保包的顺序性。
子图(d)
描述数据库中的一个用法:用于集合连接(JOIN):
- 通常两个集合做 JOIN 要两两比较,非常耗时。
- 使用 Set-Summary 表示集合 → 可以大幅减少 JOIN 操作的计算量。
- 直接比较两个摘要结构,先判断哪些元素可能重合,再进一步处理。
Membership Library 的配置特性
- 该库是可配置的,适用于:
- 单集合(单个集合的成员测试)
- 多集合(支持元素归属判定)
- 提供两种实现方式:
- 布隆过滤器向量(Vector of Bloom Filters)
- 经典方式,支持多个 Bloom Filter 组合
- 基于哈希表的 Set-Summary
- 支持配置是否允许“假阴性(false negatives)”
- 一些模式可以完全避免 false negative,只有 false positive
- 布隆过滤器向量(Vector of Bloom Filters)
接下来的章节会分别介绍这些类型的 Set-Summary、它们的使用场景,并提供 API 的使用说明。
3.2 布隆过滤器向量(Vector of Bloom Filters)
布隆过滤器(Bloom Filter, 简称 BF)是一种众所周知的空间效率极高的概率型数据结构,用于回答集合成员关系查询(即判断一个元素是否属于某个集合),具有:
- 一定概率的“假阳性”(false positive)
- 但零假阴性(false negative)
也就是说:
- 查询结果要么是“该元素可能在集合中”(可能是误判,但概率极低);
- 要么是“该元素绝对不在集合中”。
BF 的基本思想如下:
- BF 是用来表示一个包含 n 个元素的集合(例如网络应用中常见的流标识)。
- 它的原理是:分配一个大小为 m 位的 位向量(bit vector)v,初始时所有位都为 0。
- 然后选取 k 个独立的哈希函数
h1, h2, ..., hk
,这些哈希函数的结果范围是0 ~ m-1
。 - 当要插入一个元素
X
时,对X
进行 k 次哈希,得到 k 个索引位置,把这些位置上的 bit 都设置为 1。- 注意:一个 bit 可能会被多个元素重复设置为 1。
- 当我们要查询一个元素
Y
是否在集合中:- 对
Y
应用相同的 k 个哈希函数,查看结果指向的那些 bit 是否都是 1。- 如果有任意一个 bit 是 0 →
Y
一定 不在集合中 - 如果所有 bit 都是 1 →
Y
可能在集合中,但也可能是多个元素碰巧把这些位都设置成了 1(即 false positive)
- 如果有任意一个 bit 是 0 →
- 对
假阳性率的控制
假阳性率(false positive rate)可以通过调整两个参数来控制:
- 哈希函数数量
k
- 位向量长度
m
通过合理设置 k
和 m
的值,可以将误判概率降到极低,达到实际系统可接受范围。
“布隆过滤器向量” 是什么意思?
就是把多个布隆过滤器组成一个向量(数组),用于同时支持多个集合的判定。
比如你有 3 个集合(代表 3 类规则、3 个服务、3 个租户):
- 每个集合用一个布隆过滤器表示
- 插入/查询时通过元素 ID + 额外信息决定该落入哪个集合(或全部)
- 查询时可以同时判断:这个元素在哪些集合里存在?
这就是向量化 Bloom Filter 的基本思路,支持多集合归属查询(multi-set membership)。
如果不使用布隆过滤器(BF),那么准确地进行成员关系测试(membership testing)通常需要执行代价高昂的哈希表查找(hash table lookup),并且还要做完整元素的比较。
而使用布隆过滤器(BF)的优势在于:
- 把成员测试的过程简化成了一系列简单的哈希计算和对一个小型位向量(bit-vector)的内存访问;
- 这类操作可以非常容易地被优化。
因此:
- 使用布隆过滤器进行集合成员关系测试的查询吞吐量(lookup throughput),
- 会显著高于传统的哈希表查找+元素比对的方法。
传统哈希表 | 布隆过滤器 |
---|---|
需要复杂的链表/树结构遍历 | 只需要固定次数的哈希+位操作 |
需要比较完整 key 内容 | 不需要比对,只看 bit 是否是 1 |
随着元素变多,查找慢慢变慢 | 查询速度恒定,几乎 O(1) |
内存占用较大 | 极度节省内存 |
所以,BF 的最大优势就是:
极快的查询速度 + 极低的内存消耗,代价是容忍少量假阳性。
布隆过滤器(BF)适用于那些只需要管理一个集合的应用场景,元素的成员关系检测是直接针对这个单一布隆过滤器进行查询的。
前面图中讨论的示例(即防止路由环路的用例)就是这样一个应用场景的例子:
- 数据包在传输过程中,会记录它访问过的节点 ID;
- 这些节点 ID 被插入到数据包头部嵌入的布隆过滤器中;
- 每当一个新的节点收到该数据包时,会检查布隆过滤器:
- 如果布隆过滤器表明当前节点 ID 肯定不在集合中,那么可以保证这条路由没有环路(loop-free)。
为了同时支持多集合和单集合的成员关系测试,
该库实现了一种向量布隆过滤器(Vector Bloom Filter,简称 vBF)方案。
vBF 的基本概念是:
- 把多个布隆过滤器组合成一个布隆过滤器向量(即一组 Bloom Filter)。
- 在进行成员关系测试时,会同时在所有布隆过滤器上并行进行查询,
- 以确定某个元素属于哪些集合,或者不属于任何集合。
在上图中展示了 vBF 的基本原理:
- 一个元素(Element)会被同时用于查询多个布隆过滤器,
- 并返回命中(hit)的位置(即属于哪些集合)。
单个 Bloom Filter(BF) | 向量 Bloom Filter(vBF) |
---|---|
只能判断是否存在于一个集合 | 能同时判断属于哪几个集合 |
查询只用一个 bit vector | 查询需要对多个 bit vector 并行操作 |
应用于简单场景 | 应用于多租户、多分类、复杂流分类场景 |
如前所述,这类结构(指 vBF 向量布隆过滤器)有很多应用场景。
vBF 适用于需要同时针对多个集合进行成员测试的应用。
在前面图示的例子中:
- 每个集合对应一个工作线程(worker thread)所分配处理的所有流(flow)。
- 当收到一个新的数据包时,vBF 可以被快速用于判断:
- 如果这个包属于一个新的流 → 把它分配给当前负载最小的工作线程;
- 如果属于已有的流 → 把它排队到原先已经分配好的线程,以保证处理顺序正确(in-order processing)。
也就是说:
vBF 能够立即判断一个流是新流还是已有流,这一特性对于最小化响应时延(response time latency)是非常关键的。
还需要注意的一点是:
- vBF 可以通过顺序查询一组单独的 Bloom Filters来实现(即一个个查过去)。
- 但如果能够并行地查询所有集合摘要,将会带来很大的吞吐量提升(throughput advantage)。
- 在这个库(Membership Library)中,已经通过实现机制,在查询时实现了一定程度的并行性,能够一次性检查所有 Bloom Filters。
实际应用中:
- 流量探针、DPI、负载均衡器这类系统非常在意延迟,
- 因此 vBF 提供的 快速新流识别 能极大地优化系统响应性能。
比如:
- 如果发现是新流,立刻挑一个空闲线程去处理,避免堆积;
- 如果是旧流,确保同一流的包不会乱序处理。
3.3 哈希表集合摘要
基于哈希表的集合摘要(Hash-table based set-summary,简称 HTSS)
是成员库(Membership Library)中另一种实现方案。
Cuckoo Filter(参考 [Member-cfilter])就是一种 HTSS 的示例。
HTSS 和 vBF 一样,也支持多集合(multi-set)成员关系测试。
然而:
- 当集合数量较少时,vBF(Vector Bloom Filter)更合适;
- 当集合数量很大时,HTSS 更适合,并且在性能上可以轻松超过 vBF。
这是因为:
- HTSS 只使用一个哈希表来进行成员关系测试;
- 而 vBF 则需要在每个集合对应的 Bloom Filter 中逐个进行测试。
因此,总体来说:
- vBF 更适合用于集合数量较小、有限的情况;
- HTSS 更适合用于集合数量较多的情况。
项目 | 向量 Bloom Filter(vBF) | 哈希表集合摘要(HTSS) |
---|---|---|
基本结构 | 多个 Bloom Filter 组成向量 | 一个统一的哈希表 |
查询方式 | 多次 Bloom Filter 测试 | 单次哈希表查询 |
优势 | 少量集合时查询快,内存少 | 大量集合时查询更高效、扩展性更好 |
缺点 | 集合多了查询延迟高 | 哈希表需要更复杂的维护(冲突、扩容等) |
简单理解就是:
- vBF:集合数量少 → 多个小表并行查找,快。
- HTSS:集合数量多 → 统一的大表直接查找,避免多次 Bloom Filter 测试。
举例理解
场景 | 适合使用 |
---|---|
5 个分类、10 个租户 | 用 vBF 足够,简单且快 |
1000 个分类、几万个租户 | 用 HTSS 更合理,否则 vBF 每次要查几千次 Bloom Filter,太慢 |
如上图所示,攻击签名匹配(attack signature matching)
就是一个使用 HTSS 的很好的示例,其中每一个集合(set)代表一种特定长度的攻击签名。
(为了保证这个示例的正确性,假设攻击签名之间不存在包含关系,即一个签名不是另一个签名的子集。)
在这种场景下,使用 HTSS 可以实现0% 的假阴性率(false negative):
- 即当某个元素查询结果为“未找到”时,可以100% 确定它不属于任何集合。
对于数据包检测(packet inspection)应用来说:
- 能够立即知道当前负载(payload)没有匹配任何攻击签名,
- 这样就可以直接确认这个数据包是“合法的”;
- 如果发现可能匹配某个签名,则需要进一步对数据包进行深度检测(deep inspection)。
HTSS 使用了一个类似于传统哈希表的,但更简单的数据结构。
其主要区别在于:
- HTSS 只存储元素的签名(signature),而不是完整的 key/元素内容;
- 这样可以大大减少哈希表的内存占用(footprint)。
同时,HTSS 每个 entry 还存储一个值(value):
- 这个值用于指示元素所属的集合(target set)。
查找元素时的流程是:
- 对元素进行哈希计算;
- 在 HTSS 表中查找对应位置的签名;
- 如果签名匹配,则返回该元素所属集合的索引值。
这段话实际上在强调:
方面 | 细节 |
---|---|
适用案例 | 需要快速判断是否符合攻击特征(签名匹配) |
为什么用 HTSS | 快速 + 空间小 + 可以做到没有假阴性(查到"没有"就是一定没有) |
为什么不用 full key | 只存签名(短小、定长)→ 节省空间 |
假阳性来源 | 签名哈希冲突(即不同元素生成了同样的签名) |
假阴性来源 | 哈希表太满导致元素被替换/驱逐 |
假设你的攻击签名库有如下规则:
签名编号 | 签名特征(简化) |
---|---|
1 | GET /admin |
2 | POST /login |
3 | DELETE /db |
HTSS 的处理方式是:
- 把每个签名特征通过哈希,存储成一个签名值(signature);
- 查询数据包时,只需要哈希提取出的 payload 特征,
- 比对 HTSS 中对应位置的签名是否一致,
- 匹配则说明该 payload 可能命中了一个攻击签名,否则可以安全放行。
3.3.1 允许存在假阴性的集合摘要
如前所述,传统的集合摘要结构(比如布隆过滤器 Bloom Filters)是不会出现假阴性(false negative)*的。
也就是说,当一个元素查询结果为“未存在”时,可以*100%确定它真的不存在于集合中。
但是,DPDK 的 Membership Library 也支持一种基于 HTSS 的集合摘要,它允许存在假阴性概率。
在 HTSS 中:
- 当哈希表变满时,新的 key/元素将无法继续添加到表中;
- 此时通常需要扩展(resize)哈希表来容纳新的元素,但扩表操作非常昂贵(耗时、耗资源)。
一种替代方案是:
- 允许新的元素覆盖(overwrite)或驱逐(evict)已有元素(类似缓存 cache 的做法);
- 这样做的结果是,集合摘要中将出现假阴性概率。
这是因为:
- 被驱逐出去的元素在实际集合中可能仍然存在,
- 但集合摘要中已经没有它的信息了;
- 因此后续查询时,会错误地报告元素不在集合中 → 形成假阴性。
HTSS 允许假阴性的主要用途是:
- 把 HTSS 用作一种用于分发元素到不同目标集合的缓存机制。
- 通过允许 HTSS 驱逐旧元素,集合摘要可以像缓存一样追踪最近的活跃元素。
- 而老旧的不活跃元素(即很少被访问的元素)则会自动被逐出集合摘要。
需要注意的是:
- 即使引入了假阴性,
- 集合摘要仍然存在假阳性概率(false positive),
- 这意味着应用程序要么能够容忍一定的假阳性,
- 要么在发生假阳性时需要有备用处理路径(fallback path)。
传统 HTSS | 允许假阴性 HTSS |
---|---|
查询“未找到”时一定正确 | 查询“未找到”时可能出错 |
不驱逐元素,需要扩容 | 可驱逐元素,牺牲准确性换性能 |
比较耗内存 | 比较省内存,支持更多元素流动 |
适合持久准确的元素 | 适合元素活跃度变化快的应用 |
假设你在做流量探针,维护的是“最近活跃流表”。
- 如果是传统 HTSS:
- 所有流都得存住,流越来越多,表必须扩容;
- 如果是允许假阴性的 HTSS:
- 只保留最近活跃的流(例如最近几秒活跃过的流);
- 老的、不活跃的流自动被淘汰;
- 查询偶尔查不到老流,也可以接受,因为老流本身已经快超时了。
这种机制就是典型的 Cache 行为:空间固定,内容不断更新。
带假阴性的 HTSS(即作为缓存(cache)使用的 HTSS)
同样有着非常广泛的应用场景。
比如:
- 上图中高亮显示的通配符流分类(wild card flow classification,例如 ACL 规则),
- 就是这样一种典型应用场景。
在这种情况下:
- 每一个目标集合(target set)代表一个子表(sub-table),
- 每个子表包含一组规则,这些规则由特定的流掩码(flow mask)定义。
这些流掩码是互不重叠的(non-overlapping)。
对于那些同时匹配多条规则的流:
- 只会将最高优先级(highest priority)的一条规则对应的目标集合插入到子表中。
(有兴趣的读者可以参考 Open vSwitch (OvS) 中的 Mega Flow Cache (MFC) [Member-OvS] 的设计文档来了解更多细节。)
通常来说:
- 规则(ACL)的数量非常大,
- 并且有大量不同的唯一掩码(unique masks),
- 因此每个掩码对应一个独立的目标集合(target set),
- 最终形成大量的子表(sub-tables)。
由于网络流量变化很大:
- 当前活跃的流集合(active flows)也变化很大,
- 所以带假阴性的 HTSS 就可以作为缓存,
- 记录
<flow ID, 目标 ACL 子表>
对, - 用于当前活跃的流集合。
当查询未命中(miss)时(上图中红色箭头所示):
- 系统将顺序遍历所有子表(sub-tables)进行匹配;
- 一旦找到匹配的子表,
- 就将该流的 key 和对应的目标子表插入到集合摘要(即执行缓存插入操作 cache insertion)。
这样:
- 后续相同流的包就不需要再次顺序遍历所有子表,
- 可以直接通过缓存快速命中。
传统做法 | 带假阴性的 HTSS 缓存做法 |
---|---|
每次都要顺序查一遍所有 ACL 子表 | 热门流提前缓存,后续直接命中 |
延迟大,效率低 | 延迟小,吞吐量高 |
规则多了特别慢 | 只查一次,之后快速命中 |
通俗理解流程:
- 来了一个新流量包。
- 查询 HTSS,看 flow ID 有没有缓存。
-
- 命中 → 直接知道该流走哪个 ACL 子表。
- 未命中 → 顺序查所有子表,找到后插入 HTSS 缓存。
- 后续同一流的包,直接从 HTSS 命中,加速!
带假阴性的 HTSS 就是专门为 "活跃流量缓存加速" 设计的!
它接受了少量失效(false negative)作为代价,但极大地加速了主流流量的查询,非常适合高并发流量系统,比如 DPI、ACL 流分类、防火墙系统。
3.4 成员库(Membership Library)API 概览
成员库 API 的设计目标是:
- 尽可能通用(generic),
- 以支持前面章节讨论过的所有不同类型的集合摘要(Set-Summaries),以及将来可能扩展的更多类型。
从根本上说,这些 API 需要包含以下基本功能:
- 创建(creation)
- 插入(insertion)
- 删除(deletion)
- 查找(lookup)
这段话的核心意思是:
- Membership Library 提供一套统一的接口;
- 不管你用的是:
- 布隆过滤器(BF / vBF)
- 哈希表集合摘要(HTSS)
- 支持/不支持假阴性的变种
- 它们都通过同一组 API 调用进行操作。
3.4.1 集合摘要(Set-summary)创建
rte_member_create()
函数用于创建一个集合摘要结构体。
它的输入参数是一个结构体(struct),用于传递初始化集合摘要所需要的各种参数。
函数返回值是:
- 成功时:返回新创建的集合摘要的指针;
- 失败时:返回
NULL
。
创建集合摘要时,一般需要提供以下主要输入参数:
- name:创建的集合摘要的名字;
- type:要创建的集合摘要类型(库中支持的类型,比如
RTE_MEMBER_TYPE_HT
代表 HTSS,RTE_MEMBER_TYPE_VBF
代表 vBF); - key_len:元素(key)的长度。
此外,还有一些仅适用于特定类型集合摘要的参数,
或者是在不同集合摘要类型下含义稍有不同的参数。
例如:
- num_keys 参数:
- 对于 哈希表型集合摘要(HTSS),表示哈希表中允许的最大条目数(entries);
- 对于 布隆过滤器(Bloom Filter),表示预计将插入布隆过滤器的元素数量(expected keys)。
- 这个数值用于计算布隆过滤器的实际大小。
此外,还需要传递两个哈希种子(hash seeds):
- prim_hash_seed(主哈希函数的种子)
- sec_hash_seed(辅助哈希函数的种子)
这两个种子用来保证计算出的哈希值是独立的,提高哈希质量。
还有:
- socket_id 参数:
- 指定用于分配集合摘要内存的 NUMA 节点 ID,
- 以优化内存访问性能(特别在多核服务器上)。
对于 HTSS 类型,还需要传递一个额外参数:
- is_cache:
- 表示该集合摘要是否用作缓存(如果是,则允许存在假阴性)。
对于 vBF 类型,还需要传递额外的一些参数:
- num_set:
- 初始化时需要的Bloom Filters 数量(即集合的数量)。
- false_pos_rate:
- 指定目标假阳性率(false positive rate)。
num_keys
和false_pos_rate
将一起被用来:- 计算需要多少个哈希函数(k)
- 以及确定每个布隆过滤器的大小(bit vector 大小)。
总结一下:
参数 | 含义 | 备注 |
---|---|---|
name | 集合摘要名称 | 任意字符串 |
type | 集合类型 | HTSS / vBF |
key_len | key 的字节长度 | 比如 5-tuple 可能是 13~16 字节 |
num_keys | 元素数量预估 | 不同类型意义稍有差异 |
prim_hash_seed, sec_hash_seed | 主辅哈希函数种子 | 保证哈希独立性 |
socket_id | NUMA 节点 ID | 提升性能 |
is_cache | HTSS专用,是否允许假阴性 | true/false |
num_set | vBF专用,集合个数 | vBF中布隆过滤器数量 |
false_pos_rate | vBF专用,目标假阳性率 | 越低则内存需求越大 |
3.4.2 集合摘要元素插入
rte_member_add()
函数用于将一个元素/键(element/key)插入到集合摘要结构体中。
- 如果插入失败,会返回一个错误值。
- 如果插入成功,返回值则根据集合摘要的不同模式来携带额外信息,供用户使用。
具体来说:
- 对于 vBF 模式(Vector Bloom Filter),返回值为 0 表示插入成功。
- 对于 HTSS 模式(Hash Table Set Summary):
- 如果是在不允许假阴性(非缓存模式)的 HTSS 中:
- 如果哈希表已满,插入失败,返回
-ENOSPC
错误码。
- 如果哈希表已满,插入失败,返回
- 如果是在允许假阴性(缓存模式)的 HTSS 中:
- 如果插入操作没有导致任何元素被驱逐(eviction),返回值为 0;
- 如果插入操作导致驱逐了已有元素(即发生了覆盖 overwrite),返回值为 1,表示这种情况,但这不是一个错误。
- 如果是在不允许假阴性(非缓存模式)的 HTSS 中:
调用 rte_member_add()
时,传递的输入参数应包括:
- key:指向需要添加到集合摘要中的元素/键的指针。
- set_id:该 key 对应的目标集合 ID。
模式 | 插入结果 |
---|---|
vBF | 插入成功,返回 0 |
HTSS(无假阴性) | 成功返回 0,表满返回 -ENOSPC |
HTSS(带假阴性,cache 模式) | 正常插入返回 0,驱逐旧元素返回 1 |
3.4.3 集合摘要元素查询
单个元素查询:rte_member_lookup()
rte_member_lookup()
函数用于在集合摘要结构中查询单个元素/key。- 一旦找到第一个匹配(match),立即返回。
- 返回值说明:
- 1 → 找到了匹配
- 0 → 没找到匹配
- 函数的输入参数包括:
- key:指向需要查询的元素/key 的指针;
- set_id:用于返回找到匹配时目标集合的 ID(如果有匹配的话)。
批量元素查询:rte_member_lookup_bulk()
rte_member_lookup_bulk()
函数用于在集合摘要结构中批量查询一组元素/keys,查找它们的第一个匹配。- 每个 key 查询时,一旦找到第一个匹配就返回。
- 返回值:
- 返回匹配成功的 key 数量。
- 函数的输入参数包括:
- keys:指向要查询的批量 key 的指针;
- num_keys:要查询的 key 数量;
- set_ids:返回每个 key 第一次匹配到的目标集合 ID。
set_ids
是一个数组,其大小需要等于num_keys
。- 如果某个 key 没有匹配,
set_ids
中对应位置会被设置为RTE_MEMBER_NO_MATCH
。
单个元素多匹配查询:rte_member_lookup_multi()
rte_member_lookup_multi()
函数用于在集合摘要结构中查询单个元素的所有匹配(可能多个)。- 它会返回该元素在所有目标集合中的全部匹配。
- 特别注意:
- 对于 HTSS 缓存模式(cache mode HTSS),当前实现最多只匹配到一个目标集合。
- 返回值:
- 该 key 匹配到的目标集合数量。
- 函数的输入参数包括:
- key:指向需要查询的元素/key;
- max_match_per_key:用户期望最多找到的匹配数;
- set_id:返回找到的所有目标集合 ID。
set_id
数组大小需要根据max_match_per_key
设置。
- 额外说明:
- 对于 vBF,
max_match_per_key
等于集合的数量(因为一个 key 可以存在于多个集合)。 - 对于 HTSS,
max_match_per_key
通常等于每个桶(bucket)中条目的两倍。 max_match_per_key
应小于等于可能存在的最大匹配数量。
- 对于 vBF,
批量元素多匹配查询:rte_member_lookup_multi_bulk()
rte_member_lookup_multi_bulk()
函数用于在集合摘要结构中批量查询多个元素的所有匹配。- 每个 key 查询时,会返回它在所有目标集合中的全部匹配。
- 特别注意:
- HTSS 缓存模式下,每个 key 最多只匹配到一个目标集合。
- 返回值:
- 匹配到一个或多个集合的 key 的数量。
- 函数的输入参数包括:
- keys:指向要查询的批量 keys;
- num_keys:要查询的 key 数量;
- max_match_per_key:每个 key 可能匹配的最大集合数量;
- match_count:返回每个 key 匹配到的集合数量;
- set_ids:返回所有 key 匹配到的集合 ID。
set_ids
是一个二维数组(2-D array):- 每个 key 有一个对应的一维数组,其大小由
max_match_per_key
决定。
- 每个 key 有一个对应的一维数组,其大小由
- 同样,
max_match_per_key
应小于或等于可能存在的最大匹配数量。
3.4.4 集合摘要元素删除
rte_membership_delete()
函数用于从集合摘要结构中删除一个元素/key。
- 如果删除失败,将返回一个错误码。
函数的输入参数包括:
- key:指向要从集合摘要中删除的元素/key 的指针;
- set_id:要删除的元素对应的集合 ID。
需要特别注意的是:
- 当前实现的 vBF(Vector Bloom Filter)不支持删除操作。
- 如果在 vBF 上调用删除操作,将返回错误码
-EINVAL
。
(补充说明)
[1]
传统布隆过滤器(Bloom Filter)本身不支持主动删除(proactive deletion)。
支持删除功能需要额外的结构设计和实现(比如计数布隆过滤器 Counting Bloom Filter),这会带来额外的性能开销。
项目 | 说明 |
---|---|
支持删除 | 只有 HTSS 支持删除 |
不支持删除 | vBF(普通 Bloom Filter)不支持 |
为什么 vBF 不能删除?
- 传统布隆过滤器是通过哈希设置多个 bit 位为 1;
- 但是:
- 不知道哪个元素设置了这些 bit;
- 同一个 bit 可能被多个元素共享。
- 所以不能安全地把 bit 清回 0,否则会错误地影响其他元素。
如果真的需要支持删除,需要用到计数布隆过滤器(Counting Bloom Filter):
- 每个位不是 1/0,而是一个计数器(比如 8bit/16bit)
- 插入时 +1,删除时 -1;
- 计数器归零才清除对应 bit。
但是:
- 代价是 Bloom Filter 的空间、查询速度都会变差,
- 所以 DPDK 目前的 vBF 保持了传统简单版本,只做插入和查询,不支持删除。