资讯动态

Oracle 23ai向量近似检索实战:用APEX搭建毫秒级语义搜索界面

发布时间:2026/9/29 2:42:38 来源:尧图企业网站定制
如果你最近也被“语义搜索慢到没法用”这种问题卡住过那你一定能理解我这次实战的兴奋点在哪里。朋友上周吐槽他们后台的语义匹配越跑越慢数据到百万级以后老办法做余弦相似度比对要全表过一遍接口压力越来越大。我听完第一反应就是这不就是当初我在 Oracle 23ai 里折腾向量近似检索的老路吗当时我顺手用 APEX 搭了一个可视化 Demo把一次检索的时间从秒级干到了毫秒级而且全程没写一行前端代码。这篇文章就把这次实战完整记录下来适合两类人看一是想快速体验向量近似检索到底有多快、和精确检索差距有多大的人二是想知道怎么用 APEX 这种低代码平台把数据库能力变成“能点、能看、能玩”的界面的人。1. 先分清“APEX”是谁再说向量近似检索解决什么问题1.1 两个 APEX别在开头就搞混我标题里写的 APEX容易被人误会因为 IT 圈里叫 APEX 的东西至少有两个一个是 Oracle 的低代码开发平台 Application Express直接在数据库里跑 Web 应用这是咱们这篇文章的主角另一个是 Android 系统里的 APEX 模块格式用来让系统底层组件脱离整机升级也能叫 APEX。前阵子网上热词“android apex”指的就是后者它跟今天的主题没有关系。做技术交流最怕名字对不上就开始聊所以我先把话放在这里本文所有“APEX”都指 Oracle Application Express。你打开数据库自带的 Web 管理环境能看到一个叫“App Builder”的入口那就是它。1.2 为什么“近似检索”是大规模向量搜索绕不开的一条路先看一个基础问题。向量检索最常见的做法是把文本、图片、音视频转成一串数字数组也就是 Embedding 向量然后拿用户输入转换后的向量跟库里所有向量算距离距离最近的 N 条就是结果。这个思路本身没问题问题是“跟库里所有向量算一遍”在数据量小的时候无所谓一万条数据也就几十毫秒可一旦到了百万、千万条每一次查询都变成全量暴力扫描成本直接起飞。这时候就轮到“近似检索”登场。它不追求每次都精确算出全局最优的 TopK而是通过特定的索引结构和搜索策略用一定比例的召回精度换数量级的速度提升。换句话说精确检索像你在一个一万人的操场上挨个问“你认识那个人吗”近似检索像你先给所有人按身高排好队再看一个大概的区域。绝大多数实际场景里这种“近似”完全够用因为用户要的是“最相似的几个”不是“严格数学意义下最相似的几个”。Oracle 23ai 里对这块的支持是原生级别的提供了VECTOR数据类型、VECTOR_DISTANCE距离函数以及两种向量索引——HNSW和IVF。HNSW 的原理可以粗糙理解成“把向量组织成一张多层图搜索时从顶层开始逐层往下跳”搜索精度高、并发查询能力好但索引基本要住内存IVF 则是“先给全量向量做个粗聚类查询时只去少数几个簇里找”内存占用更友好适合数据持续更新的场景。选哪个取决于你的数据和机器后面实操部分我会展开说。我用一张表把两者的区别摆出来方便你后面做选型对比维度精确检索近似检索HNSW/IVF检索方式全量算距离 排序利用索引结构缩小搜索范围速度随数据量线性恶化数据量越大优势越明显结果准确性数学上严格最优高概率最优有极少偏差适用场景数据量小、要求绝对精确大规模向量、实时响应系统索引支持不需要额外索引依赖 HNSW 或 IVF 向量索引做这次实战之前我一直觉得“近似”两个字听起来像妥协真跑完才意识到在百万级向量下你根本没有多少机会做真正的精确检索近似检索不是妥协是工程正解。2. 为什么我拿 APEX 来搭这个向量检索体验台2.1 三层理由离库近、交付快、部署轻Oracle 的向量能力都长在数据库里那么“直观体验”的最好方式自然是找一个离数据库最近的工具把它露出来。APEX 恰好是我心里的最优解。第一层理由和数据库零距离。APEX 应用跑在数据库内部页面里的 SQL 直接面对VECTOR类型、VECTOR_DISTANCE函数这些“数据库原生能力”不需要额外起一个 Java/Node 服务也不用写 ORM 映射。你可以把它理解为一个能直接操作数据库的浏览器前端工具。第二层理由交付速度快。我从零搭一个带输入框、按钮、结果报表的检索页面前后不到半小时。APEX 里的经典报表、动态动作、页面项这些组件就是为此准备的拖拽配置就行不需要写大段 HTML。你是来体验向量检索的不是来体验前端的对吧第三层理由部署和分享都很轻。APEX 应用本质上是存在数据库里的一组元数据发布时只需要导出一个f文件再导入目标环境浏览器打开 URL 就能用。拿来做团队内部的原型验证、给老板演示效果都极其顺手。2.2 环境清单与版本要求Oracle 23ai 不是老版本数据库随便就能玩起来的最核心的是必须要有VECTOR类型和向量索引支持。这块在 Oracle 21c 及更早版本里没有23ai 才正式落地。我自己用的是 Oracle Database 23ai Free 版足够做实验生产环境的话建议按官方支持矩阵评估。APEX 方面23ai 数据库自带 APEX 版本通常比较新至少是 23.1 以上我这个环境里是 23.2。组件版本/参数说明Oracle Database23ai Free 23.5带 VECTOR 类型及向量索引支持APEX23.2数据库自带浏览器访问浏览器Chrome/Edge 均可APEX 是 Web 应用无需安装客户端演示数据100 万条文本向量维度 384FLOAT32数据量我故意用百万级别不然看不出近似检索的优势。你要是机器内存吃紧先跑十万条也能看到趋势只是速度对比没有百万级那么震撼。2.3 用一句话概括这次的实现路线数据库里有一张带向量列的表里面存了文本片段和对应的 EmbeddingAPEX 页面接收输入把输入转换成查询向量写一条带ORDER BY VECTOR_DISTANCE(...)的 SQL取 TopK 结果展示在页面上。整条链路从“SQL 能力”到“可视化界面”没有中间服务没有前端工程完全靠 APEX 拉通。3. 建表、灌向量、建索引把地基打扎实3.1 演示表结构怎么设计为了让后面的页面直接能用我先建一张doc_chunks表字段不多但足够典型CREATE TABLE doc_chunks ( id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, content VARCHAR2(1000), embed VECTOR(384) );VECTOR(384)表示这个向量列固定 384 维和常用的开源 Embedding 模型输出维度一致。维度和模型必须严格对应这是后面最容易踩的坑我建议你在建表时就写清楚维度是多少以后维护的人不会一脸懵。3.2 向量数据从哪里来这一步很多人会卡住表有了向量数据怎么进去我在这次的演示环境里是先用 Python 调一个本地嵌入模型把准备好的 100 万条中文短文本批量转成向量再拼接成 Oracle 能识别的字符串用TO_VECTOR转进去。大致脚本逻辑长这样import numpy as np # 假设 embed_model 是一个能输出 384 维向量的模型 # row 是 (id, content, vector_list) def to_oracle_vector(vector): return [ ,.join(str(round(float(x), 6)) for x in vector) ] # 拼一条 INSERT 语句 sql INSERT INTO doc_chunks (content, embed) VALUES (:content, TO_VECTOR(:vec))如果你不想走外部模型Oracle 23ai 内部也支持通过DBMS_VECTOR或VECTOR_EMBEDDING相关能力来生成向量但需要提前准备 ONNX 格式的嵌入模型对第一次体验的人有点门槛。我的建议是先想办法把向量灌进表里把检索链路跑通后面再去折腾嵌入生成否则容易陷在模型准备里出不来。批量插入完成后建议顺手建一个计数验证SELECT count(*) FROM doc_chunks;我这边看到 1,000,000数据落地这一步就算完成了。3.3 创建向量索引HNSW 和 IVF 怎么选建索引之前先想清楚一个问题你这次是想要“查询快”还是“更新灵活”。我的演示场景偏向于只读检索所以选了 HNSW它查询精度和速度在这类场景里更突出。Oracle 23ai 里建 HNSW 索引的大致语法是这样的CREATE VECTOR INDEX doc_hnsw_idx ON doc_chunks(embed) ORGANIZATION INMEMORY NEIGHBOR GRAPH DISTANCE COSINE WITH TARGET ACCURACY 95;如果你更在意数据持续写入、更新频繁或者服务器内存不太宽裕可以换成 IVFCREATE VECTOR INDEX doc_ivf_idx ON doc_chunks(embed) ORGANIZATION NEIGHBOR PARTITIONS DISTANCE COSINE WITH TARGET ACCURACY 95;WITH TARGET ACCURACY 95这个参数值得多说一句它不是玄学它是告诉优化器“我期望的召回率大概在 95% 左右”索引构建时会在内存占用、索引复杂度和查询精度之间做权衡。想更稳就调成 99想更快就调到 90具体哪个值适合你最好拿自己的数据集多跑几组对比。语法细节在不同版本里可能会有微调上手时以你本机数据库的官方文档为准。索引创建在百万数据量下需要一点时间HNSW 还会占用内存我 8 核 16G 的虚拟机建索引大概花了 3 分多钟这期间 CPU 会明显被吃满别以为机器坏了。4. 在 APEX 页面里跑出第一个 TopK 结果4.1 页面设计的整体思路APEX 页面要做的东西非常直观整体是“一个输入框 一个数字框 一个按钮 一块结果区域”P1_QUERY_VEC用户输入的文本对应向量字符串这是查询的核心。P1_TOP_K要返回几条结果默认 5。RUN按钮点击后触发报表区域刷新。RESULTS区域一个经典报表显示命中的 ID、文本内容、距离值。如果你嫌向量字符串太丑可以在页面上加一个常规的P1_QUERY_TEXT输入框让用户输入文字然后在后台进程里调用嵌入模型把文本转成向量再把向量字符串塞给P1_QUERY_VEC。为了不让文章篇幅失控我这次直接用向量字符串作为输入你理解链路后再往前接一层文本转向量就行。4.2 核心 SQL把距离算出来把最近的取出来整个页面里最值钱的其实就是这一条 SQLSELECT id, content, ROUND( VECTOR_DISTANCE(embed, TO_VECTOR(:P1_QUERY_VEC), COSINE), 5 ) AS distance FROM doc_chunks ORDER BY VECTOR_DISTANCE(embed, TO_VECTOR(:P1_QUERY_VEC), COSINE) FETCH FIRST :P1_TOP_K ROWS ONLY;这条 SQL 做的事分三步把字符串形式的查询向量转成真正的VECTOR类型对表里每一行算余弦距离按距离排序后取前 N 条。跟普通 SQL 最大的区别在于VECTOR_DISTANCE和TO_VECTOR这两个新面孔前者是向量距离函数后者是把文本数组转成数据库向量类型。要把“近似检索”的能力真正打开还需要一个优化器提示告诉数据库“请优先使用向量索引”。在 Oracle 23ai 里可以这样加SELECT /* VECTOR_INDEX_ON(doc_chunks) */ id, content, ROUND( VECTOR_DISTANCE(embed, TO_VECTOR(:P1_QUERY_VEC), COSINE), 5 ) AS distance FROM doc_chunks ORDER BY VECTOR_DISTANCE(embed, TO_VECTOR(:P1_QUERY_VEC), COSINE) FETCH FIRST :P1_TOP_K ROWS ONLY;VECTOR_INDEX_ON是让优化器把查询路径切到向量索引上从而走近似检索。相对的用VECTOR_INDEX_OFF可以强制做精确检索。这两个提示是后面做 AB 对比的关键开关我建议你先记下来。4.3 页面实现的完整步骤我在 APEX 里从头创建一个页面的操作步骤给你直接抄在 App Builder 里创建空白应用名字叫“Vector Search Demo”。新建一个空白页页面名为“Search”。在页面上添加两个“页面项”P1_QUERY_VEC类型选择文本P1_TOP_K类型选择数字。添加一个按钮名字叫RUN行为设置为“提交页面”。添加一个“经典报表”区域区域源类型选“SQL 查询”把 4.2 里的 SQL 贴进去。给报表区域加一个“服务器端条件”条件是P1_QUERY_VEC不为空避免一打开页面就报错。运行页面填入向量字符串点 RUN就能看到结果。如果你完全照着做第一次跑出来可能看不到数据多半是P1_QUERY_VEC里的向量和表里的维度不一致或者字符串格式里多了空格。多维向量粘贴进去以后我习惯先手动确认首尾是方括号且每个数字之间只有逗号。4.4 让页面有点“交互感”的小细节经典报表虽然能用但体验上还是有点“报表”。为了更直观我加了两个小东西一个是结果区域上面的“耗时显示”用 APEX 里一个PL/SQL进程在查询前后取SYSTIMESTAMP相减再把结果塞进页面项另一个是把结果区域改成卡片模板让每条结果展示成一张张小卡片文本内容在上相似度评分在下看起来比密密麻麻的表格舒服得多。耗时这个功能对体验“近似检索快在哪”特别有用。点一下按钮页面告诉你“本次查询 32 毫秒”比看一百行日志都直观。5. 近似检索 vs 精确检索用同一套数据做 AB 对照5.1 对照实验怎么设计要展示近似检索的价值光说“快”不够还得说“和精确检索比结果差多少”。我的做法是在同一个 APEX 应用里做两个报表区域一个用VECTOR_INDEX_ON强制走近似另一个用VECTOR_INDEX_OFF强制走精确。两个区域读同一张表同一个查询向量TopK 都设成 10页面一次跑完两边结果并排对比。这样设计的好处是杜绝了“这次跑快了但是结果变了是因为别的因素”这类干扰。唯一变量就是查询路径一个是索引辅助的近似检索一个是全量计算的精确检索。5.2 我的一组实测数据下面是来自我这台 8C16G 虚拟机上的实测数据机器性能一般跑百万级数据反而更贴近真实线上的紧张感查询模式是否用向量索引本次耗时Top1 是否一致Top5 是否一致Top10 命中重合精确检索否VECTOR_INDEX_OFF1124 ms基准基准基准近似检索是VECTOR_INDEX_ON37 ms是是9/10这个数字我在不同查询向量下跑了多次耗时基本稳定在 30~50ms 之间精确检索则稳定在 1 秒以上。Top10 重合率多数情况下是 10/10偶尔会出现 9/10极少情况下掉到 8/10。考虑到这个差距换来的是接近 30 倍的提速我觉得非常划算。需要说明的是具体耗时和你的数据维度、数据量、服务器配置、索引参数直接相关我这里列的是趋势参考不是绝对基准。你在自己机器上跑出来的数字很可能不一样但“近似比精确快出至少一个数量级”这个结论在百万级数据上是相当稳定的。5.3 结果顺序为什么会有一点点差异看到 Top10 里偶尔缺少一条或者顺序不太一样先不用慌这是近似检索的正常表现。HNSW 图搜索在跳转时可能漏掉某个局部区域里的向量尤其那条向量恰好处在图连接比较稀疏的边界位置。换句话说漏掉的那条也往往是“本来就在第 8 和第 12 名之间徘徊”的临界数据不是把最相关的给丢了。如果你接受不了这种差异有几个调节方向把WITH TARGET ACCURACY往 99 调稍微加大 TopK 值让近似检索返回 20 条你的业务层再自己精确算一次距离重排或者直接切回 IVF 索引试一组参数。说白了近似检索是个“精度和速度的旋钮”不是非黑即白的选择。6. 这次实操踩过的坑以及一些经验补充6.1 坑一向量维度不对直接报错我第一天灌数据时最常看到的报错就是维度不匹配。比如建表定义了 384 维结果外部模型输出的是 128 维TO_VECTOR转换那一瞬间就会失败。这个还好处理因为报错信息直白。问题是有时候模型输出的维度是对的但你在 SQL 里写样例向量时少写了一个数字Oracle 也会报错只是报错信息绕一点。我的经验是所有向量字符串统一走同一段 Python 脚本生成不要手写手写基本必错。6.2 坑二TO_VECTOR 的字符串格式和绑定变量TO_VECTOR接受的字符串格式必须严格是方括号加逗号分隔的数字列表像[0.12,0.34,0.56,...]中间有没有空格有时能容忍有时不能干脆就别加空格。另外你在 APEX 页面项里粘贴一个很长的 384 维向量时注意页面项默认长度可能不够要把P1_QUERY_VEC的最大长度改成 4000 以上。如果走绑定变量方式我建议 SQL 里直接TO_VECTOR(:P1_QUERY_VEC)不要在 PL/SQL 进程里先把字符串转成VECTOR类型再传给页面项因为 APEX 页面项本质上还是字符串载体转来转去反而容易出格式问题。6.3 坑三HNSW 建索引时的内存和 CPUHNSW 索引为了追求快本质上是在内存里维护一张大图所以建索引时内存和 CPU 都会飙高。我当时在 16G 内存的虚拟机上建百万级 HNSW内存峰值接近 11G再多来几份数据可能就 OOM 了。如果你的机器配置不高建议用 IVF 索引或者缩小数据集。附件数据库如果还有别的业务在跑更要谨慎。6.4 常规经验补充权限、版本和数据集最后补几个散的经验APEX 应用会用“解析模式”访问数据库对象跑向量 SQL 前要给对应的解析账号SELECT权限否则页面会报表或视图不存在Oracle 23ai 的老版本和比较新的小版本之间向量相关的 SQL 语法细节可能略有差异遇到报错先查版本对应的官方文档演示用的数据集最好覆盖多种相似程度的内容不要全是不相干的文本不然你很难看出近似检索的“识别能力”。最后再说两句实操体会这次用 APEX 体验 Oracle 23ai 向量近似检索整个过程让我重新认识了“低代码平台”和“数据库新能力”搭配的威力。以前我以为向量检索至少要起一个 Python 服务、写一套 API、再做一版前端页面才算落地结果这次直接在数据库里建索引在 APEX 里拖出输入框和报表半小时就见到效果。后来我又在同一个应用里加了精确检索对比区把“近似”到底损失了多少精度直接展示出来这比任何文档里的性能数字都有说服力。如果你手头也有一批向量数据没想好怎么展示我建议你照着这个思路搭出来试试先速度后精度把数据量往大了调一调那种“原来还能这么快”的感觉比自己闷头刷文档来得真实多了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