文章851
标签121
分类10

VS Code 多家 AI Agent 集成使用指南(Copilot / Claude Code / Codex / Gemini / DeepSeek)

一台 VS Code,同时接入 5 家 AI 厂商。本文整理每家的安装方式、唤起入口、核心快捷键、适用场景,以及它们之间该怎么配合使用。

环境:macOS + VS Code(已汉化)


目录

  1. 一图看懂:5 家 AI 在 VS Code 里的位置
  2. 通用快捷键速查表
  3. GitHub Copilot 使用方式
  4. Claude Code 使用方式
  5. Codex (OpenAI) 使用方式
  6. Gemini Code Assist 使用方式
  7. DeepSeek 使用方式
  8. 5 家对比:什么场景用哪个
  9. 常见问题排查

一、一图看懂:5 家 AI 在 VS Code 里的位置

图1:VS Code 整体布局标注图

📷 截图说明:截一张 VS Code 完整窗口,用红框标注:
① 左上角聊天面板的标签页(聊天 | CLAUDE CODE | CODEX)
② 编辑器中的灰色内联补全(Copilot)
③ 右下角状态栏的 Copilot 图标
④ 左侧活动栏的 Gemini / DeepSeek 图标

5 家 AI 分布在三个区域:

区域有谁干什么
编辑器内(内联)Copilot实时灰色代码补全,按 Tab 接受
聊天面板(标签页)Copilot Chat / Claude Code / Codex对话、Agent 改代码
左侧活动栏(独立图标)Gemini / DeepSeek各自的独立聊天面板

二、通用快捷键速查表(Mac)

编辑器内联补全(Copilot)

