这次介绍的这个框架只适用于中小项目,并且各个读写数据库结构是一致的情况,还要并且是写入数据库只有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. html之CSS样式学习笔记

    本文内容: 字体样式 文本样式 背景样式 尺寸样式 盒子模型 边框样式 边距样式 浮动布局 定位布局 [CSS3]伸缩布局 标签显示方式 列表样式 [CSS3]过渡样式 [CSS3]变换样式之2D变形 ...

  2. python appium笔记(二):元素定位

    #这里的示例是用android来说明的,xpath应该是通用的,resource-id不太清楚,没配过IOS的环境 #环境配置和一些参数的意思不清楚可以看我上一篇python appium笔记(一) ...

  3. [20181015]12C SQL Translation Framework.txt

    [20181015]12C SQL Translation Framework.txt --//12c提供一个dba改写sql语句的可能性,实际上10g,11g之前也有一个包DBMS_ADVANCED ...

  4. [20170606]11G _optimizer_null_aware_antijoin.txt

    [20170606]11G _optimizer_null_aware_antijoin.txt --//上午测试_optimizer_null_aware_antijoin,发现自己不经意间又犯了一 ...

  5. jenkins 备份配置信息

    本文介绍几种备份jenkin配置信息的方法,大家可根据实际情况做出选择. 我的测试环境如下: windows 7 jenkins 2.32.3 ____升级到___2.46.3 (长期支持版本) 多种 ...

  6. 2. svg学习笔记-svg中的坐标系统和viewbox

    我是通过<SVG精髓>这本书学习的svg,说实话,这本书写的不好,或者说翻译的不好,我没有看过这本书的原版,不知道原文写的怎么样,但是翻译出来的有些句子真的很拗口.以前老师给我们API文档 ...

  7. netsh winsock reset命令,作用是重置 Winsock 目录

    Win xp重置 编辑 要为 Windows XP 重置 Winsock,请按照下列步骤操作: 1.单击“开始”,运行中输入cmd. 2.然后输入命令 netsh winsock reset. 3.重 ...

  8. es6的正则扩展笔记之修饰符

    es6对于正则表达式添加了 u 修饰符和 y 修饰符. u 修饰符:含义为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符.    该修饰符不光会正确处理正则表达式,还会正确处 ...

  9. Java抽象类(Abstract Class)与接口(Interface)区别

    抽象类与接口比较 抽象类跟接口类似,都不能实例化,可能包含不需实现方法或已实现的方法. 抽象类可以定义一些不是静态或常量的字段,定义 public, protected, private访问级别的具体 ...

  10. Linux 简介(day1)

    一.Linux 诞生于1991年 二.创始人:林纳斯.托瓦茨(Linus Torvalds) 三.logo:企鹅 四.Linux完整系统包括 1.Linux kernel (Linux 内核) 2.f ...