Skip to main content

· 11 min read

调研日期:2026-10-21。

部署场景:将 LDA 平台部署到新的麒麟 ARM64 环境,不涉及旧数据库数据迁移;希望兼顾可靠性、较长支持周期以及全文检索、中文分词、向量检索需求。

1. 推荐结论​

推荐将以下组合作为新环境验收候选:

image: paradedb/paradedb:0.26.2-pg17
platform: linux/arm64

该组合使用 PostgreSQL 17,官方支持至 2029-11-08;镜像提供 ARM64 版本,包含 pg_search 和 pgvector,功能覆盖项目现有检索需求。

这是根据官方版本信息、镜像元数据及项目代码作出的选型建议,尚未完成该版本在 LDA 平台上的完整业务验收,不等于已证明生产稳定性。ParadeDB 0.26.2 于 2026-10-16 发布,调研时发布时间较短。

验收通过后,建议将其定为部署基线,固定镜像摘要,同时持续安排 PostgreSQL、ParadeDB 和基础系统的补丁更新。固定版本用于控制变更,不意味着长期停止更新。

2. LTS 与支持周期​

PostgreSQL 社区没有单独命名的 LTS 分支,每个大版本均提供约五年支持。官方建议使用对应大版本的最新小版本。

PostgreSQL 大版本调研时官方当前小版本官方支持截止日期
1616.152028-11-09
1717.112029-11-08
1818.62030-11-14

选择 PG17 是在成熟度和剩余支持时间之间的工程折中,不代表 PG18 不稳定。如果更重视最长剩余支持时间,也可以评估 PG18,但本次没有完成对应镜像与业务的验证。

未查到 ParadeDB 社区版本与 PostgreSQL 同等的公开五年 LTS 支持承诺。因此不能将 ParadeDB 0.26.2 + PostgreSQL 17 称为整套五年 LTS,也不能把 PG17 的支持截止日期当作该固定镜像的维护承诺。

来源:PostgreSQL 官方版本支持政策。

3. 已核查的镜像标签和架构​

本次从本机查询 Docker Hub API,并对部分标签直接查询 Docker Registry manifest。以下为调研当时的结果,标签可用性日后可能变化。

标签查询结果Linux ARM64
paradedb/paradedb:0.15.20-pg16Hub API 与 Registry 均返回 404当前无法重新拉取或核实
paradedb/paradedb:v0.15.20-pg16Registry 返回 404当前无法核实
paradedb/paradedb:0.15.26-pg16标签存在已确认
paradedb/paradedb:0.25.10-pg16标签存在,已查 manifest已确认
paradedb/paradedb:0.25.11-pg17标签存在已确认
paradedb/paradedb:0.26.2-pg17标签存在已确认

404 仅说明官方仓库当前无法按该标签取得镜像,不证明它历史上从未发布。若其他机器已有本地缓存,仍可能继续运行,但不适合作为新环境可重复部署的依赖。

0.15.20 软件本身是 2025-05-16 发布的正式版本;软件发布版本与当前 Docker 镜像标签是否存在是两件事。后续 0.15.21 还修复了查询结果、重扫描状态、排序等问题,所以不建议新部署继续围绕 0.15.20 选型。

0.15.26-pg16 可作为尽量少改旧应用的测试候选,但不属于本次长期部署的首选。0.25.11-pg17 也有可用 ARM64 镜像,本次最终推荐测试更新的正式版本 0.26.2-pg17;没有证据据此承诺后者在所有负载下都比前者稳定。

来源:

4. 推荐镜像内的组件与摘要​

根据官方 ARM64 manifest 对应的镜像配置、环境变量和构建历史核查:

组件版本或状态
PostgreSQL17.11-1.pgdg13+2
pg_search0.26.2
pgvector0.8.6
基础发行版Debian trixie
预加载配置构建历史中配置了 pg_search,pg_cron,pg_stat_statements
架构同一标签提供 linux/amd64、linux/arm64

调研时 Docker Hub 返回的多架构索引摘要:

sha256:0674d7b4e68833b9ced8fee8a59b61537f5700e2136e4f246431e767d29ed10d

ARM64 镜像 manifest 摘要:

sha256:0946349b76e56c5b4d7aec060ed1857880aad1c28439ef2cf7fa40cd07d28515

验收通过后,可使用多架构索引摘要配合平台参数固定内容:

image: paradedb/paradedb:0.26.2-pg17@sha256:0674d7b4e68833b9ced8fee8a59b61537f5700e2136e4f246431e767d29ed10d
platform: linux/arm64

该示例是镜像选型片段,不是完整 Compose 文件。新环境应使用新建的数据库数据卷,并核查镜像数据目录、PGDATA、初始化脚本和实际扩展创建情况。

