这次介绍的这个框架只适用于中小项目,并且各个读写数据库结构是一致的情况,还要并且是写入数据库只有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) >= )
redirect2WritableDB = true;
if (sql.IndexOf("DELETE", StringComparison.OrdinalIgnoreCase) >= )
redirect2WritableDB = true;
if (sql.IndexOf("INSERT", StringComparison.OrdinalIgnoreCase) >= )
redirect2WritableDB = true;
if (sql.IndexOf("--WRITE", StringComparison.OrdinalIgnoreCase) == ) //强制sql方式进入写db操作
redirect2WritableDB = true; if (redirect2WritableDB)
return Config.SQLDispatcherConfiguration.WritableDB; if (region == null || region.Length == ) //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(, );
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 = , PageSize = , OrderByColumn = "FirstName", IsAscendingSort = true });
lst.List.ForEach(t=>Console.WriteLine(t.FirstName)); srv.QueryByEmail("aaron");
}
}
}

运行2次(关闭后再运行,因为缓存还没有好,bug)

就会看到:

被查询的数据库名正好落在xml配置文件的范围

代码下载

读写分离子系统 - C# SQL分发子系统(目前只支持ADO.NET)的更多相关文章

  1. 读写分离子系统 - C# SQL分发子系统(ADO.NET,不支持EF)

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

  2. 读写分离子系统 - C# SQL分发子系统 - Entity Framework支持

    A2D Framework增加了EF支持,加上原先支持ADO.NET: 支持EF方式 支持ADO.NET方式 这次来讲如何让Entity Framework变成nb的读写分离 1. 先设计EF模型, ...

  3. sharding demo 读写分离 U (分库分表 & 不分库只分表)

    application-sharding.yml sharding: jdbc: datasource: names: ds0,ds1,dsx,dsy ds0: type: com.zaxxer.hi ...

  4. ProxySQL+Mysql实现数据库读写分离实战

    ProxySQL介绍 ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎.具有以下特性:http://www.proxysql.com/ 1.连接池,而且是multiplexing 2 ...

  5. ProxySQL 读写分离实践

    前言 ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎.具有以下特性: 连接池,而且是 multiplexing 主机和用户的最大连接数限制 自动下线后端DB 延迟超过阀值 ping ...

  6. ProxySQL 配置详解及读写分离(+GTID)等功能说明 (完整篇)

    ProxySQL是灵活强大的MySQL代理层, 是一个能实实在在用在生产环境的MySQL中间件,可以实现读写分离,支持 Query 路由功能,支持动态指定某个 SQL 进行 cache,支持动态加载配 ...

  7. Mycat高可用解决方案三(读写分离)

    Mycat高可用解决方案三(读写分离) 一.系统部署规划 名称 IP 主机名称 配置 192.168.199.112 mycat01 2核/2G Mysql主节点 192.168.199.110 my ...

  8. mysql读写分离(PHP类)

    mysql读写分离(PHP类) 博客分类: php mysql   自己实现了php的读写分离,并且不用修改程序 优点:实现了读写分离,不依赖服务器硬件配置,并且都是可以配置read服务器,无限扩展 ...

  9. [转]Spring数据库读写分离

    数据库的读写分离简单的说是把对数据库的读和写操作分开对应不同的数据库服务器,这样能有效地减轻数据库压力,也能减轻io压力. 主(master)数据库提供写操作,从(slave)数据库提供读操作,其实在 ...

随机推荐

  1. Linux重命名网卡名称

    1.查看当前网卡: nmcli connection show 可以看到我有两个网卡,其中一个为中文名称,我想将配置 2 修改为net-DHCP 2.cd到/etc/sysconfig/network ...

  2. c# .Net随机生成字符串代码

    /// <summary> /// 随机生成字符串 /// </summary> /// <param name="OperationType"> ...

  3. [20180928]ora-01426(补充).txt

    [20180928]ora-01426(补充).txt --//链接:http://www.itpub.net/thread-2105458-1-1.html--//做一点点必要的补充: 1.环境:S ...

  4. 洗礼灵魂,修炼python(84)-- 知识拾遗篇 —— 网络编程之socket

    学习本篇文章的前提,你需要了解网络技术基础,请参阅我的另一个分类的博文:网络互联技术(4)——计算机网络常识.原理剖析 网络通信要素 1.IP地址: 用来标识网络上一台独立的终端(PC或者主机) ip ...

  5. Ubuntu下使用QQ/Wechat

    实验环境:Ubuntu 16.04桌面版root用户下 安装Docker 配置Docker的apt源 $ sudo apt-get install apt-transport-https ca-cer ...

  6. February 13th, 2018 Week 7th Tuesday

    You are your greatest asset. 你就是你自己最大的资本. For most of us, there are few things that we can count on ...

  7. Docker: docker container常用命令实战(2)-数据持久化

    应用服务是在容器中运行的,容器随时会被删除,如果是个mysql容器呢?数据存储在容器里,容器删除了,数据也没了,那就是个噩梦. 所以一些数据是需要存储在容器之外的,可以是宿主机,可以是网络存储位置上, ...

  8. MySql 中文写入数据库乱码及Incorrect string value: '\xF0\x9F...' for column 'XXX' at row 1解决

    一.中文写入乱码问题 我输入的中文编码是 urf8 的,建的库是 urf8 的,但是插入MySQL总是乱码,一堆"???????????????????????".可以使用以下的方 ...

  9. Git无法提交,报错

    Failed at the ant-design-pro@2.2.1 lint-staged script. npm ERR! This is probably not a problem with ...

  10. 【ZJOI2017】仙人掌

    [ZJOI2017]仙人掌 参考博客:https://www.cnblogs.com/wfj2048/p/6636028.html 我们先求出\(dfs\)树(就是\(dfs\)一遍),然后问题就变成 ...