读写分离子系统 - C# SQL分发子系统(ADO.NET,不支持EF)
读写分离子系统 - C# SQL分发子系统(ADO.NET,不支持EF)
这次介绍的这个框架只适用于中小项目,并且各个读写数据库结构是一致的情况,还要并且是写入数据库只有1台情况。
我们来看看这个子系统适用的场景:
我们来看这个子系统的配置文件:

- <?xml version="1.0" encoding="utf-8" ?>
- <SQLDispatcher>
- <WritableDB>Server=.;Database=d1;User Id=sa;Password=111111;</WritableDB> //唯一的主数据库(写入DB)
- <ReadDBs>
- <DB>Server=.;Database=d2;User Id=sa;Password=111111;</DB> //这些是普通的对等的读数据库,只是做了些普通索引优化
- <DB>Server=.;Database=d3;User Id=sa;Password=111111;</DB> //同上
- <DB>Server=.;Database=d4;User Id=sa;Password=111111;</DB> //同上
- </ReadDBs>
- <DedicatedReadDBs>
- <DedicatedRegion>
- <Region>Optimization_Sales</Region> //这个区域代表所列出来的DB是专门针对销售报表优化索引的数据库
- <DB>Server=.;Database=d5;User Id=sa;Password=111111;</DB>
- <DB>Server=.;Database=d6;User Id=sa;Password=111111;</DB>
- </DedicatedRegion>
- <DedicatedRegion>
- <Region>Optimization_HR</Region> //这样的专门Region可以有多个区域
- <DB>Server=.;Database=d7;User Id=sa;Password=111111;</DB>
- </DedicatedRegion>
- </DedicatedReadDBs>
- </SQLDispatcher>

上述配置文件的读取,略。
业务层中,可以做到这样的写法:

- [AOPServiceEnabled()] //这句是为了和AOP代理挂钩
- public class OrderQueryService : OrderQueryServiceInterface
- {
- IOrderQueryServiceDataProvider dp = new OrderQueryServiceSqlDataProvider();
- public override QueryResult<QueryDto.OrderDto> QueryByFirstName(string firstName, PagingInfo pgInfo)
- { //这个函数没有加SQLDispatcher标记,系统会自己选择sql连接(写入sql:就那1个;读取sql:从ReadDBs中取模选中1个)
- QueryResult<QueryDto.OrderDto> lst=dp.QueryByFirstName(firstName, pgInfo);
- foreach (OrderDto o in lst.List)
- o.FirstName += DateTime.Now.ToString();
- return lst;
- }
- [SQLDispatcher("Optimization_Sales")] //显式指定sql语句走 Optimization_Sales区域
- public override QueryResult<QueryDto.OrderDto> QueryByEmail(string email)
- {
- QueryResult<QueryDto.OrderDto> lst = dp.QueryByEmail(email);
- return lst;
- }
- }

我们来看下UML:
SQLDispatcherContext用于保存当前函数的Region,这里保存的数据是瞬间的,随着函数的开始执行而有数据,随着函数的结束而被reset。
DBSelector是核心算法,用于根据配置文件算出不同的可选db,代码如下

