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

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

这里就不作详细描述了

分库分表方案基本脱离不了这个结构,受制于实现的难度,好像没有看到有很方便的实现方案框架

为了解决此问题,在CRL框架基础上作了扩展,使CRL能很好实现此方案,以之前了解到的需求,基本能满足了

本方案拆分结构表示为

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

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

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

库表结构配置

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

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

将结构以对象形式表示

DataBase 库

  1. /// <summary>
  2. /// 库
  3. /// 按主数据分垂直划分,将主数据按不同段存在不同库中
  4. /// </summary>
  5. public class DataBase:CRL.IModelBase
  6. {
  7. /// <summary>
  8. /// 库名
  9. /// </summary>
  10. public string Name
  11. {
  12. get;
  13. set;
  14. }
  15. /// <summary>
  16. /// 主数据开始INDEX
  17. /// </summary>
  18. public int MainDataStartIndex
  19. {
  20. get;
  21. set;
  22. }
  23. /// <summary>
  24. /// 主数据结束INDEX
  25. /// </summary>
  26. public int MainDataEndIndex
  27. {
  28. get;
  29. set;
  30. }
  31. /// <summary>
  32. /// 主数据表最大数据量
  33. /// </summary>
  34. public int MaxMainDataTotal
  35. {
  36. get;
  37. set;
  38. }
  39. public override string ToString()
  40. {
  41. return string.Format("名称:{0} 最大主数据量:{1} 索引开始:{2} 结束{3}", Name, MaxMainDataTotal, MainDataStartIndex, MainDataEndIndex);
  42. }
  43. }

Table 表

  1. /// <summary>
  2. /// 表
  3. /// 主数据表不分表,只按库分,其它表再按主数据段分表
  4. /// </summary>
  5. public class Table:CRL.IModelBase
  6. {
  7. /// <summary>
  8. /// 源表名
  9. /// </summary>
  10. [CRL.Attribute.Field(FieldIndexType = Attribute.FieldIndexType.非聚集)]
  11. public string TableName
  12. {
  13. get;
  14. set;
  15. }
  16. /// <summary>
  17. /// 库名
  18. /// </summary>
  19. [CRL.Attribute.Field(FieldIndexType = Attribute.FieldIndexType.非聚集)]
  20. public string DataBaseName
  21. {
  22. get;
  23. set;
  24. }
  25. /// <summary>
  26. /// 分表数
  27. /// </summary>
  28. public int TablePartTotal
  29. {
  30. get;
  31. set;
  32. }
  33. /// <summary>
  34. /// 分表最大数据量
  35. /// </summary>
  36. public int MaxPartDataTotal
  37. {
  38. get;
  39. set;
  40. }
  41. /// <summary>
  42. /// 是否为主数据表
  43. /// 主数据表在当前库只存在一个
  44. /// </summary>
  45. public bool IsMainTable
  46. {
  47. get;
  48. set;
  49. }
  50.  
  51. }

TablePart 分表

  1. /// <summary>
  2. /// 分表,主数据表不分表,只按库分
  3. /// 其它表按主数据段分
  4. /// </summary>
  5. public class TablePart : CRL.IModelBase
  6. {
  7. /// <summary>
  8. /// 库名
  9. /// </summary>
  10. [CRL.Attribute.Field(FieldIndexType = Attribute.FieldIndexType.非聚集)]
  11. public string DataBaseName
  12. {
  13. get;
  14. set;
  15. }
  16. /// <summary>
  17. /// 源表名
  18. /// </summary>
  19. [CRL.Attribute.Field(FieldIndexType = Attribute.FieldIndexType.非聚集)]
  20. public string TableName
  21. {
  22. get;
  23. set;
  24. }
  25. /// <summary>
  26. /// 分表名
  27. /// </summary>
  28. public string PartName
  29. {
  30. get;
  31. set;
  32. }
  33. /// <summary>
  34. /// 分表索引,从0开始
  35. /// </summary>
  36. public int PartIndex
  37. {
  38. get;
  39. set;
  40. }
  41. /// <summary>
  42. /// 主数据开始索引值
  43. /// </summary>
  44. public int MainDataStartIndex
  45. {
  46. get;
  47. set;
  48. }
  49. /// <summary>
  50. /// 主数据结束索引值
  51. /// </summary>
  52. public int MainDataEndIndex
  53. {
  54. get;
  55. set;
  56. }
  57. ///// <summary>
  58. ///// 分表最大数据量
  59. ///// </summary>
  60. //public int MaxPartDataTotal
  61. //{
  62. // get;
  63. // set;
  64. //}
  65. }

