如何将数据库读请求智能路由至Azure只读副本实例
基于Azure SQL只读副本的读请求分流方案
一、创建只读数据库上下文(以EF Core为例)
实现一个只读专用的DbContext,既防止误写入,又绑定只读副本的连接配置:
public class ReadOnlyAppDbContext : AppDbContext { public ReadOnlyAppDbContext(DbContextOptions<ReadOnlyAppDbContext> options) : base(options) { // 关闭实体跟踪,提升读性能同时避免误修改 ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; ChangeTracker.AutoDetectChangesEnabled = false; } // 直接禁用写操作,彻底杜绝意外写入只读副本 public override int SaveChanges(bool acceptAllChangesOnSuccess) => throw new InvalidOperationException("只读上下文不支持写操作"); public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) => throw new InvalidOperationException("只读上下文不支持写操作"); }
在配置文件中添加只读副本的连接字符串,然后注册上下文:
// 主库上下文注册 builder.Services.AddDbContext<AppDbContext>(opt => opt.UseSqlServer(builder.Configuration.GetConnectionString("PrimaryDb"))); // 只读副本上下文注册 builder.Services.AddDbContext<ReadOnlyAppDbContext>(opt => opt.UseSqlServer(builder.Configuration.GetConnectionString("ReadOnlyReplicaDb")));
二、智能路由读请求到就近实例
方案1:Azure内置只读路由(最简实现)
Azure SQL原生支持只读路由,只需在连接字符串中添加 ApplicationIntent=ReadOnly,Azure会自动将读请求路由到就近的健康只读副本,无需应用层额外逻辑:
# 只读副本连接字符串示例 ReadOnlyReplicaDb="Server=tcp:your-replica-server.database.windows.net,1433;Initial Catalog=your-db;User ID=xxx;Password=xxx;Encrypt=True;ApplicationIntent=ReadOnly"
该方案自带故障转移能力,当当前只读副本不可用时,Azure会自动切换到其他可用副本。
方案2:应用层基于地理位置路由(精准控制)
如果需要根据用户所在区域做更精细的路由,可以在应用层实现区域判断逻辑:
public class DbContextRouter { private readonly AppDbContext _primaryDb; private readonly ReadOnlyAppDbContext _euReplica; private readonly ReadOnlyAppDbContext _asiaReplica; public DbContextRouter(AppDbContext primaryDb, ReadOnlyAppDbContext euReplica, ReadOnlyAppDbContext asiaReplica) { _primaryDb = primaryDb; _euReplica = euReplica; _asiaReplica = asiaReplica; } public DbContext GetContext(bool isReadOp, string clientRegion) { // 写操作强制走主库 if (!isReadOp) return _primaryDb; // 根据用户区域选择就近只读副本 return clientRegion switch { "Asia" => _asiaReplica, "Europe" => _euReplica, _ => _euReplica // 默认 fallback 到主区域副本 }; } }
业务层调用时直接通过路由获取对应上下文:
public async Task<List<Product>> GetProducts(string userRegion) { var db = _router.GetContext(isReadOp: true, userRegion); return await db.Products.ToListAsync(); }
三、成本与扩缩容优化
- 解耦主从扩缩容:只读实例的规格调整无需跟随主库,根据只读请求的实际负载独立调整,比如低峰期降配、高峰期临时升配。
- 改用弹性池:将主库和只读副本加入弹性池,共享计算资源,避免单个实例的资源浪费,弹性池还支持自动扩缩容,进一步降低成本。
- 监控驱动优化:使用Azure Monitor跟踪只读实例的CPU、IO、内存使用率,根据监控数据调整实例规格,避免过度配置。
内容的提问来源于stack exchange,提问作者Zapnologica
相关产品推荐
相关产品推荐

