0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

百万并发场景中倒排索引与位图计算的实践

OSC开源社区 来源:OSCHINA 社区 2023-01-10 10:35 次阅读

背景

Promise 时效控单系统作为时效域的控制系统,在用户下单前、下单后等多个节点均提供服务,是用户下单黄金链路上的重要节点;控单系统主要逻辑是针对用户请求从规则库中找出符合条件的最优规则,并将该规则的时效控制结果返回客户端,比如因为临时疫情等原因针对仓、配、商家、客户四级地址等不同维度进行精细粒度的时效控制。 该系统也是 Promise 侧并发量最大的系统,双 11 高峰集群流量 TPS 在百万级别,对系统的性能要求非常高,SLA 要求在 5ms 以内,因此对海量请求在规则库 (几十万) 中如何快速正确匹配规则是该系统的技术挑战点。

朴素的解决方案

按照朴素的思想,在工程建设上,通过异步方式将规则库逐行缓存到 Redis,Key 为规则条件,Value 为规则对应结果;当用户请求过来时,对请求 Request (a,b,c,d..) 中的参数做全组合,根据全组合出的 Key 尝试找出所有可能命中的规则,再从中筛选出最优的规则。如下图所示

3872ffc4-902a-11ed-bfe3-dac502259ad0.png

该方案面临的问题是全组合的时间复杂度是 2**n,n≈12;算法的时间复杂度高且算法稳定性差,最差情况一次请求需要 4096 次计算和读取操作。当然在工程上我们可以使用本地缓存做一些优化,但是无法解决最根本的性能问题。架构简图如下所示:

389ae19c-902a-11ed-bfe3-dac502259ad0.png

新的解决方案

上面方案是从行的角度看待匹配定位的,能够命中的行的每一列必然也是符合条件的,这里面存在某种隐约的内在联系。能否反过来思考这个问题,为此我们尝试进行新的方案,当然架构简图依然如上图所示,核心优化的是命中算法。 新的方案整体采用列的倒排索引和倒排索引位运算的方式,使得计算复杂度由原来的 2n 降至 n**,且算法稳定性有非常好的保证。其中列的倒排索引是对每列的值和所分布的行 ID (即 Posting List) 建立 KV 关系,倒排索引位运算是对符合条件的列倒排索引进行列间的位运算,即通过联合查询以便快速找到符合条件的规则行。

算法详细设计

1. 预计算生成列的倒排索引和位图

通过对每列的值进行分组合并生成 Posting List,建立列值和 Posting List 的 KV 关系。以下图为例,列 A 可生成的倒排索引为:301={1},201={2,3,4,5} 等,需要说明的一点,空值也是一种候选项,也需要生成 KV 关系,如 nil={7}。

38b07b2e-902a-11ed-bfe3-dac502259ad0.png

2. 生成列的倒排索引对应位图

将步骤 1 的倒排索引转成成位图,方便后续的位图计算,转换规则为行 ID 对应位图的下标位(步骤 1、2 可以合并操作)。

38d8b4ea-902a-11ed-bfe3-dac502259ad0.png

3. 根据用户请求查找列位图,通过位图计算生成候选规则集

将用户请求中的入参作为 Key,查找符合条件的位图,对每一列进行列内和空值做 || 运算,最后列间位图做 & 运算,得到的结果是候选规则集,如下图所示:

38eac7ac-902a-11ed-bfe3-dac502259ad0.png

4. 从候选规则库中,根据业务优先级排序,查找最优的规则

以候选规则为基点,按照业务优先级排序,进行逐级位运算 &,当遍历完或位运算为 0 时,找到最后不为空的即为最优规则,该过程是从候选规则库逐渐缩小最优范围的过程。需要说明某列当用户请求位图不存在时,需要使用对应的空位图进行参与,以 B 列为例,入参 B_1102 不存在,需要使用 B_nil 参与 &。

392a8338-902a-11ed-bfe3-dac502259ad0.png

复杂度分析

