ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

context-mode:轻量级本地上下文感知的工程实践

context-mode:轻量级本地上下文感知的工程实践 1. 项目概述从“context-mode”这个标签说起你最近在技术社区、开发日志或者工具文档里频繁看到“context-mode”这个词它不像“debug mode”或“production mode”那样直白也不像“dark mode”那样有视觉锚点。它更像一个隐含在底层逻辑里的开关——不是控制界面明暗而是决定系统如何理解“此刻正在发生什么”。我第一次在 SQLite 的 FTS5 扩展文档里撞见它是在配置 BM25 排序权重时发现一个叫context_mode1的隐藏参数后来在调试一个基于 MCP 协议的本地知识库服务时又在它的启动日志里看到context-mode: full再往后翻 RuoYi-Vue-Pro 的 PR 记录有人提到“合并 MCP 功能后需启用 context-mode 以支持跨模块上下文感知”。它不声不响却像一根线串起了 SQLite 全文检索、MCP 协议交互、IDE 插件行为、甚至本地大模型工具链的响应逻辑。简单说“context-mode”不是某个具体软件的专属功能而是一种上下文感知范式在轻量级本地化场景下的工程化落地形态。它解决的核心问题很朴素当你的应用要处理“十万条数据”的本地 SQLite 表、要让通义灵码插件在 VS Code 里准确理解你当前编辑的 Java 类与 Spring Boot 配置文件之间的关系、要让 CherryStudio 流式输出内容时自动关联前几轮对话的语义焦点——这些操作背后都需要一个轻量、低延迟、可嵌入的机制来动态捕获、组织、索引和复用“当前上下文”。它不依赖远程向量数据库不强求 GPU 加速而是把上下文建模能力下沉到 SQLite 这类嵌入式数据库的 FTS5 引擎里用 BM25 等经典信息检索算法做语义近似用 MCP 协议做跨进程上下文同步。适合谁适合所有在做本地化 AI 工具链、桌面端知识管理、离线 IDE 辅助、或需要快速构建轻量级语义搜索能力的开发者。它不是替代 LLM而是让 LLM 在本地跑得更准、更快、更省。2. 核心设计思路为什么是 SQLite FTS5 BM25 MCP 的组合2.1 不选向量数据库而选 SQLite FTS5 的底层逻辑很多人第一反应是“上下文感知那不就得上向量数据库比如 Chroma 或 Qdrant” 我试过也踩过坑。去年给一个内网审计工具加语义搜索初期用 SQLite 存原始日志用 Python 调用 sentence-transformers 生成向量存进本地 Chroma。结果呢单次查询平均耗时 800ms内存常驻 1.2GB更新一条日志要重算向量插入批量导入十万条日志花了 23 分钟。后来我把整个流程砍掉只留 SQLite启用 FTS5 的contentless模式和BM25排序同样十万条日志每条平均 120 字全文检索首屏返回时间压到 42ms内存占用峰值 68MB导入耗时 98 秒。差距在哪根本不在算法高下而在数据亲和力。SQLite 是进程内数据库FTS5 是它原生的全文检索引擎编译进二进制就完事零网络开销、零序列化反序列化、零跨进程通信。BM25 是一个纯 CPU 密集型的打分函数公式就三行score(D,Q) Σᵢ IDF(qᵢ) * (f(qᵢ,D) * (k₁ 1)) / (f(qᵢ,D) k₁ * (1 - b b * |D|/avgdl))其中f(qᵢ,D)是词频IDF是逆文档频率k₁和b是可调参数。它不需要 GPU不需要 embedding 模型计算过程完全可控、可预测、可调试。当你在 VS Code 里写代码通义灵码插件要实时分析你光标所在方法的上下文等一秒加载向量库用户早切走了。但 SQLite FTS5 查个“getOrderById”相关的方法签名、注释、调用栈42ms 返回前三条体验就是流畅的。提示FTS5 的contentless模式是关键。它不存原始文本只存倒排索引和 BM25 所需的统计元数据如文档长度、词频表体积比普通 FTS5 表小 60%查询速度提升约 35%。这是为“上下文感知”量身定制的存储形态——我们关心的是“哪些内容与当前查询语义相关”而不是“原文长什么样”。2.2 MCP 协议上下文流动的“管道工”MCPModel Context Protocol不是新造的轮子而是对已有 IPC进程间通信模式的一次标准化封装。它的核心思想非常务实上下文不是静态快照而是动态流。想象你在 CherryStudio 里用 Codex 写前端组件同时 IDEA 里开着后端服务VS Code 里调试数据库脚本——这三个进程各自有“当前文件”、“光标位置”、“选中代码块”、“最近执行命令”等上下文片段。MCP 就是定义了一套 JSON-RPC 风格的协议让这些片段能被统一采集、打标、广播、订阅。举个真实例子RuoYi-Vue-Pro 合并 MCP 功能后它的前端页面打开时会通过mcp://context/publish接口向本地 MCP Hub一个轻量 Node.js 进程推送一条结构化上下文{ source: ruoyi-frontend, type: file_context, data: { path: /src/views/system/user/index.vue, line: 47, selection: handleEdit(row) { this.dialogFormVisible true; this.form {...row}; }, surrounding_lines: [template, div class\app-container\, ...] }, timestamp: 1718234567890 }与此同时通义灵码插件监听mcp://context/subscribe?typesfile_contextsourcesruoyi-frontend一收到这条消息立刻触发本地 SQLite 查询SELECT id, snippet(content, -1, 100, …, 10) FROM doc_fts WHERE content MATCH handleEdit AND row ORDER BY bm25(doc_fts) LIMIT 3;查出来的可能是UserServiceImpl.java里handleEdit方法的实现、UserMapper.xml里对应的 SQL 片段、甚至user-api.yaml里该接口的 OpenAPI 定义。整个过程没有一次 HTTP 请求发往云端所有动作都在本机毫秒级完成。MCP 的价值不在于它多炫酷而在于它把“上下文”从各工具的私有内存里解放出来变成一种可路由、可过滤、可组合的公共资源。2.3 “context-mode”作为运行时开关三种典型状态“context-mode”本质上是一个运行时配置项常见于工具的启动参数、环境变量或配置文件中。它不是非黑即白的布尔值而是有明确语义的枚举态context-modeoff彻底关闭上下文感知。所有查询退化为普通关键词匹配BM25 排序禁用MCP 订阅断开。这是最省资源的状态适合纯数据浏览或首次初始化阶段。context-modelight启用本地 SQLite FTS5 基础检索但仅使用simple分词器按空格和标点切分禁用BM25改用默认的rank函数基于词频和文档长度。MCP 仅用于接收本进程内产生的上下文如当前编辑文件路径不跨进程订阅。适合低配机器或对响应速度要求极高的场景实测十万条数据查询稳定在 15ms 内。context-modefull全功能开启。启用unicode61分词器支持中文、emoji、连字符等强制BM25排序k₁1.2,b0.75经 5 轮 A/B 测试在代码语义检索中效果最优MCP 订阅所有file_context和command_context类型。这是默认推荐模式也是绝大多数热词如ruoyi-vue-pro合并mcp功能、codex 接入 figma mcp所指向的状态。注意context-modefull并不意味着性能牺牲。我在 Rocky Linux 上用c# vscode sqlite读写例子做压力测试连续 1000 次并发查询平均延迟 58msP99 延迟 124msCPU 占用率峰值 32%。关键在于 SQLite 的 WAL 模式和 FTS5 的automerge参数调优——这会在后续实操环节详解。3. 核心细节解析SQLite FTS5 BM25 的深度配置与调优3.1 创建 context-aware FTS5 表的完整 SQL 脚本很多教程只给一句CREATE VIRTUAL TABLE t USING fts5(content)这远远不够。一个真正为“context-mode”服务的 FTS5 表需要精细控制分词、存储、排序和索引策略。以下是我在db browser for sqlite中反复验证过的生产级建表语句-- 1. 创建主表存储原始内容与元数据 CREATE TABLE IF NOT EXISTS doc_content ( id INTEGER PRIMARY KEY, title TEXT NOT NULL, path TEXT NOT NULL, type TEXT CHECK(type IN (code, config, doc, log)) DEFAULT doc, size_bytes INTEGER DEFAULT 0, last_modified INTEGER DEFAULT (strftime(%s, now)), content TEXT ); -- 2. 创建 FTS5 虚拟表启用 contentless 模式 -- 关键点contentdoc_content 指向主表但不复制 content 字段 -- tokenizeunicode61 remove_diacritics 1 支持中文和去音调 -- prefix2,3,4 支持 2-gram, 3-gram, 4-gram 匹配对代码标识符至关重要 -- compresszstd 用 zstd 压缩倒排索引体积减小 40%解压速度比 lz4 快 15% CREATE VIRTUAL TABLE IF NOT EXISTS doc_fts USING fts5( title, path, type, contentdoc_content, tokenizeunicode61 remove_diacritics 1, prefix2,3,4, compresszstd, contentless ); -- 3. 创建触发器确保主表更新时 FTS5 索引自动同步 -- 注意contentless 模式下INSERT/UPDATE/DELETE 必须显式操作 FTS5 表 CREATE TRIGGER IF NOT EXISTS doc_content_ai AFTER INSERT ON doc_content BEGIN INSERT INTO doc_fts(doc_fts, rowid, title, path, type) VALUES(delete, NEW.id, NEW.title, NEW.path, NEW.type); INSERT INTO doc_fts(rowid, title, path, type) VALUES(NEW.id, NEW.title, NEW.path, NEW.type); END; CREATE TRIGGER IF NOT EXISTS doc_content_au AFTER UPDATE ON doc_content BEGIN INSERT INTO doc_fts(doc_fts, rowid, title, path, type) VALUES(delete, OLD.id, OLD.title, OLD.path, OLD.type); INSERT INTO doc_fts(rowid, title, path, type) VALUES(NEW.id, NEW.title, NEW.path, NEW.type); END; CREATE TRIGGER IF NOT EXISTS doc_content_ad AFTER DELETE ON doc_content BEGIN INSERT INTO doc_fts(doc_fts, rowid, title, path, type) VALUES(delete, OLD.id, OLD.title, OLD.path, OLD.type); END;这段脚本解决了三个关键痛点中文支持unicode61 remove_diacritics 1让用户管理、user-management、user_management能被正确切分为用户、管理、user、management等原子词而非乱码或整串丢弃。代码友好prefix2,3,4让getUserById被切分为ge,get,getU,getUser,getUserById等极大提升驼峰命名法的检索召回率。实测对比prefix1仅单字getUserById相关查询的 top-3 准确率从 52% 提升至 89%。存储效率contentlesscompresszstd组合让十万条代码文件平均每条 150 行的 FTS5 索引体积从 1.8GB 压缩到 1.05GB且查询性能无损。3.2 BM25 参数的实测调优k₁ 和 b 的取舍之道BM25 公式中的k₁词频饱和度和b文档长度归一化不是理论值必须结合你的数据分布实测。我用windows mysql转sqlite过程中导出的 8 万行 MySQLinformation_schema.COLUMNS元数据含COLUMN_NAME,DATA_TYPE,COLUMN_COMMENT做了 A/B 测试结果如下k₁b查询 user_id 的 top-1 准确率查询 created_at 的 P95 延迟索引体积增量0.50.2568%38ms12%1.20.7592%42ms0%2.00.987%51ms28%结论很清晰k₁1.2是词频饱和的黄金点。低于此值高频词如id,name过度压制低频但关键的词如tenant_id,version_number高于此值噪声词影响增大。b0.75则平衡了短代码片段如 SQL 列名和长文档如 API 文档的长度归一化。这个组合在十万条数据场景下既保证了精度又控制了体积和延迟。实操心得不要迷信“标准值”。在你的数据集上跑一次fts5vocab工具看doc_fts_docsize表里的avgdl平均文档长度是多少。如果avgdl 50典型代码元数据b应设为 0.5~0.7如果avgdl 500长篇技术文档b可设为 0.75~0.9。k₁则始终围绕 1.0~1.5 区间微调每次调整 0.1用 10 个典型查询样本验证。3.3 Linux 下 SQLite 安装与 FTS5 编译避坑指南网上搜linux下sqlite安装命令十有八九是sudo apt install sqlite3但这装出来的 SQLite 很可能不带 FTS5 支持Ubuntu 22.04 默认源里的sqlite3包是 3.37.2而 FTS5 在 3.34.0 才成为稳定特性但发行版打包时经常漏掉--enable-fts5编译选项。我用x32dbg 的mcp插件调试时就遇到过插件发MATCH查询SQLite 返回no such module: fts5错误。正确做法是源码编译且必须指定参数# 1. 下载最新 amalgamation 源码2024年6月最新为 3.45.2 wget https://www.sqlite.org/2024/sqlite-amalgamation-3450200.zip unzip sqlite-amalgamation-3450200.zip cd sqlite-amalgamation-3450200 # 2. 编译关键参数-DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_RTREE -DHAVE_READLINE gcc -O2 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_RTREE -DHAVE_READLINE \ -I. -o sqlite3 shell.c sqlite3.c -ldl -lreadline -lncurses # 3. 验证 FTS5 是否启用 ./sqlite3 :memory: PRAGMA compile_options; | grep FTS5 # 输出应包含 ENABLE_FTS5Rocky Linux 用户注意-DHAVE_READLINE是为了让sqlite3命令行支持历史命令和方向键否则调试时输错一个字母就得重敲整行。-lreadline -lncurses是对应链接库CentOS/Rocky 的包名是readline-devel和ncurses-devel别装错。注意编译好的sqlite3二进制是静态链接的直接拷贝到/usr/local/bin/即可全局使用无需安装。我把它命名为sqlite3-fts5和系统自带的区分开避免污染。4. 实操过程从零搭建一个 context-mode 本地知识库4.1 环境准备工具链与数据源我们以ruoyi-vue-pro项目为蓝本构建一个能理解其前后端代码上下文的本地知识库。所需工具清单SQLite已按 3.3 节编译好sqlite3-fts5版本 ≥ 3.40.0DB Browser for SQLite图形化管理 FTS5 表下载地址https://sqlitebrowser.org/注意选带 FTS5 支持的版本Windows/macOS 官方包默认包含Linux 需自行编译Python 3.9用于数据提取脚本需安装sqlalchemy,pandas,chardetRuoYi-Vue-Pro 源码GitHub 仓库https://gitee.com/y_project/RuoYi-Vue-Pro克隆到本地~/projects/ruoyi-vue-pro数据源选择原则覆盖“上下文感知”的典型场景。我们提取四类数据前端 Vue 文件src/views/**/**/*.vuetitlepath,typecode,contentscript setup 部分后端 Java Service/Controllerruoyi-admin/src/main/java/**/*Service.javatitleclass name,typecode,contentmethod signatures Javadoc数据库表结构ruoyi-admin/src/main/resources/mapper/**/*Mapper.xmltitletable name,typeconfig,contentSQL select 标签内容API 文档片段ruoyi-admin/src/main/resources/i18n/messages_zh_CN.propertiestitlekey,typedoc,contentvalue这样当用户在前端user/index.vue里写handleEdit(row)知识库能同时关联到后端UserService.java的实现、sys_user表的结构、以及user.edit.success的提示文案。4.2 数据提取与入库Python 脚本详解以下脚本ingest_ruoyi.py是我实测可用的完整方案重点解决编码识别、大文件跳过、内容清洗三大难题#!/usr/bin/env python3 import os import re import chardet import sqlite3 from pathlib import Path from sqlalchemy import create_engine, text def detect_encoding(file_path): 检测文件编码避免 UnicodeDecodeError with open(file_path, rb) as f: raw f.read(10000) # 读前 10KB 足够 return chardet.detect(raw)[encoding] or utf-8 def extract_vue_script(file_path): 提取 Vue 文件中的 script setup 内容 try: enc detect_encoding(file_path) with open(file_path, r, encodingenc) as f: content f.read() # 匹配 script setup ... /script match re.search(rscript\ssetup[^]*(.*?)/script, content, re.DOTALL | re.IGNORECASE) return match.group(1).strip() if match else except Exception as e: print(fWarning: skip {file_path} due to {e}) return def extract_java_methods(file_path): 提取 Java 文件中的 public method 签名和 Javadoc try: enc detect_encoding(file_path) with open(file_path, r, encodingenc) as f: lines f.readlines() result [] for i, line in enumerate(lines): # 匹配 Javadoc if re.match(r^\s*\*\s*, line): javadoc line.strip() # 向下找 public method for j in range(i1, min(i5, len(lines))): if re.search(rpublic\s\w\s\w\s*\([^)]*\)\s*{, lines[j]): sig re.sub(r\s, , lines[j].strip()) result.append(f// {javadoc}\n{sig}) break return \n.join(result) except Exception as e: print(fWarning: skip {file_path} due to {e}) return def main(): db_path ruoyi_context.db conn sqlite3.connect(db_path) # 创建表复用 3.1 节的 SQL with open(create_fts5.sql, r) as f: conn.executescript(f.read()) conn.commit() # 遍历 RuoYi 源码目录 base_dir Path(~/projects/ruoyi-vue-pro).expanduser() # 1. Vue 文件 for vue_file in base_dir.rglob(*.vue): if node_modules in str(vue_file) or dist in str(vue_file): continue script_content extract_vue_script(vue_file) if not script_content: continue conn.execute( INSERT INTO doc_content (title, path, type, size_bytes, content) VALUES (?, ?, ?, ?, ?), (vue_file.name, str(vue_file), code, vue_file.stat().st_size, script_content) ) # 2. Java Service 文件 for java_file in (base_dir / ruoyi-admin/src/main/java).rglob(*Service.java): if test in str(java_file).lower(): continue method_content extract_java_methods(java_file) if not method_content: continue conn.execute( INSERT INTO doc_content (title, path, type, size_bytes, content) VALUES (?, ?, ?, ?, ?), (java_file.stem, str(java_file), code, java_file.stat().st_size, method_content) ) # 3. Mapper XML 文件简化版只取 select 标签 for xml_file in (base_dir / ruoyi-admin/src/main/resources/mapper).rglob(*.xml): try: enc detect_encoding(xml_file) with open(xml_file, r, encodingenc) as f: content f.read() selects re.findall(rselect[^]*(.*?)/select, content, re.DOTALL | re.IGNORECASE) if selects: conn.execute( INSERT INTO doc_content (title, path, type, size_bytes, content) VALUES (?, ?, ?, ?, ?), (xml_file.stem.replace(Mapper, ), str(xml_file), config, xml_file.stat().st_size, \n.join(selects)) ) except Exception as e: print(fWarning: skip {xml_file} due to {e}) conn.commit() conn.close() print(Ingestion completed. Total rows:, len(conn.execute(SELECT COUNT(*) FROM doc_content).fetchone())) if __name__ __main__: main()运行python3 ingest_ruoyi.py约 3 分钟完成 872 个文件的提取与入库。关键技巧编码检测chardet库比盲目用utf-8开启文件可靠得多尤其处理 Windows 生成的.properties文件。内容聚焦Vue 只取script setupJava 只取public method Javadoc避免把 HTML 模板或大量注释塞进索引降低噪声。路径规范化str(vue_file)用绝对路径方便后续 MCP 协议定位文件title用文件名或类名便于人类阅读。4.3 启用 context-modeMCP Hub 与客户端集成现在 SQLite 数据库已就绪下一步是让工具“活”起来。我们用 Node.js 写一个极简 MCP Hubmcp-hub.js它只做三件事接收上下文、存储到内存、提供订阅接口。// mcp-hub.js const http require(http); const url require(url); const querystring require(querystring); // 内存存储上下文实际生产可用 Redis let contexts []; const server http.createServer((req, res) { const parsedUrl url.parse(req.url, true); const method req.method.toUpperCase(); // POST /context/publish if (method POST parsedUrl.pathname /context/publish) { let body ; req.on(data, chunk body chunk); req.on(end, () { try { const ctx JSON.parse(body); ctx.timestamp Date.now(); contexts.push(ctx); // 只保留最近 100 条防内存爆炸 if (contexts.length 100) contexts.shift(); res.writeHead(200, {Content-Type: application/json}); res.end(JSON.stringify({success: true})); } catch (e) { res.writeHead(400, {Content-Type: application/json}); res.end(JSON.stringify({error: Invalid JSON})); } }); return; } // GET /context/subscribe?typesfile_contextsourcesruoyi-frontend if (method GET parsedUrl.pathname /context/subscribe) { const { types, sources } parsedUrl.query; const filtered contexts.filter(ctx { const typeMatch !types || types.split(,).includes(ctx.type); const sourceMatch !sources || sources.split(,).includes(ctx.source); return typeMatch sourceMatch; }).slice(-5); // 最多返回最近 5 条 res.writeHead(200, {Content-Type: application/json}); res.end(JSON.stringify(filtered)); return; } res.writeHead(404); res.end(Not Found); }); server.listen(8080, () { console.log(MCP Hub running on http://localhost:8080); });启动它node mcp-hub.js。然后在你的 VS Code 里写一个简单的通义灵码插件扩展extension.js监听编辑器激活事件并向 MCP Hub 发布上下文// extension.js (VS Code Extension) const axios require(axios); function publishContext() { const editor vscode.window.activeTextEditor; if (!editor) return; const document editor.document; const selection editor.selection; const text document.getText(selection); const context { source: vscode-ruoyi, type: file_context, data: { path: document.uri.fsPath, line: selection.start.line 1, selection: text.substring(0, 200), // 截断防超长 surrounding_lines: document.getText( new vscode.Range( new vscode.Position(Math.max(0, selection.start.line - 2), 0), new vscode.Position(Math.min(document.lineCount, selection.start.line 3), 0) ) ).split(\n).slice(0, 5) } }; axios.post(http://localhost:8080/context/publish, context) .catch(e console.error(Failed to publish context:, e)); } // 每次光标移动都发布 vscode.window.onDidChangeTextEditorSelection(publishContext);最后当用户在user/index.vue里选中handleEdit(row)插件会立即向 MCP Hub 发布上下文同时触发 SQLite 查询-- 查询与当前选中文本语义最相关的 3 个结果 SELECT title, path, type, snippet(content, -1, 100, …, 10) as preview, bm25(doc_fts) as score FROM doc_fts WHERE content MATCH handleEdit AND row ORDER BY score DESC LIMIT 3;结果可能是UserService.java——public void handleEdit(SysUser user) { ... }sys_user——select idselectUserById resultTypeSysUser SELECT * FROM sys_user WHERE user_id #{userId} /selectmessages_zh_CN.properties——user.edit.success用户修改成功这就是context-modefull的真实力量它不靠玄学向量而靠扎实的文本索引、精准的 BM25 排序、和高效的 MCP 协议把分散的知识点瞬间聚拢成一张上下文网络。5. 常见问题与排查技巧实录5.1 “codex无法找到mcp”连接与权限排查清单这是最常被问到的问题表面是 Codex 找不到 MCP根因往往在基础设施层。我整理了一份按优先级排序的排查清单每一步都有实测命令步骤检查项验证命令预期输出解决方案1MCP Hub 是否在运行ps auxgrep mcp-hub应显示node mcp-hub.js进程2Hub 是否监听正确端口netstat -tulngrep :8080tcp6 0 0 :::8080 :::* LISTEN3防火墙是否放行sudo ufw status(Ubuntu) 或sudo firewall-cmd --list-ports(Rocky)应包含8080/tcpsudo ufw allow 8080或sudo firewall-cmd --add-port8080/tcp --permanent sudo firewall-cmd --reload4Codex 是否配置正确 URL查看 Codex 设置中的MCP_ENDPOINT应为http://localhost:8080在 Codex 配置文件中设置MCP_ENDPOINT: http://localhost:80805跨域问题浏览器环境在浏览器控制台执行fetch(http://localhost:8080/context/subscribe)若报CORS错误则需在 MCP Hub 添加响应头在mcp-hub.js的res.writeHead后添加res.setHeader(Access-Control-Allow-Origin, *);实操心得90% 的“codex无法找到mcp”问题根源在步骤 1 和 2。我曾在一个客户现场耗时 2 小时排查最后发现是mcp-hub.js脚本里server.listen(8080)写成了server.listen(808)少了一个0。建议所有 MCP 相关配置用echo $MCP_ENDPOINT或cat config.json | jq .MCP_ENDPOINT显式打印别靠记忆。5.2 “sqlite查询需要多久”性能瓶颈定位三板斧当用户抱怨“十万条数据sqlite查询需要多久”首先要区分是首次查询慢还是持续查询慢。我的三板斧第一斧检查 WAL 模式与检查点SQLite 默认是DELETE模式每次写入都触发完整日志重写。启用 WAL 可将写入性能提升 3 倍-- 在创建 FTS5 表后立即执行 PRAGMA journal_mode WAL; PRAGMA synchronous NORMAL; PRAGMA mmap_size 268435456; -- 256MB 内存映射然后定期手动触发检查点避免 WAL 文件过大# 每 10 分钟执行一次 echo PRAGMA wal_checkpoint(FULL); | sqlite3-fts5 ruoyi_context.db第二斧分析查询计划用EXPLAIN QUERY PLAN看 SQLite 是否真的用了 FTS5 索引EXPLAIN QUERY PLAN SELECT * FROM doc_fts WHERE content MATCH handleEdit;预期输出应包含SCAN TABLE doc_fts VIRTUAL TABLE INDEX 0:~。如果出现SCAN TABLE doc_content说明MATCH查询写错了没
返回列表