重磅来袭,使用CRL实现大数据分库分表方案

关于分库分表方案详细介绍

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

这里就不作详细描述了,本方案拆分结构表示为

会员为业务核心,所有业务围绕会员来进行,所以垂直划分用会员编号作索引,将会员分配到不同的库

会员订单增长量是不固定的,所以需要平水拆分,和分库一样,一个表只存指定会员编号区间的订单

了解基本需求,就可以制作方案了,以下主索引表示主数据编号

库表结构配置

进行操作时,需要知道这个数据放在哪个库,哪个表,因此需要把这个划分结构做成可配置,需要配置有:

  • 数据库:一共划分为几个库,主索引区间是多少
  • 数据表:一共有几个分表,每个分表容量是多少
  • 数据表分表:属于哪个表,主索引区间是多少

将结构以对象形式表示

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);
        }
    }

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;
        }

    }

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;
        //}
    }

数据定位

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

过程表示为:初始库表配置=>按主索引定位库=>再定位到表

/// <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

时间: 2024-10-08 09:14:30

重磅来袭,使用CRL实现大数据分库分表方案的相关文章

mysql大数据解决方案--分表分库(0)

引言 对于一个大型的互联网应用,海量数据的存储和访问成为了系统设计的瓶颈问题,对于系统的稳定性和扩展性造成了极大的问题.通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式. •水平切分数据库:可以降低单台机器的负载,同时最大限度的降低了宕机造成的损失: •负载均衡策略:可以降低单台机器的访问负载,降低宕机的可能性: •集群方案:解决了数据库宕机带来的单点数据库不能访问的问题: •读写分离策略:最大限度了提高了应用中读取数据的速度和并发量: 问题描述 1.单个表数据量越大,读

C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 大数据支持分表优化

公司的短信平台,数据量越来越大了,需要对数据进行一些优化,下面是拆分后的数据库量参考. 新开发的软件模块,必须支持分表,拆表的功能一个数据表里,不适合保存1000万以上的记录新开发的业务模块,能分表的全分表,否则,将来我们无法用其他小型数据库,例如mysql 现在系统的短信已经进行了拆表接着打算把日志也进行拆表确保数据库里,没有庞大的表,随时可以切换数据库 每个人把自己负责的事情,做到自己能力的及至,做到部门能力的及至,公司能力的及至,就很有希望了有时候我说话很随意,但是一般会注意,我说出去的话

MySQL分库分表方案

1. MySQL分库分表方案 1.1. 问题: 1.2. 回答: 1.2.1. 最好的切分MySQL的方式就是:除非万不得已,否则不要去干它. 1.2.2. 你的SQL语句不再是声明式的(declarative) 1.2.3. 你招致了大量的网络延时 1.2.4. 你失去了SQL的许多强大能力 1.2.5. MySQL没有API保证异步查询返回顺序结果 1.2.6. 总结 MySQL分库分表方案 翻译一个stackoverflow上的问答,关于分库分表的缺点的,原文链接: MySQL shard

MySQL主从(MySQL proxy Lua读写分离设置,一主多从同步配置,分库分表方案)

Mysql Proxy Lua读写分离设置 一.读写分离说明 读写分离(Read/Write Splitting),基本的原理是让主数据库处理事务性增.改.删操作(INSERT.UPDATE.DELETE),而从数据库处理SELECT查询操作.数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库. 1.设置说明 Master服务器: 192.168.41.196 Slave服务器: 192.168.41.197 Proxy服务器: 192.168.41.203 2.安装Mysql Pro

【分库、分表】MySQL分库分表方案

一.Mysql分库分表方案 1.为什么要分表: 当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间. mysql中有一种机制是表锁定和行锁定,是为了保证数据的完整性.表锁定表示你们都不能对这张表进行操作,必须等我对表操作完才行.行锁定也一样,别的sql必须等我对这条数据操作完了,才能对这条数据进行操作. 2. mysql proxy:amoeba 做mysql集群,利用amoeba. 从上层的ja

MySQL 分库分表方案,总结的非常好!

前言 公司最近在搞服务分离,数据切分方面的东西,因为单张包裹表的数据量实在是太大,并且还在以每天60W的量增长. 之前了解过数据库的分库分表,读过几篇博文,但就只知道个模糊概念, 而且现在回想起来什么都是模模糊糊的. 今天看了一下午的数据库分库分表,看了很多文章,现在做个总结,"摘抄"下来.(但更期待后期的实操) 会从以下几个方面说起: 第一部分:实际网站发展过程中面临的问题. 第二部分:有哪几种切分方式,垂直和水平的区别和适用面. 第三部分:目前市面有的一些开源产品,技术,它们的优缺

如何设计可以动态扩容缩容的分库分表方案?

对于分库分表来说,主要是面对以下问题: 选择一个数据库中间件,调研.学习.测试: 设计你的分库分表的一个方案,你要分成多少个库,每个库分成多少个表,比如 3 个库,每个库 4 个表: 基于选择好的数据库中间件,以及在测试环境建立好的分库分表的环境,然后测试一下能否正常进行分库分表的读写: 完成单库单表到分库分表的迁移,双写方案: 线上系统开始基于分库分表对外提供服务: 扩容了,扩容成 6 个库,每个库需要 12 个表,你怎么来增加更多库和表呢? 是你必须面对的一个事儿,就是你已经弄好分库分表方案

分库分布的几件小事(三)可以动态扩容缩容的分库分表方案

1.扩容与缩容 这个是你必须面对的一个事儿,就是你已经弄好分库分表方案了,然后一堆库和表都建好了,基于分库分表中间件的代码开发啥的都好了,测试都ok了,数据能均匀分布到各个库和各个表里去,而且接着你还通过双写的方案咔嚓一下上了系统,已经直接基于分库分表方案在搞了. 那么现在问题来了,你现在这些库和表又支撑不住了,要继续扩容咋办?这个可能就是说你的每个库的容量又快满了,或者是你的表数据量又太大了,也可能是你每个库的写并发太高了,你得继续扩容. 缩容就是现在业务不景气了,数据量减少,并发量下降,那么

浅谈-分库分表方案

名词解释 库:database:表:table:分库分表:sharding 数据库架构演变 刚开始我们只用单机数据库就够了,随后面对越来越多的请求,我们将数据库的写操作和读操作进行分离, 使用多个从库副本(Slaver Replication)负责读,使用主库(Master)负责写, 从库从主库同步更新数据,保持数据一致.架构上就是数据库主从同步. 从库可以水平扩展,所以更多的读请求不成问题. 但是当用户量级上来后,写请求越来越多,该怎么办?加一个Master是不能解决问题的, 因为数据要保存一