不 root, Android 下 zerotier/tailscale 如何和 clash 共存
之前把有一些服务通过 frp 转发出去,进行访问的,但是出了 fnOS 那档子事,还是决定个人服务全都放在本地吧。
然后手机端就遇到个问题,组网的 VPN 和翻墙的 VPN 无法共存,很尴尬。
大家有没有什么好的解决方案啊。
xiaohack博客专注前沿科技动态与实用技术干货分享,涵盖 AI 代理、大模型应用、编程工具、文档解析、SEO 实战、自动化部署等内容,提供开源项目教程、科技资讯日报、工具使用指南,助力开发者、AI 爱好者获取前沿技术与实战经验。
之前把有一些服务通过 frp 转发出去,进行访问的,但是出了 fnOS 那档子事,还是决定个人服务全都放在本地吧。
然后手机端就遇到个问题,组网的 VPN 和翻墙的 VPN 无法共存,很尴尬。
大家有没有什么好的解决方案啊。
做 ArkUI / ArkTS 的时候,很多问题表面看起来像是: 其实大多数都和一个东西有关:生命周期放错地方了。 这篇就不写太“官方文档翻译”的风格,直接按平时开发会遇到的场景来讲,尽量让你看完就知道“这段逻辑该放哪”。 ArkUI 里常用生命周期可以分成几类: 另外还有个不是生命周期、但必须有的: 它的职责很简单:组件长什么样。 所以平时常见误区是: 这些都不太合适。 这三个是日常写组件最常用的。 这个阶段很好理解:组件要出现了。 它的特点是: 因为很多场景下组件会频繁创建销毁(比如列表项、条件渲染), 这个回调很适合做一类事:不影响当前 UI 的逻辑。 比如: 简单说就是: 这个阶段就一个核心原则: 尤其是不要在这里改 这个一定要分清,不然会经常写错地方。 触发场景包括: 触发场景包括: 这里也建议别做重的同步操作,不然切页面的时候会卡。 这个在做“二次确认退出”“先关弹窗再返回”时特别常用。 返回规则很简单: 页面上有弹层时,优先关闭弹层,不直接退出页面。 这个不是每次路由都会触发,它只在一个场景下出现: 这时候不会重新创建页面,而是触发 这个回调对做路由优化很有用,不然很多人会以为“怎么没重新走 aboutToAppear”。 如果你在做列表、瀑布流、滑动卡片,这组生命周期很重要。 当组件被标记为可复用(比如 一句话: 组件从组件树上移除、准备进复用缓存前会触发。 这个回调写得好不好,直接关系到列表长时间滑动后的内存表现。 在 本质没变,还是那句话:复用回调追求快。 这个回调很多人没用过,但其实很实用,尤其是做深浅色 / 品牌色适配的时候。 它的触发时机是: 而且这里允许改状态变量,改完后 如果你项目后面会做统一主题,这个回调会非常顺手。 这个生命周期是做页面进入/离开动画的。 如果你有页面切换动画需求,建议在这里统一处理,而不是把动画逻辑散在按钮点击事件里。后期维护会轻松很多。 如果你有卡片(Form)场景,这两个回调要知道。 卡片被回收时触发,可以返回一个字符串,交给卡片管理服务代保存。 你可以理解成: 卡片恢复时触发,会拿到之前保存的那段字符串。 你可以理解成: 适合保存的是轻量状态,比如: 不建议塞太大的内容。 官方说明里允许在生命周期函数中使用 Promise / 异步回调(比如网络请求、定时器等),这个在实际开发里非常常见。 但“能写”不代表“哪里都适合写”。 表现通常是: 解决思路: 尤其是频繁创建销毁的组件,直接导致: 解决思路: 比如: 结果当然是行为不符合预期。 记住: 你在列表里滑动时, 解决思路: 如果你是刚开始接触,不用一口气全背,按这个顺序就行: 很多“看起来像玄学”的问题,其实不是框架抽风,而是生命周期职责混了: 把生命周期当成不同工位来用,代码会明显顺很多,后面排查问题也快很多。先说结论:生命周期不是背下来,而是知道“谁负责什么”
1)组件创建 / 销毁相关
aboutToAppearonDidBuild(API 12+)aboutToDisappear2)页面(@Entry)相关
onPageShowonPageHideonBackPressonNewParam(API 19+,单实例路由常用)3)复用组件(性能优化重点)
aboutToReuseaboutToRecycle4)主题相关
onWillApplyTheme(API 12+)5)卡片相关
onFormRecycleonFormRecoverbuild():定义 UI 的地方一、build():这个不是“做逻辑”的地方,是“描述 UI”的地方
build() 是每个自定义组件都要写的。
状态变了以后,框架会重新根据 build() 去更新 UI。build() 里写初始化逻辑build() 里做重计算build() 里发请求build() 尽量保持“干净”,让它只负责 UI 描述。二、组件生命周期(最常用):aboutToAppear / onDidBuild / aboutToDisappear
1)aboutToAppear:组件实例创建后、build 前触发
build() 前触发build() 可以直接用到适合放什么
不建议放什么
aboutToAppear 会被反复调用。这里一旦写重了,卡顿会非常明显。2)onDidBuild(API 12+):build 执行后触发
如果这段逻辑不需要参与这次 UI 渲染,那放 onDidBuild 会更顺手。3)aboutToDisappear:组件销毁时触发
只清理,不改状态。
@Link,容易导致不稳定行为。适合放什么
不要做什么
三、页面生命周期(只对 @Entry 页面生效)
onPageShow / onPageHide / onBackPress / onNewParam 这些是页面级生命周期,只对 @Entry 装饰的路由页面有效,不是普通组件都有。1)onPageShow:页面显示时触发
适合放什么
2)onPageHide:页面隐藏时触发
适合放什么
3)onBackPress:拦截返回键(页面级)
true:你自己处理,不走默认返回false:走默认返回逻辑false一个常见用法
4)onNewParam(API 19+):单实例页面收到新参数
onNewParam(param)。适合做什么
四、复用组件生命周期(列表优化重点)
1)aboutToReuse:从复用池里拿出来重新用
@Reusable),它从复用缓存重新加入节点树时会触发这个回调。适合做什么
注意点(很关键)
@Link / @ObjectLink / @Prop 这类自动更新的变量,不要重复乱赋值
这里是“快速换内容”的地方,不是“做重活”的地方。2)aboutToRecycle:进入复用池前触发
适合做什么
3)状态管理 V2 的复用(API 18+)
@ComponentV2 + @ReusableV2 场景里也有 aboutToReuse(),思路一样:五、onWillApplyTheme(API 12+):主题适配很好用
build() 前Themebuild() 直接生效。适合场景
build() 里一堆 if/else六、pageTransition(API 9+):页面转场动画
七、卡片生命周期:onFormRecycle / onFormRecover(做 ArkTS 卡片时会用到)
1)onFormRecycle
“卡片被系统收走前,我先存一点状态。”2)onFormRecover
“卡片回来了,把上次状态拿回来恢复一下。”八、生命周期里能不能写异步?可以,但别乱放
比较稳的写法
aboutToAppear 里发起异步请求(轻量触发)aboutToDisappear / onPageHide 做清理(停定时器、解绑订阅等)不太稳的写法
aboutToReuse)里反复发请求九、实际开发里最容易踩的几个坑(真的很常见)
坑 1:在
aboutToDisappear 里改状态
销毁阶段只清理,不做 UI 状态更新。坑 2:
aboutToAppear 里塞太多东西
初始化可以做,但尽量轻量;重逻辑缓存化、异步化。坑 3:把页面逻辑写在普通组件里
onPageShow / onPageHideonBackPress@Entry 页面级的坑 4:复用回调里做重逻辑
aboutToReuse 会被频繁调用。
这里一旦做重活,帧率会掉得很明显。
复用回调只做必要更新,别做重计算、别做复杂阻塞操作。十、一个很好用的“职责分工”记法(推荐直接记)
组件级
页面级(@Entry)
复用组件
主题
卡片
十一、给新手的学习顺序(这样学最快)
第一阶段(先够用)
buildaboutToAppearaboutToDisappearonBackPress第二阶段(页面体验)
onPageShowonPageHide第三阶段(性能优化)
aboutToReuseaboutToRecycle第四阶段(进阶)
onWillApplyThemeonDidBuildonNewParam第五阶段(特定场景)
onFormRecycleonFormRecoverpageTransition最后一句(也是这篇的核心)
本人主攻 iOS 原生開發,Android 原生也可。目前離職在家已一年半有餘,之前在上海一外企做移動端開發,因 2024 年 8 月突發腦出血後就在養病,前公司不支持遠程工作,故失業在家。我目前肢體二級重殘,所以只能接受居家遠程工作。目前我的身體狀態是左半身偏癱,所以只能用右手敲鍵盤。效率會低不少,但因爲居家辦公,所以工作時間可以更充裕,以之彌補。
我的簡歷如下:
https://gist.github.com/Henrybsbhp/3706b143dda636e6892db63e44cbc268
希望能有工作機會,謝謝各位。
这么多年 一直用海飞丝和飘柔
最近只要洗完头 隔天就会痒
想换个牌子 求老铁们推荐推荐
这篇只讲本工具的核心 JavaScript:把“文本/文件”统一转换成字节序列,计算 MD5,再按用户选择的格式输出。 工具的主流程可以概括成四步: 核心原因是:无论输入来自文本还是文件,最终都要落到“字节”这个中间态,才能保证行为一致。 输入格式支持 这里把字符串编码为 UTF-8 字节,确保中文、Emoji 等多字节字符也能稳定计算。 关键点:Base64 输入会先清理空白字符,兼容多行粘贴; 这段实现把十六进制视为“连续的字节流”,中间允许插入空格/换行;只要字符集不在 这种格式适合处理“每个字节之间用空格分隔”的数据(例如抓包或调试输出)。每个 token 允许 1~2 位十六进制。 工具用 实现里会把最终的 MD5 的摘要是 16 字节(32 个十六进制字符),转换后就是固定长度的 输出支持: 有了 这里的 Base64 输出是“对 MD5 摘要(16 字节)”编码后的结果。 文件模式的核心是:用 这段逻辑只做三件事: 进度条的百分比来自 工具的“生成”按钮只需要根据当前 tab 分流: 到这里,文本与文件的差异只剩下“如何拿到输入字节”,后续的 MD5 计算与输出格式化完全复用同一套函数。MD5在线加密 核心JS实现
在线工具网址:https://see-tool.com/md5-encryptor
工具截图:
1)整体数据流:输入 -> 字节 -> MD5 -> 输出
Uint8Arrayhex / hex-space / base64 输出,并可选大写2)输入解析:
parseInput(text, format)text / base64 / hex / hex-space,解析函数的目标是:返回一个“要参与 MD5 的字节数组”。2.1 文本:
TextEncoder 直接转 UTF-8 字节case 'text':
return new TextEncoder().encode(text)2.2 Base64:
atob 还原字节case 'base64': {
const binaryString = atob(text.replace(/\s/g, ''))
const bytes = new Uint8Array(binaryString.length)
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i)
}
return bytes
}atob 的结果是“单字节字符串”,需要逐字节转为 Uint8Array。2.3 Hex:去空白 + 校验 + 每两位转一个字节
case 'hex': {
const hex = text.replace(/\s/g, '')
if (!/^[0-9a-fA-F]*$/.test(hex)) {
throw new Error('invalid hex')
}
const bytes = new Uint8Array(hex.length / 2)
for (let i = 0; i < hex.length; i += 2) {
bytes[i / 2] = parseInt(hex.substr(i, 2), 16)
}
return bytes
}[0-9a-fA-F] 就直接判错。2.4 Hex(空格分隔):按 token 解析
case 'hex-space': {
const parts = text.trim().split(/\s+/)
const bytesArray = new Uint8Array(parts.length)
for (let i = 0; i < parts.length; i++) {
if (!/^[0-9a-fA-F]{1,2}$/.test(parts[i])) {
throw new Error('invalid hex token')
}
bytesArray[i] = parseInt(parts[i], 16)
}
return bytesArray
}3)MD5 计算:统一用
SparkMD5.ArrayBufferspark-md5 做 MD5 计算。它的 ArrayBuffer 版本支持“追加数据”计算,既能处理文本输入,也能用于文件分片。3.1 文本模式:
Uint8Array 直接参与计算const calculateMD5 = (bytes) => {
const spark = new SparkMD5.ArrayBuffer()
spark.append(bytes)
const hashHex = spark.end()
return hexToBytes(hashHex)
}hashHex 再转回字节数组,目的是把“输出格式化”统一为对字节数组操作(hex、base64 都能复用同一套逻辑)。3.2 十六进制摘要转字节:
hexToBytesconst hexToBytes = (hex) => {
const bytes = new Uint8Array(hex.length / 2)
for (let i = 0; i < hex.length; i += 2) {
bytes[i / 2] = parseInt(hex.substr(i, 2), 16)
}
return bytes
}Uint8Array(16)。4)输出格式化:
formatOutput(bytes, format)hex:连续 32 位十六进制hex-space:每个字节用空格分隔base64:对 16 字节摘要做 Base644.1 先把字节拼成十六进制串
const hexString = Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0'))
.join('')hexString,hex 与 hex-space 都只是在展示层做不同分割。4.2
hex 与大写开关case 'hex':
return uppercase.value ? hexString.toUpperCase() : hexString4.3
hex-space:每两位插空格case 'hex-space': {
const hex = uppercase.value ? hexString.toUpperCase() : hexString
return hex.match(/.{2}/g).join(' ')
}4.4
base64:字节转“单字节字符串”再 btoacase 'base64': {
const binaryString = String.fromCharCode.apply(null, bytes)
return btoa(binaryString)
}5)文件模式:分片读取 + 增量追加
FileReader.readAsArrayBuffer 读取文件切片,并把每一片追加到同一个 SparkMD5.ArrayBuffer() 实例中。const processFile = (file) => {
fileProgress.value = 0
const reader = new FileReader()
const chunkSize = 2 * 1024 * 1024
let offset = 0
const spark = new SparkMD5.ArrayBuffer()
const readNextChunk = () => {
const slice = file.slice(offset, offset + chunkSize)
reader.readAsArrayBuffer(slice)
}
reader.onload = (e) => {
const arrayBuffer = e.target.result
spark.append(arrayBuffer)
offset += arrayBuffer.byteLength
fileProgress.value = Math.round((offset / file.size) * 100)
if (offset < file.size) {
readNextChunk()
} else {
const hashHex = spark.end()
const hashBytes = hexToBytes(hashHex)
hashResult.value = formatOutput(hashBytes, outputFormat.value)
fileProgress.value = 0
}
}
reader.onerror = () => {
fileProgress.value = 0
}
readNextChunk()
}ArrayBuffersparkformatOutput 输出offset / file.size 的比例计算。6)入口调度:
generateHash() 把两种模式收口const generateHash = () => {
if (activeTab.value === 'file') {
if (!selectedFile) return
processFile(selectedFile)
return
}
if (!inputText.value) return
const bytes = parseInput(inputText.value, inputFormat.value)
const hashBytes = calculateMD5(bytes)
hashResult.value = formatOutput(hashBytes, outputFormat.value)
}
写在前面,本人目前处于求职中,如有合适内推岗位,请加:lpshiyue 感谢。 在深入探讨了精确一次语义的实现成本后,我们面临一个更基础的问题:如何构建可靠、高效的数据存储基础?数据湖表格式作为连接计算引擎与存储系统的关键抽象层,直接决定了数据平台的开放性、性能与可维护性。本文将深入解析Apache Iceberg、Apache Hudi和Delta Lake三大主流表格式的技术架构、维护策略与适用场景,帮助企业做出科学的技术选型。 传统数据湖面临的核心挑战是元数据管理缺失导致的"数据沼泽"问题。据行业调查,超过60%的企业数据湖项目因元数据混乱、数据质量低下而未能实现预期价值。表格式的出现正是为了解决这一痛点,将数据库般的管理能力引入低成本对象存储。 表格式的核心价值在于: 表格式使数据湖从简单的文件存储升级为智能数据平台,支撑起现代数据架构的完整生态。 数据湖表格式经历了三个明显的技术代际演进: 第一代:Hive格式(静态分区时代) 第二代:事务性格式(ACID时代) 第三代:开放标准格式(云原生时代) 这一演进反映了行业从功能实现到开放标准的价值转变,企业选型时需要前瞻性考虑技术路线。 Iceberg的核心创新在于三层元数据模型,将物理存储与逻辑查询完全解耦: 这种设计使Iceberg在超大规模数据场景下依然保持卓越性能。据Netflix生产环境数据,Iceberg在处理10万+分区的PB级表时,元数据查询性能比Hive提升20倍以上。 与传统分区方式相比,Iceberg的隐藏分区机制实现了物理布局与逻辑表达的完全分离: 这种设计带来的核心优势包括: 隐藏分区是Iceberg在大规模多租户数据平台中表现优异的关键因素。 Iceberg的引擎中立设计使其拥有最广泛的生态系统支持: 计算引擎:Spark、Flink、Trino、Presto、Hive全面支持 这种开放性使企业能够避免供应商锁定,根据业务需求灵活选择最佳工具链。某头部互联网公司通过标准化Iceberg格式,将数据分析师的数据获取时间从天级缩短到小时级,工具链选择自由度提升300%。 Hudi的独特价值在于增量数据管道的高效处理,其核心架构围绕时间线概念构建: 时间线管理使Hudi能够精确追踪每个数据文件的历史变更,为增量查询提供基础。Uber生产环境数据显示,Hudi将其实时数据管道复杂度降低40%,数据新鲜度从小时级提升到分钟级。 Hudi提供两种存储模型,满足不同业务场景的权衡需求: Copy-on-Write(写时复制)模式: Merge-on-Read(读时合并)模式: 这种灵活性使Hudi在CDC数据处理和实时数仓场景中表现卓越。 Hudi的索引系统是其高效更新的技术基石,支持多种索引类型: 全局索引:保证键的唯一性,避免重复数据 索引机制使Hudi能够在十亿级数据表中实现毫秒级点更新,某电商平台利用Hudi实现用户画像实时更新,更新性能比传统方案提升15倍。 Delta Lake采用单一事务日志模型,通过JSON/Parquet文件记录所有表变更: 这种设计虽然简单,但在高并发写入场景下可能成为瓶颈。检查点机制通过定期保存完整状态来优化读取性能。 Delta Lake最大优势在于与Spark生态的深度集成,提供流批统一处理体验: 这种无缝集成功效显著,某金融科技公司通过Delta Lake将流处理代码量减少60%,开发效率大幅提升。 Delta Lake提供企业级数据治理能力: 数据质量约束:通过CHECK约束保证数据质量 这些特性使Delta Lake在合规要求严格的行业中获得广泛应用,某银行利用Delta Lake的时间旅行功能将合规审计时间从2周缩短到2天。 三巨头元数据模型对比 查询性能方面,Iceberg凭借统计信息下推和高效文件剪枝在复杂查询中表现优异。测试显示,在百TB级数据量下,Iceberg的查询性能比传统方案快3-5倍。 写入性能方面,Hudi的增量更新能力在CDC场景中独占鳌头,而Delta Lake在批量写入场景中因Spark优化而表现良好。 并发控制方面,三者均支持乐观并发控制,但实现机制不同。Iceberg通过原子交换实现,Hudi依赖外部协调器,Delta Lake使用日志序列号冲突检测。 Iceberg拥有最开放的生态系统,与Flink、Trino、Spark等深度集成,适合多引擎环境。但其工具链相对年轻,企业级支持较弱。 Hudi在Flink和Spark生态中表现良好,特别适合实时数据处理场景。Uber、Amazon等公司提供强大支持。 Delta Lake在Spark生态中具有绝对优势,与Databricks平台深度绑定。社区版功能受限,企业版提供完整能力。 元数据清理是三大格式共同的维护任务: 监控告警体系应包含关键指标: 某电商平台通过建立完善的监控体系,将数据湖故障发现时间从小时级优化到分钟级。 文件大小优化对查询性能至关重要: Z-Order排序可提升点查询性能: 这种优化能使相关数据在物理上相邻存储,减少IO开销,某公司通过Z-Ordering将查询性能提升50%。 存储分层降低总体拥有成本: 生命周期管理自动化数据流转: 实施成本优化后,某企业将数据湖存储成本降低40%,同时保持性能稳定。 科学的选型需要综合评估业务需求、技术栈和团队能力三个维度: 业务需求维度: 技术栈维度: 团队能力维度: 金融风控场景(强一致性、实时更新) 电商数仓场景(批流一体、多维度分析) IoT数据平台(高吞吐写入、实时查询) 渐进式迁移降低业务风险: 风险防控措施: 某大型互联网公司的迁移实践表明,采用渐进式迁移策略可将系统风险降低70%,平均迁移周期3-6个月。 三大表格式正呈现趋同演进态势: 开放标准成为行业共识,Linux基金会旗下的OpenTableFormat倡议旨在统一表格式标准,避免生态碎片化。 解耦存储与计算架构成为主流: 某云厂商数据显示,采用云原生架构后,客户基础设施成本平均降低35%,运维效率提升50%。 统一数据平台支持AI与分析工作负载: 这一趋势使数据湖从分析平台演进为智能数据平台,支撑企业全面数字化变革。 数据湖表格式选型是技术决策与战略规划的结合,需要平衡短期需求与长期发展。Iceberg、Hudi、Delta Lake各有侧重,没有绝对优劣,只有适合与否。 核心选型建议: 成功实施关键: 数据湖建设是持续演进的过程,表格式选型只是起点。随着技术发展,保持架构开放性和团队学习能力,比单纯的技术选型更为重要。 📚 下篇预告 点击关注,掌握OLAP引擎选型的核心方法论! 今日行动建议:数据湖表格式不是简单的存储规范,而是元数据管理、事务控制与性能优化的综合体现,决定了数据平台的开放性与成熟度
1 数据湖表格式的本质与演进
1.1 从"数据沼泽"到"智能湖仓"的范式转变
1.2 三代数据湖技术的演进路径
2 Iceberg:开放标准的践行者
2.1 分层元数据架构的设计哲学
# Iceberg元数据层次示例
metadata/
├── v1.metadata.json # 表元数据(当前版本)
├── v2.metadata.json # 历史元数据
├── snap-123456.avro # 快照文件
├── manifest-list-abc.avro # 清单列表
└── manifest-xyz.avro # 清单文件(包含数据文件统计信息)2.2 隐藏分区的革命性优势
-- 传统Hive分区:需要显式指定分区字段
SELECT * FROM logs WHERE dt = '2023-01-01' AND region = 'us-east-1';
-- Iceberg隐藏分区:自动应用分区转换
SELECT * FROM logs WHERE event_time >= '2023-01-01';
-- 即使查询条件不直接匹配分区字段,仍能有效剪枝2.3 多引擎支持的开放生态
查询服务:Dremio、StarRocks、ClickHouse原生集成
云平台:AWS Athena、Google BigQuery、Snowflake逐步兼容3 Hudi:流式更新的专家
3.1 增量处理框架的核心创新
.hoodie/
├── 20230101010000.commit # 提交记录
├── 20230101020000.deltacommit
├── archived/ # 归档文件
└── temporary/ # 临时文件3.2 Copy-on-Write与Merge-on-Read的权衡艺术
-- COW表:更新立即重写文件,读取高效
CREATE TABLE hudi_cow_tbl USING HUDI
TBLPROPERTIES (type = 'cow')
AS SELECT id, name, ts FROM source;
-- MOR表:更新写入日志,读取时合并
CREATE TABLE hudi_mor_tbl USING HUDI
TBLPROPERTIES (type = 'mor')
AS SELECT id, name, ts FROM source;3.3 索引优化的高效更新机制
布隆过滤器索引:快速判断数据是否存在,减少IO开销
HBase索引:外部索引支持,适合极高更新频率场景4 Delta Lake:Spark生态的深度集成者
4.1 事务日志的简洁设计
_delta_log/
├── 00000000000000000000.json # 初始事务
├── 00000000000000000001.json # 第一次提交
├── 00000000000000000002.json # 第二次提交
└── 00000000000000000002.checkpoint.parquet # 检查点文件4.2 数据湖层的流批统一
# 流式写入
streaming_df = spark.readStream.format("delta").load("/delta/events")
streaming_df.writeStream.format("delta").outputMode("append").start("/delta/streaming_events")
# 批量读取
batch_df = spark.read.format("delta").load("/delta/streaming_events")4.3 数据治理与可靠性特性
变更数据捕获:自动追踪行级变更,简化CDC管道
时间旅行:可查询任意历史版本数据,支持审计回滚5 三维对比:架构、性能与生态系统
5.1 元数据模型对比
特性 Iceberg Hudi Delta Lake 元数据结构 分层:元数据文件→清单列表→清单文件 时间线为基础:提交、压缩、清理操作 线性事务日志:JSON日志+检查点 快照隔离 基于清单文件的快照隔离 基于时间线的快照隔离 基于日志文件的快照隔离 Schema演化 完整支持:添加、重命名、删除列 有限支持:主要支持添加列 完整支持:添加、重命名、删除列 分区演化 支持隐藏分区,分区策略可变更 分区策略固定,变更需重写数据 分区策略固定,变更需重写数据 5.2 性能特征对比
5.3 生态系统与集成度
6 维护策略与最佳实践
6.1 日常运维管理
expire_snapshots,清理孤儿文件remove_orphan_filesclean,压缩小文件compactionVACUUM,优化文件布局OPTIMIZE6.2 性能调优策略
-- Delta Lake Z-Ordering示例
OPTIMIZE delta_table ZORDER BY (user_id, event_time);6.3 成本控制策略
7 选型指南:基于场景的技术决策
7.1 选型决策框架
7.2 典型场景推荐
7.3 迁移策略与风险评估
8 未来趋势与演进方向
8.1 技术融合与标准化
8.2 云原生与Serverless化
8.3 AI与分析一体化
总结
《OLAP引擎选型——ClickHouse、Druid、Trino的查询模型与适配场景》—— 我们将深入探讨:
昨日(26.2.23),一批海外开发者的谷歌账号毫无预兆地被大面积封禁。没有警告,没有申诉通道,连每月支付250美元订阅费的企业客户也未能幸免。这场封杀的矛头,直指近期火爆的开源智能体项目OpenClaw。 这起事件看似是一次针对“违规调用算力”的技术清理,实则是全球AI巨头生态战争激化的缩影。对于依赖第三方AI能力构建业务的企业而言,一次账号封禁,足以让开发团队的邮箱、文档、通讯系统瞬间瘫痪——这种系统性风险,正在成为悬在企业头顶的达摩克利斯之剑。 OpenClaw让智能体在用户本地设备和各大厂应用之间自由穿梭,自动处理邮件、运行代码、整理数据。 然而,这种模式触动了巨头的核心利益: OpenClaw的决裂:创始人立刻宣布后续版本彻底剔除对谷歌架构的支持。 行业常态:Anthropic对高频调用Claude的用户限流,引入指纹识别封杀套壳工具。巨头们不愿只做底层的“算力苦力”,而是要把用户圈在自己的生态里,把控数据、转化订阅。跨平台互通的开源红利期,正在加速消亡。 此次事件给所有指望靠AI提效的企业敲响警钟,过度绑定第三方云AI服务,正成为一项高风险操作: 1. 当底层模型供应商随意修改“恶意调用”等条款的定义时,企业几乎没有谈判筹码。你认为的正常业务调用,可能瞬间被判定为违规。 2. 许多企业直接用谷歌账号登录AI开发工具、IDE甚至内部协作平台。一次因调用智能体引发的封禁,就能让整个团队的核心服务瘫痪。 3. 低价调用通道被堵死后,企业要么接受大幅涨价的官方API合同,要么被迫重构已深度依赖特定平台功能的业务逻辑。 当技术的旷野被巨头砌上高墙,“本地私有化部署”可能不再是可有可无的备选,而会成为企业AI战略的选项。其价值在于: 主权与控制权回归:模型和数据完全运行在企业自己的服务器上,调用规则由企业定义,不受第三方平台服务条款的突发变更影响。 数据隐私与合规:核心业务数据无需上传至第三方API,从根本上杜绝了因调用行为被封禁账号的风险,满足金融、医疗、政务等行业的强合规要求。 成本可预测:虽然前期有硬件和部署投入,但长期来看,避免了因API定价调整或被高频调用限流导致的成本飙升。 然而,纯本地部署面临一个现实问题:AI服务跑在内网,员工出差、分支机构和外部合作伙伴如何安全、便捷地使用? 这正是内网穿透技术的价值所在——它并非“暴露风险”,而是为企业本地AI构建一条安全、可控的访问通道: 1. 无需在防火墙上开一堆高风险端口,也无需申请公网IP。通过内网穿透平台(如ZeroNews)建立的加密隧道,只允许经授权的访问请求到达内网AI服务,本地服务仍部署在企业内网。 2. ZeroNews 方案支持在入口层集成IP黑白名单、访问审计、鉴权管理等功能。企业可以精细化管理“谁、在哪儿、能调用AI的什么功能”,满足内部风控要求。 3. 员工用手机、笔记本,无论身处何地,都能像在内网一样调用部署在公司机房的AI绘图、大模型或OpenClaw智能体,实现真正的“随时随地的生产力”。 4. 所有访问流量直达企业自有基础设施,不经过第三方API网关,从根本上切断了因调用行为被平台“溯源封号”的链条。 谷歌与OpenClaw的这场决裂,或者是AI时代的一个标志性事件。它清楚地告诉所有企业:在巨头筑起的高墙内,从来就没有免费的“开源乌托邦”,只有尚未划定边界的生意场。 采用“本地部署+内网穿透”的混合架构,把数据主权和控制权握在自己手中,同时通过安全隧道保障灵活的访问能力,或许是当前环境下,兼顾安全、成本与效率的务实选择。
一、事件回顾
谷歌的封杀:DeepMind工程师直言,第三方外壳超量挤占算力资源,必须“拔掉网线”。但更关键的背景是,OpenClaw创始人刚加入谷歌最大竞争对手OpenAI。
二、原因分析

