轻量级 AOT 兼容的嵌入式 NoSQL 数据库
TinyDb 是一个 AOT 优先、受 LiteDB 启发的单文件嵌入式 NoSQL 数据库:
- 以 AOT 为准:一切功能以 NativeAOT 编译后的行为为准,不提供任何回退逻辑。
- 开发一致性:保障开发期(非 AOT/JIT)与 AOT 发布后的行为一致。
- 非 AOT 场景建议:如果你的应用不需要 NativeAOT,推荐直接使用 LiteDB(生态更成熟、特性更完整)。
- 单文件数据库 - 所有数据存储在一个文件中,部署简单
- 100% AOT 兼容 - 完全支持 Native AOT 编译,无反射依赖
- 源代码生成器 - 编译时生成序列化代码,零运行时开销
- LINQ 查询 - 完整的 LINQ 支持,类型安全的查询体验
- 动态字符串与 SQL 子集 - 支持 AOT 兼容的字符串条件、
Execute、select/insert/update/delete - ACID 事务 - 完整的事务支持,保证数据一致性
- 密码保护 - 内置数据库级别加密保护
- 只读模式 - 支持以只读方式打开数据库,安全共享数据文件
- 高性能索引 - B+树索引,支持快速数据检索
- 跨平台 - 支持 Windows、Linux、macOS
dotnet add package TinyDbusing TinyDb.Attributes;
using TinyDb.Bson;
[Entity("users")]
public partial class User
{
[Id]
public ObjectId Id { get; set; } = ObjectId.NewObjectId();
[Index]
public string Name { get; set; } = "";
[Index(Unique = true)]
public string Email { get; set; } = "";
public int Age { get; set; }
[BsonIgnore] // 此属性不会被序列化
public string? TempToken { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}using TinyDb.Core;
// 创建/打开数据库
using var db = new TinyDbEngine("myapp.db");
var users = db.GetCollection<User>();
// 插入
var user = new User { Name = "张三", Email = "zhangsan@example.com", Age = 25 };
users.Insert(user);
// 查询
var found = users.Find(u => u.Age > 20).ToList();
var one = users.FindOne(u => u.Email == "zhangsan@example.com");
// 更新
user.Age = 26;
users.Update(user);
// 删除
users.Delete(user.Id);// 复杂查询
var results = users.Query()
.Where(u => u.Age >= 18 && u.Age <= 30)
.Where(u => u.Name.StartsWith("张"))
.OrderByDescending(u => u.CreatedAt)
.Skip(10)
.Take(20)
.ToList();
// 聚合查询
var count = users.Query().Where(u => u.Age > 20).Count();
var exists = users.Query().Any(u => u.Email.Contains("@gmail.com"));TinyDb 提供一组 AOT 兼容的动态查询入口。所有动态条件和 SQL 都会先解析成内部 AST,不使用运行时动态代码生成。
using TinyDb.Query;
var adults = users.Find(
"Age >= @minAge and Name startswith @prefix",
QueryParams.Create(("minAge", 18), ("prefix", "张")))
.ToList();
var page = users.Find(
"Age >= @minAge",
QueryParams.Create(("minAge", 18)),
skip: 20,
limit: 10)
.ToList();字符串条件支持:
- 字段路径:
Id、Name、Address.City,其中Id/id/_id会按主键处理。 - 比较:
=、==、!=、<>、>、>=、<、<=,以及eq、ne、gt、gte、lt、lte。 - 逻辑:
and、or、not、括号。 - 空值:
is null、is not null。 - 字符串匹配:
contains、startswith/starts_with、endswith/ends_with。 like:支持abc%、%abc、%abc%和精确匹配,不支持_或中间多段%通配。- 函数:
contains(field, value)、startswith(field, value)、endswith(field, value)、lower(field)、upper(field)、trim(field)。 - 参数:
@name,推荐用QueryParams.Create(("name", value))传入。
暂不支持:
in/not in、子查询、正则表达式。- 任意 .NET 方法调用;只支持上面列出的固定函数。
- 匿名对象投影;AOT 下请使用
BsonDocument或带[Entity]的 DTO。
推荐新代码统一使用 Execute:
// SELECT -> BsonDocument,字段名按 SELECT 里写的名字返回
var select = users.Execute(
"select Id, Name from users where Age >= @age order by Age desc limit 10 offset 0",
QueryParams.Create(("age", 18)));
foreach (var doc in select.Documents)
{
var id = doc["Id"];
var name = doc["Name"].ToString();
}
// SELECT * 会展开为实体属性名
var all = users.Execute("select * from users where Id = @id", QueryParams.Create(("id", id)));
var userName = all.Documents.Single()["Name"].ToString();
// 泛型 DTO 投影,DTO 需要 [Entity] 并参与源生成
var summaries = users.Execute<UserSummary>(
"select Id, Name from users where Age >= @age",
QueryParams.Create(("age", 18))).Rows;
// INSERT / UPDATE / DELETE 返回影响行数
var inserted = users.Execute(
"insert into users (Id, Name, Email, Age) values (@id, @name, @email, @age)",
QueryParams.Create(("id", ObjectId.NewObjectId()), ("name", "李四"), ("email", "lisi@example.com"), ("age", 22)));
var updated = users.Execute(
"update users set Name = @name, Age = @age where Id = @id",
QueryParams.Create(("id", id), ("name", "王五"), ("age", 23)));
var deleted = users.Execute(
"delete from users where Id = @id",
QueryParams.Create(("id", id)));SQL 支持:
select * from collectionselect Id, Name from collectionselect Name as DisplayName from collectionwhere:复用字符串条件查询语法。order by Field asc|desc,支持多字段排序。limit、offset,支持字面量或参数。insert into collection (Field1, Field2) values (@v1, @v2)。update collection set Field = @value where ...。delete from collection where ...。- 字面量:字符串、数字、
true、false、null。 - DML 写入会按目标实体属性类型归一化数值字面量,避免 AOT 反序列化时因 BSON 类型不匹配而丢值。
FindSql、FindSqlDocuments、FindSql<TProjection>仍保留兼容,但内部已走Execute。TinyDbEngine.Execute<TSource>(...)和TinyDbEngine.Execute<TSource, TProjection>(...)可从引擎层按 SQL 中的集合名路由。
注意:update / delete 的 where 按 SQL 语义可省略;省略时会影响整个集合。批量写入建议显式添加 where,需要事务时用 TinyDb 事务 API 包裹。
SQL 暂不支持:
join;请使用Include(...)做实体引用加载。group by、having、聚合函数、窗口函数。- 子查询、CTE、
union。 insert into ... select ...。update set Age = Age + 1这类表达式赋值;当前只支持字面量或参数赋值。- 更新主键
Id/_id。 - DML 的嵌套字段写入;当前
insert/update只支持顶层字段。 - DML 重复写入同一字段;
insert字段列表和update set中重复字段会被拒绝。 - SQL 事务语法;需要事务时使用
BeginTransaction()/Commit()/Rollback()包裹。
Include(...) 不是 SQL join,它是在实体查询后按 DBRef / 外键元数据加载引用:
var orders = db.GetCollection<Order>()
.Include("Customer")
.FindSql("select * from orders where Total >= @min", QueryParams.Create(("min", 100)))
.ToList();Include 面向实体结果,不适用于 FindSqlDocuments / Execute(...).Documents 这种动态文档投影。
Password 可用于兼容的数据库密码保护。创建新库时同时设置 EnableEncryption = true,会启用数据页和 WAL payload 加密。已有明文库设置 EnableEncryption = true 不会被隐式迁移,会抛出异常;请显式导出或 compact 到新的加密库。
// 创建加密数据库
var options = new TinyDbOptions
{
EnableEncryption = true,
Password = "MySecurePassword123!"
};
using var secureDb = new TinyDbEngine("secure.db", options);
// 访问加密数据库
using var db = new TinyDbEngine("secure.db", new TinyDbOptions { Password = "MySecurePassword123!" });TinyDb 支持以只读模式打开数据库。只读模式下多个进程可安全共享同一个数据文件,无需担心写入锁冲突。
// 以只读模式打开数据库
var options = new TinyDbOptions
{
ReadOnly = true
};
using var db = new TinyDbEngine("myapp.db", options);
// 只读模式下仅支持查询操作
var users = db.GetCollection<User>();
var user = users.FindOne(u => u.Id == id);
// 写入操作会抛出 InvalidOperationException
// users.Insert(newUser); // ❌using var db = new TinyDbEngine("myapp.db");
var users = db.GetCollection<User>();
var orders = db.GetCollection<Order>();
// 开启事务
db.BeginTransaction();
try
{
users.Insert(new User { Name = "新用户" });
orders.Insert(new Order { UserId = "...", Amount = 99.99m });
db.Commit(); // 提交事务
}
catch
{
db.Rollback(); // 回滚事务
throw;
}| 属性 | 说明 |
|---|---|
[Entity("集合名")] |
标记实体类,指定集合名称 |
[Id] |
标记主键属性 |
[Index] |
创建索引 |
[Index(Unique = true)] |
创建唯一索引 |
[BsonIgnore] |
序列化时忽略此属性 |
[BsonField("字段名")] |
自定义 BSON 字段名 |
- 基本类型:
int,long,double,decimal,bool,string,DateTime,Guid - 可空类型:
int?,DateTime?等 - 集合类型:
List<T>,T[],Dictionary<string, T> - 嵌套对象: 支持复杂对象嵌套
- 特殊类型:
ObjectId,BsonDocument
var options = new TinyDbOptions
{
Password = "密码", // 数据库密码(可选)
EnableEncryption = true, // 创建新库时启用数据页和 WAL 加密
ReadOnly = false, // 以只读方式打开(默认 false,写入模式)
PageSize = 8192, // 页面大小(默认 8KB)
CacheSize = 1000, // 缓存页数
EnableJournaling = true, // 启用 WAL 日志
Timeout = TimeSpan.FromMinutes(5), // 操作超时时间
Logger = (level, message, ex) =>
{
Console.WriteLine($"[{level}] {message}");
if (ex != null) Console.WriteLine(ex);
}
};Logger 是可选回调,签名为 Action<TinyDbLogLevel, string, Exception?>,支持级别:Debug、Information、Warning、Error、Critical。
以下为 BenchmarkDotNet 最新实测均值(QuickIndexBenchmark,2026-07-10):
| 操作 | SynchronousWrites=true |
SynchronousWrites=false |
内存分配(true / false) |
|---|---|---|---|
Insert1000_Individual |
8,976,726.1 μs(约 111 ops/s) |
412,939.3 μs(约 2,422 ops/s) |
12.60 MB / 10.80 MB |
Insert1000_Batch |
297,528.8 μs(约 3,361 ops/s) |
215,342.3 μs(约 4,644 ops/s) |
13.27 MB / 13.49 MB |
QueryWithoutIndex |
752.2 μs |
1,827.3 μs |
280.56 KB / 292.28 KB |
QueryWithIndex |
525.3 μs |
737.6 μs |
74.23 KB / 76.57 KB |
QueryWithUniqueIndex |
331.2 μs |
419.0 μs |
18.16 KB / 18.24 KB |
FindById |
267.3 μs |
295.0 μs |
6.59 KB / 6.67 KB |
注: 测试环境为 AMD EPYC 7763 2.44GHz,.NET 9.0.12。该组基准使用
EnableJournaling=false,用于对比核心读写路径。
- 只读模式:新增只读打开数据库支持,多进程可安全共享数据文件,只读时所有写入操作抛出
InvalidOperationException。 - 写入锁文件:通过锁文件机制强制单写者访问,防止多进程同时写入导致数据损坏。
- 推迟空闲页扫描:只读模式下延迟空闲页回收,优化打开速度和并发安全。
- 存储层安全性强化:修复
DiskStream释放线程安全、大文档链完整性校验、WAL 路径穿越防护。 - 查询表达式增强:新增
string.IsNullOrEmpty和IsNullOrWhiteSpace查询函数支持。 - 配置选项验证:
TinyDbOptions构造时进行有效性校验,早期发现非法配置。 - CI 工作流完善:为
TinyDb.csproj变更自动触发 CI,版本号变化时执行完整发布流程。 - 回归验证补齐:新增只读模式、单写者锁、存储安全相关的回归测试。
- 并发与写入路径强化:细化集合写锁、页锁和文档锁边界,降低并发写入、事务提交和缓存回写路径的锁竞争。
- WAL 与持久化安全增强:加强同步刷盘、批量提交、回放校验和事务恢复路径,覆盖更多崩溃恢复与半写盘场景。
- 查询与 SQL 执行完善:补强动态 SQL/DML、运行时表达式绑定、索引规划、排序/TopK 和事务可见性相关逻辑。
- AOT 与源码生成器稳定性提升:拆分并强化源码生成器类型分析、依赖分析、字段命名和 mapper 生成路径,减少 AOT/trim 边界问题。
- 序列化与 BSON 兼容增强:强化
Decimal128、ObjectId、DateTime、数值转换、复杂集合和嵌套对象的往返转换。 - 性能基线刷新:更新
QuickIndexBenchmark基准数据,当前报告覆盖写入、索引查询、唯一索引查询和主键查找的耗时与分配。 - 回归验证补齐:新增并扩展并发、WAL、索引、查询、AOT、加密、源码生成器和序列化回归测试。
- 动态查询与 SQL 子集:新增 AOT 兼容的字符串条件查询、统一
ExecuteSQL 入口、BsonDocument动态投影、泛型 DTO 投影,以及基础select/insert/update/delete解析执行。 - SQL 能力边界文档化:明确列出当前支持的条件语法、投影规则、DML 范围,以及暂不支持的 join、聚合、子查询、表达式赋值等能力。
- WAL 崩溃恢复增强:页写入前先追加并刷新 WAL,重放时会校验磁盘页头、页号和页校验和,即使半写盘页带有最新 LSN 也会从 WAL 恢复。
- 页校验升级:页 checksum 从旧累加和升级为 CRC32,提升半写盘和静默损坏检测能力。
- 旧库兼容:校验逻辑同时接受 CRC32 和旧累加和 checksum,旧库无需迁移,页面重新写入后自然升级为 CRC32。
- 回归验证补齐:新增 WAL 半写盘恢复、CRC32 零区间计算和旧 checksum 兼容测试。
- 并发初始化修复:串行化集合注册、集合状态构建和索引管理器创建,避免
ConcurrentDictionary.GetOrAdd工厂在并发下重复执行带副作用的 schema、元数据和索引页初始化逻辑。 - Schema 写入竞态修复:
MetadataManager.EnsureSchema()增加同步与二次检查,避免首次并发访问同一实体时重复写入__sys_catalog。 - ASP.NET 元数据兼容增强:
BsonConversion原生支持JsonElement/JsonDocument,可递归转换Dictionary<string, object>中来自 JSON 请求体的对象、数组、数值、布尔值和null。 - 回归验证补齐:新增并发首次访问带索引集合的重开库验证,以及
JsonElement递归 BSON 转换测试。
- 新库初始化修复:新建数据库时立即写入并刷新有效 Header,避免应用重启后出现
Invalid database header。 - WAL 安全修复:删除主数据库文件但遗留 WAL 时,创建新库会忽略旧 WAL,避免旧日志污染新数据库。
- 索引可靠性增强:
EnsureIndex()支持回填已有文档,持久化索引根页并在重启后恢复,回填失败不会留下无效索引定义。 - 事务可见性修复:
FindById()可见当前事务中的挂起插入/删除。 - AOT 验证增强:清理 AOT/trim 警告并补充 NativeAOT 回归测试。
- AOT 复杂集合修复:恢复并增强
List<复杂类型>在 AOT 模式下的反序列化支持,覆盖Entity依赖类型中的复杂对象集合场景。 - 源生成器增强:补齐依赖复杂类型路径中的集合/字典元数据与专用序列化分支,避免
List element type ... is not supported in AOT mode异常。 - 回归验证补齐:新增相关往返序列化测试,并通过 AOT 发布后二进制运行验证。
- 分页计数增强:新增
Find(..., skip, limit, out totalCount),支持一次查询同时返回分页结果与总数。 - Query 体验增强:新增
Query().Count(out totalCount)扩展,支持在Skip/Take链路中同步获取总数与分页结果。
- 依赖精简:移除
Microsoft.IO.RecyclableMemoryStream、System.IO.Hashing、System.IO.Pipelines外部依赖,改为内置最小兼容实现。 - 性能优化:减少
ToArray等中间分配,优化批量写入与无索引全表扫描路径。
- 查询 API 增强:新增
Find重载,支持更灵活的查询调用方式。
- 并发一致性修复:新增集合级写入串行化,避免高并发写入下的索引冲突与提交竞态。
- 错误传播强化:对关键数据安全路径(如
Flush、页切换、新页回退失败等)不再吞异常,直接抛出给上层处理。 - 扫描性能优化:Raw 扫描从“整集合完整快照”改为按页紧凑快照(
Snapshot(false)),降低扫描额外开销。 - 可观测性增强:新增
TinyDbOptions.Logger回调配置,支持TinyDbLogLevel分级日志。
- 性能飞跃:通过 Span 与池化缓冲区(Pooled Buffers)实现零/低分配序列化,内存分配降低 90%+
- 核心重构:引入高性能响应式组提交(Reactive Group Commit)与锁剥离(Lock Stripping)技术
- 元数据重构:深度重构元数据管理系统,提升架构清晰度与扩展性
- 查询优化:支持谓词下推(Predicate Push-down)与排序/分页下推,提升 BSON 扫描效率
- 异步支持:新增真正的异步读取 API 接口
- 完善
[BsonIgnore]属性支持 - 新增 AOT 兼容的序列化测试
- 修复源生成器相关问题
- 2610 个测试全部通过
- 完善 DbRef 引用支持
- 增强嵌套类 Entity 支持
- 性能优化
TinyDb/
├── TinyDb/ # 核心库
├── TinyDb.SourceGenerator/ # 源代码生成器
├── TinyDb.Tests/ # 测试项目
├── TinyDb.Demo/ # 演示项目
└── TinyDb.UI/ # 可视化管理工具
dotnet run --project TinyDb.Demo- .NET 8.0 / 9.0 / 10.0
- C# 12+
- 推荐 IDE: Rider / Visual Studio 2022
欢迎提交 Issue 和 Pull Request!
# 运行测试
dotnet test
# AOT 编译测试
dotnet publish -c Release -r win-x64 --self-contained -p:PublishAot=trueMIT License - 可自由用于商业项目
如果这个项目对你有帮助,请给一个 ⭐ Star!