这次介绍的这个框架只适用于中小项目,并且各个读写数据库结构是一致的情况,还要并且是写入数据库只有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. JSON语法与JavaScript语法的区别

    JSON是独立于语言存在的,在不同的编程语言中对这种数据类型的实现不同,例如在JavaScript中使用JavaScript对象对这种数据格式进行实现,那么在java中当然是用java对象实现. 描述 ...

  2. js实现复选框的全选、全不选和反选

    js实现复选框的全选.全不选和反选 主要是用遍历的方法查找元素,然后通过改变checked的属性来选择,为true则是选中状态,为false则是未选状态 实现代码 <!DOCTYPE html& ...

  3. mssql sqlserver 从指定字符串中获取数字的方法

    转自:http://www.maomao365.com/?p=6410 摘要: 下文主要分享从指定字符串或列中获取数字信息,如下所示: 实验环境:sql server 2000 ----编写sql函数 ...

  4. 洗礼灵魂,修炼python(87)-- 知识拾遗篇 —— 线程(1)

    线程(上) 1.线程含义:一段指令集,也就是一个执行某个程序的代码.不管你执行的是什么,代码量少与多,都会重新翻译为一段指令集.可以理解为轻量级进程 比如,ipconfig,或者, python   ...

  5. python 中文报错

    解决的办法为:在程序的开头写入如下代码,这就是中文注释 #coding=utf-8

  6. 重启Zabbix Server

    重启zabbix server:systemctl restart zabbix-server #启动服务 systemctl start zabbix-server systemctl start ...

  7. oracle USING 用法

    提问 using(xx)中可以接多个列吗? using(xx)中的列可以接表名或别名吗? 在使用using的语句中,select * 可以使用吗? 如果表有别名t,select t.* from ta ...

  8. 【PAT】 B1006 换个格式输出整数

    超简单题 //直接将各位分开,分别用for循环输出 #include<stdio.h> int main(){ int num; scanf("%d",&num ...

  9. Java的基础知识四

    一.Java 流(Stream).文件(File)和IO Java.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基 ...

  10. Git Extensions 使用小结

    1.查看仓库 2.创建分支 然后会自动创建一个 Commit ,推送到远端分支即可. 3.合并分支 注意1.自动提交需要没有无法自动合并的冲突才行. 注意2.快进线指的是将别人的提交原封不动附加到自己 ...