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 ...
随机推荐
- Zookeeper简单配置
1.配置zookeeper 解压tar包后,在conf目录下,有一个zoo_sample.cfg,拷贝一份并命名为zoo.cfg cp zoo_sample.cfg zoo.cfg 编辑修改数据路径, ...
- Hadoop 2.x 之 HA 简介
HA结构图 HA是用来解决单点故障问题 DN: DataNode,启动时会往所有的NameNode汇报 NN: NameNode(主 Active(一个) 备 Standby(可以有多个)) Jo ...
- 中美贸易战再次开启,世界两极化进程正在加快形成!..... Copyright: 1688澳洲新闻网 Read more at: https://www.1688.com.au/world/international/2018/06/17/369368/
中美贸易战再次开启,世界两极化进程正在加快形成! https://www.1688.com.au/world/international/2018/06/17/369368/
- node cluster模块的使用和测试
首先安装async包 用到的有http.cluster包 http和cluster都会node自带的包,无需安装 1:创建cluster.js,代码如下,更具cpu创建多个进程 var cluster ...
- 了解fiddler:实现简单的抓包测试
fiddler是一款轻型的抓包软件 本文介绍几个常用的功能:(相信图片更直观点,上图片,右键在新标签页中打开,查看高清大图) 通过composer,我们可以修改http头部信息,修改post(),ge ...
- 修改Hosts文件,禁止访问指定网页
不知道Hosts文件什么鬼的朋友可以在网上搜索一下(大牛勿喷- -) 访问网址时,先查询本地的Hosts文件,那么如果我们将Hosts文件中的网址与IP的映射修改之后,将访问错误的IP. 如在文件尾追 ...
- 关于c++中的全局变量(不赋值的全局变量算定义)
定义有三种: 1.不赋值的定义:int a; 2.赋值的定义:int a=5; 或者 int a;a=5; 3.加extern的定义:extern int a=5;//其实和不加是一样的. 声明只有一 ...
- Struts2 - 配置文件中result 节点详解
每个 action 方法都将返回一个 String 类型的值, Struts 将根据这个值来决定响应什么结果. 每个 action 声明都必须包含有数量足够多的 result 元素, 每个 resul ...
- ACM学习历程—HDU 5459 Jesus Is Here(递推)(2015沈阳网赛1010题)
Sample Input 9 5 6 7 8 113 1205 199312 199401 201314 Sample Output Case #1: 5 Case #2: 16 Case #3: 8 ...
- ACM学习历程—HYSBZ 2818 Gcd(欧拉函数 || 莫比乌斯反演)
Description 给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对. Input 一个整数N Output 如题 Sample Input 4 Sam ...