通过上面的例子我们可以看到,在时间复杂度方面查找候选规则集时,进行一轮 || 运算,一轮 & 运算;在查找最优规则时进行一轮 & 运算,所以整体复杂度是 3n≈n。 在空间复杂度方面,相比原来的行式存储,倒排索引的存储方式,每列都需要存储行 ID,相当于多了 *(n-1)Posting List 存储空间,当然这是粗略计算,因为实际上行 ID 的存储最终转换为位图存储,在空间上有非常大的压缩空间。

工程问题 - 压缩位图

如果倒排索引位图非常稀疏,系统会存在非常大的空间浪费。我们举一个极端 case,若千万规则库中命中的行 ID 是第 1000 万位,按照传统方式 BitSet 进行存储,需要消耗 1.2MB 空间,在内存中占用存在严重浪费,有没有压缩优化方案,在 RoaringBitMap 压缩位图方案中我们找到,相同场景在压缩位图方式下仅占 144bytes;即使在 1000 万的位图空间,我们随机存储 1 万个值,两者比也是在 31K vs 2MB,近 100 倍的差距,总的来说 RoaringBitMap 压缩率非常大。 RoaringBitMap 本质上是将大块的 bitmap 拆分成各个小块,其中每个小块在需要存储数据的时候才会存在,所以当进行交集或并集运算的时候,RoaringBitMap 只需要去计算存在的块而不需要像 bitmap 那样对整个大块进行计算,既做到了压缩的存储又做到计算性能的提升。 以下图 821697800 为例,对应的 16 进制数为 30FA1D08, 其中高 16 位为 30FA,低 16 位为 1D08。先用二分查找从一级索引(即 Container Array)中找到数值为 30FA 的容器,该容器是一个 Bitmap 容器,然后在该容器查找低 16 位的数值 1D08,即十进制下 7432,在 Bitmap 中找到相应的位置,将其置为 1 即可。

3970defa-902a-11ed-bfe3-dac502259ad0.png

适用场景分析

回顾上面的设计方案我们可以看到,这种方式仅适用于 PostingList 简单如行 ID 的形式,如果是复杂对象就不适合用位图来存储。另外仅适用于等值查询,不适用于 like、in 的范围查询,为什么有这种局限性?因为这种方式依赖于搜索条件的空间,在方案中我们将值的条件作为搜索的 Key,值的条件空间希望尽可能是一个有限的、方便穷举的、小的空间。而范围查询导致这个空间变成难以穷举、近乎无限扩张的、所以不适用。

其他优化方式

除了使用位运算的方式对倒排索引加速,考虑到 Posting List 的有序性,还有其他的方式比如使用跳表、Hash 表等方式,以 ES 中采用的跳表为例,进行 & 运算实际就是在查找两个有序 Posting List 公共部分,以相互二分查找的形式,将时间复杂度控制在 log (n) 的级别。 具体参见《工业界如何利用跳表、哈希表、位图进行倒排索引加速?》:https://time.geekbang.org/column/article/221292?utm_source=related_read&utm_medium=article&utm_term=related_read

399ab9be-902a-11ed-bfe3-dac502259ad0.png

审核编辑 :李倩

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 索引
    +关注

    关注

    0

    文章

    59

    浏览量

    10408
  • Redis
    +关注

    关注

    0

    文章

    363

    浏览量

    10496
  • 位图
    +关注

    关注

    0

    文章

    6

    浏览量

    2218

原文标题:百万并发场景中倒排索引与位图计算的实践