数据定位

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

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

  1. /// <summary>
  2. /// 初始表
  3. /// </summary>
  4. public static void Init()
  5. {
  6. _DataBase = DataBaseManage.Instance.QueryList();
  7. _Table = TableManage.Instance.QueryList();
  8. _TablePart = TablePartManage.Instance.QueryList();
  9. }
  10. /// <summary>
  11. /// 按主数据索引,确定库
  12. /// </summary>
  13. /// <param name="mainDataIndex"></param>
  14. /// <returns></returns>
  15. public static DataBase GetDataBase(int mainDataIndex)
  16. {
  17. if (_DataBase.Count() == )
  18. {
  19. Init();
  20. }
  21. var db = _DataBase.Find(b => mainDataIndex >= b.MainDataStartIndex && mainDataIndex <= b.MainDataEndIndex);
  22. if (db == null)//找属于哪个库
  23. {
  24. throw new Exception("找不到指定的库,在主数据索引:" + mainDataIndex);
  25. }
  26. return db;
  27. }
  28. /// <summary>
  29. /// 按主数据索引,获取该查询位置
  30. /// </summary>
  31. /// <param name="tableName"></param>
  32. /// <param name="mainDataIndex"></param>
  33. /// <param name="db"></param>
  34. /// <returns></returns>
  35. public static Location GetLocation(string tableName, int mainDataIndex, DataBase db)
  36. {
  37. var table = _Table.Find(b => b.TableName == tableName && b.DataBaseName == db.Name);
  38. if (table == null)//找哪个表
  39. {
  40. throw new Exception(string.Format("找不到指定的表{1}在库{0}", db.Name, tableName));
  41. }
  42. TablePart part;
  43. //找分表
  44. if (table.IsMainTable)//如果只是主数据表,只按一个找就行了
  45. {
  46. part = _TablePart.Find(b => b.TableName == tableName && b.DataBaseName == db.Name);
  47. }
  48. else//其它表,按分表找
  49. {
  50. part = _TablePart.Find(b => mainDataIndex >= b.MainDataStartIndex && mainDataIndex <= b.MainDataEndIndex && b.TableName == tableName && b.DataBaseName == db.Name);
  51. }
  52. if (part == null)
  53. {
  54. throw new Exception(string.Format("找不到指定的表{1}在库{0}", db.Name, tableName));
  55. }
  56. return new Location() { DataBase = db, TablePart = part };
  57. }

传入主索引编号,调用定位方法,得到对应的库和表名称

  1. var db=GetDataBase();//定位库
  2. var location=GetLocation("Order", , db);//定位表

这样,库表位置就有了,剩下的就是对数据进行操作了,不过这需要数据访问框架能动态切换数据连接和映射对象,还好CRL能轻松实现

CRL内部调用比较复杂,这里不写具体实现了,只列出关键方法,详细实现可加入QQ群获取最新源代码

数据连接动态切换

因为CRL的数据访问是在应用层委托实现的,很容易实现,本次升级后增加了分库的区分

  1. //配置数据连接
  2. CRL.SettingConfig.GetDbAccess = (type) =>
  3. {
  4. //可按type区分数据库
  5. if (type.ShardingDataBase != null)
  6. {
  7. if (type.ShardingDataBase.Name == "db1")//定位到DB1
  8. {
  9. return WebTest.Code.LocalSqlHelper.TestConnection;
  10. }
  11. else
  12. {
  13. return WebTest.Code.LocalSqlHelper.TestConnection2;//定位到DB2
  14. }
  15. }
  16. else
  17. {
  18. return WebTest.Code.LocalSqlHelper.TestConnection;//默认库
  19. }
  20. };

分表映射

