这次介绍的这个框架只适用于中小项目,并且各个读写数据库结构是一致的情况,还要并且是写入数据库只有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. <自动化测试方案_4>第四章、选型标准

    第四章.选型标准 1,免费 2,工具可维护.可扩展 3,支持团队工作

  2. (转载)彻底的理解:WebService到底是什么?

    最近老是有人跟我提web service接口,怎么,怎么滴,我觉得很扎耳朵,web service是一种将服务器的服务封装起来的技术,表现为对外提供接口,所以,web service不是一种接口 !! ...

  3. ERP项目应该由谁来主导?

    前段时间在朋友圈看到了别人分享的公众号,主要是谈ERP项目应该由谁来主导的问题.文章的观点认为应该由哪个部门主导ERP的判断标准如下: 1.应该由一个期望上进的部门主导ERP项目: 2.应该由一个有话 ...

  4. 使用Visual Studio Team Services敏捷规划和项目组合管理(二)——VSTS中的工作项

    使用Visual Studio Team Services敏捷规划和项目组合管理(二)--VSTS中的工作项 1.通过project/team下拉菜单选择MyHealthClinic\Web,导航到W ...

  5. MS SQL CASE WHEN 的用法

    前言 由于经常使用 case when 的2种情况方式,如果=1 则*** 否则 *** 结束.久而久之,都以为只能这么用,都忘记了Case WHEN 的用法. 示例   ,              ...

  6. python datetime object 去除毫秒(microsecond)

    >>>import datetime >>>d = datetime.datetime.now().replace(microsecond=0) >>& ...

  7. ASYNC_NETWORK_IO和PREEMPTIVE_OS_WAITFORSINGLEOBJECT等待事件

    背景环境: SQL Server 2005或以上 Select * from 某个表,表的数据量约为30万行,在执行语句时通过观察sys.dm_exec_requests中的wait_type列发现是 ...

  8. Jenkins2.32用户和权限管理策略

    前言 在使用jenkins的过程中,需要为不同的角色分配相应的权限,如果jenkins的用户数据能和公司现在的帐号系统结合起来那会更好. 关于如何为用户分组,我推荐使用 role based auth ...

  9. oracle USING 用法

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

  10. 从零开始的cve分析- cve-2016-0095 简易记录

    0x00 前言 看k0shl大佬的SSCTF pwn450 Windows Kernel Exploitation Writeup一文,试着写一个x64下的poc. poc地址:https://git ...