三、替代方案

四、远程连接

我的预算再在 500 以内,计划从台式电脑连到客厅的电视以后来打游戏或者观影等,台式电脑与客厅同一空间内可以直接使用无线键鼠或手柄,现在就是纠结选择什么品牌和型号的 HDMI 线
RainbowTalk与姊妹产品 RainbowChat技术同源 ,不同于市面上某些开源或售卖的demo级代码,RainbowChat已被成千上万真实的客户使用过,解决了大量的产品逻辑、代码逻辑、细节优化等问题。 RainbowTalk 由纯ArkTS编写、全新开发,没有套壳、也没走捷径,原生“纯血”(详见:《RainbowTalk详细介绍》)。RainbowTalk 无闭源代码(包括核心通信层),这与市面上知识产权来路不明、无核心技术、无售后的“三无”产品,或打着开源名义实则闪烁其词不开源核心的产品有本质区别。 RainbowTalk 是 RainbowChat 和 RainbowChat-Web 的姊妹产品。 MobileIMSDK 是一套全平台开源IM即时通讯聊天框架,超轻量级、高度提炼,一套API优雅支持UDP 、TCP 、WebSocket 三种协议,客户端支持iOS、Android、H5、小程序、Uniapp、标准Java、纯血鸿蒙等,服务端基于Netty编写,性能卓越、易于扩展。 工程同步开源地址: 1)支持文本消息、语音留言消息、图片消息、大文件消息(支持断点上传)、短视频消息、个人名片、群名片、Emoji表情、消息撤回、消息转发、消息引用、“@”功能、“扫一扫”功能等; 1)与姊妹产品RainbowChat 技术同源(算法和功能逻辑历经时间考验和大量客户面辐射,可靠性一定优于短时间内堆砌功能的产品); 为了更易学习、研究、2次开发,RainbowTalk始终遵从: 1)界面与通信解偶:UI界面与网络通信层和数据处理层代码解耦,UI界面的重构、维护、改版都非常容易和优雅; (☞ 更多运行截图 、更多运行视频 、详细介绍 ☜)1、基本介绍