5. 与现有业务需求的对应关系​

项目需求代码中观察到的使用方式推荐组合的对应能力与待验证项
文档全文检索chunk_text @@@ :query_textpg_search 提供全文检索;验证现有查询语法和结果
BM25 排序paradedb.score(c.id)提供相关性评分;验证函数兼容、排序及 JOIN 行为
中文及多语言分词索引 text_fields 指定 ICU支持 ICU,也可评估 Jieba;验证旧配置语法和中文召回
向量字段与检索SQLAlchemy Vector,多种维度镜像包含 pgvector;验证建表、实际距离算子及查询
向量索引模型中使用 IVFFlat验证所用维度、操作符类和索引创建;不能仅凭扩展存在认定索引可用
模糊匹配初始化创建 pg_trgm验证新库中扩展创建和相关索引
用户权限和目录过滤全文查询 JOIN 文档表,按用户、文档 ID 过滤验证过滤后的召回和排序,防止跨用户数据进入结果

主要代码位置:

  • lda-api/backend/app/admin/crud/crud_doc_chunk.py
  • lda-api/backend/app/admin/crud/crud_doc_embedding.py
  • lda-api/backend/app/admin/model/sys_doc_embedding.py
  • lda-api/backend/sql/init_test_data_pg.sql
  • lda-api/backend/sql/init_extensions.sql

项目中观察到的向量维度包括 384、768、1024、1536、3072;模型中并非每种维度都定义了 IVFFlat 索引。不同索引和数据类型的维度限制需要分别核查,不能将“可以存储”与“可以按当前定义创建索引”等同。

中文分词能力参考:ParadeDB 官方分词与 v2 API 说明。

6. 上线前必须完成的业务验证​

不涉及旧数据迁移,仍需适配和验证应用的初始化 SQL 与查询 SQL,不能仅替换镜像标签后认定部署完成。

  1. 在麒麟 ARM64 上启动隔离的测试实例,核查实际 PostgreSQL 版本、可用扩展、已创建扩展和预加载配置。
  2. 创建 vector、pg_search、pg_trgm 等项目所需扩展,执行实际建表及初始化流程。镜像中安装了扩展包,不代表每个业务数据库都已创建该扩展。
  3. 使用实际初始化 SQL 验证 USING bm25、key_field 和 text_fields 的兼容性;按所选版本文档调整,不假定旧语法完全兼容。
  4. 使用中文、英文、中英文混合、短词和专有名词测试 ICU 分词、全文召回、评分和排序。若比较 Jieba,使用同一组业务样例衡量效果。
  5. 验证 @@@、paradedb.score()、JOIN、用户权限和目录过滤等现有查询。必要时对照新 API 调整代码。
  6. 验证各实际使用的向量维度、索引创建、距离计算和检索结果,并检查执行计划是否按预期使用索引。
  7. 执行代表性的并发读写、更新删除后的检索、重启恢复,以及备份恢复测试。
  8. 验收通过后,统一开发、普通部署和 ARM64 部署的版本基线,并固定镜像摘要。

7. 项目当前配置差异​

调研时读取到:

文件当前镜像
lda-api/deploy/backend/docker-compose.ymlparadedb/paradedb:0.15.20-pg16
lda-api/deploy/dev/docker-compose.ymlparadedb/paradedb:0.15.20-pg16
lda-api/deploy/backend/docker-compose.arm64.ymlparadedb/paradedb:0.25.10-pg16

这些环境当前并不一致。本次只保存调研结果,没有修改 Compose、初始化 SQL,也没有在本地或服务器上切换数据库。

8. 已完成与未完成的核查范围​

已完成:官方支持周期、发布记录、Docker Hub 标签、候选镜像架构、推荐 ARM64 镜像配置和构建历史,以及项目检索相关代码检查。

未完成:推荐镜像在麒麟服务器上的数据库启动测试、项目初始化、业务检索回归、压力测试及恢复演练。因此本报告给出的是有依据的版本候选和验收条件,不是生产验收结论或长期支持保证。

· 12 min read

本文记录 scripts/publish-mp4.cmd 的一次实际调优过程,目标是把本地 MP4 通过 FFmpeg 发布到 MediaMTX,再由浏览器通过 WebRTC 流畅播放。

数据链路​

MP4 文件
│
│ FFmpeg 解码、重新编码、按实时速度发布
▼
MediaMTX RTSP
│
│ WebRTC
▼
浏览器播放器

这条链路中的卡顿不一定来自同一个位置,可能来自:

  • MP4 本身帧率或时间戳不稳定
  • FFmpeg 编码速度跟不上
  • 编码瞬时码率波动过大
  • 关键帧间隔不适合实时传输
  • MediaMTX 或网络传输问题
  • 浏览器解码、页面渲染或本机 CPU 竞争

