高并发大流量的互联网架构,一般通过服务层来访问数据库,随着数据量的增大,数据库需要进行水平切分,分库后将数据分布到不同的数据库实例(甚至物理机器)上,以达到降低数据量,增加实例数的扩容目的。

一旦涉及分库,逃不开“分库依据”patition key的概念,使用哪一个字段来水平切分数据库呢:大部分的业务场景,会使用业务主键id。

确定了分库依据patition key后,接下来要确定的是分库算法:大部分的业务场景,会使用业务主键id取模的算法来分库,这样即能够保证每个库的数据分布是均匀的,又能够保证每个库的请求分布是均匀的,实在是简单实现负载均衡的好方法,此法在互联网架构中应用颇多。

一般有四种方式来进行拉取数据。

第一:

假如:“SELECT * FROM USER  ORDER BY AGE LIMIT 10,5;”这个意思是拉取第3页的数据。我们一般不分库的时候是按照这个方式去进行分页拉取数据的操作。但是分片以后,这样肯定拉取到的是不准确的数据。

DB1:“SELECT * FROM USER  ORDER BY AGE LIMIT 0,15;”  DB2:“SELECT * FROM USER  ORDER BY AGE LIMIT 0,15;” ..... DBn:“SELECT * FROM USER  ORDER BY AGE LIMIT 0,15;”.然后把所有的前3页的数据都拉出来,再内存排序,取第11~15条数据。

第二:

如果我们分页是禁止跳页的

还是第三页 “SELECT * FROM USER  ORDER BY AGE LIMIT 10,5;”

DB1:“SELECT * FROM USER  ORDER BY AGE LIMIT 0,5;”  DB2:“SELECT * FROM USER  ORDER BY AGE LIMIT 0,5;” ..... DBn:“SELECT * FROM USER  ORDER BY AGE LIMIT 0,5;”.然后把所有的前3页的数据都拉出来,再内存排序,取第0~5条数据。这时我们知道第一页最大的是多少了maxN,这时,我们下一页的sql语句就变成DB1:“SELECT * FROM USER WHERE AGE>maxN  ORDER BY AGE LIMIT 5,5;”  DB2:“SELECT * FROM USER WHERE AGE>maxN  ORDER BY AGE LIMIT 5,5;” ..... DBn:“SELECT * FROM USER WHERE AGE>maxN  ORDER BY AGE LIMIT 5,5;”。第三页就根据第二页的最大值去进行查询排序就不用跟第一种一样把前面所有的数据拉出来排序。

第三种就是概率问题了,要5条数据,每个库拉取5/n取整的数据当做最后的数据。

重点来了,第四种:二次查询法。

还是第三页 “SELECT * FROM USER  ORDER BY AGE LIMIT 10,5;”

n标识n个库分了。

改写sql语句:DB1:“SELECT * FROM USER  ORDER BY AGE LIMIT 10/n,5;”  DB2:“SELECT * FROM USER  ORDER BY AGE LIMIT 10/n,5;” ..... DBn:“SELECT * FROM USER  ORDER BY AGE LIMIT 10/n,5;”

查到以后:获取n个库里面最小的记录minAge;

获取到最小minAge以后,再查询一次数据库:

maxAgeDBn,表示的是第n次查询出来的“SELECT * FROM USER  ORDER BY AGE LIMIT 10/n,5;的数据里面的最大值。

DB1:“SELECT * FROM USER  WHERE AGE BETWEEN minAge AND maxAgeDB1 ORDER BY AGE LIMIT 10/n,5;”  DB2:“SELECT * FROM USER WHERE AGE BETWEEN minAge AND maxAgeDB2  ORDER BY AGE LIMIT 10/n,5;” ..... DBn:“SELECT * FROM USER WHERE AGE BETWEEN minAge AND maxAgeDBn  ORDER BY AGE LIMIT 10/n,5;”

把第二次查询的数据按照Age排序,以后可以看出比第一次查询会多出m条数据。

然后我们获取第m+1~m+6条数据就是第3页数据。