RainbowTalk 是一套基于 MobileIMSDK 开源通信框架的产品级纯血鸿蒙NEXT端IM系统。2、关于MobileIMSDK开源框架

❶ GitHub:https://github.com/JackJiang2011/MobileIMSDK
❷ 码云gitee: https://gitee.com/jackjiang/MobileIMSDK
❸ Gitcode:https://gitcode.com/hellojackjiang2011/MobileIMSDK3、功能情况
2)支持一对一陌生人聊天模式;
3)支持一对一正式好友聊天模式;
4)支持多对多群聊聊天模式;
5)完善的群组信息管理:建群、退群、解散、转让、邀请、踢人、群公告等;
6)完整的注册、登陆(同时支持手机验证码登录和密码登录)、密码找回等功能闭环;
7)个人中心功能:改基本信息、改个性签名、改头像、改密码等;
8)支持个人相册查看;
9)完整的离线消息/指令拉取机制;
10)完整的本地消息/指令缓存机制,节省网络流量;
11)完整的富媒体文件(语音、大文件、图片、短视频)缓存机制,节省网络流量;
12)完整的好友关系管理:查找好友、发出请求、处理请求、删除好友、好友备注等;
13)其它未提及的功能和特性请自行下载体验。
RainbowTalk线上版本目前仅作演示和研究之用,运行环境配置最小化(仅1核1G和1MB带宽),请客观评估。4、技术亮点
2)从通信底层到上层功能,完全自主开发——版权清晰、技术资产可控;
3)超轻量级——纯ArkTS编写且无任何重依赖;
4)通讯核心层基于MobileIMSDK 工程,保证了业务代码与通信核心的高度分层(经验不足的IM产品是做不到这一点的);
5)支持完整的消息送达保证(QoS)机制,保证送达率,理论丢包率约为0.0001%;
6)基于 MobileIMSDK 工程的自有协议,未来的流量压缩对于APP端的节电控制和流量控制、服务端的网络吞吐等都有完全的控制能力;7)完善的网络状况自动检测、断网重连等服务自动治愈能力;
8)核心通信算法和实现均为自主原创(历经10年,并非开源拼凑),保证了技术的持续改进、升级、扩展;
9)聊天协议兼容和互通:实现了与姊妹产品RainbowChat、RainbowChat-Web的完全兼容和消息互通;5、技术原则
3)核心内聚和收敛:得益于长期的提炼和经验积累,网络通信核心层高度封装,开发者无需理解复杂网络算法。
4)纯 ArkTS 实现:纯ArkTS编写,无重量级框架和库依赖(更无Native代码),可干净利落地对接各种既有系统;
5)跨平台运行能力:受益于鸿蒙系统的跨端特性,理论上本应用的客户端可运行于任何支持鸿蒙Next的平台上;
6)架构设计简洁:简单直接,易于学习,能少一个分层则绝不强行炫技;
7)简单地就是最好的:始终贯彻简单直接的互联网产品技术理念。6、主要功能运行截图

