c#本身不提供读写分离能力,必须依赖数据库主从架构与应用层连接策略协同实现;需配置独立主从连接字符串、确保主从同步正常、按操作类型显式路由sqlcommand,并规避跨库事务与延迟一致性风险。

直接上结论:C# 本身不提供读写分离能力,它只是客户端;真正的读写分离必须由数据库层(如 MySQL 主从架构)配合连接策略在应用层实现。不配置主从复制、不区分连接字符串、不控制 SqlCommand 路由方向,光靠 C# 代码“写个判断”是无效的。
MySQL 主从复制没配好,C# 做什么都白搭
读写分离的前提是数据已同步。如果 SHOW SLAVE STATUS\G 显示 Slave_IO_Running: No 或 Seconds_Behind_Master 持续增长,说明从库延迟或中断。此时所有读请求发到从库,拿到的就是过期数据。
- 确认主库
binlog_format是ROW(推荐),且server-id全局唯一 - 从库执行
CHANGE MASTER TO后必须运行START SLAVE,不是配完就自动同步 - 避免在从库写入(
read_only=ON必须设为 1,否则主从数据会错乱)
ConnectionString 必须拆成两个,且不能混用
常见错误是只维护一个连接字符串,靠 if (isWrite) 动态拼接数据库名或加 hint —— 这对 MySQL 无效,也违背读写分离本意。
- 主库连接字符串(仅用于 INSERT/UPDATE/DELETE):
"Server=master-host;Database=mydb;Uid=user;Pwd=pass;" - 从库连接字符串(仅用于 SELECT):
"Server=slave-host;Database=mydb;Uid=user;Pwd=pass;" - 不要用
Load Balancing类型连接串(如Server=host1,host2),MySQL 不支持原生负载均衡语义 - 若用连接池,两个字符串必须完全独立,否则连接复用会导致写操作误发到从库
ADO.NET 层需显式路由 SqlCommand
没有魔法开关能自动识别“这是读还是写”。你得在调用前决定走哪个连接。
- 简单场景:封装
DbConnection GetReadConnection()和DbConnection GetWriteConnection(),业务代码按需调用 - 中等复杂度:用策略模式,例如
ICommandExecutor.Execute<t>(SqlCommand cmd, CommandPurpose purpose)</t>,内部根据purpose拿对应连接 - 注意事务:一旦开启
SqlTransaction,后续所有命令必须走同一个连接(即主库),否则抛出InvalidOperationException - 避免在
DataReader未关闭时切换连接,容易触发连接池异常或超时
DataSet.DataRelation 不能替代主从同步
有人把主表 DataTable 和从表 DataTable 用 DataRelation 关联,以为这就是“主从”——这只是内存里的父子关系,和数据库主从复制毫无关系。它不涉及任何网络通信、日志同步或延迟控制。
-
DataRelation用于 WinForm 绑定、TreeView 展开、父子控件联动等 UI 场景 - 它无法保证数据一致性:主表改了,从表不会自动刷新;从库宕机,这个关系照样存在
- 真正需要的是:写操作后主动触发从库健康检查,或在读取前加
SELECT SLEEP(0.1)(不推荐)缓解延迟问题
最易被忽略的一点:从库不是“只读备份”,而是“有延迟的读副本”。业务上必须接受最终一致性,比如订单创建后立即查列表可能查不到,这不是 bug,是架构约束。硬要强一致,就得放弃读写分离,或者引入 GTID + 半同步复制 + 应用层等待机制——但那已是另一层复杂度了。











