基于 PostgreSQL 的 ABP vNext + ShardingCore 分库分表实战
🚀 基于 PostgreSQL 的 ABP vNext + ShardingCore 分库分表实战
📑 目录
- 🚀 基于 PostgreSQL 的 ABP vNext + ShardingCore 分库分表实战
- ✨ 背景介绍
- 🧱 技术选型
- 🛠️ 环境准备
- ✅ Docker Compose(多库 & 读写分离演示)
- 🚀 环境与容器启动流程
- ✅ 基础表与分表初始化脚本
- 📦 分表初始化流程
- ✅ `appsettings.json` 样板
- ✅ NuGet 包安装
- 🧩 项目实现步骤
- 1. 定义分片键接口
- 2. 实体与 DbContext
- 3. SaveChanges 拦截器
- 4. 分表路由规则
- 5. `Program.cs` 配置
- 🛠️ 服务启动与配置流程
- 6. 分片引导器服务
- 7. 控制器示例:批量插入与异常处理
- 🔄 请求处理与分片路由流程
- 8. 集成测试示例(xUnit & WebApplicationFactory)
- ✅ 性能优化与最佳实践
- 🔍 监控与指标采集流程
- 🔗 总结
✨ 背景介绍
随着系统数据量的增长,单表性能瓶颈和数据库存储压力日益显著,分库分表逐渐成为解决大数据、高并发问题的有效手段。ABP vNext 提供模块化和扩展能力,ShardingCore 是基于 EF Core 的轻量级分库分表中间件。本文结合两者,并引入监控、健康检查与读写分离等生产级要素,基于 PostgreSQL 构建一套高性能、高可用、可复现的分库分表解决方案。
🧱 技术选型
- ABP vNext 8.1.5
- EF Core 8.0.4
- PostgreSQL 15
- ShardingCore 7.8.1.21
- .NET 8 SDK
- EFCore.BulkExtensions(批量插入)
- Polly(重试与熔断)
- OpenTelemetry & Prometheus(监控与指标)
🛠️ 环境准备
✅ Docker Compose(多库 & 读写分离演示)
🚀 环境与容器启动流程
version: '3.8'
services:pg0:image: postgres:15environment:POSTGRES_DB: shard_dbPOSTGRES_USER: postgresPOSTGRES_PASSWORD: passports:- "5432:5432"pg1:image: postgres:15environment:POSTGRES_DB: shard_db_1POSTGRES_USER: postgresPOSTGRES_PASSWORD: passports:- "5433:5432"pg-replica:image: postgres:15environment:POSTGRES_DB: shard_dbPOSTGRES_USER: postgresPOSTGRES_PASSWORD: pass# 可配置流复制,示例略ports:- "5434:5432"app:build: .depends_on:- pg0- pg1- pg-replicaenvironment:ConnectionStrings__Default: Host=pg0;Database=shard_db;Username=postgres;Password=passConnectionStrings__Shard1: Host=pg1;Database=shard_db_1;Username=postgres;Password=passConnectionStrings__ReadReplica: Host=pg-replica;Database=shard_db;Username=postgres;Password=passports:- "5000:80"
✅ 基础表与分表初始化脚本
-- 创建基础表
CREATE TABLE IF NOT EXISTS public."Order" ("Id" BIGSERIAL PRIMARY KEY,"OrderNo" VARCHAR(50) NOT NULL,"Amount" NUMERIC(18,2) NOT NULL,"CreationTime" TIMESTAMPTZ NOT NULL,"CreatorUserId" BIGINT NULL
);-- 自动创建近 12 个月的分表
DO $$
BEGINFOR i IN 0..11 LOOPEXECUTE format('CREATE TABLE IF NOT EXISTS public."Order_%s" (LIKE public."Order" INCLUDING ALL);',to_char(current_date - (i || '' month'')::interval, ''YYYYMM''));END LOOP;
END
$$;
📦 分表初始化流程
✅ appsettings.json
样板
{"ConnectionStrings": {"Default": "Host=pg0;Database=shard_db;Username=postgres;Password=pass","Shard1": "Host=pg1;Database=shard_db_1;Username=postgres;Password=pass","ReadReplica": "Host=pg-replica;Database=shard_db;Username=postgres;Password=pass"}
}
✅ NuGet 包安装
dotnet add package ShardingCore
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package EFCore.BulkExtensions
dotnet add package Polly.Extensions.Http
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.Console
dotnet add package prometheus-net.AspNetCore
🧩 项目实现步骤
1. 定义分片键接口
using System;
using ShardingCore.Core.EntityMetadatas;public interface IShardingKeyIsCreationTime
{[ShardingKey]DateTime CreationTime { get; set; }
}
2. 实体与 DbContext
using System;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.Domain.Entities;public class Order : AggregateRoot<long>, ICreationAudited, IShardingKeyIsCreationTime
{public string OrderNo { get; set; }public decimal Amount { get; set; }public DateTime CreationTime { get; set; }public long? CreatorUserId { get; set; }
}using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;public class AppDbContext : AbpDbContext<AppDbContext>
{public DbSet<Order> Orders { get; set; }public AppDbContext(DbContextOptions<AppDbContext> options): base(options) { }protected override void OnModelCreating(ModelBuilder builder){base.OnModelCreating(builder);builder.Entity<Order>().Property(o => o.CreationTime).HasDefaultValueSql("NOW() at time zone 'utc'").ValueGeneratedOnAdd();}
}
3. SaveChanges 拦截器
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using ShardingCore.Core.EntityMetadatas;public class AuditInterceptor : SaveChangesInterceptor
{public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData,InterceptionResult<int> result,CancellationToken ct = default){var context = eventData.Context;if (context != null){foreach (var entry in context.ChangeTracker.Entries<Order>().Where(e => e.State == EntityState.Added)){entry.Entity.CreationTime = DateTime.UtcNow;entry.Entity.OrderNo = SnowflakeIdGenerator.NewId();}}return base.SavingChangesAsync(eventData, result, ct);}
}
4. 分表路由规则
using System;
using ShardingCore.Core.VirtualRoutes.Modify;public class OrderMonthlyRoute : AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<Order>
{public override bool AutoCreateTableByTime => true;public override string GetActualTableName(string tableName, DateTime shardingKey)=> $"{tableName}_{shardingKey:yyyyMM}";
}
5. Program.cs
配置
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Polly.Extensions.Http;
using Prometheus;
using ShardingCore.Core.VirtualRoutes.Modify;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
var cfg = builder.Configuration;// 拦截器
services.AddSingleton<AuditInterceptor>();// EF + ShardingCore
services.AddShardingDbContext<AppDbContext>().UseRouteConfig(o => o.AddShardingTableRoute<OrderMonthlyRoute>()).UseConfig((sp, o) =>{o.UseShardingQuery((conn, opt) => opt.UseNpgsql(conn)).UseShardingTransaction((conn, opt) => opt.UseNpgsql(conn)).AddDefaultDataSource("ds0", cfg["ConnectionStrings:Default"]).AddDataSource("ds1", cfg["ConnectionStrings:Shard1"]).UseReadWriteSeparation(writeConn: cfg["ConnectionStrings:Default"],readConn: cfg["ConnectionStrings:ReadReplica"],readWeight: 5, writeWeight: 1);}).AddShardingCore().AddInterceptors(sp => new[] { sp.GetRequiredService<AuditInterceptor>() });// 分片引导后台服务
services.AddHostedService<ShardingBootstrapperService>();// 健康检查
services.AddHealthChecks().AddNpgSql(cfg["ConnectionStrings:Default"], name: "shard-ds0").AddNpgSql(cfg["ConnectionStrings:Shard1"], name: "shard-ds1");// OpenTelemetry Tracing
services.AddOpenTelemetryTracing(b =>b.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ShardingDemo")).AddAspNetCoreInstrumentation().AddEntityFrameworkCoreInstrumentation().AddConsoleExporter()
);// Prometheus Metrics
services.AddMetricServer();
services.AddHttpMetrics();// HttpClient + Polly
services.AddHttpClient("defaultClient").AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(2))).AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));services.AddControllers();var app = builder.Build();
app.UseHttpMetrics();
app.MapHealthChecks("/health");
app.MapControllers();
app.Run();
🛠️ 服务启动与配置流程
6. 分片引导器服务
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using ShardingCore.Sharding.Abstractions;public class ShardingBootstrapperService : IHostedService
{private readonly IShardingBootstrapper _bootstrapper;public ShardingBootstrapperService(IShardingBootstrapper bootstrapper)=> _bootstrapper = bootstrapper;public Task StartAsync(CancellationToken ct) => _bootstrapper.StartAsync(ct);public Task StopAsync(CancellationToken ct) => _bootstrapper.DisposeAsync().AsTask();
}
7. 控制器示例:批量插入与异常处理
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories;[ApiController]
[Route("api/orders")]
public class OrderController : ControllerBase
{private readonly AppDbContext _context;public OrderController(AppDbContext context) => _context = context;[HttpPost("batch")]public async Task<IActionResult> CreateBatchAsync(CancellationToken ct){try{var orders = Enumerable.Range(1, 10).Select(_ => new Order { Amount = 99.99m }).ToList();await _context.BulkInsertAsync(orders, cancellationToken: ct);return Ok(orders);}catch (DbUpdateException ex){return StatusCode(500, new { message = "数据库写入失败", error = ex.Message });}}
}
🔄 请求处理与分片路由流程
8. 集成测试示例(xUnit & WebApplicationFactory)
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;public class OrderControllerTests : IClassFixture<WebApplicationFactory<Program>>
{private readonly HttpClient _client;public OrderControllerTests(WebApplicationFactory<Program> factory)=> _client = factory.CreateClient();[Fact]public async Task CreateBatch_Returns10Orders(){var response = await _client.PostAsync("/api/orders/batch", null);response.EnsureSuccessStatusCode();var json = await response.Content.ReadAsStringAsync();var orders = JsonSerializer.Deserialize<List<Order>>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });Assert.Equal(10, orders.Count);}
}
✅ 性能优化与最佳实践
- 基础表 DDL:提供
Order
表完整建表语句,避免“表不存在”问题 - 一键分表:脚本自动生成近 N 个月分表,支持生产环境预建表
- 业务化 ID:雪花算法生成全局唯一
OrderNo
- 批量插入:
EFCore.BulkExtensions
+CancellationToken
提升写入吞吐 - 多副本读写分离:真实多库演示,读库权重 configurable
- Polly:重试 + 熔断保障网络抖动
- 健康检查:
AddNpgSql
实时监控各分片状态 - 链路追踪:
OpenTelemetry.Exporter.Console
+ EF Core Instrumentation - 指标采集:
prometheus-net
+UseMetricServer()
🔍 监控与指标采集流程
🔗 总结
本文覆盖了从多实例 Docker Compose、基础表 DDL、分表脚本、appsettings.json
样板,到实体设计、拦截器、路由规则、完整 Program.cs
配置、后台引导、控制器示例和集成测试的全链路。帮助读者快速落地“高性能、高可用、可复现”的 ABP vNext + ShardingCore 分库分表解决方案。
相关文章:
基于 PostgreSQL 的 ABP vNext + ShardingCore 分库分表实战
🚀 基于 PostgreSQL 的 ABP vNext ShardingCore 分库分表实战 📑 目录 🚀 基于 PostgreSQL 的 ABP vNext ShardingCore 分库分表实战✨ 背景介绍🧱 技术选型🛠️ 环境准备✅ Docker Compose(多库 & 读…...

jenkins 启动报错
java.lang.UnsatisfiedLinkError: /opt/application/jdk-17.0.11/lib/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory。 解决方案: yum install freetype-devel 安装完成之后重启jenkins。...
C++ 套接字函数详细介绍
目录 头文件1. 套接字创建与配置2. 绑定地址与端口3. 连接建立4. 数据传输5. 套接字选项6. 地址转换7. 套接字关闭8. 其他实用函数 C 套接字函数详细介绍 套接字(Socket)是网络通信的基本端点,C中通常使用BSD套接字API进行网络编程。以下是主要的套接字相关函数及其…...

【合新通信】无人机天线拉远RFOF(射频光纤传输)解决方案
无人机天线拉远RFOF方案通过光纤替代传统射频电缆,实现无人机与地面控制站之间的高保真、低损耗信号传输,尤其适用于高频段(如毫米波)、远距离或复杂电磁环境下的无人机作业场景。 核心应用场景 军事侦察与电子战 隐蔽部署&…...

程序设计语言----软考中级软件设计师(自用学习笔记)
目录 1、解释器和编译器 2、程序的三种控制结构 3、程序中的数据必须具有类型 4、编译、解释程序翻译阶段 5、符号表 6、编译过程 7、上下文无关文法 8、前、中、后缀表达式 9、前、后缀表达式计算 10、语法树中、后序遍历 11、脚本语言和动态语言 12、语法分析方法…...
火山RTC 7 获得远端裸数据
一、获得远端裸数据 1、获得h264数据 1)、远端编码后视频数据监测器 /*** locale zh* type callback* region 视频管理* brief 远端编码后视频数据监测器<br>* 注意:回调函数是在 SDK 内部线程(非 UI 线程)同步抛出来的&a…...

通过SMTP协议实现Linux邮件发送配置指南
一、环境准备与基础配置 1. SMTP服务开通(以qq邮箱为例) 登录qq邮箱网页端,进入「设置」-「POP3/SMTP/IMAP」 开启「SMTP服务」并获取16位授权码(替代邮箱密码使用) 记录关键参数: SMTP服务器地址&#…...

若依框架页面
1.页面地址 若依管理系统 2.账号和密码 管理员 账号admin 密码admin123 运维 账号yuwei 密码123456 自己搭建的地址方便大家学习,不要攻击哦,谢谢啊...

44、私有程序集与共享程序集有什么区别?
私有程序集(Private Assembly)与共享程序集(Shared Assembly)是.NET框架中程序集部署的两种不同方式,它们在部署位置、版本控制、访问权限等方面存在显著差异,以下是对二者的详细比较: 1. 部署…...

【Java面试题】——this 和 super 的区别
🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:【Java】内容概括 【前言】 在Java的世界里,this和 super是两个非常重要且容易混淆的关键字。无论是在日常…...
记录 QT 在liunx 下 QFileDialog 类调用问题 ()Linux下QFileDialog没反应)
1. 2025.05.14 踩坑记录 因为QT 在 liunx 文件系统不同导致的 Windows : QString filePath QFileDialog::getOpenFileName(nullptr, "选择文件", ".", "文本文件 (*.txt);所有文件 (*.*)"); 没问题 liunx 下 打不开ÿ…...

CentOS 7 内核升级指南:解决兼容性问题并提升性能
点击上方“程序猿技术大咖”,关注并选择“设为星标” 回复“加群”获取入群讨论资格! CentOS 7 默认搭载的 3.10.x 版本内核虽然稳定,但随着硬件和软件技术的快速发展,可能面临以下问题: 硬件兼容性不足:新…...
【前端】:单 HTML 去除 Word 批注
在现代办公中,.docx 文件常用于文档编辑,但其中的批注(注释)有时需要在分享或归档前被去除。本文将从原理出发,深入剖析如何在纯前端环境下实现对 .docx 文件注释的移除,并提供完整的实现源码。最后&#x…...

解决 PicGo 上传 GitHub图床及Marp中Github图片编译常见难题指南
[目录] 0.行文概述 1.PicGo图片上传失败 2.*关于在Vscode中Marp图片的编译问题* 3.总结与启示行文概述 写作本文的动机是本人看到了Awesome Marp,发现使用 Markdown \texttt{Markdown} Markdown做PPT若加持一些 CSS , JavaScript \texttt{CSS},\texttt{JavaScript} …...

软考 系统架构设计师系列知识点之杂项集萃(59)
接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(58) 第96题 假设关系模式R(U, F),属性集U{A, B, C},函数依赖集F{A->B, B->C}。若将其分解为p{R1(U1, F1), R2(U2, F2),其中U1{A, B}, U2{A, …...

python使用matplotlib画图
【README】 plot画图有两种方法:包括 plt.plot(), ax.plot()-画多个子图 ,其中ax表示某个坐标轴; 【1】画单个图 import matplotlib # 避免兼容性问题:明确指定 matplotlib 使用兼容的后端,TkAgg 或 Agg matplotlib.use(TkAgg) …...
鸿蒙OSUniApp 开发实时聊天页面的最佳实践与实现#三方框架 #Uniapp
使用 UniApp 开发实时聊天页面的最佳实践与实现 在移动应用开发领域,实时聊天功能已经成为许多应用不可或缺的组成部分。本文将深入探讨如何使用 UniApp 框架开发一个功能完善的实时聊天页面,从布局设计到核心逻辑实现,带领大家一步步打造专…...

upload-labs通关笔记-第5关 文件上传之.ini绕过
目录 一、ini文件绕过原理 二、源码审计 三、渗透实战 1、查看提示 2、制作.user.ini文件 (1)首先创建一个文本文件 (2)保存文件名为.user.ini 2、制作jpg后缀脚本 (1)创建一个文本文件 …...

ssti模板注入学习
ssti模板注入原理 ssti模板注入是一种基于服务器的模板引擎的特性和漏洞产生的一种漏洞,通过将而已代码注入模板中实现的服务器的攻击 模板引擎 为什么要有模板引擎 在web开发中,为了使用户界面与业务数据(内容)分离而产生的&…...

填涂颜色(bfs)
归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍+ 收藏⭐ + 留言📝 含泪播种的人一定能含笑收获! 题目描述 由数字 0 0 0 组成的方阵中,有一任意形状的由数字 1 1 1 构成的闭合圈。现…...

ros1+docker环境快速搭建
快速使用python 解析ros1的bag消息ros这个东西可以说安装起来非常麻烦的,费时费力,很可能还安装不成功,特别是我的环境是ubuntu22.04 ,官方都不支持安装ros1。因此一个可行且快速的方法是使用别人配置好的ros的docker环境 一、下…...
vscode extention踩坑记
# npx vsce package --allow-missing-repository --no-dependencies #耗时且不稳定 npx vsce package --allow-missing-repository #用这行 code --install-extension $vsixFileName --force我问ai:为什么我的.vsix文件大了那么多 ai答:因为你没有用 --n…...

GpuGeek全栈AI开发实战:从零构建企业级大模型生产管线(附完整案例)
目录 背景一、算力困境:AI开发者的「三重诅咒」1.1 硬件成本黑洞1.2 资源调度失衡1.3 环境部署陷阱 二、三大核心技术突破GpuGeek的破局方案2.1 分时切片调度引擎(Time-Slicing Scheduler)2.2 异构计算融合架构2.3 AI资产自动化…...
【TroubleShoot】禁用Unity Render Graph API 兼容模式
使用Unity 6时新建了项目,有一个警告提示: The project currently uses the compatibility mode where the Render Graph API is disabled. Support for this mode will be removed in future Unity versions. Migrate existing ScriptableRenderPasses…...
数据库字段唯一性修复指南:从设计缺陷到规范实现
数据库字段唯一性修复指南:从设计缺陷到规范实现 一、问题背景 表结构设计缺陷: sys_user 表未对 dingtalk_user_id(钉钉用户ID)字段设置唯一性约束,导致数据重复,引发以下问题: 系统稳定性风…...

DataX从Mysql导数据到Hive分区表案例
0、下载DataX并解压到对应目录 DataX安装包,开箱即用,无需配置。 https://datax-opensource.oss-cn-hangzhou.aliyuncs.com/202308/datax.tar.gz 相关参考文档 https://github.com/alibaba/DataX/blob/master/hdfswriter/doc/hdfswriter.md 1、Hive分区…...
高性能编程相关
常见高性能编程技巧: 一,系统级性能优化:从系统架构设计考虑,例如消息队列,模块分成分级,IO读写带宽等 二,算法级性能优化:时间和空间优化 三,代码级性能优…...

vulnhub靶场——secarmy
靶机:secarmy靶机,IP地址为192.168.230.18 攻击:kali,IP地址为192.168.230.134 靶机和攻击机都采用VMware虚拟机,都采用NAT模式 端口扫描: nmap 192.168.230.18 -O -A -p- --reason -sV 21/tcp (ftp): 开…...

labview硬件采集
(1)硬件的描述 (2)实验步骤1: (3)实验步骤2 库名/路径的选择要使用32位的开发资料 (4)实验步骤3 (5)实验步骤4 找到DoSetV12() 设置返回类型 设置chan 设置state labv…...

openfeign与dubbo调用下载excel实践
一、前言 openfeign和dubbo均是rpc框架 RPC(Remote Procedure Call,远程过程调用)框架 是一种允许程序像调用本地方法一样调用远程服务器上函数的技术。它隐藏了底层网络通信的复杂性,让开发者可以专注于业务逻辑,实现…...