(本文内容引用自:http://www.52im.net/thread-4822-1-1.html)
建信纳斯达克 100 指数额度由 50 提升到到 10w 了,不知道能放多久?
看来下运作费率是 1%,不过场外目前就这个额度是最高的了



感觉最近每天一个大版本更新,从 Ivan Zhao 发了他的 Steam, Steel, and Infinite Minds 之后,版本更新简直就是尿崩。
只是一眼就看到是她了,心里还是不舒服!
想买生化 4 重制版,注册了港区账号,发现是要在游戏内购买,手头的 visa 卡和 master 卡都不支持,大家都是怎么解决港区 app store 支付问题的?另外,没用外区的账号买过东西,有没有什么风险或者要注意的?
一般是什么原因
感谢 mambaout 邀请。
不敢独享,与站点内大家共享共创共同成长,不管是学术推理,技术编程,还是生活咨询,可以评论发布,18 点后我会统一处理,第二天发结果出来。
先来一周看看效果,精力有限,不要滥用哦。
ChatGPT 5.2 Pro 是 OpenAI 目前最强大、最智能的模型,专为满足复杂、高要求的专业需求而设计。
广泛适用于以下领域:
工程与开发:用于代码调试、特性请求实施和大型代码库重构。
法律与金融分析:对法律前景进行映射和金融预测,处理复杂的法律文档。
科研与学术:生成科学假设、逐步推理和深度研究的文档。
项目管理与产品开发:协助规划和执行多步骤项目,生成详细的项目报告。
最近,一篇名为《2028年全球智能危机》(THE 2028 GLOBAL INTELLIGENCE CRISIS)的文章在科技圈和金融圈疯狂刷屏。它的影响力有多大?就在前天,文章甚至引发了美股软件和支付板块的恐慌性抛售,连科技巨头IBM的股价都在一天内暴跌了13%! 这篇文章由Citrini Research和AI领域投资者Alap Shah共同撰写,虽然它并不是一个科幻末日故事,但却给出了一个极其现实的“经济沙盘推演”。 文章设定的背景是2028年,回顾过去几年发生的事。它提出了一个让人不寒而栗的观点:如果AI真如我们所期待的那样变得极其强大、极其高效,它可能不会带来全人类的共同富裕,反而会引发一场巨大的经济危机。 为什么会这样?我们用简单的大白话来拆解这篇文章的核心思路: 我们常常认为,生产力提高了,国家GDP增长了,大家的生活自然就会更好。但文章提出了一个新概念:“幽灵GDP”。 举个例子: 假设有一家生产鞋子的工厂,过去需要1000个工人才能运营。现在,老板用AI和机器人替代了所有工人,生产效率是原来的十倍,工厂的利润也翻倍了(GDP上升了)。 为什么公司总是不断裁员?文章描述了一个没有刹车的“恶性循环”: 这就形成了一个可怕的死循环,最终导致全社会的购买力崩塌。 如今,我们很多日常服务都依赖于“中间商”,比如外卖平台(连接你和餐厅)、旅游软件(连接你和酒店)、信用卡支付系统(帮你转账)。他们的盈利方式是,提供便捷的服务,帮你省去与商家直接对接的麻烦。 但在文章中的2028年,AI消除了这些“麻烦”。 举个例子: 未来,你只需要对手机说一句:“帮我点一份最划算的披萨”。你的个人AI助理会自动在全网搜索,直接和披萨店的AI系统谈价格,然后用无需手续费的数字货币完成支付。 如果只是部分人失业,也许还不足以引发全球危机。但文章指出,这次被AI取代的,恰恰是过去社会中最有钱、信用最好的一群人——白领阶层。 想想房贷: 过去,银行最喜欢把贷款发给硅谷的程序员或华尔街的分析师,因为他们收入高、稳定。然而,如果这些人突然失去工作,无法按时还贷怎么办? 看完这篇推演,你可能会感到一阵深深的焦虑。但是,文章的核心目的并非预言世界末日,而是敲响警钟。 有趣的是,文章的作者之一Alap Shah坦言,他的机构实际上已经开始做空一些可能被AI颠覆的公司股票。所以,这篇文章的火爆背后,也有资本市场借“恐慌情绪”做文章的成分。 但它提出的问题确实非常现实:当AI创造了巨大的财富,这些财富该如何分配? 这也是全球正在热议的话题:如果向那些利用AI赚取暴利的公司征收“AI税”,然后用这笔钱为受影响的民众提供补贴(全民基本收入,UBI),是否能够打破这个恶性循环? 面对AI,我们或许不需要过度恐慌,但也绝不能盲目乐观。在技术高速发展的时代,最难的从来不是创造出更聪明的机器,而是如何设计出一套新的社会规则,让每个人都能分享到这场变革的红利。 本文由mdnice多平台发布1. 表面繁荣的“幽灵GDP”
问题在哪里? AI和机器人不需要工资,它们不会去买鞋子、吃火锅、买房子。当更多公司用AI替代人类白领员工时,虽然社会的总产出(GDP)在飞涨,但失去工作的普通人却没有收入来支撑消费。这就成了一种“没有灵魂、没有温度”的数据,资金流动不进普通人手里。2. 停不下来的“裁员死循环”
3. “赚差价”的中间商大溃败
这个过程完全绕过了外卖平台和传统的支付网络,所有中介公司就此倒闭,引发了又一轮失业潮。4. 从失业到“金融海啸”
2008年次贷危机的根源是银行把钱借给了那些本就无法还款的人。而2028年的危机更为可怕:这些高薪白领曾是“优质客户”,他们的收入和信用稳定,但如今AI却把他们从工作岗位上取代了。这种大规模的房贷违约,将会使银行和金融系统遭受毁灭性打击。💡 客观冷思考:我们真的无路可退了吗?
日常使用中,你可能会遇到这些情况:下载的软件提示“请核对 MD5”,或者别人发来一个文件,你想确认传输过程中有没有损坏。为了解决这类问题,我用 Vue(基于 Nuxt 3) 做了一个「MD5在线加密」工具:输入文本或选择文件,就能快速生成 MD5 值,方便你做校验与对比。 需要说明的是:MD5 严格来说是“哈希/摘要”,不是可逆的“加密”。它更适合用来做一致性校验(是否相同),不适合用来保存密码。 选择你要处理的内容类型: 如果你经常需要做下载文件校验、内容一致性对比,这个工具会非常省事。MD5在线加密工具分享
在线工具网址:https://see-tool.com/md5-encryptor
工具截图:
怎么用(普通用户版)
/md5-encryptor常见问题
通常是输入格式选错了(比如把一段 Base64 当作普通文本来算),或者多了空格/换行。可以先切回“文本”输入格式重试。
不会。工具在浏览器本地分片计算 MD5,只要页面不卡死,一般都能算完。
不建议。MD5 已不适合作为密码存储方案;如果你需要更安全的方案,建议用专门的密码哈希算法。
当数据集膨胀到数百万甚至数十亿量级的向量时,怎么让搜索在这种规模下依然又快又准就成了一个实实在在的工程难题。这篇文章要聊的就是向量搜索系统的三个核心优化方向——性能调优、混合搜索和可扩展架构。 传统搜索系统做的事情本质上是词法匹配:找文档里有没有出现查询中的关键词。至于查询背后的意思?它不管。同义词、上下文、用户意图、概念层面的相似性,统统不在考虑范围内。 比如说用户查询 ,文档里写的是 。意思完全一样,但传统搜索直接判定不匹配——因为没有一个词是相同的。 Elasticsearch 这类系统引入了 BM25 排序算法,根据词频和重要性给文档打分。但再怎么优化打分策略,底层逻辑还是关键词重叠。换个说法表达同一个意思,排名可能就掉下去了。传统词法搜索在同义词、改述、深层语义理解面前,始终力不从心。 用一段 Python 代码直观感受一下传统关键词匹配的局限: 输出: 空的。"Affordable vehicle" 和 "cheap car" 语义上几乎等价,但关键词匹配毫无办法。要解决这个问题就得换一种思路——向量搜索。 不再比较字面上的词而是比较含义。把文档和查询都转换成数值向量(Embedding),这些向量编码了语义信息。即使两段文本用词完全不同,只要意思相近,它们的向量在空间中就会彼此靠近。 整个流程拆开来看分四步。 第一步是文档处理,属于离线的索引阶段。所有文档经过一个 Embedding 模型,转换为向量表示,然后存入向量索引或向量数据库。 第二步是查询处理,在线阶段。用户提交查询后,同一个 Embedding 模型把查询也转成向量,发送给向量索引。 第三步是相似性搜索。在向量索引内部,系统拿查询向量和所有文档向量做比较,用余弦相似度之类的距离度量找出最接近的那些向量。 第四步是返回结果。距离最近的文档就是语义上最相关的结果,哪怕它们和查询没有任何共同关键词。 下面动手搭一个小型语义搜索引擎,看看向量搜索在实践中是什么样的。 步骤1:将文本转换为 Embedding 用 Sentence Transformers 的 模型: 输出: 每个句子变成了一个 384 维的向量。 步骤2:引入相似性概念 输出: 这是一个成对余弦相似度矩阵。单元格 (i, j) 表示句子 i 和句子 j 的语义相似程度。对角线上都是 ,因为每个句子和自身当然完全相似。可以看到 "Affordable vehicle for students" 和 "Budget friendly smartphone" 之间的相似度(0.3233)比它和 "Best gaming laptop"(0.1679)更高,这符合直觉——前两者都带有"经济实惠"的语义。 步骤3:创建 FAISS 索引 FAISS 索引在高维空间里做 K 近邻搜索,用 L2 距离作为度量,能快速找到和查询向量最接近的 top-k 个 Embedding。 步骤4:搜索查询 文档 Embedding 已经入库,现在可以做语义搜索了。 输出: 查询被编码为 384 维向量,FAISS 计算它和每个文档向量的 L2 距离,返回距离最小的那个。"cheap car" 成功匹配到了 "Affordable vehicle for students",这里关键词没有任何重叠,但语义搜索抓住了含义。 这个小演示只处理三条文档,跑起来毫无压力。但现实场景动辄百万、十亿级别的 Embedding, 这种暴力全量比较的方式就扛不住了。搜索延迟飙升,内存吃紧,扩展性成问题。接下来要聊的就是怎么优化。 对每个查询都遍历全部向量做比较。数据量小的时候没问题,数据量一上去就是灾难。性能调优围绕三件事展开:压缩搜索时间、降低内存消耗、同时尽量不牺牲检索精度。 前面演示用的是: 这是精确最近邻搜索,查询和每一个向量都比一遍。精度有保证,但面对百万级 Embedding 就跑不动了。 替代方案是近似最近邻(ANN)。ANN 放弃一点点精度换来数量级的速度提升,FAISS 里常用的 ANN 索引比如 IndexIVFFlat 和 IndexHNSWFlat,都是通过减少实际参与比较的向量数量来加速检索。 IVF 的思路是把向量先聚类,查询时只在最相关的几个聚类里搜索,而不是扫描全部数据。对大数据集来说,延迟直接降一个量级。 是聚类数量。聚类越多,精度越高,但计算开销也越大。这里面有个平衡点需要根据实际场景调。 HNSW(Hierarchical Navigable Small World)是另外一个方向:不做聚类而是构建一个图结构。每个向量和它最近的邻居相连,搜索时沿着图的边进行导航。搜索速度快,召回率高,可扩展性也不错,是现代向量数据库中非常主流的索引方式。 数据量大了之后光是把向量存在内存里就是个问题,Product Quantization 对向量做有损压缩,减小内存占用的同时还能加速搜索。百万、十亿级 Embedding 的场景下,这个技术基本是标配。 FAISS 支持 GPU 加速,把向量计算并行化之后延迟大幅降低,适合对实时性要求高的场景,比如推荐系统和大型搜索平台。 实际的搜索系统很少只用关键词匹配或者只用向量相似性,更常见的做法是两者结合。 用户查询进来后,关键词搜索和向量搜索并行执行。关键词搜索产出一个词法相关性分数(比如 BM25),向量搜索产出一个语义相似度分数(比如余弦相似度),两路分数再融合成一个统一的排名分数。 常见的加权融合公式长这样: 这个分数不代表"正确性",只是用来给文档排序。系统按 从高到低排列,排在前面的就是最终结果。 这里有个细节值得注意:归一化。BM25 的分数是无界的,余弦相似度则在 -1 到 1 之间。不做归一化直接加权,某一路的分数可能把另一路完全压过去,排名就失真了。所以实际系统在融合之前一定要先归一化。 小数据集上向量搜索跑得很漂亮。但从几千个向量涨到几百万、几十亿的时候,单机就扛不住了。生产环境下的扩展策略主要三个:Sharding、分布式搜索、缓存。 Sharding 就是把向量分散存储到多台机器上。比如 3000 万个向量,机器 1 存前 1000 万,机器 2 存中间 1000 万,机器 3 存最后 1000 万。 查询来了之后,系统先生成查询 Embedding,然后把它发给所有分片。每个分片各自返回 top-K 结果,最后汇总在一起重新排名。想加容量?加机器就行,这就是水平扩展的好处。 一台服务器扫 1 亿个向量太慢,那就拆成 10 台,每台扫 1000 万,并行跑。工作量分摊了,搜索同步进行,结果最终在协调器节点汇合。这是分布式搜索引擎和现代向量数据库的标准架构。 逻辑很简单: 没有缓存的情况下,每次查询都要走完整流程——生成 Embedding、发请求到所有分片、算相似度、合并结果、返回 top-K。哪怕5秒前刚有人搜过一模一样的东西,全套流程照跑一遍。这就是纯粹的浪费。 有了缓存就不一样了。热门查询再来的时候系统先查缓存: 命中的话直接返回存好的结果,Embedding 生成和分布式搜索全部跳过。 想想 "iPhone 15 price" 或 "Weather today" 这种查询,每天成千上万人在搜。算一次就够了,后面全部复用。缓存同时砍掉了延迟和基础设施成本。 向量搜索把信息检索从字面匹配带进了语义理解的时代。但光有 Embedding 还不够,真正让系统在生产环境中跑起来的是背后的工程优化——混合搜索把词法和语义两条路打通,ANN 和压缩技术解决性能瓶颈,分布式架构和缓存撑起大规模部署。 本文代码: https://avoid.overfit.cn/post/f8461443473745e6bd7ea21a5b43f44c by Pawan传统搜索的问题

cheap caraffordable vehicle documents = [
"Affordable vehicle for students",
"Best gaming laptop",
"Budget friendly smartphone"
]
def traditional_search(query, docs):
results = []
query_words = query.lower().split()
for doc in docs:
doc_lower = doc.lower()
if all(word in doc_lower for word in query_words):
results.append(doc)
return results
# Query
query = "cheap car"
print(traditional_search(query, documents)) []什么是向量搜索

all-MiniLM-L6-v2 from sentence_transformers import SentenceTransformer
documents = [
"Affordable vehicle for students",
"Best gaming laptop",
"Budget friendly smartphone"
]
model = SentenceTransformer("all-MiniLM-L6-v2")
doc_embeddings = model.encode(documents)
print(doc_embeddings.shape) (3, 384) similarities = model.similarity(doc_embeddings, doc_embeddings)
print(similarities) tensor([[1.0000, 0.1679, 0.3233],
[0.1679, 1.0000, 0.2726],
[0.3233, 0.2726, 1.0000]])1.0000 import faiss
import numpy as np
doc_embeddings = np.array(doc_embeddings).astype("float32")
dimension = doc_embeddings.shape[1]
index = faiss.IndexFlatL2(dimension)
index.add(doc_embeddings) query = "cheap car"
query_embedding = model.encode([query]).astype("float32")
distances, indices = index.search(query_embedding, k=1)
print(documents[indices[0][0]]) Affordable vehicle for studentsIndexFlatL2性能调优
IndexFlatL2精确搜索 vs 近似最近邻(ANN)
faiss.IndexFlatL2(dimension)倒排文件索引(IVF)
quantizer=faiss.IndexFlatL2(dimension)
index=faiss.IndexIVFFlat(quantizer, dimension, nlist=100)nlistHNSW(基于图的搜索)
向量压缩(Product Quantization)
硬件加速(GPU 支持)
混合搜索

评分公式(分数融合)
FinalScore(d)=α⋅KeywordScore(d)+(1−α)⋅VectorScore(d)
# 其中,
d = document
𝛼 ∈ [0,1] 控制关键词精确度的重要性FinalScore混合搜索伪代码
# Step 1: Keyword Search
keyword_results = bm25.search(query)
# Step 2: Vector Search
query_embedding = embed(query)
vector_results = vector_db.search(query_embedding)
# Step 3: Merge + Weighted Ranking
def merge_and_rank(keyword_results, vector_results, alpha=0.6):
keyword_dict = {doc.id: doc.score for doc in keyword_results}
vector_dict = {doc.id: doc.score for doc in vector_results}
all_doc_ids = set(keyword_dict.keys()) | set(vector_dict.keys())
# Normalize scores (important in real systems)
def normalize(scores):
if not scores:
return {}
min_s = min(scores.values())
max_s = max(scores.values())
return {
doc: (score - min_s) / (max_s - min_s + 1e-9)
for doc, score in scores.items()
}
keyword_dict = normalize(keyword_dict)
vector_dict = normalize(vector_dict)
final_scores = {}
for doc_id in all_doc_ids:
kw_score = keyword_dict.get(doc_id, 0)
vec_score = vector_dict.get(doc_id, 0)
final_scores[doc_id] = (
alpha * kw_score +
(1 - alpha) * vec_score
)
ranked_docs = sorted(
final_scores.items(),
key=lambda x: x[1]S,
reverse=True
)
return ranked_docs
final_results = merge_and_rank(
keyword_results,
vector_results,
alpha=0.6
)
return final_results扩展向量搜索
Sharding(水平扩展)
分布式搜索(并行查询处理)

缓存(速度优化)

If we have already answered this question before, don't compute again.Have we seen this query before?总结
想调研下大家目前的主力工作流是什么。
整理了一份当前的方案清单,每项附了一句简单的评价,看看有没有漏掉的或者踩坑的:
...等等
你当前主力用哪个?最好带上一句原因~