重磅来袭,使用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);
}
}
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() == )
{
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();//定位库
var location=GetLocation("Order", , 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 == )
{
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 = ;
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 = ;
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
重磅来袭,使用CRL实现大数据分库分表方案的更多相关文章
- CRL快速开发框架系列教程十一(大数据分库分表解决方案)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- mysql大数据解决方案--分表分库(0)
引言 对于一个大型的互联网应用,海量数据的存储和访问成为了系统设计的瓶颈问题,对于系统的稳定性和扩展性造成了极大的问题.通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式. •水 ...
- (二)基于shard-jdbc中间件,实现数据分库分表
基于shard-jdbc中间件,实现数据分库分表 Sharding-JDBC简介 Sharding配置示意图 1.水平分割 1.1 水平分库 1.2 水平分表 2.Shard-jdbc中间件 2.1 ...
- Mysql分库分表方案
Mysql分库分表方案 1.为什么要分表: 当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间. m ...
- 【分库、分表】MySQL分库分表方案
一.Mysql分库分表方案 1.为什么要分表: 当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间. ...
- Mysql 之分库分表方案
Mysql分库分表方案 为什么要分表 当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间. mysq ...
- MySQL 分库分表方案,总结的非常好!
前言 公司最近在搞服务分离,数据切分方面的东西,因为单张包裹表的数据量实在是太大,并且还在以每天60W的量增长. 之前了解过数据库的分库分表,读过几篇博文,但就只知道个模糊概念, 而且现在回想起来什么 ...
- 基于Mysql数据库亿级数据下的分库分表方案
移动互联网时代,海量的用户数据每天都在产生,基于用户使用数据的用户行为分析等这样的分析,都需要依靠数据都统计和分析,当数据量小时,问题没有暴露出来,数据库方面的优化显得不太重要,一旦数据量越来越大时, ...
- MySQL主从(MySQL proxy Lua读写分离设置,一主多从同步配置,分库分表方案)
Mysql Proxy Lua读写分离设置 一.读写分离说明 读写分离(Read/Write Splitting),基本的原理是让主数据库处理事务性增.改.删操作(INSERT.UPDATE.DELE ...
随机推荐
- 闰秒导致MySQL服务器的CPU sys过高
今天,有个哥们碰到一个问题,他有一个从库,只要是启动MySQL,CPU使用率就非常高,其中sys占比也比较高,具体可见下图. 注意:他的生产环境是物理机,单个CPU,4个Core. 于是,他抓取了CP ...
- Asp.net Core中使用Session
前言 2017年就这么悄无声息的开始了,2017年对我来说又是特别重要的一年. 元旦放假在家写了个Asp.net Core验证码登录, 做demo的过程中遇到两个小问题,第一是在Asp.net Cor ...
- ABP文档 - 目录
ABP框架 概览 介绍 多层结构 模块系统 启动配置 多租户 集成OWIN 共同结构 依赖注入 会话 缓存 日志 设置管理 时间 领域层 实体 值对象(新) 仓储 领域服务 工作单元 领域事件(Eve ...
- div实现自适应高度的textarea,实现angular双向绑定
相信不少同学模拟过腾讯的QQ做一个聊天应用,至少我是其中一个. 过程中我遇到的一个问题就是QQ输入框,自适应高度,最高高度为3row. 如果你也像我一样打算使用textarea,那么很抱歉,你一开始就 ...
- 利用bootstrap的carousel.js实现轮播图动画
前期准备: 1.jquery.js. 2.bootstrap的carousel.js. 3.bootstrap.css. 如果大家不知道在哪下载,可以联系小颖,小颖把这些js和css可以发送给你. 一 ...
- 一道返回num值的小题目
题目描述: 实现fizzBuzz函数,参数num与返回值的关系如下: .如果num能同时被3和5整除,返回字符串fizzbuzz .如果num能被3整除,返回字符串fizz .如果num能被5整除,返 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(70)-微信公众平台开发-成为开发者
系列目录 前言: 一.阅读这段系列之前,你必须花半天时间大致阅读微信公众平台的API文档,我尽量以简短快速的语言与大家分享一个过程 二.借助微信公众平台SDK Senparc.Weixin for C ...
- zookeeper源码分析之一服务端启动过程
zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...
- 当web.config文件放置在共享目录下(UNC),启动IIS会提示有错误信息500.19,伴随有错误代码0x80070003和错误代码0x80070005的解决办法
最近遇到一个很有意思的使用环境,操作人员将所有的网站应用内容投放到共享存储里面,并且使用微软的SMB协议将其以CIFS的方式共享出来,使用Windows Server 2008 R2的IIS将其连接起 ...
- Openfiler配置RAC共享存储
将 Openfiler 用作 iSCSI 存储服务器,主要操作步骤如下: 1.设置 iSCSI 服务 2.配置网络访问 3.指定物理存储器并对其分区 4.创建新的卷组 5.创建所有逻辑卷 6.为每个逻 ...