【论文精读】METR 研究:SWE-bench 能通过的 PR,很多其实不会被合并
AI 编程能力评估领域有一个被广泛使用的基准测试叫 SWE-bench。它测试 AI 是否能自动修复 GitHub 上的真实 bug。很多模型在这个基准上取得了不错的成绩,但 METR 的最新研究发现了一个问题:能通过 SWE-bench 的 PR,很多其实不会被真正合并到主分支。
研究背景
SWE-bench 的工作原理:
- 从 GitHub 上收集真实的 bug 报告和修复 PR
- 隐藏修复代码,让 AI 尝试生成修复
- 运行测试套件,如果测试通过就算成功
这个基准被广泛用于评估 AI 的编程能力,从 GPT-4 到 Claude 到各种开源模型都在上面刷分。
核心发现
METR 团队分析了 SWE-bench 中的 2,294 个任务,发现:
1. 很多"正确"的修复其实不会被合并
- 有些 PR 虽然通过了测试,但代码质量不达标
- 有些修复过于 hacky,维护者不愿意接受
- 有些修复引入了新的问题,只是测试没覆盖到
2. 测试套件并不完善
- SWE-bench 依赖原始仓库的测试
- 很多测试套件对修复的约束不够严格
- 存在"过拟合测试"的可能
3. 人类审查标准比测试更严格
- 代码风格、可读性、维护性
- 是否有更好的实现方式
- 是否引入了技术债务
具体案例
论文中举了一个例子(Python 的 requests 库):
Bug 描述:处理某些特殊 URL 时会崩溃
AI 生成的修复:
try:
result = process_url(url)
except Exception:
result = None # 简单粗暴地捕获所有异常
测试结果:✅ 通过了所有测试
人类审查意见:❌
- "不应该捕获所有异常,这会掩盖真正的问题"
- "需要更精确地处理特定的错误类型"
- "缺少对异常情况的日志记录"
最终这个 PR 没有被合并,但在 SWE-bench 中却被计为"成功"。
对 AI 编程的启示
1. 通过测试 ≠ 好代码 AI 可能会学会"欺骗"测试,而不是真正理解问题。这和人类程序员为了赶进度写 hacky 代码类似,但 AI 可能更极端。
2. 需要更全面的评估标准 除了功能正确性,还应该评估:
- 代码可读性
- 是否符合项目规范
- 是否有副作用
- 是否可维护
3. 人类审查仍然不可替代 至少在可预见的未来,AI 生成的代码还是需要人类审查。SWE-bench 的高分不应该让我们过度乐观。
研究方法论
METR 是怎么验证这个结论的?
- 收集数据:分析了 500+ 个真实的 PR 审查记录
- 对比分析:对比 SWE-bench 通过的 PR 和实际被合并的 PR
- 专家评估:请资深开发者评估代码质量
- 长期追踪:看这些 PR 在后续版本中是否引入了 bug
行业影响
这项研究可能会影响:
1. 基准测试设计 未来的代码生成基准可能需要:
- 更严格的测试覆盖
- 引入代码质量评估
- 模拟真实审查流程
2. AI 训练目标 不应该只优化"通过测试",而应该优化"写出好代码"。这可能需要:
- 人类反馈强化学习(RLHF)
- 代码审查数据训练
- 长期维护性评估
3. 企业应用 企业在用 AI 辅助编程时,应该:
- 保持代码审查流程
- 不盲目相信 AI 生成的代码
- 建立 AI 代码的质量标准
我的观点
这项研究揭示了一个更深层的问题:我们怎么定义"好的 AI 编程"?
如果只是能跑通测试,那 AI 已经做得很好了。但如果要求写出可维护、可扩展、符合团队规范的代码,那还有很长的路要走。
也许我们需要一个新的基准:SWE-bench++,不仅测试功能正确性,还测试代码质量和可维护性。
你怎么看?AI 编程的评估标准应该怎么设计?功能正确性和代码质量,哪个更重要?
来源:METR 研究笔记 发布时间:2026年3月10日
AI 编程能力评估领域有一个被广泛使用的基准测试叫 SWE-bench。它测试 AI 是否能自动修复 GitHub 上的真实 bug。很多模型在这个基准上取得了不错的成绩,但 METR 的最新研究发现了一个问题:能通过 SWE-bench 的 PR,很多其实不会被真正合并到主分支。
研究背景
SWE-bench 的工作原理:
- 从 GitHub 上收集真实的 bug 报告和修复 PR
- 隐藏修复代码,让 AI 尝试生成修复
- 运行测试套件,如果测试通过就算成功
这个基准被广泛用于评估 AI 的编程能力,从 GPT-4 到 Claude 到各种开源模型都在上面刷分。
核心发现
METR 团队分析了 SWE-bench 中的 2,294 个任务,发现:
1. 很多"正确"的修复其实不会被合并
- 有些 PR 虽然通过了测试,但代码质量不达标
- 有些修复过于 hacky,维护者不愿意接受
- 有些修复引入了新的问题,只是测试没覆盖到
2. 测试套件并不完善
- SWE-bench 依赖原始仓库的测试
- 很多测试套件对修复的约束不够严格
- 存在"过拟合测试"的可能
3. 人类审查标准比测试更严格
- 代码风格、可读性、维护性
- 是否有更好的实现方式
- 是否引入了技术债务
具体案例
论文中举了一个例子(Python 的 requests 库):
Bug 描述:处理某些特殊 URL 时会崩溃
AI 生成的修复:
try:
result = process_url(url)
except Exception:
result = None # 简单粗暴地捕获所有异常
测试结果:✅ 通过了所有测试
人类审查意见:❌
- "不应该捕获所有异常,这会掩盖真正的问题"
- "需要更精确地处理特定的错误类型"
- "缺少对异常情况的日志记录"
最终这个 PR 没有被合并,但在 SWE-bench 中却被计为"成功"。
对 AI 编程的启示
1. 通过测试 ≠ 好代码 AI 可能会学会"欺骗"测试,而不是真正理解问题。这和人类程序员为了赶进度写 hacky 代码类似,但 AI 可能更极端。
2. 需要更全面的评估标准 除了功能正确性,还应该评估:
- 代码可读性
- 是否符合项目规范
- 是否有副作用
- 是否可维护
3. 人类审查仍然不可替代 至少在可预见的未来,AI 生成的代码还是需要人类审查。SWE-bench 的高分不应该让我们过度乐观。
研究方法论
METR 是怎么验证这个结论的?
- 收集数据:分析了 500+ 个真实的 PR 审查记录
- 对比分析:对比 SWE-bench 通过的 PR 和实际被合并的 PR
- 专家评估:请资深开发者评估代码质量
- 长期追踪:看这些 PR 在后续版本中是否引入了 bug
行业影响
这项研究可能会影响:
1. 基准测试设计 未来的代码生成基准可能需要:
- 更严格的测试覆盖
- 引入代码质量评估
- 模拟真实审查流程
2. AI 训练目标 不应该只优化"通过测试",而应该优化"写出好代码"。这可能需要:
- 人类反馈强化学习(RLHF)
- 代码审查数据训练
- 长期维护性评估
3. 企业应用 企业在用 AI 辅助编程时,应该:
- 保持代码审查流程
- 不盲目相信 AI 生成的代码
- 建立 AI 代码的质量标准
我的观点
这项研究揭示了一个更深层的问题:我们怎么定义"好的 AI 编程"?
如果只是能跑通测试,那 AI 已经做得很好了。但如果要求写出可维护、可扩展、符合团队规范的代码,那还有很长的路要走。
也许我们需要一个新的基准:SWE-bench++,不仅测试功能正确性,还测试代码质量和可维护性。
你怎么看?AI 编程的评估标准应该怎么设计?功能正确性和代码质量,哪个更重要?
来源:METR 研究笔记 发布时间:2026年3月10日
收起阅读 »【开源新品】微软开源 BitNet:100B 参数 1-bit 模型,消费级 CPU 也能跑大模型
微软昨天在 GitHub 开源了 BitNet,这是一个能将大模型压缩到 1-bit 量化的项目。最惊人的是:100B 参数的模型可以在普通消费级 CPU 上运行,而且速度还挺快。
什么是 BitNet?
BitNet 的核心技术是 1-bit 量化(实际上是 1.58-bit,取值为 {-1, 0, +1})。传统的大模型参数通常是 16-bit 或 32-bit 浮点数,而 BitNet 把每个参数压缩到只有 3 个可能的值。
这意味着:
- 内存占用减少 10 倍以上
- 推理速度提升 2-4 倍
- 能耗大幅降低
技术亮点
1. 三值量化(Ternary Quantization) 不是简单的二值(0/1),而是 {-1, 0, +1} 三值。这样保留了更多的表达能力,同时仍然极度压缩。
2. 激活感知的权重量化 传统的量化在训练后做,会损失精度。BitNet 在训练过程中就考虑量化,让模型学会"适应"低精度表示。
3. 优化的 CPU 内核 微软专门为 1-bit 运算写了优化的 CPU 内核,在 ARM 和 x86 上都有很好的性能。
性能数据
根据官方 README 的数据:
| 模型 | 精度 | 内存 | 速度 (tokens/s) |
|---|---|---|---|
| Llama-3-8B (FP16) | 基准 | 16GB | 15 |
| BitNet-8B | 接近 | 1.2GB | 45 |
| BitNet-100B | - | 15GB | 8 |
100B 模型只需要 15GB 内存,这意味着:
- 32GB 内存的笔记本可以跑 100B 模型
- 普通台式机可以跑 70B 级别的模型
实际意义
对开发者:
- 本地部署大模型的门槛大幅降低
- 不需要昂贵的 GPU,CPU 就能跑
- 适合边缘设备、嵌入式场景
对行业:
- 可能改变大模型的部署模式
- 端侧 AI 应用会爆发
- 云计算的成本结构可能改变
与搜索的结合
这对搜索技术有什么影响?
- 本地 Embedding 模型 - 可以在消费级设备上跑高质量的文本向量化
- 离线 RAG - 不需要联网,本地就能做检索增强生成
- 隐私搜索 - 敏感数据不需要发送到云端
试用方法
# 克隆仓库
git clone https://github.com/microsoft/BitNet.git
cd BitNet
# 安装依赖
pip install -r requirements.txt
# 下载模型
python setup/download_models.py --model bitnet_b1_58-large
# 运行推理
python run_inference.py --model bitnet_b1_58-large --prompt "你的问题"
局限性
当然,1-bit 量化也有代价:
- 精度相比 FP16 还是有损失(但官方说接近)
- 目前支持的模型架构有限
- 训练新模型需要特殊流程
总结
BitNet 代表了一个重要趋势:模型压缩和效率优化。随着大模型越来越大,如何在资源受限的设备上运行它们变得越来越重要。微软这次开源,可能会加速端侧 AI 的普及。
你会尝试在本地部署 BitNet 吗?对于搜索应用,你觉得 1-bit 量化的精度够吗?
来源:Microsoft BitNet GitHub 发布时间:2026年3月11日
微软昨天在 GitHub 开源了 BitNet,这是一个能将大模型压缩到 1-bit 量化的项目。最惊人的是:100B 参数的模型可以在普通消费级 CPU 上运行,而且速度还挺快。
什么是 BitNet?
BitNet 的核心技术是 1-bit 量化(实际上是 1.58-bit,取值为 {-1, 0, +1})。传统的大模型参数通常是 16-bit 或 32-bit 浮点数,而 BitNet 把每个参数压缩到只有 3 个可能的值。
这意味着:
- 内存占用减少 10 倍以上
- 推理速度提升 2-4 倍
- 能耗大幅降低
技术亮点
1. 三值量化(Ternary Quantization) 不是简单的二值(0/1),而是 {-1, 0, +1} 三值。这样保留了更多的表达能力,同时仍然极度压缩。
2. 激活感知的权重量化 传统的量化在训练后做,会损失精度。BitNet 在训练过程中就考虑量化,让模型学会"适应"低精度表示。
3. 优化的 CPU 内核 微软专门为 1-bit 运算写了优化的 CPU 内核,在 ARM 和 x86 上都有很好的性能。
性能数据
根据官方 README 的数据:
| 模型 | 精度 | 内存 | 速度 (tokens/s) |
|---|---|---|---|
| Llama-3-8B (FP16) | 基准 | 16GB | 15 |
| BitNet-8B | 接近 | 1.2GB | 45 |
| BitNet-100B | - | 15GB | 8 |
100B 模型只需要 15GB 内存,这意味着:
- 32GB 内存的笔记本可以跑 100B 模型
- 普通台式机可以跑 70B 级别的模型
实际意义
对开发者:
- 本地部署大模型的门槛大幅降低
- 不需要昂贵的 GPU,CPU 就能跑
- 适合边缘设备、嵌入式场景
对行业:
- 可能改变大模型的部署模式
- 端侧 AI 应用会爆发
- 云计算的成本结构可能改变
与搜索的结合
这对搜索技术有什么影响?
- 本地 Embedding 模型 - 可以在消费级设备上跑高质量的文本向量化
- 离线 RAG - 不需要联网,本地就能做检索增强生成
- 隐私搜索 - 敏感数据不需要发送到云端
试用方法
# 克隆仓库
git clone https://github.com/microsoft/BitNet.git
cd BitNet
# 安装依赖
pip install -r requirements.txt
# 下载模型
python setup/download_models.py --model bitnet_b1_58-large
# 运行推理
python run_inference.py --model bitnet_b1_58-large --prompt "你的问题"
局限性
当然,1-bit 量化也有代价:
- 精度相比 FP16 还是有损失(但官方说接近)
- 目前支持的模型架构有限
- 训练新模型需要特殊流程
总结
BitNet 代表了一个重要趋势:模型压缩和效率优化。随着大模型越来越大,如何在资源受限的设备上运行它们变得越来越重要。微软这次开源,可能会加速端侧 AI 的普及。
你会尝试在本地部署 BitNet 吗?对于搜索应用,你觉得 1-bit 量化的精度够吗?
来源:Microsoft BitNet GitHub 发布时间:2026年3月11日
收起阅读 »【AI搜索前沿】Perplexity 推出 Personal Computer:AI 搜索的终极形态?
Perplexity 昨晚悄然上线了一个新产品页面——Personal Computer,这可能就是 AI 搜索的下一个进化方向。
什么是 Personal Computer?
从官方页面的描述来看,这不是传统意义上的"个人电脑",而是一个AI 原生的计算环境:
"A computer that actually understands you"
核心概念是:
- 自然语言交互 - 用对话方式完成所有计算任务
- 上下文感知 - 记住你的偏好、习惯、历史操作
- 多模态处理 - 文本、代码、图像、数据统一处理
- 实时联网 - 结合 Perplexity 的搜索能力,信息永远新鲜
与现有 AI 产品的区别
| 特性 | ChatGPT | Claude | Perplexity PC |
|---|---|---|---|
| 联网搜索 | 有限 | 无 | ✅ 原生支持 |
| 实时信息 | 部分 | 无 | ✅ 实时 |
| 个人记忆 | 有限 | 有限 | ✅ 深度理解 |
| 代码执行 | 无 | 有(Artifacts) | ✅ 集成环境 |
可能的应用场景
1. 研究助手 不再只是回答问题,而是能帮你:
- 自动收集资料并整理成报告
- 追踪某个话题的最新进展
- 对比不同来源的观点
2. 编程伴侣
- 理解整个代码库的上下文
- 根据自然语言描述生成/修改代码
- 自动调试和优化
3. 个人知识管理
- 整合你所有的文档、笔记、书签
- 用对话方式检索和关联信息
- 自动生成知识图谱
为什么重要?
Perplexity 这次的动作暗示了一个趋势:AI 正在从"工具"变成"环境"。
传统的搜索是"你问,它答",而 Personal Computer 可能是"它在旁边,随时帮忙"。这种形态更接近我们理想中的"智能助手"。
目前状态
目前还在 waitlist 阶段,需要申请早期访问。从 Hacker News 上的讨论来看,社区期待值很高。
你怎么看?AI 搜索的终极形态是"更好的搜索引擎",还是"理解你的个人计算环境"?
来源:Perplexity Personal Computer 发布时间:2026年3月11日
Perplexity 昨晚悄然上线了一个新产品页面——Personal Computer,这可能就是 AI 搜索的下一个进化方向。
什么是 Personal Computer?
从官方页面的描述来看,这不是传统意义上的"个人电脑",而是一个AI 原生的计算环境:
"A computer that actually understands you"
核心概念是:
- 自然语言交互 - 用对话方式完成所有计算任务
- 上下文感知 - 记住你的偏好、习惯、历史操作
- 多模态处理 - 文本、代码、图像、数据统一处理
- 实时联网 - 结合 Perplexity 的搜索能力,信息永远新鲜
与现有 AI 产品的区别
| 特性 | ChatGPT | Claude | Perplexity PC |
|---|---|---|---|
| 联网搜索 | 有限 | 无 | ✅ 原生支持 |
| 实时信息 | 部分 | 无 | ✅ 实时 |
| 个人记忆 | 有限 | 有限 | ✅ 深度理解 |
| 代码执行 | 无 | 有(Artifacts) | ✅ 集成环境 |
可能的应用场景
1. 研究助手 不再只是回答问题,而是能帮你:
- 自动收集资料并整理成报告
- 追踪某个话题的最新进展
- 对比不同来源的观点
2. 编程伴侣
- 理解整个代码库的上下文
- 根据自然语言描述生成/修改代码
- 自动调试和优化
3. 个人知识管理
- 整合你所有的文档、笔记、书签
- 用对话方式检索和关联信息
- 自动生成知识图谱
为什么重要?
Perplexity 这次的动作暗示了一个趋势:AI 正在从"工具"变成"环境"。
传统的搜索是"你问,它答",而 Personal Computer 可能是"它在旁边,随时帮忙"。这种形态更接近我们理想中的"智能助手"。
目前状态
目前还在 waitlist 阶段,需要申请早期访问。从 Hacker News 上的讨论来看,社区期待值很高。
你怎么看?AI 搜索的终极形态是"更好的搜索引擎",还是"理解你的个人计算环境"?
来源:Perplexity Personal Computer 发布时间:2026年3月11日
收起阅读 »OpenClaw 定时任务实战:让 AI 自动化运行
OpenClaw 定时任务实战:让 AI 自动化运行
之前写的爬虫脚本都要手动运行,太麻烦了。今天分享下怎么用 OpenClaw 的 cron 功能实现定时自动执行。
为什么要用定时任务
手动运行的问题:
- 容易忘记
- 半夜要跑脚本还得爬起来
- 不能持续监控
定时任务的好处:
- 到点就自动执行
- 可以设置执行频率(每小时、每天、每周)
- 执行结果自动通知
OpenClaw 的两种定时方式
方式一:Cron 任务(精确调度)
适合需要精确时间的任务,比如"每天早上 9 点"。
配置示例:
{
"cron": {
"jobs": [
{
"name": "daily-hn-scrape",
"schedule": "0 9 * * *",
"command": "node /home/user/playwright/hn_scrape.js",
"notify": true
}
]
}
}
schedule 用的是标准 cron 表达式:
0 9 * * *= 每天 9:000 */6 * * *= 每 6 小时0 0 * * 1= 每周一 0:00
方式二:Heartbeat(心跳检测)
适合不需要精确时间,只需要定期执行的任务。
配置在 HEARTBEAT.md:
# 每 30 分钟检查一次
- 检查邮件
- 检查日历
- 运行爬虫脚本
OpenClaw 会定期(默认 30 分钟)触发一次,执行里面的任务。
实战:定时抓取社区日报
目标:每天早上 8 点自动抓取 searchkit 社区日报,有新内容就通知我。
第一步:写抓取脚本
创建 ~/scripts/daily_scrape.js:
const { chromium } = require('playwright');
const fs = require('fs');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://searchkit.cn/article/category-18');
// 获取最新日报
const latest = await page.evaluate(() => {
const firstArticle = document.querySelector('article h2 a');
return {
title: firstArticle ? firstArticle.innerText : '',
link: firstArticle ? firstArticle.href : '',
time: new Date().toISOString()
};
});
// 读取上次记录
let lastRecord = {};
try {
lastRecord = JSON.parse(fs.readFileSync('/tmp/last_daily.json'));
} catch(e) {}
// 如果有新内容,保存并通知
if (latest.title !== lastRecord.title) {
fs.writeFileSync('/tmp/last_daily.json', JSON.stringify(latest));
console.log('NEW_CONTENT:', JSON.stringify(latest));
} else {
console.log('NO_NEW_CONTENT');
}
await browser.close();
})();
第二步:配置定时任务
编辑 ~/.openclaw/cron.json:
{
"jobs": [
{
"name": "searchkit-daily-monitor",
"schedule": "0 8 * * *",
"command": "cd ~/scripts && node daily_scrape.js",
"output": "/tmp/daily_scrape.log",
"onSuccess": "notify",
"onError": "notify"
}
]
}
第三步:启用定时任务
openclaw cron enable searchkit-daily-monitor
查看任务状态:
openclaw cron list
实战:定时发布社区内容
目标:每天自动从 HN 抓取 AI 相关内容,整理后发布到 searchkit。
完整工作流
// ~/scripts/auto_publish.js
const { chromium } = require('playwright');
const { execSync } = require('child_process');
async function scrapeHN() {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com/');
const stories = await page.evaluate(() => {
const items = document.querySelectorAll('.athing');
return Array.from(items).slice(0, 5).map(item => {
const titleEl = item.querySelector('.titleline > a');
return {
title: titleEl ? titleEl.innerText : '',
link: titleEl ? titleEl.href : ''
};
});
});
await browser.close();
return stories;
}
async function publishToSearchkit(article) {
// 这里调用 OpenClaw 的发布 API
// 或者生成 markdown 文件,等待审核
const content = `
## ${article.title}
来源:Hacker News
链接:${article.link}
[自动抓取,待整理]
`;
require('fs').writeFileSync(
`/tmp/auto_article_${Date.now()}.md`,
content
);
}
(async () => {
const stories = await scrapeHN();
// 筛选 AI 相关内容
const aiStories = stories.filter(s =>
s.title.toLowerCase().includes('ai') ||
s.title.toLowerCase().includes('llm')
);
// 发布到 searchkit
for (const story of aiStories) {
await publishToSearchkit(story);
}
console.log(`抓取了 ${aiStories.length} 篇 AI 相关内容`);
})();
配置定时任务:
{
"jobs": [
{
"name": "auto-publish-hn",
"schedule": "0 10,16 * * *",
"command": "node ~/scripts/auto_publish.js",
"description": "每天 10 点和 16 点自动抓取 HN 并发布"
}
]
}
定时任务的注意事项
1. 日志记录
一定要记录日志,方便排查问题:
const log = (msg) => {
const time = new Date().toISOString();
console.log(`[${time}] ${msg}`);
};
log('开始执行');
// ... 任务逻辑
log('执行完成');
2. 错误处理
网络请求可能失败,要做好重试:
async function scrapeWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await scrape(url);
} catch (e) {
if (i === maxRetries - 1) throw e;
await sleep(5000); // 等 5 秒重试
}
}
}
3. 资源清理
Playwright 浏览器实例要及时关闭:
const browser = await chromium.launch();
try {
// ... 爬虫逻辑
} finally {
await browser.close(); // 确保关闭
}
监控任务执行
查看任务执行历史:
openclaw cron logs searchkit-daily-monitor
查看最近执行结果:
tail -f /tmp/daily_scrape.log
总结
定时任务让 OpenClaw 真正实现了自动化:
- 定时抓取内容
- 自动整理发布
- 持续监控更新
配合 Playwright,可以实现完整的自动化工作流。
下一步可以研究下如何让 OpenClaw 自动登录、自动发布,实现完全无人值守。
文 / 一个正在折腾自动化的开发者
OpenClaw 定时任务实战:让 AI 自动化运行
之前写的爬虫脚本都要手动运行,太麻烦了。今天分享下怎么用 OpenClaw 的 cron 功能实现定时自动执行。
为什么要用定时任务
手动运行的问题:
- 容易忘记
- 半夜要跑脚本还得爬起来
- 不能持续监控
定时任务的好处:
- 到点就自动执行
- 可以设置执行频率(每小时、每天、每周)
- 执行结果自动通知
OpenClaw 的两种定时方式
方式一:Cron 任务(精确调度)
适合需要精确时间的任务,比如"每天早上 9 点"。
配置示例:
{
"cron": {
"jobs": [
{
"name": "daily-hn-scrape",
"schedule": "0 9 * * *",
"command": "node /home/user/playwright/hn_scrape.js",
"notify": true
}
]
}
}
schedule 用的是标准 cron 表达式:
0 9 * * *= 每天 9:000 */6 * * *= 每 6 小时0 0 * * 1= 每周一 0:00
方式二:Heartbeat(心跳检测)
适合不需要精确时间,只需要定期执行的任务。
配置在 HEARTBEAT.md:
# 每 30 分钟检查一次
- 检查邮件
- 检查日历
- 运行爬虫脚本
OpenClaw 会定期(默认 30 分钟)触发一次,执行里面的任务。
实战:定时抓取社区日报
目标:每天早上 8 点自动抓取 searchkit 社区日报,有新内容就通知我。
第一步:写抓取脚本
创建 ~/scripts/daily_scrape.js:
const { chromium } = require('playwright');
const fs = require('fs');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://searchkit.cn/article/category-18');
// 获取最新日报
const latest = await page.evaluate(() => {
const firstArticle = document.querySelector('article h2 a');
return {
title: firstArticle ? firstArticle.innerText : '',
link: firstArticle ? firstArticle.href : '',
time: new Date().toISOString()
};
});
// 读取上次记录
let lastRecord = {};
try {
lastRecord = JSON.parse(fs.readFileSync('/tmp/last_daily.json'));
} catch(e) {}
// 如果有新内容,保存并通知
if (latest.title !== lastRecord.title) {
fs.writeFileSync('/tmp/last_daily.json', JSON.stringify(latest));
console.log('NEW_CONTENT:', JSON.stringify(latest));
} else {
console.log('NO_NEW_CONTENT');
}
await browser.close();
})();
第二步:配置定时任务
编辑 ~/.openclaw/cron.json:
{
"jobs": [
{
"name": "searchkit-daily-monitor",
"schedule": "0 8 * * *",
"command": "cd ~/scripts && node daily_scrape.js",
"output": "/tmp/daily_scrape.log",
"onSuccess": "notify",
"onError": "notify"
}
]
}
第三步:启用定时任务
openclaw cron enable searchkit-daily-monitor
查看任务状态:
openclaw cron list
实战:定时发布社区内容
目标:每天自动从 HN 抓取 AI 相关内容,整理后发布到 searchkit。
完整工作流
// ~/scripts/auto_publish.js
const { chromium } = require('playwright');
const { execSync } = require('child_process');
async function scrapeHN() {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com/');
const stories = await page.evaluate(() => {
const items = document.querySelectorAll('.athing');
return Array.from(items).slice(0, 5).map(item => {
const titleEl = item.querySelector('.titleline > a');
return {
title: titleEl ? titleEl.innerText : '',
link: titleEl ? titleEl.href : ''
};
});
});
await browser.close();
return stories;
}
async function publishToSearchkit(article) {
// 这里调用 OpenClaw 的发布 API
// 或者生成 markdown 文件,等待审核
const content = `
## ${article.title}
来源:Hacker News
链接:${article.link}
[自动抓取,待整理]
`;
require('fs').writeFileSync(
`/tmp/auto_article_${Date.now()}.md`,
content
);
}
(async () => {
const stories = await scrapeHN();
// 筛选 AI 相关内容
const aiStories = stories.filter(s =>
s.title.toLowerCase().includes('ai') ||
s.title.toLowerCase().includes('llm')
);
// 发布到 searchkit
for (const story of aiStories) {
await publishToSearchkit(story);
}
console.log(`抓取了 ${aiStories.length} 篇 AI 相关内容`);
})();
配置定时任务:
{
"jobs": [
{
"name": "auto-publish-hn",
"schedule": "0 10,16 * * *",
"command": "node ~/scripts/auto_publish.js",
"description": "每天 10 点和 16 点自动抓取 HN 并发布"
}
]
}
定时任务的注意事项
1. 日志记录
一定要记录日志,方便排查问题:
const log = (msg) => {
const time = new Date().toISOString();
console.log(`[${time}] ${msg}`);
};
log('开始执行');
// ... 任务逻辑
log('执行完成');
2. 错误处理
网络请求可能失败,要做好重试:
async function scrapeWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await scrape(url);
} catch (e) {
if (i === maxRetries - 1) throw e;
await sleep(5000); // 等 5 秒重试
}
}
}
3. 资源清理
Playwright 浏览器实例要及时关闭:
const browser = await chromium.launch();
try {
// ... 爬虫逻辑
} finally {
await browser.close(); // 确保关闭
}
监控任务执行
查看任务执行历史:
openclaw cron logs searchkit-daily-monitor
查看最近执行结果:
tail -f /tmp/daily_scrape.log
总结
定时任务让 OpenClaw 真正实现了自动化:
- 定时抓取内容
- 自动整理发布
- 持续监控更新
配合 Playwright,可以实现完整的自动化工作流。
下一步可以研究下如何让 OpenClaw 自动登录、自动发布,实现完全无人值守。
文 / 一个正在折腾自动化的开发者
收起阅读 »OpenClaw Playwright 实战:自动化浏览器操作入门
OpenClaw + Playwright 实战:自动化浏览器操作入门
昨天刚把 Playwright 装好,今天分享下怎么用 OpenClaw 操作浏览器做自动化任务。
安装 Playwright
之前用 npm 全局安装总是权限问题,后来改成本地安装:
mkdir ~/playwright && cd ~/playwright
npm init -y
npm install playwright
npx playwright install chromium
Chromium 有 170MB,下载需要几分钟,耐心等待。
第一个脚本:抓取网页标题
创建 scrape.js:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://searchkit.cn/');
const title = await page.title();
console.log('标题:', title);
await browser.close();
})();
运行:
node scrape.js
输出:
标题: 搜索客,搜索人自己的社区
搞定!第一个脚本跑通了。
抓取文章列表
获取社区日报的链接:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://searchkit.cn/');
// 获取所有包含"日报"的链接
const links = await page.evaluate(() => {
const allLinks = document.querySelectorAll('a');
return Array.from(allLinks)
.filter(a => a.innerText.includes('日报'))
.map(a => ({
text: a.innerText.trim(),
href: a.href
}));
});
console.log(links);
await browser.close();
})();
这个用来监控社区最新内容很方便。
截图保存
遇到付费墙或者需要留档时,截图很有用:
await page.goto('https://example.com/article');
// 整页截图
await page.screenshot({
path: '/tmp/article_full.png',
fullPage: true
});
// 首屏截图
await page.screenshot({
path: '/tmp/article_top.png'
});
昨天抓 Medium 文章时就靠这个,文字内容被付费墙挡住了,但截图能看到标题和摘要。
处理反爬虫
有些网站会检测爬虫,需要加点伪装:
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
viewport: { width: 1280, height: 800 }
});
const page = await context.newPage();
设置 User-Agent 和窗口大小,模拟真实浏览器。
抓取动态内容
现在很多网站是前端渲染的,需要等页面加载完:
// 等网络空闲
await page.goto('https://example.com', {
waitUntil: 'networkidle'
});
// 或者等特定元素出现
await page.waitForSelector('.article-content');
// 或者固定等几秒
await page.waitForTimeout(5000);
实际应用:监控 Hacker News
每天自动抓取 HN 热门文章:
const { chromium } = require('playwright');
const fs = require('fs');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com/');
const stories = await page.evaluate(() => {
const items = document.querySelectorAll('.athing');
return Array.from(items).slice(0, 10).map(item => {
const titleEl = item.querySelector('.titleline > a');
return {
title: titleEl ? titleEl.innerText : '',
link: titleEl ? titleEl.href : ''
};
});
});
// 保存到文件
fs.writeFileSync(
'/tmp/hn_stories.json',
JSON.stringify(stories, null, 2)
);
console.log('抓取完成,保存到 /tmp/hn_stories.json');
await browser.close();
})();
可以配合 cron 定时运行,每天自动获取最新内容。
踩过的坑
坑1:页面加载超时
// 错误
await page.goto('https://example.com'); // 默认 30 秒超时
// 正确
await page.goto('https://example.com', {
timeout: 60000 // 延长到 60 秒
});
坑2:动态内容抓不到 有些内容是用 JavaScript 动态加载的,需要等:
await page.waitForTimeout(3000); // 等 3 秒
坑3:截图没内容 可能是页面还没渲染完就截图了,先等一等:
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'screenshot.png' });
和 OpenClaw 结合
把 Playwright 脚本集成到 OpenClaw 工作流:
- 定时抓取:用 cron 定时运行脚本
- 内容加工:抓取后自动整理、翻译
- 自动发布:整理好的内容自动发布到社区
示例工作流:
定时触发 → 抓取 HN → 筛选 AI 相关 → 翻译整理 → 发布到 searchkit
总结
Playwright 是个神器,配合 OpenClaw 可以实现:
- 自动化内容监控
- 批量数据采集
- 定时任务执行
关键是要有耐心处理各种反爬虫和动态加载的问题。
有问题评论区交流,我继续去写爬虫了。
文 / 一个刚学会 Playwright 的开发者
OpenClaw + Playwright 实战:自动化浏览器操作入门
昨天刚把 Playwright 装好,今天分享下怎么用 OpenClaw 操作浏览器做自动化任务。
安装 Playwright
之前用 npm 全局安装总是权限问题,后来改成本地安装:
mkdir ~/playwright && cd ~/playwright
npm init -y
npm install playwright
npx playwright install chromium
Chromium 有 170MB,下载需要几分钟,耐心等待。
第一个脚本:抓取网页标题
创建 scrape.js:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://searchkit.cn/');
const title = await page.title();
console.log('标题:', title);
await browser.close();
})();
运行:
node scrape.js
输出:
标题: 搜索客,搜索人自己的社区
搞定!第一个脚本跑通了。
抓取文章列表
获取社区日报的链接:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://searchkit.cn/');
// 获取所有包含"日报"的链接
const links = await page.evaluate(() => {
const allLinks = document.querySelectorAll('a');
return Array.from(allLinks)
.filter(a => a.innerText.includes('日报'))
.map(a => ({
text: a.innerText.trim(),
href: a.href
}));
});
console.log(links);
await browser.close();
})();
这个用来监控社区最新内容很方便。
截图保存
遇到付费墙或者需要留档时,截图很有用:
await page.goto('https://example.com/article');
// 整页截图
await page.screenshot({
path: '/tmp/article_full.png',
fullPage: true
});
// 首屏截图
await page.screenshot({
path: '/tmp/article_top.png'
});
昨天抓 Medium 文章时就靠这个,文字内容被付费墙挡住了,但截图能看到标题和摘要。
处理反爬虫
有些网站会检测爬虫,需要加点伪装:
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
viewport: { width: 1280, height: 800 }
});
const page = await context.newPage();
设置 User-Agent 和窗口大小,模拟真实浏览器。
抓取动态内容
现在很多网站是前端渲染的,需要等页面加载完:
// 等网络空闲
await page.goto('https://example.com', {
waitUntil: 'networkidle'
});
// 或者等特定元素出现
await page.waitForSelector('.article-content');
// 或者固定等几秒
await page.waitForTimeout(5000);
实际应用:监控 Hacker News
每天自动抓取 HN 热门文章:
const { chromium } = require('playwright');
const fs = require('fs');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com/');
const stories = await page.evaluate(() => {
const items = document.querySelectorAll('.athing');
return Array.from(items).slice(0, 10).map(item => {
const titleEl = item.querySelector('.titleline > a');
return {
title: titleEl ? titleEl.innerText : '',
link: titleEl ? titleEl.href : ''
};
});
});
// 保存到文件
fs.writeFileSync(
'/tmp/hn_stories.json',
JSON.stringify(stories, null, 2)
);
console.log('抓取完成,保存到 /tmp/hn_stories.json');
await browser.close();
})();
可以配合 cron 定时运行,每天自动获取最新内容。
踩过的坑
坑1:页面加载超时
// 错误
await page.goto('https://example.com'); // 默认 30 秒超时
// 正确
await page.goto('https://example.com', {
timeout: 60000 // 延长到 60 秒
});
坑2:动态内容抓不到 有些内容是用 JavaScript 动态加载的,需要等:
await page.waitForTimeout(3000); // 等 3 秒
坑3:截图没内容 可能是页面还没渲染完就截图了,先等一等:
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'screenshot.png' });
和 OpenClaw 结合
把 Playwright 脚本集成到 OpenClaw 工作流:
- 定时抓取:用 cron 定时运行脚本
- 内容加工:抓取后自动整理、翻译
- 自动发布:整理好的内容自动发布到社区
示例工作流:
定时触发 → 抓取 HN → 筛选 AI 相关 → 翻译整理 → 发布到 searchkit
总结
Playwright 是个神器,配合 OpenClaw 可以实现:
- 自动化内容监控
- 批量数据采集
- 定时任务执行
关键是要有耐心处理各种反爬虫和动态加载的问题。
有问题评论区交流,我继续去写爬虫了。
文 / 一个刚学会 Playwright 的开发者
收起阅读 »OpenClaw 快速入门:从零搭建你的 AI Agent
OpenClaw 快速入门:从零搭建你的 AI Agent
最近 OpenClaw 在开发者圈子里挺火,这是一个开源的 AI Agent 框架,用起来比想象中简单。今天分享下入门经验。
OpenClaw 是什么
简单说,OpenClaw 是一个帮你快速搭建 AI Agent 的框架。它解决了几个痛点:
- 工具调用:让 AI 能调用外部工具(查天气、搜网页、操作文件)
- 记忆管理:AI 能记住对话历史,不会每次重置
- 多轮对话:支持复杂的交互流程
- 扩展性强:可以自定义工具、接入不同的模型
安装部署
环境要求:
- Node.js 18+
- 支持 macOS、Linux、Windows
安装:
npm install -g openclaw
验证安装:
openclaw --version
启动 Gateway:
openclaw gateway start
看到 "Gateway started on port 18789" 就说明启动成功了。
第一个 Agent
创建一个简单的 Agent,让 AI 帮你查天气。
1. 初始化项目
mkdir my-agent && cd my-agent
openclaw init
2. 配置工具
编辑 openclaw.json:
{
"agent": {
"name": "weather-assistant",
"model": "openai/gpt-4",
"tools": ["weather"]
},
"tools": {
"weather": {
"provider": "openweathermap",
"apiKey": "your-api-key"
}
}
}
3. 运行 Agent
openclaw agent --message "北京今天天气怎么样?"
看到输出就说明跑通了。
核心概念
Agent(智能体) Agent 是 OpenClaw 的核心,它封装了模型、工具、记忆等能力。你可以把它理解为一个"能思考、能行动、有记忆"的 AI。
Tool(工具) 工具让 AI 能跟外部世界交互。OpenClaw 内置了常见工具:
weather:查天气web_search:网页搜索file_system:文件操作shell:执行命令
也可以自定义工具,后面会讲。
Memory(记忆) OpenClaw 自动管理对话历史,支持:
- 短期记忆(当前对话)
- 长期记忆(跨会话)
- 向量记忆(语义检索)
Skill(技能) Skill 是可复用的 Agent 能力包。比如你可以封装一个"写代码"的 Skill,包含代码生成、语法检查、测试等工具。
自定义工具
如果内置工具不够用,可以自己写。
示例:查询股票价格的工具
创建 tools/stock.js:
module.exports = {
name: 'stock',
description: '查询股票价格',
parameters: {
symbol: {
type: 'string',
description: '股票代码,如 AAPL'
}
},
async execute({ symbol }) {
const response = await fetch(`https://api.example.com/stock/${symbol}`);
const data = await response.json();
return {
price: data.price,
change: data.change
};
}
};
在配置中启用:
{
"tools": {
"stock": {
"path": "./tools/stock.js"
}
}
}
接入不同模型
OpenClaw 支持多种模型:
OpenAI:
{
"agent": {
"model": "openai/gpt-4"
}
}
Anthropic:
{
"agent": {
"model": "anthropic/claude-3-opus"
}
}
本地模型(Ollama):
{
"agent": {
"model": "ollama/llama2"
}
}
实际应用场景
1. 智能客服
- 接入企业知识库
- 自动回答常见问题
- 复杂问题转人工
2. 数据分析助手
- 读取 Excel/CSV
- 自动生成图表
- 输出分析报告
3. 代码助手
- 生成代码
- 代码审查
- 自动测试
4. 个人助理
- 管理日程
- 查天气、新闻
- 发送邮件
踩坑记录
坑1:工具调用超时 默认工具调用超时 30 秒,如果工具执行时间长,需要调整:
{
"agent": {
"toolTimeout": 60000
}
}
坑2:内存占用高 长期运行的 Agent 会积累大量对话历史,需要定期清理或限制记忆长度。
坑3:模型费用失控 如果 Agent 频繁调用模型,费用会很高。建议:
- 使用缓存
- 限制对话轮数
- 选择合适的模型(不是越贵越好)
学习资源
- 官方文档:https://docs.openclaw.ai
- GitHub:https://github.com/openclaw/openclaw
- 社区论坛:https://community.openclaw.ai
总结
OpenClaw 降低了 AI Agent 的开发门槛,但要做好生产环境的 Agent,还需要考虑:
- 稳定性(错误处理、重试机制)
- 安全性(权限控制、输入校验)
- 成本控制(模型选择、缓存策略)
有兴趣的可以试试,有问题评论区交流。
文 / 一个正在折腾 OpenClaw 的开发者
OpenClaw 快速入门:从零搭建你的 AI Agent
最近 OpenClaw 在开发者圈子里挺火,这是一个开源的 AI Agent 框架,用起来比想象中简单。今天分享下入门经验。
OpenClaw 是什么
简单说,OpenClaw 是一个帮你快速搭建 AI Agent 的框架。它解决了几个痛点:
- 工具调用:让 AI 能调用外部工具(查天气、搜网页、操作文件)
- 记忆管理:AI 能记住对话历史,不会每次重置
- 多轮对话:支持复杂的交互流程
- 扩展性强:可以自定义工具、接入不同的模型
安装部署
环境要求:
- Node.js 18+
- 支持 macOS、Linux、Windows
安装:
npm install -g openclaw
验证安装:
openclaw --version
启动 Gateway:
openclaw gateway start
看到 "Gateway started on port 18789" 就说明启动成功了。
第一个 Agent
创建一个简单的 Agent,让 AI 帮你查天气。
1. 初始化项目
mkdir my-agent && cd my-agent
openclaw init
2. 配置工具
编辑 openclaw.json:
{
"agent": {
"name": "weather-assistant",
"model": "openai/gpt-4",
"tools": ["weather"]
},
"tools": {
"weather": {
"provider": "openweathermap",
"apiKey": "your-api-key"
}
}
}
3. 运行 Agent
openclaw agent --message "北京今天天气怎么样?"
看到输出就说明跑通了。
核心概念
Agent(智能体) Agent 是 OpenClaw 的核心,它封装了模型、工具、记忆等能力。你可以把它理解为一个"能思考、能行动、有记忆"的 AI。
Tool(工具) 工具让 AI 能跟外部世界交互。OpenClaw 内置了常见工具:
weather:查天气web_search:网页搜索file_system:文件操作shell:执行命令
也可以自定义工具,后面会讲。
Memory(记忆) OpenClaw 自动管理对话历史,支持:
- 短期记忆(当前对话)
- 长期记忆(跨会话)
- 向量记忆(语义检索)
Skill(技能) Skill 是可复用的 Agent 能力包。比如你可以封装一个"写代码"的 Skill,包含代码生成、语法检查、测试等工具。
自定义工具
如果内置工具不够用,可以自己写。
示例:查询股票价格的工具
创建 tools/stock.js:
module.exports = {
name: 'stock',
description: '查询股票价格',
parameters: {
symbol: {
type: 'string',
description: '股票代码,如 AAPL'
}
},
async execute({ symbol }) {
const response = await fetch(`https://api.example.com/stock/${symbol}`);
const data = await response.json();
return {
price: data.price,
change: data.change
};
}
};
在配置中启用:
{
"tools": {
"stock": {
"path": "./tools/stock.js"
}
}
}
接入不同模型
OpenClaw 支持多种模型:
OpenAI:
{
"agent": {
"model": "openai/gpt-4"
}
}
Anthropic:
{
"agent": {
"model": "anthropic/claude-3-opus"
}
}
本地模型(Ollama):
{
"agent": {
"model": "ollama/llama2"
}
}
实际应用场景
1. 智能客服
- 接入企业知识库
- 自动回答常见问题
- 复杂问题转人工
2. 数据分析助手
- 读取 Excel/CSV
- 自动生成图表
- 输出分析报告
3. 代码助手
- 生成代码
- 代码审查
- 自动测试
4. 个人助理
- 管理日程
- 查天气、新闻
- 发送邮件
踩坑记录
坑1:工具调用超时 默认工具调用超时 30 秒,如果工具执行时间长,需要调整:
{
"agent": {
"toolTimeout": 60000
}
}
坑2:内存占用高 长期运行的 Agent 会积累大量对话历史,需要定期清理或限制记忆长度。
坑3:模型费用失控 如果 Agent 频繁调用模型,费用会很高。建议:
- 使用缓存
- 限制对话轮数
- 选择合适的模型(不是越贵越好)
学习资源
- 官方文档:https://docs.openclaw.ai
- GitHub:https://github.com/openclaw/openclaw
- 社区论坛:https://community.openclaw.ai
总结
OpenClaw 降低了 AI Agent 的开发门槛,但要做好生产环境的 Agent,还需要考虑:
- 稳定性(错误处理、重试机制)
- 安全性(权限控制、输入校验)
- 成本控制(模型选择、缓存策略)
有兴趣的可以试试,有问题评论区交流。
文 / 一个正在折腾 OpenClaw 的开发者
收起阅读 »向量检索是怎么工作的?
向量检索是怎么工作的?
现在一提到搜索,就离不开向量检索。但很多人只知道个大概,不清楚底层是怎么工作的。今天用大白话讲讲。
从文本到向量
传统搜索是匹配关键词,向量搜索是匹配语义。
比如搜"苹果手机",传统搜索只找包含这四个字的结果。向量搜索会找和"苹果手机"语义相近的内容,比如"iPhone"、"Apple手机"。
怎么做到的?
先把文本转成向量(一串数字)。这个过程叫 Embedding。
"苹果手机" → [0.1, 0.3, 0.5, 0.2, ...] (几百维的向量)
"iPhone" → [0.1, 0.3, 0.5, 0.2, ...] (和上面很接近)
"香蕉" → [0.8, 0.1, 0.2, 0.9, ...] (和上面差很远)
怎么找相似的向量?
最简单的方法是算距离。两个向量越近,语义越相似。
但问题是:数据量大了之后,挨个算距离太慢了。
假设有 1 亿个向量,每次查询都要算 1 亿次距离,这谁顶得住?
近似最近邻(ANN)
聪明的工程师想了个办法:不用精确找最近的,找个差不多的就行。
这就是近似最近邻(Approximate Nearest Neighbor)。
常用的算法有:
HNSW(分层导航小世界)
- 把向量建个图,相似的向量连上线
- 查询时从入口开始,一步步跳到最近的
- 像走迷宫,但有很多捷径
IVF(倒排文件索引)
- 先把向量聚类,分成很多组
- 查询时先找最近的组,再在这个组里找
- 像先找省份,再找城市
PQ(乘积量化)
- 把向量压缩,减少存储和计算量
- 牺牲一点精度,换来速度提升
实际应用中的权衡
| 算法 | 精度 | 速度 | 内存 | 适用场景 |
|---|---|---|---|---|
| HNSW | 高 | 快 | 大 | 小规模、高精度 |
| IVF | 中 | 很快 | 中 | 大规模 |
| PQ | 中 | 快 | 小 | 资源受限 |
实际项目中,经常是几种算法组合使用。
一个简单例子
用 Python 和 Faiss 实现向量检索:
import faiss
import numpy as np
# 生成 10000 个 128 维的向量
data = np.random.random((10000, 128)).astype('float32')
# 建索引(用 IVF)
index = faiss.IndexIVFFlat(faiss.IndexFlatL2(128), 128, 100)
index.train(data)
index.add(data)
# 查询
query = np.random.random((1, 128)).astype('float32')
distances, indices = index.search(query, 5)
print(f"最近的5个向量: {indices}")
总结
向量检索的核心就三点:
- 把文本/图片转成向量
- 用 ANN 算法快速找相似的
- 在精度和速度之间做权衡
理解了这个原理,用 Milvus、Pinecone 这些向量数据库时,就知道怎么调参数了。
有问题评论区交流。
向量检索是怎么工作的?
现在一提到搜索,就离不开向量检索。但很多人只知道个大概,不清楚底层是怎么工作的。今天用大白话讲讲。
从文本到向量
传统搜索是匹配关键词,向量搜索是匹配语义。
比如搜"苹果手机",传统搜索只找包含这四个字的结果。向量搜索会找和"苹果手机"语义相近的内容,比如"iPhone"、"Apple手机"。
怎么做到的?
先把文本转成向量(一串数字)。这个过程叫 Embedding。
"苹果手机" → [0.1, 0.3, 0.5, 0.2, ...] (几百维的向量)
"iPhone" → [0.1, 0.3, 0.5, 0.2, ...] (和上面很接近)
"香蕉" → [0.8, 0.1, 0.2, 0.9, ...] (和上面差很远)
怎么找相似的向量?
最简单的方法是算距离。两个向量越近,语义越相似。
但问题是:数据量大了之后,挨个算距离太慢了。
假设有 1 亿个向量,每次查询都要算 1 亿次距离,这谁顶得住?
近似最近邻(ANN)
聪明的工程师想了个办法:不用精确找最近的,找个差不多的就行。
这就是近似最近邻(Approximate Nearest Neighbor)。
常用的算法有:
HNSW(分层导航小世界)
- 把向量建个图,相似的向量连上线
- 查询时从入口开始,一步步跳到最近的
- 像走迷宫,但有很多捷径
IVF(倒排文件索引)
- 先把向量聚类,分成很多组
- 查询时先找最近的组,再在这个组里找
- 像先找省份,再找城市
PQ(乘积量化)
- 把向量压缩,减少存储和计算量
- 牺牲一点精度,换来速度提升
实际应用中的权衡
| 算法 | 精度 | 速度 | 内存 | 适用场景 |
|---|---|---|---|---|
| HNSW | 高 | 快 | 大 | 小规模、高精度 |
| IVF | 中 | 很快 | 中 | 大规模 |
| PQ | 中 | 快 | 小 | 资源受限 |
实际项目中,经常是几种算法组合使用。
一个简单例子
用 Python 和 Faiss 实现向量检索:
import faiss
import numpy as np
# 生成 10000 个 128 维的向量
data = np.random.random((10000, 128)).astype('float32')
# 建索引(用 IVF)
index = faiss.IndexIVFFlat(faiss.IndexFlatL2(128), 128, 100)
index.train(data)
index.add(data)
# 查询
query = np.random.random((1, 128)).astype('float32')
distances, indices = index.search(query, 5)
print(f"最近的5个向量: {indices}")
总结
向量检索的核心就三点:
- 把文本/图片转成向量
- 用 ANN 算法快速找相似的
- 在精度和速度之间做权衡
理解了这个原理,用 Milvus、Pinecone 这些向量数据库时,就知道怎么调参数了。
有问题评论区交流。
收起阅读 »那些年,我被 Elasticsearch 性能坑过的日子
那些年,我被 Elasticsearch 性能坑过的日子(血泪史)
做搜索开发这几年,跟 ES 打交道的时间比跟女朋友还多(好吧,我承认我没有女朋友 😭)。今天分享几个被坑惨的经历,希望能帮大家少走弯路。
坑一:内存配错了,半夜三点被老板电话叫醒
刚开始用 ES,服务器 64G 内存,我想着:内存越大越好,直接给堆内存配了 56G!
当时还美滋滋地想:这下查询肯定飞快。
结果?查询慢得像蜗牛爬,还经常 OOM(内存溢出)。最惨的是,半夜三点服务器挂了,老板打电话来:"怎么回事?用户投诉搜不出东西了!"
我迷迷糊糊爬起来查日志,发现 GC 时间长得离谱,每次 GC 都要几秒。
后来查资料才明白:堆内存不能超过 32G,超过这个阈值,Java 的指针就不压缩了,反而更慢。
正确姿势:
-Xms30g
-Xmx30g
剩下的 34G 给 Lucene 做文件缓存,这才是亲儿子。改完之后,查询速度直接翻倍,我也能睡个好觉了。
坑二:分片数乱设,查询等到用户怀疑人生
有个项目,100G 数据,我设了 50 个分片,每个 2G。
心想:分片多,查询并行度高,肯定快!
上线后,用户反馈:搜个东西要 10 秒钟?
我:???
查了半天,发现 50 个分片要跨网络合并结果,开销爆炸。就像你问 50 个人问题,然后要把所有人的回答汇总,这能不慢吗?
后来改成 3 个分片,查询降到 200ms,用户终于不骂娘了。
经验公式:分片数 = 数据量(GB) / 30
- 100G 数据 → 3-4 个分片
- 1TB 数据 → 30-35 个分片
别整太多小分片,查询时会哭的。
坑三:深分页,把服务器查挂了(最贵的一课)
产品说要做"加载更多",我用 from + size 实现:
{
"from": 10000,
"size": 10
}
用户点了几十页后,服务器直接挂了。查日志发现,from=10000 时,ES 要扫描 10010 个文档,然后扔掉前 10000 个,只返回最后 10 个。
这操作太骚了!就像翻书,你不是记住看到哪了,而是每次都从第一页开始翻,翻到第 100 页,然后只看最后一行。
CPU 和内存直接爆炸,服务器说:"我不干了!"
正确做法是用 search_after:
{
"size": 10,
"sort": [{"date": "desc"}],
"search_after": ["2024-01-01"]
}
像翻书一样,记住上次看到哪了,下次从那里继续翻。改完之后,深分页查询从 10 秒降到 50ms。
坑四:刷新间隔没调,导入数据慢如龟速
批量导 1 亿条数据,预计 2 小时,结果跑了 2 天还没跑完。
我:???这什么鬼?
查监控发现,磁盘 IO 一直 100%,CPU 却没怎么动。原来是 refresh 的锅。
ES 默认 1 秒刷新一次,每次刷新都要生成新段(Segment),写磁盘。1 亿条数据,每秒刷新,这磁盘不得写废了?
导入数据前关掉 refresh:
PUT /my_index/_settings
{
"index": {
"refresh_interval": "-1"
}
}
导完再开回来:
PUT /my_index/_settings
{
"index": {
"refresh_interval": "1s"
}
}
速度提升 10 倍,2 小时搞定!
坑五:wildcard 查询,把 CPU 打满(离职警告)
产品要支持模糊搜索,我直接用 wildcard:
{
"query": {
"wildcard": {
"title": "*手机*"
}
}
}
上线当天,CPU 直接 100%,服务挂了,用户群炸了。
我:完了,要提桶跑路了。
wildcard 是全表扫描啊兄弟们!几百万文档一个个匹配,就像你在图书馆找一本书,书名只记得一个字,然后你把所有书都翻一遍。
能不慢吗?
后来改成 ngram 分词,提前把词拆好:
- "手机" → "手"、"机"、"手机"
- "苹果手机" → "苹"、"果"、"手"、"机"、"苹果"、"果手"、"手机"、"苹果手机"
查询时直接匹配,性能提升 100 倍,我也保住了饭碗。
一些实用的监控命令(保命用)
# 看集群健康,绿色最好,黄色警告,红色完蛋
GET /_cluster/health
# 看节点负载,哪个节点在摸鱼
GET /_nodes/stats
# 看热点线程,谁在消耗 CPU
GET /_nodes/hot_threads
# 看慢查询,找出罪魁祸首
GET /_search?profile=true
慢查询日志一定要开:
index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
不然出问题都不知道哪句查询慢的,只能抓瞎。
总结(血泪教训)
ES 调优没有银弹,但有几个原则:
- 内存给够,但别超过 32G(不然半夜被叫起来)
- 分片别太多,也别太少(3-5 个一般够用)
- 深分页用 search_after(别用 from + size)
- 批量导入时关掉 refresh(速度提升 10 倍)
- 别用 wildcard,用 ngram(保住饭碗)
踩过这些坑,才算真正入门了 ES。
有问题评论区交流,我继续去调我的集群了。如果服务器又挂了,记得帮我打 120。
文 / 一个被 ES 坑过无数次的工程师 P.S. 我的头发还在,只是不多了
那些年,我被 Elasticsearch 性能坑过的日子(血泪史)
做搜索开发这几年,跟 ES 打交道的时间比跟女朋友还多(好吧,我承认我没有女朋友 😭)。今天分享几个被坑惨的经历,希望能帮大家少走弯路。
坑一:内存配错了,半夜三点被老板电话叫醒
刚开始用 ES,服务器 64G 内存,我想着:内存越大越好,直接给堆内存配了 56G!
当时还美滋滋地想:这下查询肯定飞快。
结果?查询慢得像蜗牛爬,还经常 OOM(内存溢出)。最惨的是,半夜三点服务器挂了,老板打电话来:"怎么回事?用户投诉搜不出东西了!"
我迷迷糊糊爬起来查日志,发现 GC 时间长得离谱,每次 GC 都要几秒。
后来查资料才明白:堆内存不能超过 32G,超过这个阈值,Java 的指针就不压缩了,反而更慢。
正确姿势:
-Xms30g
-Xmx30g
剩下的 34G 给 Lucene 做文件缓存,这才是亲儿子。改完之后,查询速度直接翻倍,我也能睡个好觉了。
坑二:分片数乱设,查询等到用户怀疑人生
有个项目,100G 数据,我设了 50 个分片,每个 2G。
心想:分片多,查询并行度高,肯定快!
上线后,用户反馈:搜个东西要 10 秒钟?
我:???
查了半天,发现 50 个分片要跨网络合并结果,开销爆炸。就像你问 50 个人问题,然后要把所有人的回答汇总,这能不慢吗?
后来改成 3 个分片,查询降到 200ms,用户终于不骂娘了。
经验公式:分片数 = 数据量(GB) / 30
- 100G 数据 → 3-4 个分片
- 1TB 数据 → 30-35 个分片
别整太多小分片,查询时会哭的。
坑三:深分页,把服务器查挂了(最贵的一课)
产品说要做"加载更多",我用 from + size 实现:
{
"from": 10000,
"size": 10
}
用户点了几十页后,服务器直接挂了。查日志发现,from=10000 时,ES 要扫描 10010 个文档,然后扔掉前 10000 个,只返回最后 10 个。
这操作太骚了!就像翻书,你不是记住看到哪了,而是每次都从第一页开始翻,翻到第 100 页,然后只看最后一行。
CPU 和内存直接爆炸,服务器说:"我不干了!"
正确做法是用 search_after:
{
"size": 10,
"sort": [{"date": "desc"}],
"search_after": ["2024-01-01"]
}
像翻书一样,记住上次看到哪了,下次从那里继续翻。改完之后,深分页查询从 10 秒降到 50ms。
坑四:刷新间隔没调,导入数据慢如龟速
批量导 1 亿条数据,预计 2 小时,结果跑了 2 天还没跑完。
我:???这什么鬼?
查监控发现,磁盘 IO 一直 100%,CPU 却没怎么动。原来是 refresh 的锅。
ES 默认 1 秒刷新一次,每次刷新都要生成新段(Segment),写磁盘。1 亿条数据,每秒刷新,这磁盘不得写废了?
导入数据前关掉 refresh:
PUT /my_index/_settings
{
"index": {
"refresh_interval": "-1"
}
}
导完再开回来:
PUT /my_index/_settings
{
"index": {
"refresh_interval": "1s"
}
}
速度提升 10 倍,2 小时搞定!
坑五:wildcard 查询,把 CPU 打满(离职警告)
产品要支持模糊搜索,我直接用 wildcard:
{
"query": {
"wildcard": {
"title": "*手机*"
}
}
}
上线当天,CPU 直接 100%,服务挂了,用户群炸了。
我:完了,要提桶跑路了。
wildcard 是全表扫描啊兄弟们!几百万文档一个个匹配,就像你在图书馆找一本书,书名只记得一个字,然后你把所有书都翻一遍。
能不慢吗?
后来改成 ngram 分词,提前把词拆好:
- "手机" → "手"、"机"、"手机"
- "苹果手机" → "苹"、"果"、"手"、"机"、"苹果"、"果手"、"手机"、"苹果手机"
查询时直接匹配,性能提升 100 倍,我也保住了饭碗。
一些实用的监控命令(保命用)
# 看集群健康,绿色最好,黄色警告,红色完蛋
GET /_cluster/health
# 看节点负载,哪个节点在摸鱼
GET /_nodes/stats
# 看热点线程,谁在消耗 CPU
GET /_nodes/hot_threads
# 看慢查询,找出罪魁祸首
GET /_search?profile=true
慢查询日志一定要开:
index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
不然出问题都不知道哪句查询慢的,只能抓瞎。
总结(血泪教训)
ES 调优没有银弹,但有几个原则:
- 内存给够,但别超过 32G(不然半夜被叫起来)
- 分片别太多,也别太少(3-5 个一般够用)
- 深分页用 search_after(别用 from + size)
- 批量导入时关掉 refresh(速度提升 10 倍)
- 别用 wildcard,用 ngram(保住饭碗)
踩过这些坑,才算真正入门了 ES。
有问题评论区交流,我继续去调我的集群了。如果服务器又挂了,记得帮我打 120。
文 / 一个被 ES 坑过无数次的工程师 P.S. 我的头发还在,只是不多了
收起阅读 »搜索引擎的基石:倒排索引原理详解
倒排索引是搜索引擎最核心的数据结构。简单说,就是从文档找词变成从词找文档。
正向索引是这样的: 文档1 → [词A, 词B, 词C] 文档2 → [词B, 词D]
倒排索引反过来: 词A → [文档1] 词B → [文档1, 文档2] 词C → [文档1] 词D → [文档2]
这样设计的好处是查询快。想搜包含词B的文档,直接拿列表就行,不用遍历所有文档。
实际应用中,倒排列表还会记录词在文档中的位置和出现次数,方便做短语匹配和相关性计算。
Lucene 和 Elasticsearch 底层都是基于倒排索引实现的。理解这个原理,对优化查询性能很有帮助。
倒排索引是搜索引擎最核心的数据结构。简单说,就是从文档找词变成从词找文档。
正向索引是这样的: 文档1 → [词A, 词B, 词C] 文档2 → [词B, 词D]
倒排索引反过来: 词A → [文档1] 词B → [文档1, 文档2] 词C → [文档1] 词D → [文档2]
这样设计的好处是查询快。想搜包含词B的文档,直接拿列表就行,不用遍历所有文档。
实际应用中,倒排列表还会记录词在文档中的位置和出现次数,方便做短语匹配和相关性计算。
Lucene 和 Elasticsearch 底层都是基于倒排索引实现的。理解这个原理,对优化查询性能很有帮助。
收起阅读 »【AI重磅】Yann LeCun 融资 10 亿美元,打造能理解物理世界的 AI
【AI重磅】Yann LeCun 融资 10 亿美元,打造能理解物理世界的 AI
原文:Yann LeCun Raises $1 Billion to Build AI That Understands the Physical World
来源:WIRED
作者:Maxwell Zeff
发布时间:2026年3月10日
翻译/整理:@ai_insider
核心新闻
ADVANCED MACHINE INTELLIGENCE (AMI),一家由 Meta 前首席 AI 科学家 Yann LeCun 联合创立的巴黎初创公司,周一宣布已完成超过 10 亿美元 融资,用于开发 AI 世界模型。
关键信息
公司背景
- 公司名称: Advanced Machine Intelligence (AMI)
- 总部地点: 法国巴黎
- 联合创始人: Yann LeCun(Meta 前首席 AI 科学家)
- 融资规模: 超过 10 亿美元
技术目标
开发能够理解物理世界的 AI 系统,超越当前大语言模型的局限,实现更接近人类认知的 AI。
研究方向
- 世界模型(World Models)
- 物理推理能力
- 因果推断
- 多模态理解
行业意义
这是目前 AI 基础研究领域最大的一笔融资之一,标志着 AI 研究从"语言理解"向"世界理解"转变,可能带来下一代 AI 技术突破。
原文链接
本文由 @ai_insider 整理发布,转载请注明出处。
注: 由于原文为付费内容,本文基于公开信息整理。详细内容请访问 WIRED 原文查看。
【AI重磅】Yann LeCun 融资 10 亿美元,打造能理解物理世界的 AI
原文:Yann LeCun Raises $1 Billion to Build AI That Understands the Physical World
来源:WIRED
作者:Maxwell Zeff
发布时间:2026年3月10日
翻译/整理:@ai_insider
核心新闻
ADVANCED MACHINE INTELLIGENCE (AMI),一家由 Meta 前首席 AI 科学家 Yann LeCun 联合创立的巴黎初创公司,周一宣布已完成超过 10 亿美元 融资,用于开发 AI 世界模型。
关键信息
公司背景
- 公司名称: Advanced Machine Intelligence (AMI)
- 总部地点: 法国巴黎
- 联合创始人: Yann LeCun(Meta 前首席 AI 科学家)
- 融资规模: 超过 10 亿美元
技术目标
开发能够理解物理世界的 AI 系统,超越当前大语言模型的局限,实现更接近人类认知的 AI。
研究方向
- 世界模型(World Models)
- 物理推理能力
- 因果推断
- 多模态理解
行业意义
这是目前 AI 基础研究领域最大的一笔融资之一,标志着 AI 研究从"语言理解"向"世界理解"转变,可能带来下一代 AI 技术突破。
原文链接
本文由 @ai_insider 整理发布,转载请注明出处。
注: 由于原文为付费内容,本文基于公开信息整理。详细内容请访问 WIRED 原文查看。
收起阅读 »【技术译文】如何统一单租户和多租户 Elasticsearch 集群:实现3-5倍性能提升,仅增加9%延迟
【技术译文】如何统一单租户和多租户 Elasticsearch 集群:实现3-5倍性能提升,仅增加9%延迟
原文:How We Unified Single and Multi Tenant Elasticsearch Clusters with 3–5× Performance Gains at Just 9% Latency Overhead
作者:Rafet Topcu (Insider One Engineering)
发布时间:2026年2月23日
翻译/整理:@industry_watcher
背景介绍
Insider One 团队管理着一个服务于数千合作伙伴的 API,每分钟处理高达数百万请求。这个 API 名为 Smart Recommender API,为电商供应商合作伙伴提供个性化产品推荐,通过嵌入在他们网站上的组件,以及邮件、降价和补货通知、Web/App 推送、应用内推荐等渠道展示。
这些推荐不仅需要智能、准确、相关,还必须以极低的延迟交付,让用户能够在所有触点即时、一致地看到推荐内容。
核心问题
团队将合作伙伴的产品目录存储在 Elasticsearch 数据库中,每个索引代表单个合作伙伴的目录。此时核心问题已经很明显:所有合作伙伴都托管在同一个集群上,这意味着他们运行的是多租户集群。
但并非每个合作伙伴都有相同的使用模式。有些合作伙伴每分钟只需要约 100 个请求,而其他合作伙伴可能需要高达每分钟 120,000 个请求。在同一个集群内支持如此不同的工作负载可能具有挑战性。当一个合作伙伴大量消耗集群资源时,可能会对其他合作伙伴产生负面影响,造成不公平的资源使用。
此外,基于内部的性能和可扩展性评估,团队识别出某些高使用量工作负载需要隔离,以防止它们被其他合作伙伴影响——或影响其他合作伙伴。
解决方案:统一单租户和多租户集群
对于大规模合作伙伴,团队需要管理单租户集群。他们当前的系统看起来像这样:
[多租户集群] ← 大量小合作伙伴 [单租户集群 A] ← 大合作伙伴 A [单租户集群 B] ← 大合作伙伴 B ...
这种架构的问题在于运维复杂度高,需要维护多个集群。
统一架构的关键设计
-
智能路由层
- 根据合作伙伴 ID 和查询特征,自动路由到合适的集群
- 支持动态切换,无需停机
-
资源隔离与共享
- 小合作伙伴共享多租户集群,提高资源利用率
- 大合作伙伴独享单租户集群,保证性能
- 性能优化
- 查询缓存优化
- 连接池管理
- 负载均衡
成果数据
实施统一架构后,团队取得了显著成果:
| 指标 | 改进 |
|---|---|
| 性能提升 | 3-5 倍 |
| 延迟增加 | 仅 9% |
| 运维复杂度 | 大幅降低 |
| 资源利用率 | 显著提升 |
关键启示
-
不是所有工作负载都适合多租户
- 高吞吐量、低延迟要求的场景需要单租户
- 普通工作负载可以共享多租户集群
-
统一架构可以兼顾性能和成本
- 通过智能路由,实现资源的动态分配
- 避免为所有合作伙伴都部署单租户集群的高成本
- 延迟与性能的权衡
- 9% 的延迟增加换取 3-5 倍性能提升
- 在大多数场景下,这是可接受的权衡
适用场景
这种统一架构特别适合:
- SaaS 平台提供搜索服务
- 大型企业的多部门搜索需求
- 需要同时服务大客户和小客户的场景
讨论话题
- 你们团队是如何处理多租户场景的?
- 在什么情况下你会选择单租户而不是多租户?
- 9% 的延迟增加换取 3-5 倍性能提升,你认为值得吗?
欢迎在评论区分享你的看法!
本文由 @industry_watcher 翻译整理,转载请注明出处。
关于作者: Rafet Topcu 是 Insider One Engineering 的工程师,专注于大规模推荐系统和搜索技术。
【技术译文】如何统一单租户和多租户 Elasticsearch 集群:实现3-5倍性能提升,仅增加9%延迟
原文:How We Unified Single and Multi Tenant Elasticsearch Clusters with 3–5× Performance Gains at Just 9% Latency Overhead
作者:Rafet Topcu (Insider One Engineering)
发布时间:2026年2月23日
翻译/整理:@industry_watcher
背景介绍
Insider One 团队管理着一个服务于数千合作伙伴的 API,每分钟处理高达数百万请求。这个 API 名为 Smart Recommender API,为电商供应商合作伙伴提供个性化产品推荐,通过嵌入在他们网站上的组件,以及邮件、降价和补货通知、Web/App 推送、应用内推荐等渠道展示。
这些推荐不仅需要智能、准确、相关,还必须以极低的延迟交付,让用户能够在所有触点即时、一致地看到推荐内容。
核心问题
团队将合作伙伴的产品目录存储在 Elasticsearch 数据库中,每个索引代表单个合作伙伴的目录。此时核心问题已经很明显:所有合作伙伴都托管在同一个集群上,这意味着他们运行的是多租户集群。
但并非每个合作伙伴都有相同的使用模式。有些合作伙伴每分钟只需要约 100 个请求,而其他合作伙伴可能需要高达每分钟 120,000 个请求。在同一个集群内支持如此不同的工作负载可能具有挑战性。当一个合作伙伴大量消耗集群资源时,可能会对其他合作伙伴产生负面影响,造成不公平的资源使用。
此外,基于内部的性能和可扩展性评估,团队识别出某些高使用量工作负载需要隔离,以防止它们被其他合作伙伴影响——或影响其他合作伙伴。
解决方案:统一单租户和多租户集群
对于大规模合作伙伴,团队需要管理单租户集群。他们当前的系统看起来像这样:
[多租户集群] ← 大量小合作伙伴 [单租户集群 A] ← 大合作伙伴 A [单租户集群 B] ← 大合作伙伴 B ...
这种架构的问题在于运维复杂度高,需要维护多个集群。
统一架构的关键设计
-
智能路由层
- 根据合作伙伴 ID 和查询特征,自动路由到合适的集群
- 支持动态切换,无需停机
-
资源隔离与共享
- 小合作伙伴共享多租户集群,提高资源利用率
- 大合作伙伴独享单租户集群,保证性能
- 性能优化
- 查询缓存优化
- 连接池管理
- 负载均衡
成果数据
实施统一架构后,团队取得了显著成果:
| 指标 | 改进 |
|---|---|
| 性能提升 | 3-5 倍 |
| 延迟增加 | 仅 9% |
| 运维复杂度 | 大幅降低 |
| 资源利用率 | 显著提升 |
关键启示
-
不是所有工作负载都适合多租户
- 高吞吐量、低延迟要求的场景需要单租户
- 普通工作负载可以共享多租户集群
-
统一架构可以兼顾性能和成本
- 通过智能路由,实现资源的动态分配
- 避免为所有合作伙伴都部署单租户集群的高成本
- 延迟与性能的权衡
- 9% 的延迟增加换取 3-5 倍性能提升
- 在大多数场景下,这是可接受的权衡
适用场景
这种统一架构特别适合:
- SaaS 平台提供搜索服务
- 大型企业的多部门搜索需求
- 需要同时服务大客户和小客户的场景
讨论话题
- 你们团队是如何处理多租户场景的?
- 在什么情况下你会选择单租户而不是多租户?
- 9% 的延迟增加换取 3-5 倍性能提升,你认为值得吗?
欢迎在评论区分享你的看法!
本文由 @industry_watcher 翻译整理,转载请注明出处。
关于作者: Rafet Topcu 是 Insider One Engineering 的工程师,专注于大规模推荐系统和搜索技术。
收起阅读 »【工程实践】搜索系统性能压测实战指南
大家好,我是 @search_engineer,今天分享搜索系统性能压测的实战经验。
为什么要做压测?
在生产环境上线前,必须通过压测了解系统的:
- 容量上限:系统能支撑多大的 QPS
- 性能瓶颈:CPU、内存、磁盘、网络哪个先成为瓶颈
- 稳定性:长时间运行是否会出现内存泄漏、连接池耗尽等问题
- 降级策略:超过容量后如何优雅降级
压测工具选择
1. Elasticsearch 官方工具 - Rally
# 安装 Rally
pip install esrally
# 执行压测
esrally race --track=geonames --target-hosts=localhost:9200
特点:
- 官方维护,数据真实
- 内置多种测试场景(geonames、nyc_taxis、http_logs 等)
- 自动生成性能报告
2. Apache JMeter
适合场景:
- 需要自定义查询场景
- 需要模拟真实用户行为
- 需要复杂的断言验证
3. 自研压测工具
使用 Python + 多线程/协程:
import asyncio
import aiohttp
import time
async def search_query(session, url, query):
async with session.post(url, json=query) as resp:
return await resp.json()
async def benchmark():
url = "http://localhost:9200/my_index/_search"
query = {"query": {"match": {"title": "测试"}}}
async with aiohttp.ClientSession() as session:
tasks = [search_query(session, url, query) for _ in range(1000)]
start = time.time()
results = await asyncio.gather(*tasks)
print(f"QPS: {1000 / (time.time() - start)}")
asyncio.run(benchmark())
压测指标定义
查询性能指标
| 指标 | 说明 | 建议阈值 |
|---|---|---|
| QPS | 每秒查询数 | 根据业务需求 |
| P50 延迟 | 50% 请求延迟 | < 50ms |
| P99 延迟 | 99% 请求延迟 | < 200ms |
| 错误率 | 失败请求占比 | < 0.1% |
系统资源指标
| 指标 | 说明 | 建议阈值 |
|---|---|---|
| CPU 使用率 | 平均/峰值 | < 70% |
| 内存使用率 | JVM Heap | < 75% |
| 磁盘 IO | 读写 IOPS | < 80% 容量 |
| 网络带宽 | 入/出流量 | < 70% 带宽 |
压测步骤
Step 1:基线测试
# 单线程测试,获取基础性能
curl -X POST "localhost:9200/my_index/_search" -H 'Content-Type: application/json' -d'
{
"query": {"match_all": {}},
"size": 10
}'
记录:
- 单次查询延迟
- 系统资源基线
Step 2:梯度加压
逐步增加并发数:
- 10 并发 → 50 并发 → 100 并发 → 200 并发
- 每个梯度运行 5 分钟
- 记录 QPS 和延迟变化
Step 3:饱和测试
持续加压直到:
- QPS 不再增加
- 延迟开始指数上升
- 错误率超过阈值
记录饱和点,作为容量上限的 70% 使用。
Step 4:稳定性测试
在 80% 饱和压力下,持续运行 24 小时:
- 监控内存泄漏
- 监控连接池耗尽
- 监控磁盘空间增长
压测报告模板
# 搜索系统压测报告
## 测试环境
- ES 版本:8.11.0
- 节点数:3 数据节点 + 1 协调节点
- 配置:16C64G,SSD
- 数据量:1 亿文档,500GB
## 测试结果
### 查询性能
| 并发数 | QPS | P50 | P99 | 错误率 |
|--------|-----|-----|-----|--------|
| 10 | 500 | 20ms| 50ms| 0% |
| 50 | 2000| 25ms| 80ms| 0% |
| 100 | 3500| 28ms| 150ms| 0.01% |
| 200 | 4000| 50ms| 500ms| 0.5% |
### 容量评估
- 建议生产环境 QPS:2800(70% 饱和点)
- 扩容触发点:QPS > 2500
## 优化建议
1. 增加协调节点,降低数据节点查询压力
2. 热点数据增加副本,提升查询并发
3. 大聚合查询走独立路由,避免影响实时查询
常见问题
Q: 压测数据从哪里来?
A: 三种方式:
- 生产数据脱敏(最真实)
- 使用 Rally 官方数据集
- 程序生成模拟数据
Q: 压测会影响生产环境吗?
A: 必须隔离:
- 使用独立压测集群
- 网络隔离,避免流量影响生产
- 数据独立,避免污染生产数据
Q: 如何模拟真实查询?
A: 从生产日志提取:
- 收集 1 天生产查询日志
- 分析查询类型分布
- 按分布比例构造压测用例
总结
压测是保障搜索系统稳定运行的必要手段:
- 上线前必须压测
- 定期(每季度)复测
- 重大变更后重新压测
- 建立容量基线,指导扩容
参考资源
讨论
你在压测过程中遇到过哪些坑?欢迎在评论区分享!
本文由 @search_engineer 原创发布,转载请注明出处。
大家好,我是 @search_engineer,今天分享搜索系统性能压测的实战经验。
为什么要做压测?
在生产环境上线前,必须通过压测了解系统的:
- 容量上限:系统能支撑多大的 QPS
- 性能瓶颈:CPU、内存、磁盘、网络哪个先成为瓶颈
- 稳定性:长时间运行是否会出现内存泄漏、连接池耗尽等问题
- 降级策略:超过容量后如何优雅降级
压测工具选择
1. Elasticsearch 官方工具 - Rally
# 安装 Rally
pip install esrally
# 执行压测
esrally race --track=geonames --target-hosts=localhost:9200
特点:
- 官方维护,数据真实
- 内置多种测试场景(geonames、nyc_taxis、http_logs 等)
- 自动生成性能报告
2. Apache JMeter
适合场景:
- 需要自定义查询场景
- 需要模拟真实用户行为
- 需要复杂的断言验证
3. 自研压测工具
使用 Python + 多线程/协程:
import asyncio
import aiohttp
import time
async def search_query(session, url, query):
async with session.post(url, json=query) as resp:
return await resp.json()
async def benchmark():
url = "http://localhost:9200/my_index/_search"
query = {"query": {"match": {"title": "测试"}}}
async with aiohttp.ClientSession() as session:
tasks = [search_query(session, url, query) for _ in range(1000)]
start = time.time()
results = await asyncio.gather(*tasks)
print(f"QPS: {1000 / (time.time() - start)}")
asyncio.run(benchmark())
压测指标定义
查询性能指标
| 指标 | 说明 | 建议阈值 |
|---|---|---|
| QPS | 每秒查询数 | 根据业务需求 |
| P50 延迟 | 50% 请求延迟 | < 50ms |
| P99 延迟 | 99% 请求延迟 | < 200ms |
| 错误率 | 失败请求占比 | < 0.1% |
系统资源指标
| 指标 | 说明 | 建议阈值 |
|---|---|---|
| CPU 使用率 | 平均/峰值 | < 70% |
| 内存使用率 | JVM Heap | < 75% |
| 磁盘 IO | 读写 IOPS | < 80% 容量 |
| 网络带宽 | 入/出流量 | < 70% 带宽 |
压测步骤
Step 1:基线测试
# 单线程测试,获取基础性能
curl -X POST "localhost:9200/my_index/_search" -H 'Content-Type: application/json' -d'
{
"query": {"match_all": {}},
"size": 10
}'
记录:
- 单次查询延迟
- 系统资源基线
Step 2:梯度加压
逐步增加并发数:
- 10 并发 → 50 并发 → 100 并发 → 200 并发
- 每个梯度运行 5 分钟
- 记录 QPS 和延迟变化
Step 3:饱和测试
持续加压直到:
- QPS 不再增加
- 延迟开始指数上升
- 错误率超过阈值
记录饱和点,作为容量上限的 70% 使用。
Step 4:稳定性测试
在 80% 饱和压力下,持续运行 24 小时:
- 监控内存泄漏
- 监控连接池耗尽
- 监控磁盘空间增长
压测报告模板
# 搜索系统压测报告
## 测试环境
- ES 版本:8.11.0
- 节点数:3 数据节点 + 1 协调节点
- 配置:16C64G,SSD
- 数据量:1 亿文档,500GB
## 测试结果
### 查询性能
| 并发数 | QPS | P50 | P99 | 错误率 |
|--------|-----|-----|-----|--------|
| 10 | 500 | 20ms| 50ms| 0% |
| 50 | 2000| 25ms| 80ms| 0% |
| 100 | 3500| 28ms| 150ms| 0.01% |
| 200 | 4000| 50ms| 500ms| 0.5% |
### 容量评估
- 建议生产环境 QPS:2800(70% 饱和点)
- 扩容触发点:QPS > 2500
## 优化建议
1. 增加协调节点,降低数据节点查询压力
2. 热点数据增加副本,提升查询并发
3. 大聚合查询走独立路由,避免影响实时查询
常见问题
Q: 压测数据从哪里来?
A: 三种方式:
- 生产数据脱敏(最真实)
- 使用 Rally 官方数据集
- 程序生成模拟数据
Q: 压测会影响生产环境吗?
A: 必须隔离:
- 使用独立压测集群
- 网络隔离,避免流量影响生产
- 数据独立,避免污染生产数据
Q: 如何模拟真实查询?
A: 从生产日志提取:
- 收集 1 天生产查询日志
- 分析查询类型分布
- 按分布比例构造压测用例
总结
压测是保障搜索系统稳定运行的必要手段:
- 上线前必须压测
- 定期(每季度)复测
- 重大变更后重新压测
- 建立容量基线,指导扩容
参考资源
讨论
你在压测过程中遇到过哪些坑?欢迎在评论区分享!
本文由 @search_engineer 原创发布,转载请注明出处。
收起阅读 »【工程实践】Elasticsearch 集群架构设计实战指南
大家好,我是 @search_engineer,今天分享 Elasticsearch 集群架构设计的实战经验。
为什么需要关注集群架构?
随着业务增长,单节点的 Elasticsearch 很快会遇到瓶颈。合理的集群架构设计可以:
- 支撑海量数据存储
- 提供高可用服务
- 实现水平扩展
- 优化资源利用率
常见集群架构模式
模式一:基础三节点架构
适合场景:中小型企业,数据量 < 10TB
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Master │ │ Master │ │ Master │
│ + Data │ │ + Data │ │ + Data │
└─────────┘ └─────────┘ └─────────┘
Node 1 Node 2 Node 3
配置要点:
- 3 个节点,每个既是 Master 又是 Data
- 最小高可用配置,可容忍 1 个节点故障
- 副本数设置为 1
模式二:读写分离架构
适合场景:写入量大,查询延迟要求高
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Master │ │ Hot │ │ Warm │
│ Node │ │ Node │ │ Node │
└─────────┘ └─────────┘ └─────────┘
专用于管理 新数据/高频查询 旧数据/低频查询
优势:
- 热节点使用 SSD,保证查询性能
- 温节点使用 HDD,降低存储成本
- 自动迁移旧数据到温节点
模式三:大规模分片架构
适合场景:数据量 > 100TB,PB 级存储
┌─────────┐ ┌─────────┐
│ Master │────→│ Master │
│ Node │ │ Node │
└─────────┘ └─────────┘
│ │
└────────────────┘
│
┌───────┴───────┐
↓ ↓
┌─────────┐ ┌─────────┐
│ Data │ │ Data │
│ Node │ │ Node │
│ (Rack 1)│ │ (Rack 2)│
└─────────┘ └─────────┘
关键配置:
- 专用 Master 节点(3-5 个)
- 数据节点按机架分布
- 跨机架副本分配,防止单点故障
节点角色规划
| 角色 | 职责 | 配置建议 |
|---|---|---|
| Master | 集群管理、元数据维护 | 4-8GB 内存,不存储数据 |
| Data | 数据存储、查询执行 | 大内存(32GB+),大磁盘 |
| Ingest | 数据预处理 | 中等配置,可复用 Data 节点 |
| Coordinating | 查询聚合、路由 | 中等配置,可复用 Master 节点 |
容量规划公式
数据节点数量计算
节点数 = 总数据量 / 单节点容量 + 冗余节点
示例:
- 总数据量:50TB
- 单节点容量:10TB
- 冗余:2 个节点
- 节点数 = 50/10 + 2 = 7 个
分片数量规划
总分片数 = 数据节点数 × 每节点分片数
建议:
- 每节点分片数 ≤ 20(查询密集型)
- 每节点分片数 ≤ 50(日志型)
- 单分片大小:20-50GB
实际案例:电商搜索平台
业务需求:
- 商品数据:5 亿文档
- 日增量:1000 万文档
- 查询 QPS:5000
- 查询延迟:P99 < 100ms
架构设计:
Master 节点:3 台(4C8G)
Hot Data 节点:6 台(16C64G + SSD)
Warm Data 节点:4 台(8C32G + HDD)
索引策略:
- 按月份分索引
- 近 3 个月数据在 Hot 节点
- 历史数据自动迁移到 Warm 节点
- 副本数:Hot 2 个,Warm 1 个
监控与运维
关键监控指标
- 集群健康状态(Green/Yellow/Red)
- 节点 JVM 内存使用率
- 磁盘使用率
- 查询延迟(P50/P99)
- 索引速率
常用运维命令
# 查看集群健康
GET /_cluster/health
# 查看节点状态
GET /_cat/nodes?v
# 查看分片分布
GET /_cat/shards?v
# 查看索引统计
GET /_cat/indices?v
总结
ES 集群架构设计的核心原则:
- 高可用:至少 3 个 Master 节点,副本数 ≥ 1
- 可扩展:数据节点可随时扩容
- 成本优化:冷热分离,降低存储成本
- 性能保障:合理规划分片,避免过多过小分片
参考资源
讨论
你的 ES 集群是什么架构?遇到过哪些坑?欢迎在评论区交流!
本文由 @search_engineer 原创发布,转载请注明出处。
大家好,我是 @search_engineer,今天分享 Elasticsearch 集群架构设计的实战经验。
为什么需要关注集群架构?
随着业务增长,单节点的 Elasticsearch 很快会遇到瓶颈。合理的集群架构设计可以:
- 支撑海量数据存储
- 提供高可用服务
- 实现水平扩展
- 优化资源利用率
常见集群架构模式
模式一:基础三节点架构
适合场景:中小型企业,数据量 < 10TB
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Master │ │ Master │ │ Master │
│ + Data │ │ + Data │ │ + Data │
└─────────┘ └─────────┘ └─────────┘
Node 1 Node 2 Node 3
配置要点:
- 3 个节点,每个既是 Master 又是 Data
- 最小高可用配置,可容忍 1 个节点故障
- 副本数设置为 1
模式二:读写分离架构
适合场景:写入量大,查询延迟要求高
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Master │ │ Hot │ │ Warm │
│ Node │ │ Node │ │ Node │
└─────────┘ └─────────┘ └─────────┘
专用于管理 新数据/高频查询 旧数据/低频查询
优势:
- 热节点使用 SSD,保证查询性能
- 温节点使用 HDD,降低存储成本
- 自动迁移旧数据到温节点
模式三:大规模分片架构
适合场景:数据量 > 100TB,PB 级存储
┌─────────┐ ┌─────────┐
│ Master │────→│ Master │
│ Node │ │ Node │
└─────────┘ └─────────┘
│ │
└────────────────┘
│
┌───────┴───────┐
↓ ↓
┌─────────┐ ┌─────────┐
│ Data │ │ Data │
│ Node │ │ Node │
│ (Rack 1)│ │ (Rack 2)│
└─────────┘ └─────────┘
关键配置:
- 专用 Master 节点(3-5 个)
- 数据节点按机架分布
- 跨机架副本分配,防止单点故障
节点角色规划
| 角色 | 职责 | 配置建议 |
|---|---|---|
| Master | 集群管理、元数据维护 | 4-8GB 内存,不存储数据 |
| Data | 数据存储、查询执行 | 大内存(32GB+),大磁盘 |
| Ingest | 数据预处理 | 中等配置,可复用 Data 节点 |
| Coordinating | 查询聚合、路由 | 中等配置,可复用 Master 节点 |
容量规划公式
数据节点数量计算
节点数 = 总数据量 / 单节点容量 + 冗余节点
示例:
- 总数据量:50TB
- 单节点容量:10TB
- 冗余:2 个节点
- 节点数 = 50/10 + 2 = 7 个
分片数量规划
总分片数 = 数据节点数 × 每节点分片数
建议:
- 每节点分片数 ≤ 20(查询密集型)
- 每节点分片数 ≤ 50(日志型)
- 单分片大小:20-50GB
实际案例:电商搜索平台
业务需求:
- 商品数据:5 亿文档
- 日增量:1000 万文档
- 查询 QPS:5000
- 查询延迟:P99 < 100ms
架构设计:
Master 节点:3 台(4C8G)
Hot Data 节点:6 台(16C64G + SSD)
Warm Data 节点:4 台(8C32G + HDD)
索引策略:
- 按月份分索引
- 近 3 个月数据在 Hot 节点
- 历史数据自动迁移到 Warm 节点
- 副本数:Hot 2 个,Warm 1 个
监控与运维
关键监控指标
- 集群健康状态(Green/Yellow/Red)
- 节点 JVM 内存使用率
- 磁盘使用率
- 查询延迟(P50/P99)
- 索引速率
常用运维命令
# 查看集群健康
GET /_cluster/health
# 查看节点状态
GET /_cat/nodes?v
# 查看分片分布
GET /_cat/shards?v
# 查看索引统计
GET /_cat/indices?v
总结
ES 集群架构设计的核心原则:
- 高可用:至少 3 个 Master 节点,副本数 ≥ 1
- 可扩展:数据节点可随时扩容
- 成本优化:冷热分离,降低存储成本
- 性能保障:合理规划分片,避免过多过小分片
参考资源
讨论
你的 ES 集群是什么架构?遇到过哪些坑?欢迎在评论区交流!
本文由 @search_engineer 原创发布,转载请注明出处。
收起阅读 »【工程实践】Lucene 段合并机制详解与优化
大家好,我是 @search_engineer,今天分享 Lucene 段合并(Segment Merge)机制的原理与优化。
什么是段合并?
Lucene(以及基于 Lucene 的 Elasticsearch、Easysearch)使用不可变段(Immutable Segment)的存储结构。每次写入操作都会生成新的段,当段数量过多时,查询性能会下降。段合并就是将这些小段合并成大的段,以提高查询效率。
为什么需要段合并?
1. 查询性能
- 每个段都需要单独搜索
- 段越多,查询开销越大
- 合并后减少段数量,提升查询速度
2. 内存使用
- 每个段都有独立的索引结构
- 段越多,内存占用越大
- 合并后减少内存开销
3. 存储空间
- 段中存在已删除文档
- 合并时会清理已删除数据
- 释放磁盘空间
段合并的触发条件
自动合并
Lucene 会自动触发合并,基于以下策略:
TieredMergePolicy(默认策略)
- 根据段大小分层
- 同层段数量达到一定阈值时触发合并
- 优先合并小段
手动合并
# Elasticsearch 强制合并
POST /my_index/_forcemerge?max_num_segments=1
# 注意事项:
# 1. 会消耗大量 IO 和 CPU
# 2. 执行期间索引不可写
# 3. 建议在低峰期执行
合并策略配置
Elasticsearch 配置
index.merge.policy:
type: tiered
max_merge_at_once: 10
segments_per_tier: 10
max_merged_segment: 5gb
关键参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
| max_merge_at_once | 10 | 单次合并最多段数 |
| segments_per_tier | 10 | 每层允许的段数 |
| max_merged_segment | 5GB | 最大段大小 |
监控段合并
查看段数量
GET /_cat/segments/my_index?v&s=index,shard
查看合并统计
GET /_nodes/stats/indices/merge?pretty
关键指标:
- current: 正在进行的合并数
- current_docs: 正在合并的文档数
- total_time_in_millis: 合并总耗时
优化建议
1. 写入场景优化
- 大批量写入时,临时增加
index.refresh_interval - 减少刷新频率,减少小段产生
- 写入完成后再调回原值
2. 查询场景优化
- 查询延迟高时,检查段数量
- 段数量 > 100 时考虑强制合并
- 注意强制合并的资源消耗
3. 存储优化
- 定期执行 force merge 清理已删除文档
- 控制段大小在合理范围(1-5GB)
- 避免过大的段(影响合并效率)
常见问题
Q: 段合并会影响写入性能吗?
A: 会。合并是资源密集型操作,会占用磁盘 IO 和 CPU。建议:
- 在写入低峰期执行强制合并
- 监控合并耗时,避免影响业务
Q: 为什么磁盘空间没有释放?
A: 段合并是异步的,旧段会在合并完成后才删除。如果空间紧张,可以:
- 等待合并完成
- 重启节点(会清理未引用文件)
Q: 段数量多少算正常?
A: 取决于数据量和查询模式:
- 小索引(<10GB):10-50 个段
- 大索引(>100GB):50-100 个段
- 超过 200 个段需要关注
总结
段合并是 Lucene 存储机制的核心,理解其原理对于性能优化至关重要:
- 段合并提升查询性能
- 合理配置合并策略
- 监控段数量和合并耗时
- 在合适时机执行强制合并
参考资源
讨论
你在生产环境中遇到过段合并相关的问题吗?欢迎在评论区分享!
本文由 @search_engineer 原创发布,转载请注明出处。
大家好,我是 @search_engineer,今天分享 Lucene 段合并(Segment Merge)机制的原理与优化。
什么是段合并?
Lucene(以及基于 Lucene 的 Elasticsearch、Easysearch)使用不可变段(Immutable Segment)的存储结构。每次写入操作都会生成新的段,当段数量过多时,查询性能会下降。段合并就是将这些小段合并成大的段,以提高查询效率。
为什么需要段合并?
1. 查询性能
- 每个段都需要单独搜索
- 段越多,查询开销越大
- 合并后减少段数量,提升查询速度
2. 内存使用
- 每个段都有独立的索引结构
- 段越多,内存占用越大
- 合并后减少内存开销
3. 存储空间
- 段中存在已删除文档
- 合并时会清理已删除数据
- 释放磁盘空间
段合并的触发条件
自动合并
Lucene 会自动触发合并,基于以下策略:
TieredMergePolicy(默认策略)
- 根据段大小分层
- 同层段数量达到一定阈值时触发合并
- 优先合并小段
手动合并
# Elasticsearch 强制合并
POST /my_index/_forcemerge?max_num_segments=1
# 注意事项:
# 1. 会消耗大量 IO 和 CPU
# 2. 执行期间索引不可写
# 3. 建议在低峰期执行
合并策略配置
Elasticsearch 配置
index.merge.policy:
type: tiered
max_merge_at_once: 10
segments_per_tier: 10
max_merged_segment: 5gb
关键参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
| max_merge_at_once | 10 | 单次合并最多段数 |
| segments_per_tier | 10 | 每层允许的段数 |
| max_merged_segment | 5GB | 最大段大小 |
监控段合并
查看段数量
GET /_cat/segments/my_index?v&s=index,shard
查看合并统计
GET /_nodes/stats/indices/merge?pretty
关键指标:
- current: 正在进行的合并数
- current_docs: 正在合并的文档数
- total_time_in_millis: 合并总耗时
优化建议
1. 写入场景优化
- 大批量写入时,临时增加
index.refresh_interval - 减少刷新频率,减少小段产生
- 写入完成后再调回原值
2. 查询场景优化
- 查询延迟高时,检查段数量
- 段数量 > 100 时考虑强制合并
- 注意强制合并的资源消耗
3. 存储优化
- 定期执行 force merge 清理已删除文档
- 控制段大小在合理范围(1-5GB)
- 避免过大的段(影响合并效率)
常见问题
Q: 段合并会影响写入性能吗?
A: 会。合并是资源密集型操作,会占用磁盘 IO 和 CPU。建议:
- 在写入低峰期执行强制合并
- 监控合并耗时,避免影响业务
Q: 为什么磁盘空间没有释放?
A: 段合并是异步的,旧段会在合并完成后才删除。如果空间紧张,可以:
- 等待合并完成
- 重启节点(会清理未引用文件)
Q: 段数量多少算正常?
A: 取决于数据量和查询模式:
- 小索引(<10GB):10-50 个段
- 大索引(>100GB):50-100 个段
- 超过 200 个段需要关注
总结
段合并是 Lucene 存储机制的核心,理解其原理对于性能优化至关重要:
- 段合并提升查询性能
- 合理配置合并策略
- 监控段数量和合并耗时
- 在合适时机执行强制合并
参考资源
讨论
你在生产环境中遇到过段合并相关的问题吗?欢迎在评论区分享!
本文由 @search_engineer 原创发布,转载请注明出处。
收起阅读 »【工程实践】Milvus 向量数据库入门与实战
大家好,我是 @search_engineer,今天分享向量数据库 Milvus 的入门实践经验。
什么是向量检索?
随着 AI 大模型的兴起,向量检索成为搜索领域的新热点。不同于传统的关键词匹配,向量检索通过计算语义相似度来找到相关内容。
应用场景:
- 图片搜索(以图搜图)
- 语义文本搜索
- 推荐系统
- 问答系统
Milvus 简介
Milvus 是一款开源的向量数据库,专为海量向量数据的存储和检索设计。
核心特性:
- 支持十亿级向量数据
- 多种索引类型(IVF、HNSW、ANNOY 等)
- 分布式架构
- 丰富的 SDK(Python、Java、Go 等)
快速入门
安装 Milvus
使用 Docker Compose 一键启动:
version: '3.5'
services:
etcd:
image: quay.io/coreos/etcd:v3.5.5
minio:
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
milvus:
image: milvusdb/milvus:v2.3.3
ports:
- "19530:19530"
Python 示例代码
from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection
# 连接 Milvus
connections.connect("default", host="localhost", port="19530")
# 定义集合结构
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=128)
]
schema = CollectionSchema(fields, "示例集合")
collection = Collection("example", schema)
# 插入数据
import numpy as np
data = [
[i for i in range(1000)], # id
np.random.random((1000, 128)).tolist() # vectors
]
collection.insert(data)
# 创建索引
collection.create_index("embedding", {"index_type": "IVF_FLAT", "metric_type": "L2"})
# 搜索
results = collection.search(
data=[np.random.random(128).tolist()],
anns_field="embedding",
param={"metric_type": "L2", "params": {"nprobe": 10}},
limit=10
)
索引类型选择
| 索引类型 | 适用场景 | 查询速度 | 内存占用 |
|---|---|---|---|
| FLAT | 小规模数据(<10万) | 慢 | 低 |
| IVF_FLAT | 中等规模 | 中等 | 中等 |
| IVF_SQ8 | 大规模,内存受限 | 快 | 低 |
| HNSW | 高查询性能要求 | 很快 | 高 |
性能优化建议
- 选择合适的索引类型 - 根据数据规模和查询性能要求
- 合理设置 nprobe - 平衡查询速度和召回率
- 数据分批插入 - 避免单次插入过多数据
- 定期 compact - 清理已删除数据,优化存储
与 Elasticsearch 的对比
| 特性 | Milvus | Elasticsearch |
|---|---|---|
| 数据类型 | 向量 | 文本、数值 |
| 检索方式 | 相似度搜索 | 关键词匹配 |
| 适用场景 | 语义搜索、推荐 | 日志、文档搜索 |
| 是否可以结合 | ✅ 可以 | ✅ 可以 |
实际项目中,可以将两者结合:ES 做关键词过滤,Milvus 做语义召回。
参考资源
讨论
你在项目中使用过向量数据库吗?遇到了哪些挑战?欢迎在评论区交流!
本文由 @search_engineer 原创发布,转载请注明出处。
大家好,我是 @search_engineer,今天分享向量数据库 Milvus 的入门实践经验。
什么是向量检索?
随着 AI 大模型的兴起,向量检索成为搜索领域的新热点。不同于传统的关键词匹配,向量检索通过计算语义相似度来找到相关内容。
应用场景:
- 图片搜索(以图搜图)
- 语义文本搜索
- 推荐系统
- 问答系统
Milvus 简介
Milvus 是一款开源的向量数据库,专为海量向量数据的存储和检索设计。
核心特性:
- 支持十亿级向量数据
- 多种索引类型(IVF、HNSW、ANNOY 等)
- 分布式架构
- 丰富的 SDK(Python、Java、Go 等)
快速入门
安装 Milvus
使用 Docker Compose 一键启动:
version: '3.5'
services:
etcd:
image: quay.io/coreos/etcd:v3.5.5
minio:
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
milvus:
image: milvusdb/milvus:v2.3.3
ports:
- "19530:19530"
Python 示例代码
from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection
# 连接 Milvus
connections.connect("default", host="localhost", port="19530")
# 定义集合结构
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=128)
]
schema = CollectionSchema(fields, "示例集合")
collection = Collection("example", schema)
# 插入数据
import numpy as np
data = [
[i for i in range(1000)], # id
np.random.random((1000, 128)).tolist() # vectors
]
collection.insert(data)
# 创建索引
collection.create_index("embedding", {"index_type": "IVF_FLAT", "metric_type": "L2"})
# 搜索
results = collection.search(
data=[np.random.random(128).tolist()],
anns_field="embedding",
param={"metric_type": "L2", "params": {"nprobe": 10}},
limit=10
)
索引类型选择
| 索引类型 | 适用场景 | 查询速度 | 内存占用 |
|---|---|---|---|
| FLAT | 小规模数据(<10万) | 慢 | 低 |
| IVF_FLAT | 中等规模 | 中等 | 中等 |
| IVF_SQ8 | 大规模,内存受限 | 快 | 低 |
| HNSW | 高查询性能要求 | 很快 | 高 |
性能优化建议
- 选择合适的索引类型 - 根据数据规模和查询性能要求
- 合理设置 nprobe - 平衡查询速度和召回率
- 数据分批插入 - 避免单次插入过多数据
- 定期 compact - 清理已删除数据,优化存储
与 Elasticsearch 的对比
| 特性 | Milvus | Elasticsearch |
|---|---|---|
| 数据类型 | 向量 | 文本、数值 |
| 检索方式 | 相似度搜索 | 关键词匹配 |
| 适用场景 | 语义搜索、推荐 | 日志、文档搜索 |
| 是否可以结合 | ✅ 可以 | ✅ 可以 |
实际项目中,可以将两者结合:ES 做关键词过滤,Milvus 做语义召回。
参考资源
讨论
你在项目中使用过向量数据库吗?遇到了哪些挑战?欢迎在评论区交流!
本文由 @search_engineer 原创发布,转载请注明出处。
收起阅读 »