在CRL内部实现了分表映射,如果传入了定位,则按定位找到分表名

  1. public static string GetTableName(string tableName, DbContext dbContext)
  2. {
  3. if (dbContext != null && dbContext.UseSharding)
  4. {
  5. if (dbContext.ShardingMainDataIndex == )
  6. {
  7. throw new Exception("未设置分表定位索引,dbContext.ShardingMainDataIndex");
  8. }
  9. var location = Sharding.DBService.GetLocation(tableName, dbContext.ShardingMainDataIndex, dbContext.DBLocation.ShardingDataBase);
  10. tableName = location.TablePart.PartName;
  11. }
  12. return tableName;
  13. }

以上过程表示为

整体封装

和之前CRL业务类封装一样,增加了CRL.Sharding.BaseProvider,继承实现业务对象,就能实现分库分表了

对比之前,此类删除了一些无关方法,增加了SetLocation定位方法

以文档带的例子讲解

会员实现

这里要注意主键的问题,不能为自增

  1. public class MemberSharding : CRL.IModel
  2. {
  3. [CRL.Attribute.Field(KeepIdentity=true)]//保持插入主键
  4. public int Id
  5. {
  6. get;
  7. set;
  8. }
  9. public string Name
  10. {
  11. get;
  12. set;
  13. }
  14. }
  15. public class MemberManage : CRL.Sharding.BaseProvider<MemberSharding>
  16. {
  17. public static MemberManage Instance
  18. {
  19. get { return new MemberManage(); }
  20. }
  21. }

订单实现

  1. public class OrderSharding : CRL.IModelBase
  2. {
  3. public int MemberId
  4. {
  5. get;
  6. set;
  7. }
  8. public string Remark
  9. {
  10. get;
  11. set;
  12. }
  13. }
  14. public class OrderManage : CRL.Sharding.BaseProvider<OrderSharding>
  15. {
  16. public static OrderManage Instance
  17. {
  18. get { return new OrderManage(); }
  19. }
  20. }

初始库表配置

以下会创建两个库 db1,db2
db1会员编号为1~10 ,db2会员编号为 11~20 ,当插入会员编号小于11的数据,则会定位到db1,11到20则会定位到db2
订单表OrderSharding设定为最大主数据容量5,1~5编号的会员订单会放在OrderSharding,6~10则会放到OrderSharding_1

  1. CRL.Sharding.DB.DataBaseManage.Instance.CleanData();
  2. //创建库分组
  3. var db = new CRL.Sharding.DB.DataBase();
  4. db.Name = "db1";
  5. db.MaxMainDataTotal = ;
  6. CRL.Sharding.DB.DataBaseManage.Instance.Create(db);
  7. CRL.Sharding.DB.DataBaseManage.Instance.Create(db);
  8. //创建表
  9. var dbList = CRL.Sharding.DB.DataBaseManage.Instance.QueryList();
  10. foreach(var item in dbList)
  11. {
  12. var table = new CRL.Sharding.DB.Table();
  13. table.TableName = "MemberSharding";
  14. table.IsMainTable = true;
  15. CRL.Sharding.DB.TableManage.Instance.Create(item, table, out error);
  16.  
  17. var table2 = new CRL.Sharding.DB.Table();
  18. table2.TableName = "OrderSharding";
  19. table2.IsMainTable = false;
  20. table2.MaxPartDataTotal = ;
  21. CRL.Sharding.DB.TableManage.Instance.Create(item, table2, out error);
  22.  
  23. //创建分区
  24. CRL.Sharding.DB.TablePartManage.Instance.Create(table2, out error);
  25. }

插入会员和订单测试

插入的会员和订单会按库表配置分配到不同的表

这里为了演示,将定位信息保存了

  1. var m = new Code.Sharding.MemberSharding();
  2. m.Id = Convert.ToInt32(TextBox1.Text);//主索引编号
  3. var location = CRL.Sharding.DBService.GetLocation("MemberSharding", m.Id);
  4. m.Name = location.ToString();
  5. Code.Sharding.MemberManage.Instance.SetLocation(m.Id).Add(m);
  6.  
  7. var order = new Code.Sharding.OrderSharding();
  8. order.MemberId = m.Id;
  9. var location2 = CRL.Sharding.DBService.GetLocation("OrderSharding", m.Id);
  10. order.Remark = location2.ToString();
  11. Code.Sharding.OrderManage.Instance.SetLocation(m.Id).Add(order);
  12. Label1.Text = "插入会员编号" + m.Id + "," + location + " 订单" + location2;

