重磅來襲,使用CRL實現大數據分庫分表方案


關於分庫分表方案詳細介紹

http://blog.csdn.net/bluishglc/article/details/7696085

這里就不作詳細描述了

分庫分表方案基本脫離不了這個結構,受制於實現的難度,好像沒有看到有很方便的實現方案框架

為了解決此問題,在CRL框架基礎上作了擴展,使CRL能很好實現此方案,以之前了解到的需求,基本能滿足了

本方案拆分結構表示為

會員為業務核心,所有業務圍繞會員來進行,所以垂直划分用會員編號作索引,將會員分配到不同的庫

會員訂單增長量是不固定的,所以需要平水拆分,和分庫一樣,一個表只存指定會員編號區間的訂單

了解基本需求,就可以制作方案了,以下主索引表示主數據編號

 

庫表結構配置

進行操作時,需要知道這個數據放在哪個庫,哪個表,因此需要把這個划分結構做成可配置,需要配置有:

  • 數據庫:一共划分為幾個庫,主索引區間是多少
  • 數據表:一共有幾個分表,每個分表容量是多少
  • 數據表分表:屬於哪個表,主索引區間是多少

將結構以對象形式表示

DataBase 庫

/// <summary>
    ////// 按主數據分垂直划分,將主數據按不同段存在不同庫中
    /// </summary>
    public class DataBase:CRL.IModelBase
    {
        /// <summary>
        /// 庫名
        /// </summary>
        public string Name
        {
            get;
            set;
        }
        /// <summary>
        /// 主數據開始INDEX
        /// </summary>
        public int MainDataStartIndex
        {
            get;
            set;
        }
        /// <summary>
        /// 主數據結束INDEX
        /// </summary>
        public int MainDataEndIndex
        {
            get;
            set;
        }
        /// <summary>
        /// 主數據表最大數據量
        /// </summary>
        public int MaxMainDataTotal
        {
            get;
            set;
        }
        public override string ToString()
        {
            return string.Format("名稱:{0} 最大主數據量:{1} 索引開始:{2} 結束{3}", Name, MaxMainDataTotal, MainDataStartIndex, MainDataEndIndex);
        }
    }
View Code

Table 表

 /// <summary>
    ////// 主數據表不分表,只按庫分,其它表再按主數據段分表
    /// </summary>
    public class Table:CRL.IModelBase
    {
        /// <summary>
        /// 源表名
        /// </summary>
        [CRL.Attribute.Field(FieldIndexType = Attribute.FieldIndexType.非聚集)]
        public string TableName
        {
            get;
            set;
        }
        /// <summary>
        /// 庫名
        /// </summary>
        [CRL.Attribute.Field(FieldIndexType = Attribute.FieldIndexType.非聚集)]
        public string DataBaseName
        {
            get;
            set;
        }
        /// <summary>
        /// 分表數
        /// </summary>
        public int TablePartTotal
        {
            get;
            set;
        }
        /// <summary>
        /// 分表最大數據量
        /// </summary>
        public int MaxPartDataTotal
        {
            get;
            set;
        }
        /// <summary>
        /// 是否為主數據表
        /// 主數據表在當前庫只存在一個
        /// </summary>
        public bool IsMainTable
        {
            get;
            set;
        }

    }
View Code

TablePart 分表

/// <summary>
    /// 分表,主數據表不分表,只按庫分
    /// 其它表按主數據段分
    /// </summary>
    public class TablePart : CRL.IModelBase
    { 
        /// <summary>
        /// 庫名
        /// </summary>
        [CRL.Attribute.Field(FieldIndexType = Attribute.FieldIndexType.非聚集)]
        public string DataBaseName
        {
            get;
            set;
        }
        /// <summary>
        /// 源表名
        /// </summary>
        [CRL.Attribute.Field(FieldIndexType = Attribute.FieldIndexType.非聚集)]
        public string TableName
        {
            get;
            set;
        }
        /// <summary>
        /// 分表名
        /// </summary>
        public string PartName
        {
            get;
            set;
        }
        /// <summary>
        /// 分表索引,從0開始
        /// </summary>
        public int PartIndex
        {
            get;
            set;
        }
        /// <summary>
        /// 主數據開始索引值
        /// </summary>
        public int MainDataStartIndex
        {
            get;
            set;
        }
        /// <summary>
        /// 主數據結束索引值
        /// </summary>
        public int MainDataEndIndex
        {
            get;
            set;
        }
        ///// <summary>
        ///// 分表最大數據量
        ///// </summary>
        //public int MaxPartDataTotal
        //{
        //    get;
        //    set;
        //}
    }
View Code

 

數據定位

通過以上配置,就可以按主索引進行表定位了

過程表示為:初始庫表配置=>按主索引定位庫=>再定位到表

