NewLife.XCode是一个有15年历史的开源数据中间件,支持netcore/net45/net40,由新生命团队(2002~2020)开发完成并维护至今,以下简称XCode。
整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中,代表作有百亿级大数据实时计算项目。
开源地址:https://github.com/NewLifeX/X (求star, 1116+)

大部分应用系统的查询远远大于写入,在单体应用迈入分布式应用得到横向扩展能力之前,最高的一个门槛就是查询过多,以至于数据库超负荷工作!
常见的解决办法就是读写分离把应用的查询操作分摊到一个或多个只读从库上,主库仅用于写入数据以及事务性操作。企业级应用中MySql一般主从部署,可以把从库充分利用起来,公有云数据库一般支持给数据库添加多台只读实例。
通过读写分离,扩展从库,可以轻易做到查询性能无限扩展!

!!阅读本文之前,建议阅读
语雀内容

如何使用读写分离

在XCode中使用读写分离,不需要修改任何代码,仅需要修改连接字符串配置即可。
如下测试代码:

  1. var r0 = Role.FindByName("Stone");
  2. r0?.Delete();
  3. var r = new Role();
  4. r.Name = "Stone";
  5. r.Insert();
  6. var r2 = Role.FindByName("Stone");
  7. XTrace.WriteLine("FindByName: {0}", r2.ToJson());
  8. r.Enable = true;
  9. r.Update();
  10. var r3 = Role.Find(Role._.Name == "STONE");
  11. XTrace.WriteLine("Find: {0}", r3.ToJson());
  12. r.Delete();
  13. var n = Role.FindCount();
  14. XTrace.WriteLine("count={0}", n);

默认情况下只会在Membership连接上执行读写操作,我们来修改一下连接配置:

<add name="Membership" connectionString="Server=rw.mysql.rds.aliyuncs.com;Port=3306;Database=Membership;Uid=data;Pwd=Pass@word;" providerName="MySql.Data.MySqlClient"/>
<add name="Membership.readonly" connectionString="Server=ro.mysql.rds.aliyuncs.com;Port=3306;Database=Membership;Uid=data;Pwd=Pass@word;" providerName="MySql.Data.MySqlClient"/>

如上,只需要在Membership基础上,增加一个Membership.readonly的连接名,指向MySql从库(或只读实例)即可。XCode在查询时将会把请求放到后一个连接上去执行。

执行结果如下:
image.png
可以看到,Insert/Update/Delete还是在Membership主库上执行,而Select跑到了Membership.readonly从库上执行。

读写分离规则

以下情况使用主库连接:

  • 添删改 Insert/Delete/Update
  • 查询(事务操作内部)
  • 正向工程、反向工程
  • 通过DAL指定主库连接名

以下情况使用从库连接:

  • 查询(非事务操作)
  • 实体缓存和对象缓存
  • 通过DAL指定从库连接名

注意!!!如果从库同步有延迟,将有可能产生读取不到最新数据的情况,在实际应用中要做好风险评估!