文章出处:【微信号:OSC开源社区,微信公众号:OSC开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Mysql索引是什么东西?索引有哪些特性?索引是如何工作的?

    作为开发人员,碰到了执行时间较长的 sql 时,基本上大家都会说” 加个索引吧”。但是索引是什么东西,索引有哪些特性,下面和大家简单讨论一下。
    的头像 发表于 12-24 16:20 422次阅读
    Mysql<b class='flag-5'>索引</b>是什么东西?<b class='flag-5'>索引</b>有哪些特性?<b class='flag-5'>索引</b>是如何工作的?

    服务器并发的概念

    在编写服务器时,如果服务器的设计初衷是要可以承担百万、千万的客户端连接,那么默认的情况下,Linux操作系统提供的相关配置参数(比如说进程可分配的文件数目等)是不能够满足我们的程序需求的,因此需要
    的头像 发表于 11-10 10:05 1381次阅读
    服务器<b class='flag-5'>并发</b>的概念

    索引的底层实现详解

    说一说索引的底层实现? Hash索引 基于哈希表实现,只有精确匹配索引所有列的查询才有效,对于每一行数据,存储引擎都会对所有的索引计算一个
    的头像 发表于 10-09 10:26 492次阅读
    <b class='flag-5'>索引</b>的底层实现详解

    索引是什么意思 优缺点有哪些

    1. 索引是什么? 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。 索引是一种数据结构。数据库
    的头像 发表于 10-09 10:19 1451次阅读

    mysql数据库索引失效的10种场景

    今天就跟大家一起聊聊,mysql数据库索引失效的10种场景,给曾经踩过坑,或者即将要踩坑的朋友们一个参考。 1. 准备工作 所谓空口无凭,如果我直接把索引失效的这些场景丢出来,可能没有
    的头像 发表于 10-07 16:31 914次阅读
    mysql数据库<b class='flag-5'>索引</b>失效的10种<b class='flag-5'>场景</b>

    MySQL索引的常用知识点

    索引结构:B+树 索引其实是一种数据结构 注意B+树是MySQL,索引默认的结构;一张表至少有一个索引(主键索引),是可以有多个
    的头像 发表于 09-30 16:43 247次阅读

    HarmonyOS如何使用异步并发能力进行开发

    UI的同时,后台也能执行耗时操作,从而避免应用出现卡顿。 并发能力在多种场景中都有应用,其中包括单次I/O任务、CPU密集型任务、I/O密集型任务和同步任务等。开发者可以根据不同的场景,选择相应的
    发表于 09-22 17:35

    基于FPGA计算的理论与实践

    的设备可以计算数百万量级的函数基本闸门,以数百兆赫的速度运行。提高速度和容量,额外的,特殊的元素可以嵌入到阵列,例如作为大型存储器、乘法器、用于算术和逻辑功能的快速进位逻辑,甚至是完整的微处理器
    发表于 09-21 06:04

    工业物联网平台如何应对高并发应用场景

    面对的巨大挑战。对此,数之能提供高并发、官翻机接入的工业物联网平台,可以适应高并发场景应用需求。 高并发是一种在“同一时间点或极短时间内出现大量的操作请求”的
    的头像 发表于 09-06 14:21 298次阅读

    移动应用高级语言开发——并发探索

    单核设备,任一个时刻只有一个任务能够运行,其运行顺序是不固定的;而在多核场景,同一时间可以多项任务并行。 并发与并行 02►常见的并发
    发表于 08-28 17:08

    异构计算场景下构建可信执行环境

    ”“独立”发展。在异构计算场景的迭代,从以CPU为中心,到CPU只是普通的PU,到未来可能没有CPU。而机密计算却非常“忠诚”,始终围绕CPU,即使最新的ARM CCA技术仍是围绕C
    发表于 08-15 17:35

    芯驰科技全场景车规芯片赋能中央计算架构

    邀出席并发表《全场景车规芯片赋能中央计算架构》主题演讲,分享芯驰对于未来智能汽车中央计算架构的思考,以及芯驰如何用全场景的车规芯片布局来支持
    的头像 发表于 06-20 10:22 526次阅读
    芯驰科技全<b class='flag-5'>场景</b>车规芯片赋能中央<b class='flag-5'>计算</b>架构

    有关BMP位图的原理和解析

    BMP是 Bitmap(位图)的简称,是windows显示图片的基本格式。
    的头像 发表于 05-20 16:15 1511次阅读
    有关BMP<b class='flag-5'>位图</b>的原理和解析

    关于并发编程与线程安全的思考与实践

    并发编程的意义是充分的利用处理器的每一个核,以达到最高的处理性能,可以让程序运行的更快。而处理器也为了提高计算速率,作出了一系列优化,比如:
    的头像 发表于 05-11 10:04 239次阅读
    关于<b class='flag-5'>并发</b>编程与线程安全的思考与<b class='flag-5'>实践</b>

    无线通信研究的一个新热点—索引调制技术

    索引调制技术已有的研究成果,从不同索引方式以及索引调制的应用,分别简要概述了各种索引调制的基本原理,以及索引调制应用在哪些方面。   IM在
    发表于 05-10 16:44