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

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. H5坦克大战之【建造敌人的坦克】

      公司这几天在准备新版本的上线,今天才忙里偷闲来写这篇博客.接着上一篇的"H5坦克大战之[玩家控制坦克移动2]"(http://www.cnblogs.com/zhouhuan/ ...

  2. 12306官方火车票Api接口

    2017,现在已进入春运期间,真的是一票难求,深有体会.各种购票抢票软件应运而生,也有购买加速包提高抢票几率,可以理解为变相的黄牛.对于技术人员,虽然写一个抢票软件还是比较难的,但是还是简单看看123 ...

  3. webpack入门教程之Hello webpack(一)

    webpack入门教程系列为官网Tutorials的个人译文,旨在给予想要学习webpack的小伙伴一个另外的途径.如有不当之处,请大家指出. 看完入门教程系列后,你将会学习到如下内容: 1.如何安装 ...

  4. OpenCASCADE Job - dimue

  5. AFNetworking 3.0 源码解读(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking

    我们应该看到过很多类似这样的例子:某个控件拥有加载网络图片的能力.但这究竟是怎么做到的呢?看完这篇文章就明白了. 前言 这篇我们会介绍 AFNetworking 中的3个UIKit中的分类.UIAct ...

  6. 【Python五篇慢慢弹】快速上手学python

    快速上手学python 作者:白宁超 2016年10月4日19:59:39 摘要:python语言俨然不算新技术,七八年前甚至更早已有很多人研习,只是没有现在流行罢了.之所以当下如此盛行,我想肯定是多 ...

  7. Android 在Android代码中执行命令行

    1.路径最好不要是自己拼写的路径/mnt/shell/emulated/0/wifidog.conf 最好是通过方法获取的路径,不然可能导致命令无效  (挂载点的原因) public static f ...

  8. 好用的Markdown编辑器一览 readme.md 编辑查看

    https://github.com/pandao/editor.md https://pandao.github.io/editor.md/examples/index.html Editor.md ...

  9. Configure a bridged network interface for KVM using RHEL 5.4 or later?

    environment Red Hat Enterprise Linux 5.4 or later Red Hat Enterprise Linux 6.0 or later KVM virtual ...

  10. BZOJ 2119: 股市的预测 [后缀数组 ST表]

    2119: 股市的预测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 331  Solved: 153[Submit][Status][Discuss ...