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配置文件的范围

    代码下载

  • 相关阅读:
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第三节 梯度下降法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (下)实操篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第一节 KNN算法 (下)实操篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第一节 KNN算法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第二章 pandas 第五节 Getting Started with pandas
    Oracle数据库安装和授权
    c# 如何获取JSON文件以及如何获取Config文件(framework 和 net .Core)
    C#Core查询数据库存储EXCEL文件
    如何在WINDOW系统下编译P12证书制作
  • 原文地址:https://www.cnblogs.com/aarond/p/SQLDispatcher.html
Copyright © 2011-2022 走看看