- public class DBSelector
- {
- public static DB SelectDB(string sql, string region)
- {
- bool redirect2WritableDB = false;
- sql = sql.Trim().TrimStart('\r').TrimStart('\n');
- if (sql.IndexOf("UPDATE", StringComparison.OrdinalIgnoreCase) >= 0)
- redirect2WritableDB = true;
- if (sql.IndexOf("DELETE", StringComparison.OrdinalIgnoreCase) >= 0)
- redirect2WritableDB = true;
- if (sql.IndexOf("INSERT", StringComparison.OrdinalIgnoreCase) >= 0)
- redirect2WritableDB = true;
- if (sql.IndexOf("--WRITE", StringComparison.OrdinalIgnoreCase) == 0) //强制sql方式进入写db操作
- redirect2WritableDB = true;
- if (redirect2WritableDB)
- return Config.SQLDispatcherConfiguration.WritableDB;
- if (region == null || region.Length == 0) //from normal read dbs
- {
- int random = GenerateRandomNumber();
- int dbIndex = random % Config.SQLDispatcherConfiguration.ReadDBs.Count;
- return Config.SQLDispatcherConfiguration.ReadDBs[dbIndex];
- }
- DedicatedRegion r = Config.SQLDispatcherConfiguration.DedicatedRegions.Find(t => t.Region.Equals(region, StringComparison.OrdinalIgnoreCase));
- if (r == null)
- throw new Exception("No such Dedicated Region Identifier.");
- {
- int random = GenerateRandomNumber();
- int dbIndex = random % r.DBs.Count;
- return r.DBs[dbIndex];
- }
- }
- private static int GenerateRandomNumber()
- {
- Random Random1 = new Random();
- //产生0到1000的随机数
- int i1 = Random1.Next(0, 1001);
- return i1;
- }
- }

SqlHelperCoordinator类只是简单的根据DBSelector算出的结果调度真正的SqlHelper来执行:

- public sealed class SqlHelperCoordinator
- {
- public static int ExecuteNonQuery(CommandType commandType, string commandText, params SqlParameter[] commandParameters)
- {
- string region = SQLDispatcherContext.GetCurrentContext().Region;
- DB db=DBSelector.SelectDB(commandText, region);
- return SqlHelper.ExecuteNonQuery(db.ConnectionString, commandType, commandText, commandParameters);
- }
- public static SqlDataReader ExecuteReader(CommandType commandType, string commandText, params SqlParameter[] commandParameters)
- {
- string region = SQLDispatcherContext.GetCurrentContext().Region;
- DB db = DBSelector.SelectDB(commandText, region);
- return SqlHelper.ExecuteReader(db.ConnectionString, commandType, commandText, commandParameters);
- }
- }

Console测试代码(记得打开Sql profile检测sql哦):

- static void Main(string[] args)
- {
- InstancePoolResolver.Register<OrderQueryServiceInterface, OrderQueryService>();
- using (OrderQueryServiceInterface srv = InstancePoolResolver.Resolve<OrderQueryServiceInterface>())
- {
- while (true)
- {
- //Thread.Sleep(1000);
- Console.ReadKey();
- QueryResult<Core.QueryService.QueryDto.OrderDto> lst=srv.QueryByFirstName("aaron", new CoreFramework.QueryService.PagingInfo() { PageIndex = 0, PageSize = 10, OrderByColumn = "FirstName", IsAscendingSort = true });
- lst.List.ForEach(t=>Console.WriteLine(t.FirstName));
- srv.QueryByEmail("aaron");
- }
- }
- }

