Mysql分片后分页排序拉取数据的方法
高并发大流量的互联网架构,一般通过服务层来访问数据库,随着数据量的增大,数据库需要进行水平切分,分库后将数据分布到不同的数据库实例(甚至物理机器)上,以达到降低数据量,增加实例数的扩容目的。
一旦涉及分库,逃不开“分库依据”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分片后分页排序拉取数据的方法的更多相关文章
- canal从mysql拉取数据,并以protobuf的格式往kafka中写数据
大致思路: canal去mysql拉取数据,放在canal所在的节点上,并且自身对外提供一个tcp服务,我们只要写一个连接该服务的客户端,去拉取数据并且指定往kafka写数据的格式就能达到以proto ...
- HBase指定大量列集合的场景下并发拉取数据时卡住的问题排查
最近遇到一例,HBase 指定大量列集合的场景下,并发拉取数据,应用卡住不响应的情形.记录一下. 问题背景 退款导出中,为了获取商品规格编码,需要从 HBase 表 T 里拉取对应的数据. T 对商品 ...
- Spark Streaming中向flume拉取数据
在这里看到的解决方法 https://issues.apache.org/jira/browse/SPARK-1729 请是个人理解,有问题请大家留言. 其实本身flume是不支持像KAFKA一样的发 ...
- 用setTimeout 代替 setInterval实时拉取数据
在开发中,我们常常碰到需要定时拉取网站数据,如: setInterval(function(){ $.ajax({ url: 'xx', success: function( response ){ ...
- Kafka消费者拉取数据异常Unexpected error code 2 while fetching data
Kafka消费程序间歇性报同一个错: 上网没查到相关资料,只好自己分析.通过进一步分析日志发现,只有在拉取某一个特定的topic的数据时报错,如果拉取其他topic的数据则不会报错.而从这个异常信息来 ...
- Mysql日期类型大小比较---拉取给定时间段的记录
我们知道,mysql里边,日期类型有很多表现形式,date, datetime,timestamp等类型.考虑这样一种场景: 按时间段拉取给定时间段的内容,这时,我们就得使用日期类型的比较了. 表结构 ...
- ios开发-程序压后台后,悄悄的抓取数据~~
我们使用某个app的时候,当我们将程序压到后台之后,我们希望它还能从服务器抓取一些数据,类似微博,微信,qq这些程序压后台 之后,我们依然能看到icon上显示未读数量.但是ios系统是伪多任务操作系统 ...
- LoadRunner测试ajax框架,回放后系统中没有产生数据解决方法
1.QTP11 下载地址:http://www.genilogix.com/downloads/unified-functional-testing/quicktest-professional-11 ...
- MySQL中order by排序时,数据存在null咋办
order by排序是最常用的功能,但是排序有时会遇到数据为空null的情况,这样排序就会乱了,这里以MySQL为例,记录我遇到的问题和解决思路. 问题: 网页要实现table的行鼠标拖拽排序,我用A ...
随机推荐
- Oracle的权限管理
--授予系统权限语法 grant system_privilege | all privileges --可以设置select any dictionary权限之外的索引系统权限 to {user i ...
- spring boot: 一般注入说明(四) Profile配置,Environment环境配置 @Profile注解
1.通过设定Environment的ActiveProfile来设置当前context所需要的环境配置,在开发中使用@Profile注解类或方法,达到不同情况下选择实例化不同的Bean. 2.使用jv ...
- java:类集操作总结
java:类集操作总结 1.List接口允许有重复的元素,Set接口中不允许有重复的元素 2.ArrayList,和Vector的区别 3.set依靠equals和hashCode区分 4.TreeS ...
- 9 python 数据类型—字典
字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据.python对key进行哈希函数运算,根据计算的结果决定value的存储地址,所以字典是无序存储的,且key必须是可 ...
- 两个stack实现一个queue
package com.hzins.suanfa; import java.util.Stack; /** * 两个stack实现一个queue * @author Administrator * * ...
- I.MX6 Python3 OpenCV
/************************************************************************* * I.MX6 Python3 OpenCV * ...
- UVA11059 - Maximum Product
1.题目名称 Maximum Product 2.题目地址 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemi ...
- JS性能之滚动条之外的其他部分
问题: 如果一个页面宽高比较大,也就是页面需要滚动条来查看其他页面内容,这时候,在滚动条之外的其他部分,是依然处于运行状态. 比如那部分有视频播放,则那些视频虽然在当前窗口看不到,但它们还是会处于播放 ...
- org.springframework.web.client.HttpClientErrorException: 400 null
异常代码: org.springframework.web.client.HttpClientErrorException: 400 null 已解决. 百度了一下400代表无法解析请求. 说明请求是 ...
- 动态webService
using System; using System.Net; using System.IO; using System.CodeDom; using Microsoft.CSharp; using ...