/// <summary>
        /// 初始表
        /// </summary>
        public static void Init()
        {
            _DataBase = DataBaseManage.Instance.QueryList();
            _Table = TableManage.Instance.QueryList();
            _TablePart = TablePartManage.Instance.QueryList();
        }
        /// <summary>
        /// 按主數據索引,確定庫
        /// </summary>
        /// <param name="mainDataIndex"></param>
        /// <returns></returns>
        public static DataBase GetDataBase(int mainDataIndex)
        {
            if (_DataBase.Count() == 0)
            {
                Init();
            }
            var db = _DataBase.Find(b => mainDataIndex >= b.MainDataStartIndex && mainDataIndex <= b.MainDataEndIndex);
            if (db == null)//找屬於哪個庫
            {
                throw new Exception("找不到指定的庫,在主數據索引:" + mainDataIndex);
            }
            return db;
        }
        /// <summary>
        /// 按主數據索引,獲取該查詢位置
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="mainDataIndex"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        public static Location GetLocation(string tableName, int mainDataIndex, DataBase db)
        {
            var table = _Table.Find(b => b.TableName == tableName && b.DataBaseName == db.Name);
            if (table == null)//找哪個表
            {
                throw new Exception(string.Format("找不到指定的表{1}在庫{0}", db.Name, tableName));
            }
            TablePart part;
            //找分表
            if (table.IsMainTable)//如果只是主數據表,只按一個找就行了
            {
                part = _TablePart.Find(b => b.TableName == tableName && b.DataBaseName == db.Name);
            }
            else//其它表,按分表找
            {
                part = _TablePart.Find(b => mainDataIndex >= b.MainDataStartIndex && mainDataIndex <= b.MainDataEndIndex && b.TableName == tableName && b.DataBaseName == db.Name);
            }
            if (part == null)
            {
                throw new Exception(string.Format("找不到指定的表{1}在庫{0}", db.Name, tableName));
            }
            return new Location() { DataBase = db, TablePart = part };
        }

傳入主索引編號,調用定位方法,得到對應的庫和表名稱

var db=GetDataBase(100);//定位庫
var location=GetLocation("Order", 100, db);//定位表

這樣,庫表位置就有了,剩下的就是對數據進行操作了,不過這需要數據訪問框架能動態切換數據連接和映射對象,還好CRL能輕松實現

CRL內部調用比較復雜,這里不寫具體實現了,只列出關鍵方法,詳細實現可加入QQ群獲取最新源代碼

 

數據連接動態切換

因為CRL的數據訪問是在應用層委托實現的,很容易實現,本次升級后增加了分庫的區分

//配置數據連接
            CRL.SettingConfig.GetDbAccess = (type) =>
            {
                //可按type區分數據庫
                if (type.ShardingDataBase != null)
                {
                    if (type.ShardingDataBase.Name == "db1")//定位到DB1
                    {
                        return WebTest.Code.LocalSqlHelper.TestConnection;
                    }
                    else
                    {
                        return WebTest.Code.LocalSqlHelper.TestConnection2;//定位到DB2
                    }
                }
                else
                {
                    return WebTest.Code.LocalSqlHelper.TestConnection;//默認庫
                }
            };

 

分表映射

在CRL內部實現了分表映射,如果傳入了定位,則按定位找到分表名

public static string GetTableName(string tableName, DbContext dbContext)
        {
            if (dbContext != null && dbContext.UseSharding)
            {
                if (dbContext.ShardingMainDataIndex == 0)
                {
                    throw new Exception("未設置分表定位索引,dbContext.ShardingMainDataIndex");
                }
                var location = Sharding.DBService.GetLocation(tableName, dbContext.ShardingMainDataIndex, dbContext.DBLocation.ShardingDataBase);
                tableName = location.TablePart.PartName;
            }
            return tableName;
        }

以上過程表示為

 

整體封裝

和之前CRL業務類封裝一樣,增加了CRL.Sharding.BaseProvider,繼承實現業務對象,就能實現分庫分表了

對比之前,此類刪除了一些無關方法,增加了SetLocation定位方法

以文檔帶的例子講解

會員實現

這里要注意主鍵的問題,不能為自增

public class MemberSharding : CRL.IModel
    {
        [CRL.Attribute.Field(KeepIdentity=true)]//保持插入主鍵
        public int Id
        {
            get;
            set;
        }
        public string Name
        {
            get;
            set;
        }
    }
    public class MemberManage : CRL.Sharding.BaseProvider<MemberSharding>
    {
        public static MemberManage Instance
        {
            get { return new MemberManage(); }
        }
    }

訂單實現

public class OrderSharding : CRL.IModelBase
    {
        public int MemberId
        {
            get;
            set;
        }
        public string Remark
        {
            get;
            set;
        }
    }
    public class OrderManage : CRL.Sharding.BaseProvider<OrderSharding>
    {
        public static OrderManage Instance
        {
            get { return new OrderManage(); }
        }
    }

 