运行2次(关闭后再运行,因为缓存还没有好,bug)
就会看到:
被查询的数据库名正好落在xml配置文件的范围
心怀远大理想。
为了家庭幸福而努力。
用A2D科技,服务社会。
读写分离子系统 - C# SQL分发子系统(ADO.NET,不支持EF)的更多相关文章
- 读写分离子系统 - C# SQL分发子系统 - Entity Framework支持
A2D Framework增加了EF支持,加上原先支持ADO.NET: 支持EF方式 支持ADO.NET方式 这次来讲如何让Entity Framework变成nb的读写分离 1. 先设计EF模型, ...
- 读写分离子系统 - C# SQL分发子系统(目前只支持ADO.NET)
这次介绍的这个框架只适用于中小项目,并且各个读写数据库结构是一致的情况,还要并且是写入数据库只有1台情况. 我们来看看这个子系统适用的场景: 我们来看这个子系统的配置文件: <?xml vers ...
- EF Core3.0+ 通过拦截器实现读写分离与SQL日志记录
前言 本文主要是讲解EF Core3.0+ 通过拦截器实现读写分离与SQL日志记录 注意拦截器只有EF Core3.0+ 支持,2.1请考虑上下文工厂的形式实现. 说点题外话.. 一晃又大半年没更新技 ...
- ProxySQL 读写分离实践
前言 ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎.具有以下特性: 连接池,而且是 multiplexing 主机和用户的最大连接数限制 自动下线后端DB 延迟超过阀值 ping ...
- ProxySQL 配置详解及读写分离(+GTID)等功能说明 (完整篇)
ProxySQL是灵活强大的MySQL代理层, 是一个能实实在在用在生产环境的MySQL中间件,可以实现读写分离,支持 Query 路由功能,支持动态指定某个 SQL 进行 cache,支持动态加载配 ...
- mysql读写分离(PHP类)
mysql读写分离(PHP类) 博客分类: php mysql 自己实现了php的读写分离,并且不用修改程序 优点:实现了读写分离,不依赖服务器硬件配置,并且都是可以配置read服务器,无限扩展 ...
- 基于amoeba实现mysql数据库的读写分离/负载均衡
一.Amoeba的简述:[来自百度百科] Amoeba是一个以MySQL为底层数据存储,并对应用提供MySQL协议接口的proxy.它集中地响应应用的请求,依据用户事先设置的规则,将SQL请 ...
- ProxySQL+Mysql实现数据库读写分离实战
ProxySQL介绍 ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎.具有以下特性:http://www.proxysql.com/ 1.连接池,而且是multiplexing 2 ...
- ProxySQL实现Mysql读写分离 - 部署手册
ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎.ProxySQL是用C++语言开发的,也是percona推的一款中间件,虽然也是一个轻量级产品,但性能很好(据测试,能处理千亿级的数 ...
随机推荐
- Mediator - 中介者模式
定义 用一个中介对象来封装一系列的对象的交互.中介者使各对象不须要显示地相互使用,从而使其耦合松散,并且能够独立的改变他们之间的交互. 案例 比方有一个图像界面,在界面上有一个输入框LineEdit, ...
- applet授权数字签名
一.压缩你的class类文件为jar包 1.如果你的须要压缩的类文件存在的包为:cn.mbq.test1和cn.mbq.test2 2.进入你的classes文件夹,在DOS窗体中运行命令:jar c ...
- 健身小管家--android app源码
把做了近一个月的android程序源码放出来,里面包括但不限于如下内容: 1. 简单的android项目结构 2. 通用的adapter,不再为每一个ListView都写一个adapter,只要用此一 ...
- MEF框架在Silverlight中应用(15)
原文:MEF框架在Silverlight中应用(15) MEF框架在Silverlight中应用 代码下载 MEF框架是微软提供的一个插件框架.应用概括为一句话:输入,输出,组合.下面是具体在Silv ...
- jquery自己主动旋转的登录界面的背景代码登录页背景图
在其他网站上看到比较爽Web登录界面.背景图片可以自己主动旋转. 介绍给大家.有兴趣的可以改改下来作为自己的系统登录界面. 如图: watermark/2/text/aHR0cDovL2Jsb2cuY ...
- 安卓CTS官方文档之兼容性测试套件简介-attach
官方英文文档原文:https://source.android.com/compatibility/cts-intro.html Compatibility Test Suite 兼容性测试套件 H ...
- GitHub上整理
GitHub上整理 技术站点 Hacker News:非常棒的针对编程的链接聚合网站 Programming reddit:同上 MSDN:微软相关的官方技术集中地,主要是文档类 infoq:企业级应 ...
- Java 之Integer相等比较
1.问题提出 今天在和同事讨论问题的时候,无意间谈到了Integer对象的比较,先看下代码: package test; public class IntegerEqual { /** * @para ...
- [C++] 获取IE代理server的账号password
非常多程序须要使用'浏览器设置'的代理server,IE设置的代理server有可能是须要账号password的.如何编程获取浏览器设置的代理server的账号password呢? InternetQ ...
- .net操作PDF的一些资源(downmoon收集)
因为业务需要,搜集了一些.net操作pdf的一些资源,特在此分享. 1.如何从 Adobe 可移植文档格式 (PDF) 文件中复制文本和图形 http://support.microsoft.com/ ...