×

SQLite 只是玩具数据库?这个 .NET 开发者最熟悉的陌生人,你真的用对了吗

独孤求败 独孤求败 发表于2026-05-07 14:41:14 浏览31 评论0

抢沙发发表评论

SQLite 是一个嵌入式关系型数据库引擎。
它不需要独立服务进程,不需要安装配置,甚至整个数据库就是一个文件。

它由 C 语言编写,源码完全开放(Public Domain),具备轻量、可靠、零运维等特点。

从 iPhone 上的聊天记录、浏览器缓存,到大量 IoT 设备和移动应用,本地数据存储背后几乎都能看到 SQLite 的身影。

但在 .NET 圈子里,SQLite 的评价却始终两极分化。

做单元测试时,很多人喜欢用 SQLite 内存库替代 SQL Server:

  • 不用搭环境
  • 启动极快
  • 跑测试方便

可一旦到了生产环境,尤其是 Web API、高并发写入、持久化场景,大家的第一反应往往是:

“SQLite 扛不住吧?”
“那不就是个玩具数据库?”

问题是——

你真的了解 SQLite 吗?

很多开发者对 SQLite 的认知,还停留在“本地缓存”“单机 Demo”“测试替身”。

但事实上:

  • SQLite 支持事务
  • 支持 WAL
  • 支持索引、触发器、视图
  • 支持 JSON、窗口函数
  • 甚至已经被用于大量生产级应用

它不是“简化版 MySQL”,而是一个真正完整的关系型数据库。

今天这篇文章,就从 .NET 开发者视角,深入聊透 SQLite:

  • NuGet 包到底该怎么选
  • WAL 模式为什么能提升并发
  • 什么场景适合它,什么场景必须换库

看完后,你对 SQLite 的印象,大概率会彻底改观。

🔗 官网:


https://www.sqlite.org
图片

⚡ 三步跑起来:安装 → 连接 → CRUD

SQLite 在 .NET 里没有内置支持,必须通过第三方 ADO.NET 提供程序。当前唯一稳妥的选择是 Microsoft.Data.Sqlite——微软官方维护,内嵌跨平台 sqlite3 二进制(通过 SQLitePCLRaw),无需手动部署 DLL。

至于 System.Data.SQLite?它已经多年未维护,在 .NET 6+ 项目中极容易因运行时标识不匹配报 DllNotFoundException 或 BadImageFormatException。除非你还在维护 .NET Framework 4.6.1 以下的老项目,否则新项目一律用 Microsoft.Data.Sqlite。


dotnet add package Microsoft.Data.Sqlite
图片

最简示例——创建数据库、建表、插入数据:




























using Microsoft.Data.Sqlite;
// 1. 打开连接(文件不存在会自动创建)using var conn = new SqliteConnection("Data Source=app.db");conn.Open();
// 2. 建表using var cmd = conn.CreateCommand();cmd.CommandText = @"CREATE TABLE IF NOT EXISTS Users (    Id INTEGER PRIMARY KEY AUTOINCREMENT,    Name TEXT NOT NULL,    Age INTEGER CHECK(Age >= 0),    CreatedAt TEXT DEFAULT (datetime('now')))";cmd.ExecuteNonQuery();
// 3. 插入数据(参数化查询,防止 SQL 注入)cmd.CommandText = "INSERT INTO Users (Name, Age) VALUES (@name, @age)";cmd.Parameters.Clear();cmd.Parameters.AddWithValue("@name", "张三");cmd.Parameters.AddWithValue("@age", 28);cmd.ExecuteNonQuery();
// 4. 查询cmd.CommandText = "SELECT COUNT(*) FROM Users";var count = (long)cmd.ExecuteScalar();Console.WriteLine($"用户数: {count}");

三步走完:安装 NuGet 包 → 打开连接 → 执行 SQL。路径自动创建、参数自动绑定,不需要装任何系统级依赖。

🧬 .NET 生态里的 SQLite 工具链:一张表看懂各种 NuGet 包

SQLite 是部署量最大的数据库引擎,嵌入式、零配置、单文件。但 .NET 生态里围绕它的包五花八门,初次接触很容易选错。