list1,list2,list3,假如出来的是3个list。list1没有多出数据,list2多出来2条,list3多出3条。那其实在list1比minAge小的为3条,list2比minAge小的是1条,list3是0条,那么整个数据比minAge小的有3+1+0为4条,那其实minAge在整个的数据中排名第5位。然后我们根据第二次查出的数据排序以后获取第6-11数据就好了。

最后看一下实现的代码:

public static PageResult<T> QueryPage<T>(string sql)
{ if (sql == null || sql.Length == )
{
throw new Exception("参数Sql为空!!");
}
var connections = ShardingConnUtils.GetAllConnection();
if (connections == null || connections.Count == )
{
throw new Exception("请先设置连接字符串!!");
}
var resut = new PageResult<T>();
//SELECT * FROM TSTravelInfo ti WHERE 1=1 ORDER BY ti.TICreateTime ASC LIMIT 5,10;
var offset = ;
var pageSize = ;
var orderBy = string.Empty;
var upperSql = sql.ToUpper();
var isDesc = upperSql.IndexOf(" DESC ")<?false:true;
Regex r = new Regex(" LIMIT ");
var splits = r.Split(upperSql);
if (splits != null && splits.Length > )
{
var number = splits[];
var sizes = number.Trim().TrimEnd(';').Split(',');
if (sizes.Length == )
{
pageSize = Convert.ToInt32(sizes[]);
}
else
{
offset = Convert.ToInt32(sizes[]);
pageSize = Convert.ToInt32(sizes[]);
} var sqlNoPage = splits[];
var newOffset = offset / connections.Count; var listData = new List<T>[connections.Count];
var allCount = ;
for (int i = ; i < connections.Count; i++)
{
var connection = connections[i];
var sqlNew = sqlNoPage + " LIMIT " + newOffset + " ," + pageSize + ";";
var rCount = new Regex(" FROM ");
var sqlCount = "select count(1) FROM " + rCount.Split(sqlNoPage)[];
var data = connection.Query<T>(sqlNew);
allCount += Convert.ToInt32(connection.ExecuteScalar(sqlCount));
listData[i] = data.AsList<T>();
}
var minsList = listData.Select(p => p.FirstOrDefault()).ToList();
orderBy = GetOrderBy(sql);
SortT(minsList, orderBy,isDesc);
var dataMin = minsList.FirstOrDefault();
var value = GetProporyValue<T>(dataMin, orderBy);
var newListData = new List<T>[connections.Count];
var addCount = ;
if (!string.IsNullOrEmpty(orderBy))
{
for (int i = ; i < listData.Length; i++)
{
if (listData[i] == null || !listData[i].Any())
{
newListData[i] = (null);
continue;
}
var maxValue = GetProporyValue<T>(listData[i].Last(), orderBy);
var secendNewSql = string.Empty;
var hasWhere = upperSql.IndexOf(" WHERE ")<?false:true ;
var sqlNoOrder = string.Empty;
Regex rOrderBy = new Regex("ORDER\\s+BY");
var sqlNoOrderList = rOrderBy.Split(sql.ToUpper());
if (hasWhere)
{
sqlNoOrder = sqlNoOrderList[];
}
else
{
sqlNoOrder= sqlNoOrderList[]+" where 1=1 ";
}
var sqlSort =r.Split(sqlNoOrderList[])[]; if (isDesc)
{
secendNewSql = sqlNoOrder + string.Format(" and {0} Between '{1}' and '{2}' Order by {3}", orderBy, maxValue, value,sqlSort);
}
else
{
secendNewSql = sqlNoPage + string.Format(" and {0} Between '{1}' and '{2}' Order by {3}", orderBy, value, maxValue,sqlSort);
}
var secData = connections[i].Query<T>(secendNewSql);
var count = secData.Count() - listData[i].Count();
addCount += count;
newListData[i] = secData.ToList();
}
}
var allOffect = offset * connections.Count - addCount;
var allData = new List<T>();
foreach (var item in newListData)
{
allData.AddRange(item);
}
SortT(allData,orderBy,isDesc);
resut.Result = allData.GetRange(addCount, pageSize);
resut.TotalCount = allCount;
}
return resut;
} private static string GetOrderBy(string sql)
{
var orderBy = string.Empty;
Regex r = new Regex(" LIMIT ");
var splits = r.Split(sql.ToUpper());
if (splits != null && splits.Length > )
{
var r1 = new Regex("ORDER\\s+BY");
var newstr = r1.Split(splits[]);
var b = string.Empty;
if (newstr[].EndsWith("DESC"))
{
b = newstr[].TrimEnd(new char[] { 'D', 'E', 'S', 'C' });
}
if (newstr[].EndsWith("ASC"))
{
b = newstr[].TrimEnd(new char[] { 'A', 'S', 'C' });
}
var index = b.IndexOf('.');
if (index == -)
{
var indexLastNo= sql.ToUpper().LastIndexOf(b); return sql.Substring(indexLastNo, b.Length).Trim();
}
orderBy = b.Substring(index + );
}
var indexLast=sql.ToUpper().LastIndexOf(orderBy);
return sql.Substring(indexLast, orderBy.Length).Trim();
} public static object GetProporyValue<T>(T t, string proName)
{
var type = t.GetType();
return type.GetProperty(proName).GetValue(t);
} private static void SortT<T>(List<T> list, string orderByName,bool isDesc)
{
var newList = new List<T>();
int lastSwapPos = , lastSwapPosTemp = , size = list.Count;
for (int i = ; i < size - ; i++)
{
lastSwapPos = lastSwapPosTemp;
for (int j = size - ; j > lastSwapPos; j--)
{
var type = list[j - ].GetType();
var pro = type.GetProperty(orderByName);
bool isBig = false;
if (pro.PropertyType.IsValueType)
{
var value1 = Convert.ToInt64(pro.GetValue(list[j - ]));
var value2 = Convert.ToInt64(pro.GetValue(list[j]));
isBig = value1 - value2 > ? true : false;
}
else
{
var value1 = pro.GetValue(list[j - ]).ToString();
var value2 = pro.GetValue(list[j]).ToString();
isBig = value1.CompareTo(value1) > ? true : false;
}
if (isDesc)
{
isBig = !isBig;
}
if (isBig)
{
T temp = list[j - ];
list[j - ] = list[j];
list[j] = temp;
lastSwapPosTemp = j;
}
}
if (lastSwapPos == lastSwapPosTemp)
break;
}
}

