本文介绍 LiteOrm 的性能优化技巧。
{
"LiteOrm": {
"DataSources": [
{
"Name": "DefaultConnection",
"ConnectionString": "Server=localhost;Database=TestDb;...",
"PoolSize": 16,
"MaxPoolSize": 100,
"KeepAliveDuration": "00:10:00"
}
]
}
}
| 参数 | 默认值 | 说明 |
|---|---|---|
PoolSize |
16 | 连接池缓存的最大连接数 |
MaxPoolSize |
100 | 最大并发连接数 |
KeepAliveDuration |
00:10:00 | 连接保活时长 |
LiteOrm 默认使用参数化查询,防止 SQL 注入的同时提高查询计划缓存命中率。
var minAge = 18;
var users = await userService.SearchAsync(u => u.Age >= minAge);
// 生成 SQL: SELECT * FROM Users WHERE Age >= @0
// 使用插值字符串,{name} 会被参数化传入
var name = "admin";
var users = await userViewDAO.Search($"WHERE UserName = {name}").ToListAsync();
using static LiteOrm.Common.Expr;
// 不推荐:查询所有字段
var users = await userService.SearchAsync();
// 推荐:使用 SearchAs 选择字段
var result = await userService.SearchAs<UserView>(
From<UserView>()
.Where(Prop("Age") > 18)
.Select("Id", "UserName", "DeptName")
);
LiteOrm.Demo\Demos\WindowFunctionDemo.cs 中使用 SearchAs<T> 和投影来避免读取不必要的列:
var results = await factory.SalesDAO
.WithArgs([tableMonth])
.SearchAs<SalesWindowView>(selectExpr)
.ToListAsync();
这种模式特别适合报表、排行榜、聚合视图等“结果模型与实体模型不同”的查询。
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 实体映射 | ObjectViewDAO<T> |
自动映射到强类型 |
| 大数据量处理 | DataViewDAO<T> |
直接返回 DataTable |
| 流式处理 | IAsyncEnumerable |
内存占用低 |
// 大偏移量分页(慢)
var page = await userService.SearchAsync(
q => q.Where(u => u.Age >= 18)
.OrderByDescending(u => u.CreateTime)
.Skip(10000).Take(20) // 偏移量大时慢
);
// 推荐:基于 ID 的游标分页(快)
var lastId = 10000;
var page = await userService.SearchAsync(
q => q.Where(u => u.Age >= 18 && u.Id > lastId)
.OrderByDescending(u => u.Id)
.Take(20)
);
// 单条插入(多次网络往返)
for (int i = 0; i < 100; i++)
{
await userService.InsertAsync(new User { UserName = $"user{i}", Age = 18 + i % 10, CreateTime = DateTime.Now });
}
// 批量插入(一次网络往返)
await userService.BatchInsertAsync(users); // 推荐
LiteOrm.Demo\Data\DbInitializer.cs 中使用批量插入初始化多组数据:
await deptService.BatchInsertAsync(depts);
await userService.BatchInsertAsync(users);
await salesService.BatchInsertAsync(records);
这个模式适合种子数据初始化、压测准备、演示数据构造等场景。相比循环逐条插入,它能显著减少数据库往返次数。
// 单条更新(多次网络往返)
foreach (var user in users)
{
await userService.UpdateAsync(user);
}
// 批量更新(一次网络往返)
await userService.BatchUpdateAsync(users); // 推荐
LiteOrm.Tests\ServiceTests.cs 对批量操作有一组很典型的闭环验证:
using static LiteOrm.Common.Expr;
await service.BatchInsertAsync(users);
var inserted = await viewService.SearchAsync(Lambda<TestUser>(u => u.Name!.StartsWith("Batch")));
foreach (var user in inserted)
user.Age += 5;
await service.BatchUpdateAsync(inserted);
await service.BatchDeleteAsync(inserted);
如果你的业务需要导入一批数据、批量修正后再清理,这种模式可直接套用。
IBulkProvider / BulkProviderFactory(高性能批量提供器)IBulkProvider 配合 BulkProviderFactory 构成 LiteOrm 的高性能批量操作扩展(可选依赖),用于大规模插入/更新/删除时显著减少网络往返与数据库负载。
示例:批量插入(伪代码)
var factory = services.GetRequiredService<BulkProviderFactory>();
var provider = factory.GetProvider(dbConnection.GetType());
await provider.BulkInsertAsync(ToDataTable(users), dbConnection, transaction);
IBulkProvider 实现LiteOrm.Demo\Demos\MySqlBulkInsertProvider.cs 文件中提供了一个真实的 IBulkProvider 实现(类名为 MySqlBulkCopyProvider):
[AutoRegister(Key = typeof(MySqlConnection))]
public class MySqlBulkCopyProvider : IBulkProvider
{
public async Task<int> BulkInsertAsync(
DataTable dt,
IDbConnection dbConnection,
IDbTransaction transaction,
CancellationToken cancellationToken = default)
{
MySqlBulkCopy bulkCopy = new MySqlBulkCopy(
dbConnection as MySqlConnection,
transaction as MySqlTransaction);
bulkCopy.DestinationTableName = dt.TableName;
bulkCopy.ConflictOption = MySqlBulkLoaderConflictOption.Replace;
for (int i = 0; i < dt.Columns.Count; i++)
bulkCopy.ColumnMappings.Add(new MySqlBulkCopyColumnMapping(i, dt.Columns[i].ColumnName));
return (await bulkCopy.WriteToServerAsync(dt).ConfigureAwait(false)).RowsInserted;
}
}
这个例子说明了两点:
IBulkProvider 可以按数据库连接类型自动注册,并通过 BulkProviderFactory 获取。在 LiteOrm 中的实现位置(参考):
示例:批量更新(按主键)
// 将需要更新的数据转换为 DataTable,然后调用 provider.BulkInsert/BulkInsertAsync 或 provider 支持的 BulkUpdate
await provider.BulkInsertAsync(ToDataTable(usersToUpdate), dbConnection, transaction);
可配置选项(常见):
注意事项:
IBulkProvider 时,务必在测试环境评估索引负载、日志增长与锁等待;对于写密集型场景,考虑在导入期间禁用辅助索引或延迟索引重建。IBulkProvider 的实现会因数据库不同而不同:例如 SQL Server 常使用 SqlBulkCopy,MySQL 可使用 LOAD DATA INFILE 或 MySqlBulkCopy。请参考 LiteOrm.Demo 中的示例实现。// 同步(阻塞线程)
var users = userService.Search();
// 异步(释放线程)
var users = await userService.SearchAsync(); // 推荐
// 串行查询
var users = await userService.SearchAsync();
var departments = await departmentService.SearchAsync();
// 并行查询
var userTask = userService.SearchAsync();
var departmentTask = departmentService.SearchAsync();
await Task.WhenAll(userTask, departmentTask);
var users = userTask.Result;
var departments = departmentTask.Result;
确保查询条件字段有适当索引:
-- 查询条件
WHERE DeptId = 2 AND Age >= 18
-- 建议索引
CREATE INDEX idx_users_dept_age ON Users(DeptId, Age);
// N+1 查询(不推荐)
var sales = await salesService.SearchAsync(tableArgs: [DateTime.Now.ToString("yyyyMM")]);
foreach (var sale in sales)
{
var user = await userService.GetObjectAsync(sale.SalesUserId); // 每次查询
}
// 关联查询(推荐)
var sales = await salesService.SearchAsync<SalesRecordView>(tableArgs: [DateTime.Now.ToString("yyyyMM")]);
// 自动 JOIN,一次查询
// 低效
int count = await userService.CountAsync(u => u.Age >= 18);
if (count > 0) { ... }
// 高效
bool exists = await userService.ExistsAsync(u => u.Age >= 18);
if (exists) { ... }
LiteOrm.Tests\ServiceTests.cs 中直接验证了 ExistsAsync 和 CountAsync 的差异化用途:
using static LiteOrm.Common.Expr;
bool exists = await viewService.ExistsAsync(Lambda<TestUser>(u => u.Name == "Unique"));
int count = await viewService.CountAsync(Lambda<TestUser>(u => u.Age >= 50));
ExistsAsyncCountAsync// ASP.NET Core 中使用 Scoped
builder.Host.RegisterLiteOrm(options =>
{
options.RegisterScope = true; // 推荐
});
var sessionManager = SessionManager.Current;
sessionManager.BeginTransaction();
try
{
// 操作
sessionManager.Commit();
}
catch
{
sessionManager.Rollback();
throw;
}
using static LiteOrm.Common.Expr;
// 大数据量查询
await foreach (var user in userViewDAO.Search(Prop("Age") >= 18))
{
// 流式处理,避免一次性加载到内存
Process(user);
}
// 不推荐:存储大文本
[Column("Content")]
public string LargeContent { get; set; } // 可能很大
// 推荐:存储引用
[Column("ContentId")]
public long ContentId { get; set; } // 外键引用
LiteOrm 相比其他 ORM 的性能优势:
| 操作 | LiteOrm | EF Core | Dapper |
|---|---|---|---|
| 插入 1000 条 | ~16ms | ~150ms | ~215ms |
| 更新 1000 条 | ~25ms | ~126ms | ~248ms |
| 关联查询 | ~9ms | ~15ms | ~9ms |