分类
包名
推荐度
适用场景 & 说明
ADO.NET 驱动
Microsoft.Data.Sqlite
⭐⭐⭐ 首选
微软官方维护,跨平台开箱即用,无原生依赖
ADO.NET 驱动
System.Data.SQLite
❌ 不推荐
部署依赖 native DLL,Linux/macOS 环境极易出错
ADO.NET 驱动
SQLitePCLRaw
⭐ 按需使用
需要自定义 SQLite 编译版本(FTS5/JSON1 扩展)时使用
ORM
Microsoft.EntityFrameworkCore.Sqlite
⭐⭐⭐ 首选
基于 EF Core 框架开发,仅需替换 UseSqlite 即可使用
轻量映射
Dapper + Microsoft.Data.Sqlite
⭐⭐⭐ 首选
习惯手写 SQL,同时需要对象映射功能
加密
Microsoft.Data.Sqlite.Core + SQLitePCLRaw.bundle_e_sqlcipher
⭐ 按需使用
需求数据库文件加密(基于 SQLCipher)


大多数场景:Microsoft.Data.Sqlite + EF Core / Dapper 就够用了。如果高并发写入,再加一个 EntityFrameworkCore.Sqlite.Concurrency。

💥 为什么你的 SQLite 一并发就崩?

这可能是 SQLite 被误解最深的环节。很多人用了默认配置,一上来多个线程同时写,直接报 SQLITE_BUSY,然后下结论:“SQLite 不适合生产环境。”

问题不出在 SQLite 本身,而出在你没开WAL 模式

SQLite 默认使用 DELETE 日志模式,写入时对整个数据库文件加排他锁,写阻塞读、读阻塞写。同一时间只允许一个写入操作。多个线程抢着写,直接抛 database is locked,这不是 Bug,是默认设置太保守了。

WAL(Write-Ahead Logging) 是解决这个问题的关键:读操作不阻塞写操作,写操作不阻塞读操作。WAL 模式下写入并发可达 100-200 QPS,混合读写负载轻松支持 1000+ QPS——对初创公司和中小项目来说,完全够用了。

启用 WAL 只需一行 PRAGMA








using var conn = new SqliteConnection("Data Source=app.db");conn.Open();
// 在连接打开后立即执行using var cmd = conn.CreateCommand();cmd.CommandText = "PRAGMA journal_mode = WAL;";cmd.ExecuteNonQuery();

WAL 模式具有持久性——第一次执行后,数据库文件永久保持 WAL 模式,不需要每次连接都重新设置。在 MAUI 等资源受限场景中,采用 WAL 模式并调整连接参数是解决 SQLITE_IOERR 最有效的策略。

🧠 三层优化,从能用到能打

WAL 模式只是第一层。真正扛住生产环境,还需要事务和 PRAGMA 的组合拳。

第一层:批量插入用事务包起来

单条 INSERT 默认自动提交,100 条就是 100 次磁盘刷写。放到同一个显式事务里,性能提升 5–10 倍。















using var tx = conn.BeginTransaction();var cmd = conn.CreateCommand();cmd.Transaction = tx; // 这句必须有,否则事务不生效cmd.CommandText = "INSERT INTO Users (Name, Age) VALUES (@name, @age)";
for (int i = 0; i < 1000; i++){    cmd.Parameters.Clear();    cmd.Parameters.AddWithValue("@name", $"用户{i}");    cmd.Parameters.AddWithValue("@age", 20 + i % 50);    cmd.ExecuteNonQuery();}
tx.Commit();

关键细节:事务中的所有 command 必须共用同一个 connection 实例,且 command.Transaction 必须显式赋值——少写这句,事务完全无效。

第二层:PRAGMA 微调磁盘行为

WAL 只是日志模式的开关,搭配以下 PRAGMA,性能再上一档:










using var cmd = conn.CreateCommand();cmd.CommandText = @"    PRAGMA journal_mode = WAL;         -- 读写不互斥    PRAGMA synchronous = NORMAL;       -- 平衡安全与性能(默认 FULL 会每次刷盘)    PRAGMA busy_timeout = 60000;       -- 写锁等待 60 秒,不立即抛异常    PRAGMA cache_size = -65536;        -- 增大缓存,减少磁盘 I/O    PRAGMA temp_store = MEMORY;        -- 临时表存内存";cmd.ExecuteNonQuery();