测试代码:

//注册数据库连接
IDbConnection db0 = new MySqlConnection(ConfigurationManager.ConnectionStrings["db0"].ConnectionString);
IDbConnection db1 = new MySqlConnection(ConfigurationManager.ConnectionStrings["db1"].ConnectionString);
IDbConnection db2 = new MySqlConnection(ConfigurationManager.ConnectionStrings["db2"].ConnectionString); Dictionary<string, IDbConnection> connectionDic = new Dictionary<string, IDbConnection>();
connectionDic.Add("", db0);
connectionDic.Add("", db1);
connectionDic.Add("", db2); ShardingConnUtils.RegisConnGroup(connectionDic); var pages=ShardingCore.QueryPage<User>("select * from User order by Age desc limit 10,5;");

最后说一下,这个是看了沈剑沈老师的公众号知道了,具体的可以看下面的链接:http://mp.weixin.qq.com/s/h99sXP4mvVFsJw6Oh3aU5A。

讲的不好的,大家可以去看这篇文章~_~!!。

Mysql分片后分页排序拉取数据的方法的更多相关文章

  1. canal从mysql拉取数据,并以protobuf的格式往kafka中写数据

    大致思路: canal去mysql拉取数据,放在canal所在的节点上,并且自身对外提供一个tcp服务,我们只要写一个连接该服务的客户端,去拉取数据并且指定往kafka写数据的格式就能达到以proto ...

  2. HBase指定大量列集合的场景下并发拉取数据时卡住的问题排查

    最近遇到一例,HBase 指定大量列集合的场景下,并发拉取数据,应用卡住不响应的情形.记录一下. 问题背景 退款导出中,为了获取商品规格编码,需要从 HBase 表 T 里拉取对应的数据. T 对商品 ...

  3. Spark Streaming中向flume拉取数据

    在这里看到的解决方法 https://issues.apache.org/jira/browse/SPARK-1729 请是个人理解,有问题请大家留言. 其实本身flume是不支持像KAFKA一样的发 ...

  4. 用setTimeout 代替 setInterval实时拉取数据

    在开发中,我们常常碰到需要定时拉取网站数据,如: setInterval(function(){ $.ajax({ url: 'xx', success: function( response ){ ...

  5. Kafka消费者拉取数据异常Unexpected error code 2 while fetching data

    Kafka消费程序间歇性报同一个错: 上网没查到相关资料,只好自己分析.通过进一步分析日志发现,只有在拉取某一个特定的topic的数据时报错,如果拉取其他topic的数据则不会报错.而从这个异常信息来 ...

  6. Mysql日期类型大小比较---拉取给定时间段的记录

    我们知道,mysql里边,日期类型有很多表现形式,date, datetime,timestamp等类型.考虑这样一种场景: 按时间段拉取给定时间段的内容,这时,我们就得使用日期类型的比较了. 表结构 ...

  7. ios开发-程序压后台后,悄悄的抓取数据~~

    我们使用某个app的时候,当我们将程序压到后台之后,我们希望它还能从服务器抓取一些数据,类似微博,微信,qq这些程序压后台 之后,我们依然能看到icon上显示未读数量.但是ios系统是伪多任务操作系统 ...

  8. LoadRunner测试ajax框架,回放后系统中没有产生数据解决方法

    1.QTP11 下载地址:http://www.genilogix.com/downloads/unified-functional-testing/quicktest-professional-11 ...

  9. MySQL中order by排序时,数据存在null咋办

    order by排序是最常用的功能,但是排序有时会遇到数据为空null的情况,这样排序就会乱了,这里以MySQL为例,记录我遇到的问题和解决思路. 问题: 网页要实现table的行鼠标拖拽排序,我用A ...

随机推荐

  1. 算法(Algorithms)第4版 练习 1.3.32

    ADT: /** * see if Steque is empty * @return {@code true} Steque is empty * {@code false} Steque isn' ...

  2. python循环切片

    x = [0,99, 'a', 1, 2, 'b',5, 3, 0,'a' ,1, 8, 5,'b',5,9,5] b=[] c=[] d=[] for i in range(len(x)): if ...

  3. HTML5坦克大战1

    在JavaScript中,不要在变量为定义之前去使用,这样很难察觉并且无法运行. 颜色不对. 当我的坦克移动时,敌人坦克消失. tankGame3.html <!DOCTYPE html> ...

  4. javascript 数组 去重

    javascript数组去重有如下 方法: 一) 利用 数组中的 indexOf判断  例如: Array.prototype.unique=function(){ var n=[]; for(var ...

  5. MVC中Controller弹出提示框

    1.删除时查询是否有先关数据,有提示删除相关数据:成功刷新页面:失败提示删除失败 Controller: 有相关数据:return RedirectToAction("Index" ...

  6. myeclipes如何调试web项目

    你可以右击项目,然后选中那个debug as,然后选择open debug dialog,在project中选择要运行的项目,sever中选择服务器,然后单击debug就ok了,,

  7. 自定义ajax小工具以及使用

    function createXMLHttpRequest(){ try{ return new XMLHttpRequest(); }catch(e){ try{ return new Active ...

  8. 【转】C++11 标准新特性:Defaulted 和 Deleted 函数

    原文链接http://www.ibm.com/developerworks/cn/aix/library/1212_lufang_c11new/ 本文将介绍 C++11 标准的两个新特性:defaul ...

  9. HasnMap的一种遍历方式:Map.Entry 和 Map.entrySet()

    1.Map.Entry 和 Map.entrySet()分别是什么?  Map.entrySet():根据名字便可知道,这是一个集合,是一个映射项的set. Map.Entry<k,v>: ...

  10. android:layout_weight的真实含义/android:layout_gravity的条件

    用layout_weight的时候,不要把宽度(或是高度,你想分配weight的那个)设成match_parent. android:layout_weight只适用于LinearLayout and ...