快捷键功能
Tab接受当前灰色建议
Esc拒绝建议
Option + ]下一条建议
Option + [上一条建议
Cmd + →只接受建议中的一个词

聊天 / Agent 面板

快捷键功能
Cmd + Ctrl + I打开/聚焦 Copilot Chat
Cmd + I编辑器内联对话(选中代码后直接改)
Cmd + Shift + P命令面板(所有 AI 命令的总入口)
Esc(Claude Code 输入框提示时)把选中代码附加到 Claude 对话

通用代码操作

快捷键功能
Fn + Ctrl + Space手动触发传统代码补全(gopls 等)
F12转到定义
Cmd + Shift + X扩展市场
💡 Mac 注意Ctrl + Space 默认被系统输入法切换占用,所以传统补全需要 Fn + Ctrl + Space,或者去 Cmd+Shift+P → Preferences: Open Keyboard Shortcuts 里把 Trigger Suggest 改绑成别的键。

三、GitHub Copilot —— 主力内联补全

3.1 它是什么

Copilot 的核心价值不是聊天,是写代码时的实时补全。聊天功能它有,但和 Claude/Codex 比不算强项。另外 Copilot Chat 是个"模型集合",里面可以切换 GPT-5 系列和 Claude 系列模型。

3.2 安装与登录

code --install-extension GitHub.copilot
code --install-extension GitHub.copilot-chat

登录流程:

  1. Cmd + Shift + P → 输入 GitHub Copilot: Sign in
  2. 浏览器弹出 GitHub 授权页 → 点 Authorize Visual Studio Code
  3. 回到 VS Code,右下角出现 Copilot 图标即成功

2026-06-12T14:53:12.png

📷 截图说明:浏览器里 GitHub 的 Authorize 授权确认页

⚠️ 免费版需要先在 github.com/copilot 开通 Copilot Free 计划(每月 2000 次补全 + 50 次对话)。
⚠️ 国内网络访问 GitHub 不稳定,登录失败多试几次或挂代理。

3.3 核心用法 1:内联补全(每天都在用)

直接写代码,写到一半停顿一下,灰色建议自动出现:

2026-06-12T14:53:43.png

📷 截图说明:在 main.go 里输入 func parseConfig( 后停顿,截下灰色补全建议出现的瞬间
你输入:  func health(w http.ResponseWriter, r *http.Request) {
灰色建议:    w.WriteHeader(http.StatusOK)          ← 按 Tab 接受
             fmt.Fprintln(w, "ok")
         }

技巧:用注释引导补全。 先写一行中文注释描述意图,Copilot 会按注释生成代码:

// 从环境变量读取端口,默认 8080,启动 HTTP 服务并支持优雅退出
func main() {
    // ← 这里停顿,Copilot 会生成整段

3.4 核心用法 2:Copilot Chat 对话

  • 打开方式:Cmd + Ctrl + I,或点击聊天面板的"新建聊天"
  • 注意区分:下拉菜单里的"新建 Copilot CLI 会话"是终端模式,新手别用这个,用"新建聊天"

2026-06-12T14:54:14.png

📷 截图说明:Copilot Chat 面板,点开右下角模型下拉框,展示 GPT-5 / Claude 可切换的模型列表

聊天里的常用指令(输入框里直接打):

指令作用
/explain解释当前文件/选中代码
/fix修复当前问题
/tests生成单元测试
@workspace 哪里实现了登录逻辑跨整个项目提问
@terminal 这个报错怎么解决针对终端报错提问

3.5 核心用法 3:选中代码右键

选中代码 → 右键 → Copilot 子菜单 → Explain / Fix / Generate Tests


四、Claude Code —— 复杂任务 Agent 主力

4.1 它是什么

Claude Code 是 Agent 模式的代表:你给它一个任务("给这个文件全部加上中文注释"、"重构这个函数"),它自己扫描项目、改文件、跑测试、汇报结果。理解大项目上下文的能力是 5 家里最强的。

4.2 安装与登录

code --install-extension Anthropic.claude-code

打开方式:点击聊天面板顶部的 CLAUDE CODE 标签页,首次使用按提示登录 Anthropic 账号。

2026-06-12T14:54:45.png

📷 截图说明:Claude Code 标签页打开状态,底部输入框可见 "Ask Claude to edit..."

4.3 核心用法 1:直接派任务

在输入框里用自然语言描述任务即可,支持中文

给 main.go 的所有函数补上中文注释
把这个项目的启动端口改成从配置文件读取
帮我看看为什么 /health 接口返回 404

它会展示执行过程(扫描了哪些文件、跑了什么命令),改完代码后你可以点 审核 查看 diff,撤销 回滚。

2026-06-12T14:55:28.png

📷 截图说明:Claude Code 执行任务时的过程视图,包含 Bash/Read 步骤和最后的"已编辑 main.go +8 -1 撤销/审核"按钮

4.4 核心用法 2:把选中代码带进对话

  1. 在编辑器里选中代码
  2. 看 Claude Code 输入框,会提示 "Esc to attach selected text"
  3. 此时直接输入问题,选中的代码会作为上下文一起发送

4.5 核心用法 3:终端模式

习惯命令行的话,直接在终端运行:

claude

功能和面板一致,还能用 /init 生成项目说明文件、/model 切换模型。

4.6 实用设置

  • 输入框下方的 "Ask before edits" 开关:开 = 每次改文件前先问你;关 = 自动改。新手建议开。

五、Codex (OpenAI) —— 右键集成最顺手

5.1 它是什么

OpenAI 的 Agent 扩展,能力定位和 Claude Code 类似(对话 + 自动改代码),特色是右键菜单集成最好

5.2 打开方式

点击聊天面板顶部的 CODEX 标签页。

5.3 核心用法:Add to Codex Thread(招牌功能)

  1. 选中一段代码
  2. 右键 → Add to Codex Thread
  3. 代码直接进入 Codex 对话,输入你想怎么改

2026-06-12T14:56:01.png

📷 截图说明:选中代码后的右键菜单,红框标注 "Add to Codex Thread" 那一项

这是目前 5 家里"选中代码 → 进对话"路径最短的一个,改局部代码非常快。


六、Gemini Code Assist —— 免费额度大户

6.1 安装与登录

code --install-extension google.geminicodeassist

打开方式:左侧活动栏的 Gemini 图标(菱形星星),首次使用登录 Google 账号。个人版免费,每天有大量免费请求次数。

2026-06-12T14:56:28.png

📷 截图说明:左侧活动栏 Gemini 图标 + 打开后的聊天面板

6.2 核心用法

  • 聊天面板直接提问(支持中文)
  • 选中代码 → 右键 → Gemini: Explain this / Generate unit tests
  • 也有内联补全,但和 Copilot 同开会打架,建议只留一家内联补全(见第八节)
⚠️ 需要能访问 Google 服务的网络环境。

七、DeepSeek —— 国内直连,按量计费便宜

7.1 安装

扩展市场搜 DeepSeek R1(colourafredi 出品,238K 下载那个)。市场里 DeepSeek 相关扩展很多,注意分两类:

  • 独立使用:DeepSeek R1、DeepSeek Code Generator —— 填 API Key 直接用 ✅
  • 依赖 Copilot 订阅:DeepSeek for GitHub Copilot、DeepSeek V4 for Copilot Chat —— 是把 DeepSeek 模型挂进 Copilot Chat 里用的,需要 Copilot 已激活

7.2 配置 API Key

  1. 访问 platform.deepseek.com 注册并创建 API Key
  2. VS Code 里打开该扩展设置,粘贴 Key

2026-06-12T14:56:59.png

📷 截图说明:DeepSeek 扩展的设置页,API Key 输入框位置(Key 本身打码)

7.3 核心用法

左侧活动栏点 DeepSeek 图标打开面板,对话即可。最大优势:国内不用梯子,直连,价格极低。


八、5 家对比:什么场景用哪个

场景首选原因
日常写代码实时补全Copilot内联补全延迟最低、最顺滑
大重构 / 跨文件任务 / 找 bug 根因Claude Code项目级理解最强,Agent 自动执行
选中一段代码快速改Codex右键 Add to Thread 路径最短
免费额度用光了过渡Gemini个人版免费额度大
没有梯子的环境DeepSeek国内直连唯一选择

推荐的日常组合

内联补全:只开 Copilot 一家(多家同开会冲突)
Agent 任务:Claude Code 为主
局部修改:Codex 右键
备胎:Gemini / DeepSeek

关闭多余的内联补全(重要!)

多个扩展同时提供内联补全会互相打架。建议设置中只保留 Copilot:

  1. Cmd + , 打开设置
  2. 搜索 inline suggest,确认 Copilot 开启
  3. 在 Gemini 设置里搜 code completion,关掉它的补全(保留聊天)

网络要求一览

厂商国内直连
DeepSeek✅ 可以
Copilot / Claude / Codex / Gemini❌ 需要代理

九、常见问题排查

Q1:code 命令在终端不可用(command not found: code)

Cmd + Shift + P → 输入 Shell Command: Install 'code' command in PATH → 执行 → 重开终端

或手动加 PATH:

echo 'export PATH="/Applications/Visual Studio Code.app/Contents/Resources/app/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

Q2:Go 代码没有传统补全提示

大概率是 gopls 没就绪或不在 module 里:

cd 你的项目目录
go mod tidy

然后 Cmd + Shift + PGo: Restart Language Server

Q3:想要 Cursor 那种"哪行有问题直接标在行尾"的效果

Error Lens 扩展(usernamehw 出品),错误信息直接显示在代码行尾,配合 gopls 使用。

Q4:Copilot 装了但右键菜单没有它

没登录或没订阅。检查右下角状态栏 Copilot 图标:有 X = 未激活。去 github.com/copilot 开通 Free 计划后重新 Sign in。

Q5:装语言包后界面还是英文

Cmd + Shift + PConfigure Display Language → 选 zh-cn → 重启。注意别装成 "for VS Code Speech" 的语音包(图标是麦克风的那个是语音包,装错很常见)。

Q6:聊天面板标签页想增加/调整位置

面板标签(聊天 / CLAUDE CODE / CODEX)支持拖拽:按住一个面板的标题栏,拖到目标标签栏松手即可合并成标签页。


写在最后

5 家 AI 不是用来"全都开着"的,而是各司其职:Copilot 管手感(补全),Claude Code 管脑子(复杂任务),Codex 管手速(局部修改),Gemini 和 DeepSeek 管兜底(额度和网络)。先把 Copilot 内联补全 + Claude Code Agent 这两个用熟,就已经超过大部分人的使用效率了。

RabbitMQ 联调踩坑实践:对接 CFD 桥方 Queue API

场景:衍生品对冲侧通过 RabbitMQ 接入桥方(CFD 流动性桥)的 Queue API,订阅对冲账户的余额与持仓推送。
:Go + github.com/rabbitmq/amqp091-go + 长驻消费者进程 + 结构化诊断日志([CFD-MQ-DIAG])。
核心结论:MQ「能连上」≠「能消费」≠「能认证」≠「能收推送」——四层要分开验证,分开记日志

1. 先建立正确的心智模型

1.1 两套凭证、两层协议

这是整个联调中最容易踩的坑:RabbitMQ 传输层凭证Queue API 应用层凭证是两套完全独立的账号体系。

层级用途账号示例(脱敏)配置项
RabbitMQ 传输层TCP/TLS 建连、Publish / Consumemq_client_userCFD_MQ_USER / CFD_MQ_PASSWORD
Queue API 应用层msg=31 认证、订阅账户推送trader@example.com(客户端登录邮箱)CFD_MQ_API_USERNAME / CFD_MQ_API_PASSWORD

踩坑 #1:把 RabbitMQ 密码填进了 CFD_MQ_API_PASSWORD
此时桥方 RabbitMQ 日志会显示 authenticated and granted access(传输层 OK),但 Queue API 仍会用 msg=30 明确拒绝。两边日志看起来"互相矛盾",本质是层级混淆。

1.2 四个阶段,逐级解锁

flowchart TD
  A[① TCP + AMQP over TLS] --> B[② 队列 Consume 权限]
  B --> C[③ Queue API 认证 msg=31 → 32]
  C --> D[④ 订阅 msg=100]
  D --> E[收到 104/105 账户持仓推送]

每一层失败的表现都不同,绝不能拿上一层 OK 推断下一层 OK。诊断工具和日志也必须按层拆开输出。

1.3 与桥方对齐过的配置(脱敏示例)

值(示例)配置项
Hostmq.bridge.example.com:5673CFD_MQ_HOST / CFD_MQ_PORT
VHost/your_vhostCFD_MQ_VHOST
ExchangeBridgeAPICFD_MQ_EXCHANGE
Consumer queue与 exchange 同名 BridgeAPICFD_MQ_QUEUE_IN
Routing key无独立 rk,直接使用 exchange 名CFD_MQ_ROUTING_KEY 留空 → 代码回退到 exchange 名
订阅账户HEDGE_ACC_01(注意:≠ FIX 链路账户 FIX_ACC_01CFD_MQ_SUBSCRIBE_ACCOUNTS
TLS必须CFD_MQ_USE_TLS=true

2. 踩坑时间线(按排查顺序)

坑 A:TLS 握手在生产运行时失败,本地却正常

现象:本地 CLI 探测脚本 TLS 一切正常;部署到生产容器后约 60s 握手失败。

原因:本地与生产的运行时环境不一致——代理、CA 证书、SNI 配置、出口 IP 都可能不同。(原 PHP 项目的版本是 Swoole 协程 hook 干扰了 stream_socket_enable_crypto;Go 没有这个问题,但同类教训完全成立。)

Go 侧正确姿势

import (
    "crypto/tls"
    amqp "github.com/rabbitmq/amqp091-go"
)

func dial(cfg Config) (*amqp.Connection, error) {
    tlsCfg := &tls.Config{
        ServerName: cfg.Host, // 显式指定 SNI,避免经 LB/代理时证书校验失败
        MinVersion: tls.VersionTLS12,
        // 切忌为了"先通再说"设置 InsecureSkipVerify: true 然后忘记删
    }
    uri := fmt.Sprintf("amqps://%s:%s@%s:%d%s",
        url.QueryEscape(cfg.MQUser), url.QueryEscape(cfg.MQPassword),
        cfg.Host, cfg.Port, cfg.VHost)
    return amqp.DialTLS(uri, tlsCfg)
}

教训连通性探测必须在与生产相同的运行时/网络环境里跑。本地过了 ≠ 容器里能过;同时确认出口 IP 是否在桥方白名单内。


坑 B:队列「存在」≠ 能 Consume

现象

ACCESS_REFUSED - read access to queue 'BridgeAPI' in vhost '/your_vhost'
refused for user 'mq_client_user'

而诊断报告同时显示:

STEP-04  queue exists => OK
STEP-04b consume      => DENIED

原因:RabbitMQ 的 passive declareQueueDeclarePassive,只检查队列是否存在)和 Consume(真正读消息)所需的 ACL 权限不同。桥方只给了 declare/write,没给 read。

Go 侧诊断要分两步测

// 第一步:队列是否存在(不需要 read 权限)
if _, err := ch.QueueDeclarePassive(queue, true, false, false, false, nil); err != nil {
    report("STEP-04", "queue exists", err) // 队列名错 or 不存在
    return
}
report("STEP-04", "queue exists => OK", nil)

// 第二步:单独验证 consume(需要 read 权限;channel 可能因 ACL 错误被关闭,需新开)
ch2, _ := conn.Channel()
if _, err := ch2.Consume(queue, "diag-probe", false, false, false, false, nil); err != nil {
    report("STEP-04b", "consume => DENIED", err) // ← 唯一 blocker 在这里
    return
}
report("STEP-04b", "consume => OK", nil)

教训:诊断必须单独测 Consume,不能只看 queue exists。发给桥方的日志要写清:

队列名正确、队列存在,唯一问题是 consume ACL

注意:ACL 错误会关闭当前 channel(403 是 channel 级异常),后续操作要重开 channel,否则会被连带报错干扰判断。


坑 C:桥方说了队列名,但问题根本不在命名

桥方原话:Queue name same as exchange — BridgeAPI

我们的误解:以为还缺一个独立的队列名配置。
实际情况:队列名早已配对,真正卡住的是 consume 权限(坑 B)。

发给桥方的标准句式(避免对方往错误方向排查):

Queue name BridgeAPI is CORRECT (queue = exchange).
Queue EXISTS. Publish OK.
ONLY blocker: user mq_client_user has NO read/consume permission on queue BridgeAPI.

教训:跨团队联调时,日志和措辞要主动排除已验证项,只留唯一变量,桥方才能一次改对。


坑 D:先 Publish 认证、后注册 Consumer → 响应被"漏接"

现象msg=31 认证请求已成功 publish,但 30 秒内队列「零消息」,迟迟等不到 msg=32

原因:桥方响应非常快。如果 Consume 注册晚于 Publishmsg=32 可能在消费者就位之前就已抵达——取决于队列配置(TTL、auto-delete、竞争消费者),这条响应可能永远收不到。

Go 侧正确顺序——Consume 返回的 delivery channel 必须建立:

// 1. 先注册消费者,拿到 delivery channel
deliveries, err := ch.Consume(queueIn, consumerTag, false, false, false, false, nil)
if err != nil { return err }
log.Info("STEP-02b consumer_registered", "queue", queueIn)

// 2. 再发认证请求
if err := publishAuth(ch, exchange, routingKey, apiUser, apiPass); err != nil {
    return err
}
log.Info("STEP-03 auth msg=31 => sent")

// 3. 带超时等待 msg=32
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
for {
    select {
    case d := <-deliveries:
        env, err := unpackAuto(d) // 见坑 E
        // ... 判断 msg=32 / msg=30
    case <-ctx.Done():
        return fmt.Errorf("ERR-AUTH-TIMEOUT: ZERO messages on queue in 30s")
    }
}

教训:异步 MQ 联调要假设响应可能比你的下一行代码更快。先绑定、再请求,是铁律。


坑 E:消息收到了,却解析成乱码 module=842073610

现象

STEP-MSG recv module=842073610 message_id=858927411 bytes=30

按约定的 [module int32 LE][messageId int32 LE][protobuf body] 解析前 8 字节,得到荒谬的大整数。

原因出站和入站的信封格式不对称。我们发出去带 8 字节头,但桥方送回来的消息常常:

  1. 裸 protobuf body(错误响应 / 认证响应),没有任何头;或者
  2. module / messageId 放在 AMQP headersamqp.Table)里,body 仍是纯 protobuf。

实际 body 十六进制开头形如 0a06...12 14...——典型的 protobuf 字符串字段(0x0A = field 1, wire type 2),那串"大整数"其实是把字符串内容当成了小端 int32。

解法:实现 unpackAuto,按以下顺序探测:

func unpackAuto(d amqp.Delivery) (Envelope, error) {
    // 1. 小端 8 字节头:module/messageId 落在合法枚举集合内才接受
    if env, ok := tryHeader(d.Body, binary.LittleEndian); ok {
        return env, nil
    }
    // 2. 大端 8 字节头
    if env, ok := tryHeader(d.Body, binary.BigEndian); ok {
        return env, nil
    }
    // 3. AMQP headers 携带类型信息
    if mt, ok1 := d.Headers["moduleType"]; ok1 {
        if ot, ok2 := d.Headers["objectType"]; ok2 {
            return Envelope{Module: toInt32(mt), MessageID: toInt32(ot), Body: d.Body}, nil
        }
    }
    // 4. 无头 protobuf 探测:依次尝试反序列化为 ErrorResponse / AuthResponse
    return probeBareProto(d.Body)
}

func tryHeader(b []byte, bo binary.ByteOrder) (Envelope, bool) {
    if len(b) < 8 {
        return Envelope{}, false
    }
    m, id := int32(bo.Uint32(b[0:4])), int32(bo.Uint32(b[4:8]))
    if !isKnownModule(m) || !isKnownMessageID(id) { // 27/30/31/32/100/104/105...
        return Envelope{}, false
    }
    return Envelope{Module: m, MessageID: id, Body: b[8:]}, true
}

教训不要假设入站信封和出站对称。联调早期日志就应该带上 hex_prefix(body 前 N 字节十六进制)和完整 amqp_headers JSON,否则连"格式不对"这个结论都得不出来。


坑 F:headers 里 objectType=31,body 却是 msg=30 错误响应

现象

amqp_headers: {
  "apiVersion": "2.62.x",
  "moduleType": 0,
  "objectType": 31
}

headers 声称是 31(认证请求类型),body 反序列化出来却是 QueueErrorResponse,真实 message_id = 30

STEP-03 auth msg=30 error message=****** detail=ops@bridge.example.com
STEP-03 auth msg=31 => REJECTED
[PROBLEM] code=ERR-AUTH-REJECTED

字段解读

字段含义
message=******错误体里的 Message 字符串。注意:当其长度与你提交的 API 密码一致时,极可能就是把你提交的密码原样回显了——日志落盘前务必脱敏!
detail=ops@bridge.example.com桥方内部 operator / 系统标识,不是提示你改 api_login
headers 里的 objectType=31桥方实现 quirk:headers 写的是请求类型,body 却是错误响应。以 body 解析结果为准

解法(业务侧):向桥方确认登录邮箱对应的 Queue API 专用密码(通常 = 客户端登录密码,≠ RabbitMQ 密码)。

教训:从「认证超时」进化到「认证被明确拒绝」,靠的是信封解析修对(坑 E)。解析不对时,你只会误以为"桥方没回包",把锅甩错方向。另外:桥方可能在错误消息里回显密码,自己的日志管道必须做脱敏


坑 G:「认证超时」vs「认证拒绝」——日志必须能区分

日志关键词含义优先排查
ZERO messages on queue in 30s队列完全静默路由、RabbitMQ 凭证、桥方是否处理 msg=31
messages received but no msg=32有回包但类型不符信封格式(坑 E)、是否其实是 msg=30
msg=30 error / ERR-AUTH-REJECTED明确拒绝Queue API 账号 / 密码 / 权限
msg=32 authenticated=true认证通过继续 subscribe

四种状态对应四个不同的责任方和动作,混在一条 "auth failed" 里就等于没记。


3. 出站 / 入站消息格式备忘

3.1 我们发出(Publish)

默认信封:[module int32 LE][messageId int32 LE][protobuf body]

func packOutbound(module, messageID int32, body []byte) []byte {
    buf := make([]byte, 8+len(body))
    binary.LittleEndian.PutUint32(buf[0:4], uint32(module))
    binary.LittleEndian.PutUint32(buf[4:8], uint32(messageID))
    copy(buf[8:], body)
    return buf
}

认证示例:module=0(General),messageId=31(QueueAuthenticationRequest)。

3.2 桥方送回(Consume)——至少有四种形态

形态识别方式
8B LE 头 + bodymodule / messageId 落在合法枚举内(0/27/30/32/104/105…)
8B BE 头 + body同上,换大端
无头 protobuf + AMQP headersmoduleType / objectType 在 headers,body 纯 protobuf
无头 error/auth protobuf0x0A 开头的字符串字段等特征,靠反序列化探测

3.3 常用 messageId(GeneralModule)

ID含义
27MessageReceived(服务端确认收到 publish)
30QueueErrorResponse
31QueueAuthenticationRequest
32QueueAuthenticationResponse
100AccountApiSubscribeRequest
104Account 推送
105AccountPosition 推送

4. 诊断工具与 Go 工程布局

4.1 一键桥方报告(推荐先跑这个,输出直接发桥方)

go run ./cmd/mq-bridge-report            # 控制台输出
go run ./cmd/mq-bridge-report -save /tmp/cfd_mq_diag.txt

报告分层输出,重点关注:

  • STEP-04:队列名、是否存在
  • STEP-04bconsume 权限(与 04 分开!)
  • STEP-05publish 权限
  • STEP-07:在线 auth 探测
  • 末尾 BRIDGE SUMMARYPASSED / FAILED / NEEDS-BRIDGE

4.2 消费者实盘日志

go run ./cmd/mq-account-consumer

成功路径应依次出现:

STEP-02  amqp_connect => OK
STEP-02b consumer_registered queue=BridgeAPI
STEP-03  auth msg=31 => sent
STEP-03  auth msg=32 response authenticated=true
STEP-04  subscribe msg=100 => OK (received 104/105)

4.3 建议的包结构

cmd/
  mq-bridge-report/      # 桥方诊断 CLI(分层探测 + 汇总)
  mq-account-consumer/   # 长驻消费者
internal/
  config/                # env 配置加载与校验
  mqclient/              # amqp091 封装:TLS dial、channel 管理、自动重连
  envelope/              # packOutbound / unpackAuto(坑 E)
  diag/                  # 问题码、脱敏、报告格式化
  accountmap/            # 账户推送 → 落库/缓存
proto/                   # 桥方 .proto 与生成代码

5. 联调 Checklist(下次从零对接照着勾)

5.1 配置

  • [ ] CFD_MQ_HOST / PORT / VHOST / USE_TLS 与桥方文档一致
  • [ ] CFD_MQ_QUEUE_IN = exchange 名(若桥方约定 queue = exchange)
  • [ ] routing key 留空时,确认代码回退到 exchange 名的逻辑
  • [ ] CFD_MQ_API_USERNAME = 客户端登录名(不是 RabbitMQ user!)
  • [ ] CFD_MQ_API_PASSWORD = 客户端登录密码(单独向桥方确认)
  • [ ] CFD_MQ_SUBSCRIBE_ACCOUNTS = 对冲账户(如 HEDGE_ACC_01
  • [ ] FIX 链路账户与 MQ 订阅账户可能不同,各自单独问清

5.2 权限(列成清单让桥方一次性开齐)

  • [ ] 出口 IP 白名单(生产容器的真实出口 IP)
  • [ ] RabbitMQ user:configure / write on exchange(publish)
  • [ ] RabbitMQ user:read(consume) on consumer queue —— 不是只能 declare
  • [ ] Queue API:登录账号已开通 MQ 认证(msg=31)权限

5.3 自测顺序

  1. mq-bridge-report → TCP / TLS / queue / consume / publish 五连测
  2. mq-account-consumer → auth → subscribe → 104/105
  3. 缓存层:消费者状态 key、账户快照缓存
  4. 可选:对冲账户快照落库

5.4 发给桥方的日志必备字段

  • 时间戳(注明时区或统一 UTC)
  • egress_ip
  • RabbitMQ user / vhost / queue / exchange
  • api_login绝不带密码明文;注意 msg=30 可能回显密码,先脱敏)
  • 原始错误:msg=30message / detail / body_hex
  • amqp_headers JSON
  • 标准问题码:ERR-QUEUE-CONSUME-DENIED / ERR-AUTH-REJECTED / ERR-AUTH-TIMEOUT

6. 顺带统一口径:与对冲业务相关的三个误区

  1. RabbitMQ 通 ≠ 对冲账户资金可见 —— 只有推送链路全通后的 equity / exposure 才有监控意义。
  2. FIX 下单只带 symbol + qty —— 与 Queue API 是两条独立链路,权限和账户都分开申请。
  3. 平台展示杠杆不进 MQ/FIX —— 桥方账户风险看 exposure / equity,与用户选择的杠杆倍数无关。

7. 本次联调状态快照

阶段时间结果
consume ACL 缺失D1ERR-QUEUE-CONSUME-DENIED
consume 已开通D2 01:04 UTCBridgeReport READY
auth 超时D2 09:05 UTC无 msg=32
收到消息但解析错误D2 09:07 UTCmodule=842073610(信封不对称)
信封修复后拿到明确拒绝D2 16:56 UTCmsg=30 / ERR-AUTH-REJECTED

写笔记时的 blocker:Queue API 凭据被桥方拒绝,待桥方确认正确的 API 密码与 MQ 认证权限。


8. 一句话总结

对接桥方 Queue API 的核心不是「连上 RabbitMQ」,而是:用对第二套密码过 msg=31、用对的信封解析认出 msg=30/32、提前注册 consumer 接住回包;诊断日志要把「队列名是对的」和「权限/认证是错的」分开写清楚,桥方才能一次改对。

聊聊长连接协议选型:从 MQTT、WebSocket 到 HTTP/3 的一点畅想

写在前面:这篇是我自己整理长连接这块时记的笔记,不算特别专业,更多是工程视角的"大白话版"。如果你正在做实时推送、物联网、IM 这类需要保持长连接的活儿,希望能给你一点参考。

关于我

一个普通后端开发,平时主要写服务端,偶尔也碰点前端和嵌入式。最近因为项目上需要做实时推送,把几个长连接方案都摸了一遍,顺手把学到的和踩到的记下来,整理成这篇笔记。

不算什么专家,就是把自己理解的东西用大白话讲一遍,有说得不对的地方欢迎指正。


一、先说结论:没有银弹

长连接协议的选型,本质上是在实时性、可靠性、资源开销、网络穿透能力、生态成熟度这几个维度之间做权衡。没有哪个方案是全能的,脱离场景谈"哪个最好"都是耍流氓。

先放一张全景图,帮你建立个整体印象:

mindmap
  root((长连接方案))
    HTTP 长轮询
      实现简单
      兼容性最好
      实时性差/资源浪费
      适合兜底
    WebSocket
      全双工
      Web 端事实标准
      需自建重连/心跳
      适合 IM/协同/弹幕
    MQTT
      轻量 pub/sub
      QoS 可靠性
      依赖 Broker
      适合物联网/海量设备
    SSE
      单向下行
      自带重连
      实现简单
      适合流式推送/通知
    HTTP3 / WebTransport
      解决队头阻塞
      连接迁移
      依赖 UDP
      生态待成熟

下面我把主流的几个方案挨个过一遍。


二、主流方案逐个聊

1. HTTP 长轮询 / 短轮询(Polling)

最古老、最朴素的方案。客户端隔一段时间问一次服务器"有没有新消息",或者发个请求挂在那等服务器有数据了再返回(长轮询)。

优点

  • 实现简单,纯 HTTP,任何环境都能跑
  • 防火墙、代理几乎不会拦
  • 不需要特殊的服务端支持

缺点

  • 实时性差,短轮询有延迟,长轮询也有连接重建的开销
  • 浪费资源,大量无效请求,服务器扛连接很吃力
  • HTTP 头部开销大,每次请求都带一堆 header

适用场景

  • 实时性要求不高、又懒得上复杂方案的小项目
  • 对兼容性要求极高、连 WebSocket 都可能被掐断的恶劣网络环境(兜底方案)
说实话,现在新项目基本不会主动选它了,更多是作为降级兜底。

下面这张图能直观看出长轮询和真正的全双工长连接的区别——长轮询是"一问一答还得反复重连",而 WebSocket 是"建好一条管子双向随便聊":

sequenceDiagram
    participant C as 客户端
    participant S as 服务器
    Note over C,S: HTTP 长轮询(反复重建连接)
    C->>S: 请求(挂起等待)
    S-->>C: 有数据了才返回
    C->>S: 立刻再发一个请求
    S-->>C: 继续挂起等待...
    Note over C,S: WebSocket(一次握手,长期双向)
    C->>S: HTTP Upgrade 握手
    S-->>C: 101 切换协议
    C->>S: 随时发消息
    S-->>C: 随时推消息

2. WebSocket

目前 Web 端实时通信的事实标准。基于 TCP,通过 HTTP 升级握手建立全双工通道。

优点

  • 全双工,双向实时,延迟低
  • 复用 HTTP 基础设施(走 80/443,握手就是个 HTTP Upgrade)
  • 浏览器原生支持,前端友好
  • 防火墙穿透能力还不错(尤其 wss 走 443)

缺点

  • 协议本身不管重连、心跳、保活,这些都得自己撸(断网检测、指数退避、心跳包……)
  • 没有 QoS,断线期间的消息会丢,得在应用层做补偿
  • 没有内置的发布/订阅,多端广播、topic 订阅要自己在上层搭
  • 长连接占服务器资源,海量连接时扩展和负载均衡(粘性会话)比较头疼

适用场景

  • Web 端的 IM、协同编辑、实时弹幕、在线游戏、行情推送
  • 凡是「浏览器要实时收发」的,基本首选

3. MQTT

为物联网而生的轻量发布/订阅协议,基于 TCP(也能跑在 WebSocket 上)。

它和前面几个最大的不同是:通信不是端到端的,而是所有人都连到一个 Broker(消息中转站),通过 topic 来收发——这也是它能轻松支持一对多的根本原因:

graph LR
    D1[温度传感器] -->|publish: home/temp| B((MQTT Broker))
    D2[湿度传感器] -->|publish: home/humidity| B
    B -->|subscribe: home/#| A[手机 App]
    B -->|subscribe: home/temp| C[告警服务]
    B -->|subscribe: home/#| E[数据看板]

优点

  • 极轻量,头部最小才 2 字节,特别适合低带宽、弱网、省电的设备
  • 原生发布/订阅模型,天然支持一对多、topic 订阅
  • 有 QoS 分级(0/1/2),可以按需保证消息送达
  • 支持遗嘱消息(Last Will)、保留消息、持久会话,断线场景考虑得很周到

缺点

  • 强依赖 Broker,是潜在的单点和性能瓶颈,集群方案不如 HTTP 生态成熟
  • QoS 1 会有重复消息(要做幂等),QoS 2 开销和延迟都大
  • 默认端口(1883/8883)常被企业防火墙拦,不易被 HTTP 代理转发
  • 没有原生的请求-响应模型(5.0 之前做起来很别扭)
  • payload 是裸字节,没 schema,格式得自己约定

适用场景

  • 物联网设备接入:传感器、智能家居、车联网、工业网关
  • 海量设备、低功耗、弱网环境
  • 需要发布/订阅和消息可靠性的推送系统
我自己的体感:设备一旦上了一定规模,且网络环境复杂,MQTT 的稳定性和省资源确实明显。但 Broker 的运维(集群、监控、消息堆积清理)也得花心思。

4. SSE(Server-Sent Events)

经常被忽略的一个方案。基于 HTTP,服务器单向往客户端推数据流。

优点

  • 实现简单,就是个长连接的 HTTP 响应流
  • 浏览器原生支持,自带自动重连
  • 走 HTTP,穿透性好

缺点

  • 只能单向(服务器→客户端),客户端要发数据还得另开请求
  • 老的 HTTP/1.1 下有浏览器并发连接数限制(HTTP/2 缓解了)
  • 二进制支持弱,主要是文本

适用场景

  • 只需要服务器单向推送的场景:消息通知、实时日志、AI 流式输出、股价/进度推送
现在很多 AI 应用的"打字机效果"流式输出,底层就是 SSE。单向推送场景下,它比 WebSocket 简单得多。

三、横向对比一张表

维度长轮询WebSocketMQTTSSE
通信方向半双工全双工全双工(pub/sub)单向(下行)
实时性一般
资源开销
消息可靠性无(需自建)有 QoS
发布订阅原生
防火墙穿透极好一般
浏览器支持原生原生需 over WS原生
典型场景兜底Web 实时交互物联网/海量设备单向流式推送

四、HTTP/3 来了,它解决了什么?

聊到这,绕不开 HTTP/3。它基于 QUIC(跑在 UDP 上),对长连接来说有几个真正动人的特性:

先看一眼它和 HTTP/2 在协议栈上的根本区别——HTTP/3 把传输层从 TCP 换成了基于 UDP 的 QUIC,TLS 也被合进了 QUIC 里:

graph TB
    subgraph HTTP2["HTTP/2"]
        A1[HTTP/2] --> A2[TLS 1.2/1.3]
        A2 --> A3[TCP]
        A3 --> A4[IP]
    end
    subgraph HTTP3["HTTP/3"]
        B1[HTTP/3] --> B2["QUIC(内含 TLS 1.3)"]
        B2 --> B3[UDP]
        B3 --> B4[IP]
    end

1. 干掉了队头阻塞(Head-of-Line Blocking)
HTTP/2 虽然多路复用,但底层还是一条 TCP,只要丢一个包,后面所有的流都得排队等。HTTP/3 的多个流相互独立,丢包只影响那一条流。弱网下体验差距很明显。

2. 连接迁移(Connection Migration)
这是杀手级特性。QUIC 用 Connection ID 而不是 IP 四元组来标识连接。手机从 WiFi 切到 5G、IP 变了,连接居然不断,也不用重新握手。对移动端长连接来说,这简直是梦寐以求。

3. 更快的握手
传输层握手和 TLS 握手合并,首连 1-RTT,重连甚至 0-RTT,比传统 TCP + TLS 快一截。

4. 强制加密
内置 TLS 1.3,安全性是默认项。

但是——别急着 all in

HTTP/3 也有它的现实问题:

  • UDP 被封/限速:不少企业防火墙、运营商对 UDP 不友好甚至直接 ban,这时只能回退到 TCP。受限网络里这是真实痛点。
  • CPU 开销更高:QUIC 在用户态实现,加解密、拥塞控制都吃 CPU,内核优化远不如几十年沉淀的 TCP,海量连接时服务器成本是笔实账。
  • 生态还在追赶:服务端(nginx 的 HTTP/3 还比较新)、负载均衡、监控、抓包排错的工具链,成熟度都不如 TCP。
  • 它不直接替代 WebSocket/MQTT:HTTP/3 优化的是"管道"质量,你还是需要上层的推送机制。真正对标 WebSocket 的是 WebTransport(基于 HTTP/3,支持双向流 + 不可靠数据报,可以看作 WebSocket 的下一代),以及 RFC 9220 定义的 WebSocket over HTTP/3
一句话总结:HTTP/3 优化的是「管子」,MQTT / WebSocket 解决的是「怎么往管子里推消息」,它们不是互斥的,而是可以叠在一起的。

五、对未来的一点畅想

写到这,想聊点不那么"技术参数"的东西。

我个人是挺看好 HTTP/3 + WebTransport 这条路线的,尤其是连接迁移这个特性,对移动互联网时代的长连接体验是质的提升。理想中的未来可能是:Web 端用 WebTransport(HTTP/3),设备端用 MQTT,两边在网关层汇聚——各取所长:

graph TB
    W[Web/移动端] -->|WebTransport over HTTP3| G{统一接入网关}
    APP[App 客户端] -->|WebSocket / WebTransport| G
    DEV[物联网设备] -->|MQTT over TCP| G
    G --> MQ[消息队列 / 总线]
    MQ --> SVC[后端业务服务]

这么搭的好处是:前端享受 HTTP/3 的抗弱网和切网优势,设备端继续用省电可靠的 MQTT,网关层把协议差异屏蔽掉,后端只面对统一的消息总线。

但技术演进从来不只是技术问题。HTTP/3 要真正普及,最大的变量其实是「生态配合」

  • 运营商和网络设备厂商得对 UDP 更友好,别动不动就限速封禁,否则 QUIC 永远在"回退到 TCP"的路上。
  • CDN 和云厂商得把 HTTP/3 的支持做成默认开箱即用,而不是要用户费劲折腾配置(好在 Cloudflare、Google、各大云已经在推了)。
  • 中间件和负载均衡得跟上,Nginx、Envoy 这些得让 HTTP/3 像今天用 HTTP/2 一样顺手。
  • 可观测性工具链得补齐,QUIC 加密了一切,抓包排错比 TCP 难得多,没有趁手的工具,出了线上问题会很抓狂。
  • 浏览器和客户端 SDK得把 WebTransport 的 API 打磨成熟、文档完善,降低开发者上手门槛。

这些不是某一家厂商能单独搞定的,需要整个产业链一起往前推。就像当年从 HTTP/1.1 到 HTTP/2,也是 Google 先用 SPDY 趟路、再标准化、再生态跟进,花了好几年才铺开。

所以我的判断是:HTTP/3 在「能用 UDP 的、对体验敏感的、移动端为主的」场景里会越来越主流,但完全取代 TCP 系(包括 MQTT over TCP)还需要相当长的时间,可能五年十年都未必彻底。在那之前,老老实实做好降级兜底、按场景混搭,才是工程上稳妥的姿势。

技术这东西,新的不一定立刻就好用,旧的也不会马上就死掉。看清自己的场景,比追新更重要。


六、给个简单的选型口诀

  • 浏览器要双向实时 → WebSocket(未来看 WebTransport)
  • 只要服务器单向推 → SSE
  • 海量设备、弱网、省电 → MQTT
  • 极端兼容、要兜底 → 长轮询
  • 移动端、对弱网和切网敏感、UDP 可用 → 关注 HTTP/3 / WebTransport
  • 拿不准 → 先 WebSocket 起步,遇到瓶颈再针对性换

行式、列式、向量数据库怎么选?一篇写给工程师的选型指南

面向有经验的后端/数据/平台工程师。本文不堆术语,只讲清楚三类数据库各自擅长什么、在什么场景下会踩坑,以及真实业务里怎么组合使用。原理部分点到为止,深入链接放在文末。

数据库选型这件事,八成的争论其实源于一个误会:把"存储模型"当成了"产品好坏"来比。行式、列式、向量,本质是三种不同的数据组织方式,各自为一类访问模式做了极致优化。选错的代价不是"慢一点",而是整个架构跑偏。

下面按"它怎么存 → 适合什么 → 什么时候别用 → 真实案例"的结构,逐个拆开。


一、行式数据库:为"一次操作一条记录"而生

它怎么存

行式数据库把一整条记录的所有字段连续存放在磁盘上:

Block 1: [1, 张三, 25, 北京] [2, 李四, 30, 上海]
Block 2: [3, 王五, 28, 广州] [4, 赵六, 35, 深圳]

读一条完整记录,一次磁盘 IO 就能拿全所有字段。

📊 建议配图:行式存储 vs 列式存储的磁盘布局对比图(可搜索 "row vs columnar storage layout")

适合什么场景

行式数据库的统治区是 OLTP(在线事务处理),核心诉求是:

  • 高并发的单条/小批量读写:下单、转账、改资料,每次只碰几行
  • 强事务保证:ACID、行级锁、MVCC,金融和交易场景的硬需求
  • 频繁更新:订单状态流转、库存扣减
  • 点查询:按主键或索引精确定位

代表产品:MySQL、PostgreSQL、Oracle、SQL Server

什么时候别用

当你的查询变成 SELECT AVG(amount) FROM orders WHERE created_at > '2025-01-01',扫描上亿行只为了算几个聚合值时,行式数据库会被迫读入大量你根本不需要的列。这类大规模分析查询,行式库会越跑越吃力。

真实案例

电商订单系统:用户下单、支付、查看"我的订单",全部是单条记录的读写,并发高、要求强一致。PostgreSQL / MySQL 是默认选择。等到要做"近 30 天 GMV 趋势分析"时,数据会被同步到下游的列式数仓——而不是在订单库上硬跑。


二、列式数据库:为"扫一整列做分析"而生

它怎么存

列式数据库把同一个字段的所有值连续存放:

ID:   [1, 2, 3, 4]
姓名: [张三, 李四, 王五, 赵六]
年龄: [25, 30, 28, 35]
城市: [北京, 上海, 广州, 深圳]

SUM(年龄)?只读"年龄"这一列就够了,完全不碰其他列。

为什么快(一句话原理)

两个关键优势:只读需要的列(IO 大幅减少)+ 同列数据类型一致、重复度高,压缩率极高(常见 5~10 倍压缩)。再叠加向量化执行(SIMD 批量计算),聚合查询能比行式库快几个数量级。

适合什么场景

列式数据库的主场是 OLAP(在线分析处理):

  • 大规模聚合统计:SUM、AVG、COUNT、GROUP BY
  • 数据仓库 / BI 报表:多维分析、下钻
  • 日志与时序分析:海量数据写入后做查询
  • 宽表少列查询:几百列的表,每次只查其中几列

代表产品:ClickHouse、Apache Doris、Apache Druid、Amazon Redshift、Google BigQuery

💡 前面聊到的 Doris,本质就属于这一类——列式 OLAP 数仓,MPP 架构,主打实时分析。它额外提供了可选的行存格式来支持高并发点查,以及较新版本的向量检索能力,但定位仍是分析型数据库。

什么时候别用

别拿它当业务主库。 列式库对单条记录的更新/删除很不友好——改一行可能要触碰多个列文件。高频 UPDATE、强事务、行级锁这些 OLTP 需求,列式库要么不支持要么很弱。

真实案例

实时数据看板:某 App 要展示"实时在线人数、各渠道转化漏斗、分钟级 PV/UV"。数据量每天数十亿行,查询都是聚合。用 ClickHouse / Doris,亚秒级返回;若用 MySQL,聚合查询直接超时。


三、向量数据库:为"找相似"而生

它怎么存

向量数据库存的不是结构化字段,而是高维向量(embedding)——由模型把文本、图片、音频编码成的一串浮点数:

"如何重置密码"      → [0.12, -0.45, 0.88, ...]   (通常 768 / 1536 维)
"忘记密码怎么办"    → [0.10, -0.43, 0.90, ...]   ← 语义接近,向量也接近
"今天天气不错"      → [-0.88, 0.21, 0.05, ...]   ← 语义无关,向量很远
📊 建议配图:embedding 向量空间中"语义相近 = 距离相近"的示意图(可搜索 "vector embedding similarity space")

为什么不一样(一句话原理)

行式/列式都在做精确匹配(WHERE city = '北京'),而向量数据库做的是相似度搜索:给一个查询向量,找出空间中距离最近的 K 个向量(KNN/ANN)。为了在亿级向量里快速找到近邻,它用 HNSW、IVF 等近似最近邻(ANN)索引,牺牲一点点精度换取巨大的速度提升。

适合什么场景

  • 语义搜索:按意思搜,而非按关键词
  • RAG(检索增强生成):大模型应用的标配,先检索相关文档再喂给 LLM
  • 推荐系统:找"相似商品 / 相似用户"
  • 图像 / 音频 / 视频检索:以图搜图、声纹匹配
  • 去重与异常检测:找相似内容

代表产品:专用库 Milvus、Pinecone、Weaviate、Qdrant;扩展方案 pgvector(PostgreSQL 插件)、Elasticsearch、Redis

什么时候别用

  • 需要精确查询和事务时:向量库不是用来替代业务库的
  • 数据量不大(几万条以内):直接在 PostgreSQL 装 pgvector 就够了,没必要上专用集群
  • 需要复杂的结构化过滤 + 强一致:向量库的元数据过滤能力通常较弱

真实案例

企业知识库问答:员工问"报销流程是什么",系统先把问题转成向量,在 Milvus 里检索出最相关的几篇制度文档,再连同问题一起交给 LLM 生成答案。这就是典型的 RAG,向量库是它的检索底座。


四、横向对比表

维度行式数据库列式数据库向量数据库
存储单位一整行记录一整列字段高维向量
查询方式精确匹配精确匹配 + 聚合相似度(近邻)搜索
核心场景OLTP 事务OLAP 分析AI 检索 / RAG
强项单条读写、事务、高并发大规模聚合、高压缩语义相似、非结构化检索
弱项大数据聚合慢单条更新慢、事务弱不做精确查询、过滤弱
典型产品MySQL、PostgreSQLClickHouse、DorisMilvus、Pinecone、pgvector
写入模式高频小事务批量导入批量 upsert

一句话记忆:行式管事务,列式管分析,向量管相似


五、选型决策路径

实际选型不是三选一,而是看主导访问模式。给一个可直接套用的判断流程:

你的核心查询是什么?
│
├─ 频繁读写单条记录、要事务保证
│     → 行式数据库(MySQL / PostgreSQL)
│
├─ 海量数据做聚合统计、报表分析
│     → 列式数据库(ClickHouse / Doris)
│
├─ 按"语义/相似度"检索,或在做 RAG/推荐
│     → 向量数据库(Milvus / pgvector)
│
└─ 既要事务又要轻量向量检索,且数据量不大
      → PostgreSQL + pgvector(一个库搞定,降低运维成本)

现实中往往是组合拳

成熟系统几乎不会只用一种。一个典型的"业务 + 分析 + AI"架构长这样:

业务写入  →  MySQL/PostgreSQL(行式,处理交易)
                    │
                    │ CDC / ETL 同步
                    ▼
              ClickHouse/Doris(列式,跑报表分析)

文档/知识  →  Embedding 模型  →  Milvus/pgvector(向量,支撑语义检索与 RAG)

三者各司其职,而不是互相替代。选型的本质,是为每一类访问模式匹配最合适的存储模型。


六、几个容易踩的坑

  1. "列式 = 更快" 的误解:列式只在分析型查询上快。拿它做高频点查或更新,性能可能还不如行式。
  2. 混淆两种"向量化":列式库里的"向量化执行"指 SIMD 批量计算,和向量数据库的"相似度检索"完全是两回事,别被名字带偏。
  3. 过早上专用向量库:数据量不大时,pgvector 能省掉一整套独立集群的运维成本,够用就好。
  4. 指望一个库通吃:HTAP(行列混合)、多模数据库确实在发展,但目前在各自专项上仍打不过专用方案。架构上接受"组合使用"通常更务实。

延伸阅读

  • 行式 vs 列式存储原理:搜索 "columnar storage internals" / ClickHouse 官方文档的存储引擎章节
  • ANN 近邻搜索算法:HNSW 与 IVF 论文,或 Milvus 官方文档的索引选型指南
  • RAG 架构实践:LangChain / LlamaIndex 文档
  • HTAP 与多模数据库的发展:TiDB、SingleStore 的架构白皮书
">