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

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实现大数据分库分表方案的更多相关文章

  1. CRL快速开发框架系列教程十一(大数据分库分表解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

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

    引言 对于一个大型的互联网应用,海量数据的存储和访问成为了系统设计的瓶颈问题,对于系统的稳定性和扩展性造成了极大的问题.通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式. •水 ...

  3. (二)基于shard-jdbc中间件,实现数据分库分表

    基于shard-jdbc中间件,实现数据分库分表 Sharding-JDBC简介 Sharding配置示意图 1.水平分割 1.1 水平分库 1.2 水平分表 2.Shard-jdbc中间件 2.1 ...

  4. Mysql分库分表方案

    Mysql分库分表方案 1.为什么要分表: 当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间. m ...

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

    一.Mysql分库分表方案 1.为什么要分表: 当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间. ...

  6. Mysql 之分库分表方案

    Mysql分库分表方案 为什么要分表 当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间. mysq ...

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

    前言 公司最近在搞服务分离,数据切分方面的东西,因为单张包裹表的数据量实在是太大,并且还在以每天60W的量增长. 之前了解过数据库的分库分表,读过几篇博文,但就只知道个模糊概念, 而且现在回想起来什么 ...

  8. 基于Mysql数据库亿级数据下的分库分表方案

    移动互联网时代,海量的用户数据每天都在产生,基于用户使用数据的用户行为分析等这样的分析,都需要依靠数据都统计和分析,当数据量小时,问题没有暴露出来,数据库方面的优化显得不太重要,一旦数据量越来越大时, ...

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

    Mysql Proxy Lua读写分离设置 一.读写分离说明 读写分离(Read/Write Splitting),基本的原理是让主数据库处理事务性增.改.删操作(INSERT.UPDATE.DELE ...

随机推荐

  1. 闰秒导致MySQL服务器的CPU sys过高

    今天,有个哥们碰到一个问题,他有一个从库,只要是启动MySQL,CPU使用率就非常高,其中sys占比也比较高,具体可见下图. 注意:他的生产环境是物理机,单个CPU,4个Core. 于是,他抓取了CP ...

  2. Asp.net Core中使用Session

    前言 2017年就这么悄无声息的开始了,2017年对我来说又是特别重要的一年. 元旦放假在家写了个Asp.net Core验证码登录, 做demo的过程中遇到两个小问题,第一是在Asp.net Cor ...

  3. ABP文档 - 目录

    ABP框架 概览 介绍 多层结构 模块系统 启动配置 多租户 集成OWIN 共同结构 依赖注入 会话 缓存 日志 设置管理 时间 领域层 实体 值对象(新) 仓储 领域服务 工作单元 领域事件(Eve ...

  4. div实现自适应高度的textarea,实现angular双向绑定

    相信不少同学模拟过腾讯的QQ做一个聊天应用,至少我是其中一个. 过程中我遇到的一个问题就是QQ输入框,自适应高度,最高高度为3row. 如果你也像我一样打算使用textarea,那么很抱歉,你一开始就 ...

  5. 利用bootstrap的carousel.js实现轮播图动画

    前期准备: 1.jquery.js. 2.bootstrap的carousel.js. 3.bootstrap.css. 如果大家不知道在哪下载,可以联系小颖,小颖把这些js和css可以发送给你. 一 ...

  6. 一道返回num值的小题目

    题目描述: 实现fizzBuzz函数,参数num与返回值的关系如下: .如果num能同时被3和5整除,返回字符串fizzbuzz .如果num能被3整除,返回字符串fizz .如果num能被5整除,返 ...

  7. ASP.NET MVC5+EF6+EasyUI 后台管理系统(70)-微信公众平台开发-成为开发者

    系列目录 前言: 一.阅读这段系列之前,你必须花半天时间大致阅读微信公众平台的API文档,我尽量以简短快速的语言与大家分享一个过程 二.借助微信公众平台SDK Senparc.Weixin for C ...

  8. zookeeper源码分析之一服务端启动过程

    zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...

  9. 当web.config文件放置在共享目录下(UNC),启动IIS会提示有错误信息500.19,伴随有错误代码0x80070003和错误代码0x80070005的解决办法

    最近遇到一个很有意思的使用环境,操作人员将所有的网站应用内容投放到共享存储里面,并且使用微软的SMB协议将其以CIFS的方式共享出来,使用Windows Server 2008 R2的IIS将其连接起 ...

  10. Openfiler配置RAC共享存储

    将 Openfiler 用作 iSCSI 存储服务器,主要操作步骤如下: 1.设置 iSCSI 服务 2.配置网络访问 3.指定物理存储器并对其分区 4.创建新的卷组 5.创建所有逻辑卷 6.为每个逻 ...