
SQLite-Vec开源GitHub7.9K Star335 Fork用CPython编写、零依赖SQLite向量搜索插件以vec0虚拟表为核心采用分块存储和暴力KNN搜索策略在10万级向量规模下可实现亚百毫秒延迟。前身是sqlite-vss2K Star73 Fork依赖Facebook的Faiss C库存在问题仅支持Linux和macOS、整个向量索引必须驻留内存、事务语义存在缺陷Faiss每次提交需要完整重写索引、编译极为困难且二进制文件体积达3~5MB。SQLite-Vec纯C实现意味着二进制仅几百KB能编译到Linux、macOS、Windows、WASM浏览器、iOS、Android、树莓派等所有目标平台。事务完全遵循SQLite的ACID语义INSERT/UPDATE/DELETE均支持原子操作和正确回滚。定位嵌入式/本地优先应用、边缘AI、单文件部署、原型开发、数据集在10万到50万向量以内的生产场景。对比SQLite-Vector注意和嵌入式向量数据库Zvec、ObjectBox、SQLite-Vector、SatoriDB里提到的SQLite-Vector是两个不同的项目前者是SQLite-AI官方实现。对比维度sqlite-vecsqlite-vector源码风格单体C文件amalgamation模块化多文件按ISA分离存储方式vec0虚拟表普通表BLOB字段向量类型float32、int8、bitfloat32/16/bf16 int8/uint8/1bit TurboQuantSIMD覆盖AVX内联在主体中AVX2、AVX512、NEON、RVV、SSE2独立文件索引策略默认ANNHNSW/IVF实验性默认精确搜索HNSW可选预索引需要不需要内存极小无额外开销默认30MB目标场景轻量级、嵌入式、浏览器WASM移动端、边缘AI、生产级成熟度pre-v1破坏性变更中生产级稳定架构当执行CREATE VIRTUAL TABLE ... USING vec0(...)时扩展会在SQLite中创建一组影子表shadow tables来管理数据其架构与SQLite内置FTS5全文搜索高度类似。vec0虚拟表对应以下内部表影子表功能{table}_chunks记录每个分块的元数据大小、序列号、分区键值{table}_rowids将用户rowid映射到内部分块位置{table}_vector_chunksNN每个向量列一张存储原始向量数据BLOB{table}_metadata_chunksNN每个元数据列一张存储元数据值支持位图过滤{table}_auxiliary存储辅助列数据非索引仅用于SELECT{table}_info键值对形式的表配置信息向量不是逐行存储的而是被分组到固定大小的分块chunk中。每个分块默认包含8192条向量以连续BLOB形式紧凑排列。两个核心优势KNN查询时逐块从磁盘读取无需将全部数据载入内存分块内的连续布局可改善CPU缓存命中率。每个分块维护一个有效性位图validity bitmap删除操作仅清除位图标记而非立即回收空间。vec0表支持四种列类型SQL示例-- 创建带完整列类型的表CREATEVIRTUALTABLEvec_articlesUSINGvec0(article_idINTEGERPRIMARYKEY,-- 主键headline_embeddingfloat[384],-- 向量列float32/int8/bityearINTEGERPARTITIONKEY,-- 分区键内部分片加速过滤查询categoryTEXT,-- 元数据列可出现在KNN的WHERE中word_countINTEGER,-- 元数据列headlineTEXT,-- 辅助列前缀非索引仅返回urlTEXT-- 辅助列);-- 带元数据过滤的KNN查询SELECTarticle_id,headline,url,distanceFROMvec_articlesWHEREheadline_embeddingMATCH:query_embeddingANDk10ANDyear2025ANDcategoryIN(Technology,Science)ANDword_count500ORDERBYdistance;分区键是性能优化的利器按分区值对分块进行分片KNN查询过滤分区键时可直接跳过不匹配的分块实测可带来3倍以上加速。元数据列支持、!、、、、、IN (...)操作符在KNN搜索过程中通过位图进行高效预过滤。辅助列存储在独立表中适合放置大文本或URL等不参与搜索的数据。检索算法与距离计算穷举暴力brute-forceKNN搜索查询执行流程SQLite查询规划器调用xBestIndex检测到向量列上的MATCH约束选择KNN查询计划xFilter/xNext遍历所有分块如果存在分区键约束跳过不匹配的分块对每个分块读取向量BLOB→应用元数据过滤位图→计算查询向量与每个有效向量的距离维护top-K最小堆最终返回距离最近的K个结果执行向量相似性搜索时遵循特定的处理流程距离度量函数度量适用类型说明vec_distance_L2(a, b)L2欧氏距离float32、int8vec0 KNN默认距离vec_distance_cosine(a, b)余弦距离1-余弦相似度float32、int80相同2完全相反vec_distance_hamming(a, b)汉明距离bitbit向量默认距离距离计算使用硬件加速x86/x64平台上使用AVX/AVX2指令集_mm256_loadu_ps等每次循环处理16个float32元素ARM平台使用NEON指令集。可通过SELECT vec_debug()查看编译时启用哪些SIMD特性。量化与降维当数据集超过10万向量时暴力搜索的延迟会线性增长提供两种核心优化手段。二进制量化Binary Quantization将每个float32维度压缩为1bit正值→1非正值→0实现32倍存储压缩和约10倍查询加速代价是5-10%的召回率损失。某些嵌入模型如mxbai-embed-large-v1、nomic-embed-text-v1.5专门针对二进制量化训练损失更小。-- 将float32向量二进制量化SELECTvec_quantize_binary([0.3, -0.1, 0.8, -0.5, 0.2, -0.9, 0.1, 0.7]);-- 返回XA510100101-- 创建bit向量表CREATEVIRTUALTABLEvec_binaryUSINGvec0(embeddingbit[768]);Matryoshka降维利用支持截断的嵌入模型如OpenAI的text-embedding-3-small将高维向量截断到较低维度-- 将 1536 维截断到 512 维再归一化INSERTINTOvec_short(rowid,embedding)SELECTrowid,vec_normalize(vec_slice(embedding,0,512))FROMvec_full;两种技术可叠加使用先截断到512维再二进制量化到bit[512]实现极致压缩。数据规模推荐策略万向量直接使用float32任意维度查询近乎即时1-10万向量float32分区键用于过滤查询10-50万向量二进制量化或Matryoshka降维配合分区键50-100万向量二进制量化必选评估延迟是否可接受100万向量建议迁移到PGVector或专用向量数据库关键调优手段包括启用WAL模式PRAGMA journal_modeWAL提升写入吞吐设置PRAGMA mmap_size将数据库文件内存映射以获得接近内存的读取速度批量插入务必包裹在单一事务中SQLite每秒仅处理几十次事务提交但可处理数万次插入。陷阱KNN搜索在vec0虚拟表内先于JOIN和外部WHERE条件执行。如果你用JOIN关联外部表并在WHERE中过滤这些过滤不会减少KNN的搜索范围。正确做法是将过滤条件放入vec0表的元数据列中或设置较大k值进行过量检索后在应用层做二次过滤。当前已知限制元数据列不支持LIKE、REGEXP、NULL值频繁删除插入后数据库文件膨胀VACUUM无法完全回收空间继承SQLite的单写者限制。实战提供多语言SDKpipinstallsqlite-vecnpminstallsqlite-vec# Rubygeminstallsqlite-veccargoaddsqlite-vec go get-ugithub.com/asg017/sqlite-vec-go-bindings/cgoPythonPython SDK示例importsqlite3importstructimportsqlite_vecdefserialize_f32(vector:list[float])-bytes:将浮点数列表序列化为紧凑的二进制格式returnstruct.pack(%sf%len(vector),*vector)# 初始化数据库并加载扩展dbsqlite3.connect(:memory:)db.enable_load_extension(True)sqlite_vec.load(db)db.enable_load_extension(False)# 打印版本信息sqlite_version,vec_versiondb.execute(select sqlite_version(), vec_version()).fetchone()print(fsqlite_version{sqlite_version}, vec_version{vec_version})# 创建vec0虚拟表4维float32向量db.execute(CREATE VIRTUAL TABLE vec_items USING vec0(embedding float[4]))# 插入向量数据items[(1,[0.1,0.1,0.1,0.1]),(2,[0.2,0.2,0.2,0.2]),(3,[0.3,0.3,0.3,0.3]),(4,[0.4,0.4,0.4,0.4]),(5,[0.5,0.5,0.5,0.5]),]withdb:forrow_id,vecinitems:db.execute(INSERT INTO vec_items(rowid, embedding) VALUES (?, ?),[row_id,serialize_f32(vec)],)# KNN查询找到最近的3个向量query[0.3,0.3,0.3,0.3]rowsdb.execute( SELECT rowid, distance FROM vec_items WHERE embedding MATCH ? ORDER BY distance LIMIT 3 ,[serialize_f32(query)]).fetchall()forrow_id,distinrows:print(frowid{row_id}, distance{dist})语义搜索importsqlite3,struct,sqlite_vecfromopenaiimportOpenAI clientOpenAI()defserialize_f32(v):returnstruct.pack(%sf%len(v),*v)defget_embedding(text):returnclient.embeddings.create(inputtext,modeltext-embedding-3-small).data[0].embedding# 建库dbsqlite3.connect(semantic.db)db.enable_load_extension(True)sqlite_vec.load(db)db.execute(CREATE TABLE docs (id INTEGER PRIMARY KEY, content TEXT))db.execute(CREATE VIRTUAL TABLE vec_docs USING vec0(embedding float[1536]))# 存入文档documents[广义相对论描述引力是时空弯曲的几何效应,量子力学揭示微观世界的概率本质,深度学习通过多层神经网络学习数据的层次化表示,]fori,docinenumerate(documents):embget_embedding(doc)db.execute(INSERT INTO docs VALUES (?, ?),[i1,doc])db.execute(INSERT INTO vec_docs(rowid, embedding) VALUES (?, ?),[i1,serialize_f32(emb)])db.commit()# 语义搜索query_embget_embedding(什么是引力)resultsdb.execute( SELECT d.content, v.distance FROM vec_docs v JOIN docs d ON d.id v.rowid WHERE v.embedding MATCH ? ORDER BY v.distance LIMIT 3 ,[serialize_f32(query_emb)]).fetchall()forcontent,distanceinresults:print(f[{distance:.4f}]{content})Java以Windows开发环境为例在GitHub Release页面下载稳定版或alpha版本我下载的是sqlite-vec-0.1.10-alpha.4-loadable-windows-x86_64.tar.gz打开Git Bash执行命令tar zxvf sqlite-vec-0.1.10-alpha.4-loadable-windows-x86_64.tar.gz得到vec0.dll文件。引入最新版依赖dependencygroupIdorg.xerial/groupIdartifactIdsqlite-jdbc/artifactIdversion3.53.2.0/versionscopecompile/scope/dependency直接给出demo源码publicclassDemoSQLiteVec{publicstaticvoidmain(String[]args){// SQLite连接字符串建立向量数据库Stringurljdbc:sqlite:.\\sqlite_vec.db;// 创建向量表StringcreateTableSqlcreate virtual table IF NOT EXISTS vec_demo using vec0(sample_embedding float[8]);StringinsertDataSqlinsert into vec_demo(rowId, sample_embedding) values(1, [-0.200, 0.250, 0.341, -0.211, 0.645, 0.935, -0.316, -0.924]),(2, [0.443, -0.501, 0.355, -0.771, 0.707, -0.708, -0.185, 0.362]),(3, [0.716, -0.927, 0.134, 0.052, -0.669, 0.793, -0.634, -0.162]),(4, [-0.710, 0.330, 0.656, 0.041, -0.990, 0.726, 0.385, -0.958]);StringselectSqlselect rowId, distance from vec_demo where sample_embedding match [0.890, 0.544, 0.825, 0.961, 0.358, 0.0196, 0.521, 0.175] order by distance limit 2;// 数据内容表只能通过rowId来进行关联StringcreateTableSql2CREATE TABLE IF NOT EXISTS vec_metadata (rowId INTEGER PRIMARY KEY, describe TEXT, label TEXT);StringinsertDataSql2insert into vec_metadata(rowId, describe, label) values(1,数据描述1,数据标签1),(2,数据描述2,数据标签2),(3,数据描述3,数据标签3),(4,数据描述4,数据标签4);// 数据内容表只能通过rowId来进行关联查询。vec_demo的查询必须是独立子查询否则总是会报错// [SQLITE_ERROR] SQL error or missing database (A LIMIT or k ? constraint is required on vec0 knn queries.)StringselectSql2SELECT ve.rowId, ve.sample_embedding, vm.describe, vm.label FROM (SELECT rowId, sample_embedding, distance FROM vec_demo WHERE sample_embedding MATCH [0.890, 0.544, 0.825, 0.961, 0.358, 0.0196, 0.521, 0.175] ORDER BY distance LIMIT 2) ve JOIN vec_metadata vm ON ve.rowId vm.rowId ;// 创建sqlite配置对象启用加载扩展功能SQLiteConfigconfignewSQLiteConfig();config.enableLoadExtension(true);// 使用配置初始化数据库连接try(ConnectionconnDriverManager.getConnection(url,config.toProperties());Statementstmtconn.createStatement()){// 加载sqlite-vec扩展库load_extension方法只能识别绝对路径stmt.execute(SELECT load_extension(D:\\Program\\vec0.dll));// 建表stmt.execute(createTableSql);// 插入数据stmt.execute(insertDataSql);// 查询数据ResultSetrsstmt.executeQuery(selectSql);// 打印结果while(rs.next()){intidrs.getInt(rowId);floatars.getFloat(distance);System.out.println(Row ID: id distance: a);}// 创建关联信息表、插入数据并查询stmt.execute(createTableSql2);stmt.execute(insertDataSql2);ResultSetrs2stmt.executeQuery(selectSql2);while(rs2.next()){intidrs2.getInt(rowId);Stringdescribers2.getString(describe);Stringlabelrs2.getString(label);System.out.println(Row ID: id describe: describe label: label);}}catch(Exceptione){log.error(error,e);}}}解读使用sqlite-vec最少需创建2张表。1张虚拟向量表只存储rowid和向量信息。另一张表存储rowid和文本信息或其它标签信息。检索时需要进行双表关联检索才能得到想要的信息。双表关联的语法要求查询虚拟向量表时必须是单表查询且必须指定limit记录数。拓展项目作者还提供两个用于生成文本嵌入Text Embedding的项目配合sqlite-vec使用sqlite-rembed基于远程APIOpenAI、Nomic、Ollama等生成文本嵌入sqlite-lembed基于.gguf格式的本地嵌入模型生成文本嵌入。