初始庫表配置

以下會創建兩個庫 db1,db2
db1會員編號為1~10 ,db2會員編號為 11~20 ,當插入會員編號小於11的數據,則會定位到db1,11到20則會定位到db2
訂單表OrderSharding設定為最大主數據容量5,1~5編號的會員訂單會放在OrderSharding,6~10則會放到OrderSharding_1

CRL.Sharding.DB.DataBaseManage.Instance.CleanData();
            //創建庫分組
            var db = new CRL.Sharding.DB.DataBase();
            db.Name = "db1";
            db.MaxMainDataTotal = 10;
            CRL.Sharding.DB.DataBaseManage.Instance.Create(db);
            CRL.Sharding.DB.DataBaseManage.Instance.Create(db);
            //創建表
            var dbList = CRL.Sharding.DB.DataBaseManage.Instance.QueryList();
            foreach(var item in dbList)
            {
                var table = new CRL.Sharding.DB.Table();
                table.TableName = "MemberSharding";
                table.IsMainTable = true;
                CRL.Sharding.DB.TableManage.Instance.Create(item, table, out error);

                var table2 = new CRL.Sharding.DB.Table();
                table2.TableName = "OrderSharding";
                table2.IsMainTable = false;
                table2.MaxPartDataTotal = 5;
                CRL.Sharding.DB.TableManage.Instance.Create(item, table2, out error);

                //創建分區
                CRL.Sharding.DB.TablePartManage.Instance.Create(table2, out error);
            }

 

插入會員和訂單測試

插入的會員和訂單會按庫表配置分配到不同的表

這里為了演示,將定位信息保存了

var m = new Code.Sharding.MemberSharding();
            m.Id = Convert.ToInt32(TextBox1.Text);//主索引編號
            var location = CRL.Sharding.DBService.GetLocation("MemberSharding", m.Id);
            m.Name = location.ToString();
            Code.Sharding.MemberManage.Instance.SetLocation(m.Id).Add(m);

            var order = new Code.Sharding.OrderSharding();
            order.MemberId = m.Id;
            var location2 = CRL.Sharding.DBService.GetLocation("OrderSharding", m.Id);
            order.Remark = location2.ToString();
            Code.Sharding.OrderManage.Instance.SetLocation(m.Id).Add(order);
            Label1.Text = "插入會員編號" + m.Id + "," + location + " 訂單" + location2;

 

數據查詢

通過主索引編號定位庫表,就能跟正常查詢一樣操作了

 //會員查詢
var id  = Convert.ToInt32(TextBox1.Text);//主索引編號
            var list = Code.Sharding.OrderManage.Instance.SetLocation(id).QueryList(b => b.MemberId == id);
            GridView1.DataSource = list;
            GridView1.DataBind();
//訂單查詢
 var id = Convert.ToInt32(TextBox1.Text);//主索引編號
            var list = Code.Sharding.MemberManage.Instance.SetLocation(id).QueryList(b => b.Id == id);
            GridView1.DataSource = list;
            GridView1.DataBind();

結果演示如下

分表聯合查詢

查詢當前庫所有分表訂單union結果

設定union方式后,會遍歷分表生成union查詢

var id = Convert.ToInt32(TextBox1.Text);
            var orderManage = Code.Sharding.OrderManage.Instance.SetLocation(id);
            var query = orderManage.GetLambdaQuery();
            query.UnionType = UnionType.UnionAll;//只需設定union方式即可
            var list = orderManage.QueryList(query);
            GridView1.DataSource = list;
            GridView1.DataBind();

 

主索引的問題

以上演示主索引編號為手動輸入,實際業務是需要從索引表產生,CRL.Sharding也增加了索引獲取

CRL.Sharding.DB.DataSequenceManage.Instance.GetSequence();

 

分表結構創建

分表創建貌似比較麻煩,需要手動創建維護,然而不需要,CRL自動幫你創建了,這就是CRL強大之所在,在找不到表結構時,會按對象結構創建

 

分庫分表難點

庫表配置比較容易實現,比較麻煩的地方是從統一入口進行數據操作管理,要達到通用性,框架需要適應不同業務需求,封裝和繼承需要得到很好的支持

 

本方案已封裝在CRL2.4,在文檔示例中也有體現

演示地址:http://crl.changqidongli.com/page/Sharding.aspx

 

CRL最新版2.4代碼在QQ群可以得到,有其它想法歡迎討論,CRL3.0征集思路中,想共同開發的可與我聯系

QQ群:1582632  密語:CRL

CRL框架介紹和早期源碼:http://www.cnblogs.com/hubro/p/4616570.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM