better-sqlite3虚拟表不支持异步生成器函数的替代方案咨询
解决better-sqlite3虚拟表不支持异步生成器的替代方案
better-sqlite3的虚拟表rows属性仅支持同步生成器,无法直接在其中执行异步操作(如文本分割、LLM嵌入生成)。以下是两种可行的替代方案:
方案1:同步触发器+异步任务队列
通过触发器将待处理数据写入队列表,再用独立的异步进程消费队列完成异步操作,避免阻塞SQLite的同步逻辑。
步骤1:创建相关数据表和触发器
-- 待处理队列表:存储需要异步处理的文本 CREATE TABLE IF NOT EXISTS PendingText ( id INTEGER PRIMARY KEY AUTOINCREMENT, cleaned_text TEXT NOT NULL, processed BOOLEAN DEFAULT 0 -- 0=未处理,1=已处理,-1=处理失败 ); -- 目标存储表:最终保存分割后的文本块和嵌入向量 CREATE TABLE IF NOT EXISTS SplitText ( chunk TEXT NOT NULL, embedding BLOB NOT NULL -- 用BLOB存储嵌入向量的Buffer ); -- 假设主表为Documents,插入后触发写入待处理队列 CREATE TRIGGER IF NOT EXISTS after_document_insert AFTER INSERT ON Documents FOR EACH ROW BEGIN INSERT INTO PendingText (cleaned_text) VALUES (NEW.cleaned_text); END;
步骤2:实现异步队列消费逻辑
async function processPendingQueue(db) { while (true) { // 取出一条未处理的记录(用FOR UPDATE SKIP LOCKED避免并发冲突) const getPendingStmt = db.prepare(` SELECT id, cleaned_text FROM PendingText WHERE processed = 0 LIMIT 1 FOR UPDATE SKIP LOCKED `); const pendingTask = getPendingStmt.get(); if (!pendingTask) { // 无待处理任务时,短暂休眠后再轮询 await new Promise(resolve => setTimeout(resolve, 1000)); continue; } try { // 执行异步文本分割和嵌入生成 const chunks = await splitter.splitText(pendingTask.cleaned_text); const res = await ollama.embed({ model: 'nomic-embed-text', input: chunks }); const embeddings = res.embeddings; // 批量插入结果到目标表(用事务保证原子性) const insertStmt = db.prepare('INSERT INTO SplitText (chunk, embedding) VALUES (?, ?)'); db.transaction(() => { chunks.forEach((chunk, index) => { // 将嵌入向量转为Buffer存储 insertStmt.run(chunk, Buffer.from(JSON.stringify(embeddings[index]))); }); })(); // 标记任务为已处理 db.prepare('UPDATE PendingText SET processed = 1 WHERE id = ?').run(pendingTask.id); } catch (err) { console.error(`处理任务ID ${pendingTask.id} 失败:`, err); // 标记任务为处理失败,可后续添加重试逻辑 db.prepare('UPDATE PendingText SET processed = -1 WHERE id = ?').run(pendingTask.id); } } } // 启动异步队列处理器 processPendingQueue(db);
方案2:手动触发异步处理逻辑
放弃触发器和虚拟表,在插入主表数据后直接调用异步处理函数,适用于不需要自动触发、可控制处理时机的场景。
// 插入主表并触发异步处理的函数 async function addDocumentWithProcessing(db, cleanedText) { // 同步插入主表数据 db.prepare('INSERT INTO Documents (cleaned_text) VALUES (?)').run(cleanedText); // 立即执行异步文本分割和嵌入生成 await processTextToEmbeddings(db, cleanedText); } async function processTextToEmbeddings(db, cleanedText) { const chunks = await splitter.splitText(cleanedText); const res = await ollama.embed({ model: 'nomic-embed-text', input: chunks }); const embeddings = res.embeddings; // 批量插入结果到SplitText表 const insertStmt = db.prepare('INSERT INTO SplitText (chunk, embedding) VALUES (?, ?)'); db.transaction(() => { chunks.forEach((chunk, index) => { insertStmt.run(chunk, Buffer.from(JSON.stringify(embeddings[index]))); }); })(); }
核心原因说明
better-sqlite3的虚拟表基于SQLite的同步虚拟表机制,SQLite本身不支持异步操作,因此rows属性仅接受同步生成器,无法在其中执行await异步调用。上述方案通过将异步逻辑剥离到SQLite同步流程之外,完美绕过了这个限制。
内容的提问来源于stack exchange,提问作者punkish
相关产品推荐
相关产品推荐