数据查询

通过主索引编号定位库表,就能跟正常查询一样操作了

  1. //会员查询
  2. var id = Convert.ToInt32(TextBox1.Text);//主索引编号
  3. var list = Code.Sharding.OrderManage.Instance.SetLocation(id).QueryList(b => b.MemberId == id);
  4. GridView1.DataSource = list;
  5. GridView1.DataBind();
  6. //订单查询
  7. var id = Convert.ToInt32(TextBox1.Text);//主索引编号
  8. var list = Code.Sharding.MemberManage.Instance.SetLocation(id).QueryList(b => b.Id == id);
  9. GridView1.DataSource = list;
  10. GridView1.DataBind();

结果演示如下

分表联合查询

查询当前库所有分表订单union结果

设定union方式后,会遍历分表生成union查询

  1. var id = Convert.ToInt32(TextBox1.Text);
  2. var orderManage = Code.Sharding.OrderManage.Instance.SetLocation(id);
  3. var query = orderManage.GetLambdaQuery();
  4. query.UnionType = UnionType.UnionAll;//只需设定union方式即可
  5. var list = orderManage.QueryList(query);
  6. GridView1.DataSource = list;
  7. GridView1.DataBind();

主索引的问题

以上演示主索引编号为手动输入,实际业务是需要从索引表产生,CRL.Sharding也增加了索引获取

  1. 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. Sql Server系列:分区表操作

    1. 分区表简介 分区表在逻辑上是一个表,而物理上是多个表.从用户角度来看,分区表和普通表是一样的.使用分区表的主要目的是为改善大型表以及具有多个访问模式的表的可伸缩性和可管理性. 分区表是把数据按设 ...

  2. expect用法

    1. [#!/usr/bin/expect]  这一行告诉操作系统脚本里的代码使用那一个shell来执行.这里的expect其实和linux下的bash.windows下的cmd是一类东西.  注意: ...

  3. transtion:过渡动画

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px Monaco; color: #4f5d66 } p.p2 { margin: 0.0px 0 ...

  4. ajax异步请求

    做前端开发的朋友对于ajax异步更新一定印象深刻,作为刚入坑的小白,今天就和大家一起聊聊关于ajax异步请求的那点事.既然是ajax就少不了jQuery的知识,推荐大家访问www.w3school.c ...

  5. 【SSM框架】Spring + Springmvc + Mybatis 基本框架搭建集成教程

    本文将讲解SSM框架的基本搭建集成,并有一个简单demo案例 说明:1.本文暂未使用maven集成,jar包需要手动导入. 2.本文为基础教程,大神切勿见笑. 3.如果对您学习有帮助,欢迎各种转载,注 ...

  6. [.NET] C# 知识回顾 - 事件入门

    C# 知识回顾 - 事件入门 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6057301.html 序 之前通过<C# 知识回顾 - 委托 de ...

  7. 对Thoughtworks的有趣笔试题实践

    记得2014年在网上看到Thoughtworks的一道笔试题,当时觉得挺有意思,但是没动手去写.这几天又在网上看到了,于是我抽了一点时间写了下,我把程序运行的结果跟网上的答案对了一下,应该是对的,但是 ...

  8. Atitit 解决Unhandled event loop exception错误的办法

    Atitit 解决Unhandled event loop exception错误的办法 查看workspace/.metadata/.log org.eclipse.swt.SWTError: No ...

  9. MMORPG大型游戏设计与开发(攻击区域 扇形)

    距离上次发布已经有了很长一段时间,期间由于各种原因没有更新这方面的技术分享,在这里深表遗憾.在MMO或其他的游戏中,会有针对各种形状的计算,通常在攻击区域里不会很复杂,常见的为矩形.圆形.扇形.今天分 ...

  10. linux常用命令(1)cd命令

    1 命令格式:cd [目录名]2 命令功能切换当前目录至dirName3 常用范例3.1 进入系统根目录cd /3.2 进入上级目录cd ..   或者 cd ..//3.3 进入当前用户主目录当前用 ...