zoukankan      html  css  js  c++  java
  • 读写分离子系统

    这次介绍的这个框架只适用于中小项目,并且各个读写数据库结构是一致的情况,还要并且是写入数据库只有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('
    ').TrimStart('
    ');
                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配置文件的范围

    代码下载

  • 相关阅读:
    union和union all 合并查询
    c#中取整,向上取,向下取
    多个DataSet数据合并
    js未定义判断
    C# 获取时间差状态
    SQL中inner join、outer join和cross join的区别
    朗朗跄跄的受伤,跌跌撞撞的坚强
    IIS7.5 平台.NET无后缀名伪静态实现办法-服务器配置
    检查Android系统版本
    js 实现搜索功能
  • 原文地址:https://www.cnblogs.com/aarond/p/SQLDispatcher.html
Copyright © 2011-2022 走看看