Web API 场景下,这套配置可以把“并发一写就崩”降到“偶尔排队等待”。

第三层:数据库重建后必须跑 ANALYZE

SQLite 的查询优化器依赖统计信息来做索引选择。数据库从零建完、批量 insert 了大量数据后,如果没有跑 ANALYZE(或 PRAGMA optimize),查询计划可能极差。

有用户反馈:应用首次运行时,每 10 万条记录的 upsert 耗时约 300ms;重启应用后,同样的操作耗时膨胀到 3500ms,整整慢了 10 倍以上。最终发现是统计信息过时导致的查询计划退化。解决方案很简单——在数据库初始化完成后跑一次 PRAGMA optimize:




using var cmd = conn.CreateCommand();cmd.CommandText = "PRAGMA optimize;";cmd.ExecuteNonQuery();

这对数据库首次初始化后的查询性能有显著影响。

🛡️ 生产环境还要注意什么?

连接字符串路径必须绝对路径

SQLite 数据库就是一个文件,路径写错 = 创建空库 / 操作了别的文件。相对路径在 IDE 调试时可能正常,发布到 Docker 容器或生产环境后行为完全不同。







//推荐:显式构造绝对路径var dataPath = Path.Combine(AppContext.BaseDirectory, "data", "app.db");var conn = new SqliteConnection($"Data Source={dataPath}");
//  不推荐:相对路径风险大var conn = new SqliteConnection("Data Source=app.db");

连接不要长期持有

SQLite 没有连接池,并发写入能力弱。读多写少的场景下可以复用同一个 SqliteConnection 实例,但不能跨线程共享,用完之后及时 Dispose。

外键默认不开启

SQLite 的设计中,外键约束默认是关闭的。如果需要级联删除等外键行为,连接打开后需立刻执行:



cmd.CommandText = "PRAGMA foreign_keys = ON;";cmd.ExecuteNonQuery();

这一步很多人都漏掉,结果发现 DELETE 不会触发 CASCADE,还以为是 Bug。

异步不要在文档构建里写

SQLite 不支持真正的异步 I/O。如果在连接打开期间执行 await 延迟操作(如 HTTP 调用),后续对同一数据的写入可能因为连接状态不一致而失败。

内存模式是双刃剑

Data Source=:memory: 可以在内存中运行数据库,关闭即消失,适合单元测试。但它不支持 WAL 模式,因为没有磁盘文件。单元测试时记得提前种数据。

🎯 什么时候不该用 SQLite?

SQLite 不是万能的。遇到以下情况,果断换 PostgreSQL / SQL Server:

  • 高并发写入场景:写入并发 > 200 QPS,WAL 模式也扛不住,其他线程会排队等待

  • 需要网络访问的架构:多个服务实例需要同时读写同一份数据,SQLite 文件级锁是瓶颈

  • 大事务场景:单次事务超过 100MB,传统回滚日志模式可能比 WAL 还快

  • 行级安全或复杂权限:需要 GRANT/REVOKE 级别的权限控制

🎯 最终选型速查表

场景
推荐方案
桌面应用(WPF / MAUI / WinForms)
EF Core + SQLite
单机 API / 小型服务
Dapper + SQLite + WAL 模式
单元测试(替代真实数据库)
EF Core + SQLite 内存模式
需要加密存储
SQLCipher
CLI 工具 / 脚本
Microsoft.Data.Sqlite 直接用 ADO.NET

SQLite 不是“玩具数据库”,而是一把被严重低估的瑞士军刀。

它设计精巧、部署极简、性能足够,是全球部署最广的嵌入式数据库。把它用好,不是“打开连接、写几句 SQL”那么简单——WAL 模式、事务粒度、PRAGMA 微调、索引统计、并发模型,每一个环节都直接影响它在生产环境的稳定性。

当你真正掌握这些的时候,你会发现:原来很多场景根本不需要上 PostgreSQL,一个管好用的 SQLite,完全能扛住。


群贤毕至

访客