EF Core序列生成:使用数据库序列作为主键的完整指南
引言
你是否在为高并发场景下的主键冲突而烦恼?是否希望获得比自增列更灵活的主键生成策略?EF Core的序列(Sequence)功能正是你的解决方案!本文将深入探讨如何使用数据库序列作为主键生成器,提供从基础概念到高级用法的完整指南。
通过本文,你将掌握:
- ✅ 数据库序列的核心概念和优势
- ✅ EF Core中配置序列的多种方式
- ✅ 序列与HiLo模式的结合使用
- ✅ 迁移操作和性能优化技巧
- ✅ 实际应用场景和最佳实践
什么是数据库序列?
数据库序列(Sequence)是数据库中的一个独立对象,用于生成唯一的数字序列。与自增列不同,序列不依赖于特定表,可以在多个表之间共享,提供更大的灵活性。
核心优势对比
| 特性 | 自增列 | 数据库序列 |
|---|---|---|
| 跨表共享 | ❌ 不支持 | ✅ 支持 |
| 预分配 | ❌ 不支持 | ✅ 支持(HiLo模式) |
| 灵活性 | ⚠️ 有限 | ✅ 高度灵活 |
| 性能 | ✅ 优秀 | ✅ 优秀 |
| 迁移复杂度 | ⚠️ 中等 | ⚠️ 中等 |
基础配置:使用序列作为主键
1. 简单序列配置
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Id)
.UseSequence(); // 使用默认序列
}
2. 自定义序列名称和架构
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Id)
.UseSequence("ProductSequence", "dbo");
// 或者先定义序列再使用
modelBuilder.HasSequence<int>("ProductSequence", "dbo")
.StartsAt(1000)
.IncrementsBy(1);
}
高级用法:HiLo模式与序列结合
HiLo(High-Low)模式是EF Core中一种高效的主键生成策略,它结合了序列和客户端缓存机制。
HiLo模式配置
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置模型使用HiLo模式
modelBuilder.UseHiLo("HiLoSequence", "dbo");
// 自定义序列参数
modelBuilder.HasSequence<int>("HiLoSequence", "dbo")
.StartsAt(1000)
.IncrementsBy(100); // 每次增加100,客户端缓存100个ID
}
HiLo模式工作原理
序列配置选项详解
完整的序列配置
modelBuilder.HasSequence<int>("CustomSequence", "schema")
.StartsAt(1000) // 起始值
.IncrementsBy(5) // 增量
.HasMin(1000) // 最小值
.HasMax(9999) // 最大值
.IsCyclic(); // 是否循环
多实体共享序列
// 定义共享序列
modelBuilder.HasSequence<int>("SharedSequence");
// 多个实体使用同一个序列
modelBuilder.Entity<Order>()
.Property(o => o.Id)
.UseSequence("SharedSequence");
modelBuilder.Entity<Invoice>()
.Property(i => i.Id)
.UseSequence("SharedSequence");
迁移操作
生成迁移脚本
当配置序列后,EF Core迁移会自动生成相应的SQL语句:
-- 创建序列
CREATE SEQUENCE [dbo].[ProductSequence]
AS int
START WITH 1
INCREMENT BY 1;
-- 修改表使用序列
ALTER TABLE [Products]
ADD DEFAULT (NEXT VALUE FOR [dbo].[ProductSequence]) FOR [Id];
序列维护操作
EF Core支持多种序列操作:
// 在迁移中操作序列
migrationBuilder.AlterSequence(
name: "ProductSequence",
schema: "dbo",
incrementBy: 10);
migrationBuilder.RestartSequence(
name: "ProductSequence",
schema: "dbo",
startValue: 1000);
性能优化与最佳实践
1. 批量插入优化
// 使用序列时批量插入的性能最佳实践
using var transaction = context.Database.BeginTransaction();
try
{
var products = GenerateProducts(1000);
context.Products.AddRange(products);
context.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
2. 序列缓存配置
// 根据业务需求调整序列缓存大小
modelBuilder.HasSequence<int>("HighPerfSequence")
.StartsAt(1)
.IncrementsBy(1000) // 大增量减少数据库调用
.HasCache(100); // 缓存100个值
3. 监控和诊断
// 监控序列使用情况
var sequenceInfo = context.Database.SqlQueryRaw<SequenceUsage>(
"SELECT * FROM sys.sequences WHERE name = {0}", "ProductSequence");
实际应用场景
场景1:分布式系统主键生成
// 在多实例环境中使用序列确保全局唯一性
public class DistributedIdGenerator
{
private readonly IDbContextFactory<AppDbContext> _factory;
public async Task<long> GenerateIdAsync(string entityType)
{
await using var context = _factory.CreateDbContext();
var nextVal = await context.Database.SqlQueryRaw<long>(
$"SELECT NEXT VALUE FOR {entityType}_Sequence").FirstAsync();
return nextVal;
}
}
场景2:订单编号生成
// 自定义订单编号生成逻辑
public class OrderService
{
public async Task<string> GenerateOrderNumberAsync()
{
var sequenceValue = await GetNextSequenceValueAsync("OrderSequence");
return $"ORD-{DateTime.Now:yyyyMMdd}-{sequenceValue:D6}";
}
}
常见问题与解决方案
Q1: 序列值跳号问题
原因: 事务回滚或缓存机制 解决方案: 如果需要连续编号,使用自增列或调整缓存策略
Q2: 性能考虑
建议: 对于高并发场景,使用HiLo模式减少数据库调用
Q3: 迁移兼容性
注意: 不同数据库对序列的支持程度不同,需要测试目标数据库的兼容性
总结
EF Core的序列功能为主键生成提供了强大而灵活的解决方案。通过合理配置序列参数、结合HiLo模式以及遵循最佳实践,你可以在各种业务场景中实现高效、可靠的主键生成机制。
关键要点回顾:
- 🎯 序列提供跨表共享和高度灵活的主键生成
- 🎯 HiLo模式结合序列和客户端缓存,优化性能
- 🎯 合理配置序列参数满足不同业务需求
- 🎯 迁移操作支持完整的序列生命周期管理
现在你已经掌握了EF Core序列生成的完整知识体系,可以在实际项目中灵活运用这些技术来解决主键生成的各种挑战了!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