因此,调参之前应该先确定问题出现在哪一层。

素材信息​

使用 ffprobe 检查测试视频:

ffprobe -v error `
-show_entries format=duration,bit_rate:stream=index,codec_type,codec_name,width,height,r_frame_rate,avg_frame_rate,time_base,sample_rate,channels `
-of json `
.\scripts\test.mp4

测试素材的主要参数为:

项目数值
视频编码H.264 High Profile
分辨率720×404
帧率25 FPS
视频码率约 669 kbps
B 帧存在
音频编码AAC
采样率44.1 kHz
声道双声道

源视频包含 B 帧。浏览器通过 WebRTC 播放 H.264 时通常不适合直接传输带 B 帧的视频,因此不能简单使用:

-c:v copy

仍然需要将视频重新编码为不含 B 帧、浏览器兼容性更好的 H.264 Baseline 流。

调整前的参数​

原脚本的视频编码部分主要使用:

-c:v libx264
-preset ultrafast
-tune zerolatency
-profile:v baseline
-pix_fmt yuv420p
-crf 20
-g 30
-keyint_min 30
-sc_threshold 0
-bf 0

这里使用 CRF 控制画面质量。CRF 的特点是尽量保持视觉质量,但场景复杂度变化时,瞬时码率也会明显变化。

对于保存到磁盘的视频,码率波动通常不是问题;对于低延迟实时传输,过大的瞬时码率可能造成接收端缓冲不均匀,表现为画面偶尔停顿后追帧。

原来的关键帧间隔为 30 帧。对于 25 FPS 素材,相当于:

30 ÷ 25 = 1.2 秒

关键帧体积通常远大于普通预测帧。关键帧过于频繁,会增加瞬时带宽和编码数据量。

最终改动​

最终脚本的视频参数调整为:

-c:v libx264
-preset ultrafast
-tune zerolatency
-profile:v baseline
-pix_fmt yuv420p
-b:v 1500k
-maxrate 1500k
-bufsize 3000k
-g 50
-keyint_min 50
-sc_threshold 0
-bf 0

完整命令位于:

scripts/publish-mp4.cmd

1. 从 CRF 改为受约束的码率控制​

移除:

-crf 20

增加:

-b:v 1500k
-maxrate 1500k
-bufsize 3000k

参数含义:

  • -b:v 1500k:目标视频码率约为 1500 kbps。
  • -maxrate 1500k:限制编码器的瞬时输出码率。
  • -bufsize 3000k:设置码率控制缓冲区,大小约等于 2 秒目标码率。

这组参数的目的不是单纯降低码率,而是让数据输出更均匀,减少 WebRTC 接收端遇到突然码率峰值的概率。

1500 kbps 对当前 720×404、25 FPS 的素材有较大余量。更高分辨率可以参考以下起点:

分辨率帧率建议起始码率
640×36025–30 FPS600–1000 kbps
1280×72025–30 FPS1500–3000 kbps
1920×108025–30 FPS3000–6000 kbps

这些只是起点。人物动作复杂、画面纹理丰富时通常需要更高码率。

2. 关键帧间隔调整为 2 秒​

修改前:

-g 30 -keyint_min 30

修改后:

-g 50 -keyint_min 50

测试视频是 25 FPS,所以 50 帧对应 2 秒:

50 ÷ 25 = 2 秒

这样既能让新加入的 WebRTC 客户端较快等到关键帧,又不会过于频繁地产生较大的 I 帧。

如果素材是 30 FPS,可以考虑使用:

-g 60 -keyint_min 60

通用计算方式:

关键帧间隔 = 帧率 × 2

3. 禁止场景切换额外插入关键帧​

-sc_threshold 0

默认情况下,x264 可能在画面发生大幅变化时额外插入关键帧。这对普通视频压缩有帮助,但可能让实时流的码率出现不可预测的峰值。

设置为 0 后,关键帧节奏更加固定。

4. 禁用 B 帧​

-bf 0

B 帧需要参考前后帧,会增加帧重排和播放延迟。MediaMTX 官方也指出,浏览器 WebRTC 对包含 B 帧的 H.264 流兼容性不好。

实时 WebRTC 场景通常应禁用 B 帧。

5. 保留低延迟编码设置​

-preset ultrafast
-tune zerolatency
  • ultrafast:降低编码复杂度和 CPU 使用量,代价是相同画质下码率更高。
  • zerolatency:关闭或缩短编码器内部会增加延迟的缓冲机制。

当前项目还要同时运行本地 ASR 推理,因此优先减少视频编码对 CPU 的占用。

6. 保留 Baseline 与 yuv420p​

-profile:v baseline
-pix_fmt yuv420p

这两个参数用于提高浏览器和 WebRTC 的兼容性:

  • Baseline Profile 避免使用部分复杂编码特性。
  • yuv420p 是浏览器和硬件解码器支持最广泛的像素格式。

曾尝试但最终撤回的参数​

调试期间曾尝试:

-fflags +genpts
-fps_mode cfr

它们分别用于重新生成时间戳和强制输出恒定帧率。

但测试素材本身已经是标准的 25 FPS,并且时间戳正常。在约 15 秒的测试中,强制 CFR 导致 FFmpeg 报告:

dup=3 drop=0

这表示 FFmpeg 为了满足强制帧率复制了 3 帧。重复帧虽然不等于真正的编码卡顿,但肉眼可能看到轻微顿挫。

因此最终撤回了这两个参数。经验是:

不要在源视频时间戳正常时无条件重建时间戳或强制帧率。

如果某个素材确实是可变帧率、时间戳损坏或时间戳不连续,再针对该素材使用这些参数。

音频参数​

音频编码部分保持为:

-c:a libopus
-b:a 64k
-ar 48000
-ac 1
-application lowdelay

参数含义:

  • 转码为 WebRTC 兼容性良好的 Opus。
  • 码率为 64 kbps。
  • 采样率为 48 kHz,这是 Opus 和 WebRTC 的常用采样率。
  • 转换为单声道,足够用于语音识别并减少数据量。
  • 使用 Opus 低延迟模式。

ASR 后端会再次将音频转换为模型需要的 16 kHz 单声道 PCM。

如何判断编码器是否跟得上​

运行脚本时注意 FFmpeg 的状态行:

frame= 321 fps=25 ... speed=1.04x

重点关注:

字段含义
fpsFFmpeg 当前处理帧率
speed处理速度相对于实时速度的倍数
dup为满足帧率要求而复制的帧数
drop丢弃的帧数

对于 25 FPS 实时流,理想状态是:

fps ≈ 25
speed ≈ 1.0x
drop = 0

本次测试中 FFmpeg 稳定输出约 25 FPS,速度约为 1.04x,且没有丢帧,说明 CPU 编码能力足够,问题不是编码器处理不过来。

分层排查卡顿​

1. 直接播放原始 MP4​

ffplay .\scripts\test.mp4

如果这里也卡顿,问题来自素材本身。

2. 播放 RTSP​

先启动推流脚本,然后执行:

ffplay -rtsp_transport tcp rtsp://localhost:8554/mystream

如果原始 MP4 流畅,但 RTSP 卡顿,应检查 FFmpeg 日志、MediaMTX 和编码参数。

3. 播放 WebRTC​

打开:

http://localhost:8889/mystream/

如果 RTSP 流畅而 WebRTC 卡顿,问题更可能出现在浏览器解码、WebRTC 传输或页面渲染层。

4. 检查本机资源竞争​

当前项目会同时运行:

  • FFmpeg H.264 视频转码
  • FFmpeg 音频解码
  • sherpa-onnx CPU 语音识别
  • MediaMTX
  • 浏览器视频解码与页面渲染

即使 FFmpeg 没有报告丢帧,ASR 推理也可能抢占 CPU,造成浏览器渲染不稳定。可以临时停止后端 ASR,再观察视频是否改善。

最终命令​

脚本最终使用的核心命令如下:

ffmpeg -hide_banner -re -stream_loop -1 -i "输入文件.mp4" `
-map 0:v:0 -map 0:a:0? `
-c:v libx264 `
-preset ultrafast `
-tune zerolatency `
-profile:v baseline `
-pix_fmt yuv420p `
-b:v 1500k `
-maxrate 1500k `
-bufsize 3000k `
-g 50 `
-keyint_min 50 `
-sc_threshold 0 `
-bf 0 `
-c:a libopus `
-b:a 64k `
-ar 48000 `
-ac 1 `
-application lowdelay `
-f rtsp `
-rtsp_transport tcp `
"rtsp://localhost:8554/mystream"

其中:

  • -re 让 FFmpeg 按正常播放速度读取 MP4,而不是尽快发送完整文件。
  • -stream_loop -1 让视频无限循环。
  • -map 0:a:0? 中的 ? 表示音轨是可选的,没有音轨时仍可推送视频。

总结​

本次改善的核心不是让 FFmpeg“跑得更快”,而是让实时数据输出更加稳定:

  1. 从质量优先的 CRF 改为带上限和缓冲的码率控制。
  2. 将关键帧间隔调整为适合 25 FPS 实时流的 2 秒。
  3. 保持无 B 帧的 WebRTC 兼容编码。
  4. 不随意重建正常素材的时间戳或强制帧率。
  5. 通过 fps、speed、dup 和 drop 数据验证调优效果。