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

一旦涉及分库,逃不开“分库依据”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版 练习 2.2.23

    测试结果: 算法(Algorithms)第4版 练习 2.2.10 算法(Algorithms)第4版 练习 2.2.11(1) 算法(Algorithms)第4版 练习 2.2.11(2) 算法(A ...

  2. linux挂载/卸载优盘

    linux载入优盘 查看优盘 ls /dev/ sdb1 挂载到/mnt/usb目录 mount /dev/sdb1 /mnt/usb ntfs优盘需要安装ntfs-3g mount -t ntfs- ...

  3. 分享知识-快乐自己:Linux下安装 erlang 及 RabbitmMQ

    Linux下安装 erlang 及 RabbitmMQ: 下载地址一  下载地址二 下载地址三 安装依赖: yum install ncurses-devel 安装 erlang: 1):下载Erla ...

  4. linux应用之make命令详解

    从源代码安装过软件的朋友一定对 ./configure && make && make install 安装三步曲非常熟悉了.然而究竟这个过程中的每一步幕后都发生了些什 ...

  5. django admin后台显示H5颜色选项卡

    一般使用django后台,特别是有颜色的字段的时候避免手动输入一大推的颜色,可以使用h5颜色选项卡,这样用户就直接可以通过选项卡来选择自己所需的颜色.这个时候可以通过后台admin form来进行定制 ...

  6. html网页小图标

    <link rel="shortcut icon" href="../image/favicon.ico"/>

  7. BEC listen and translation exercise 45

    So the Counselling Services we offer deal with any problems arising from your studies or in your lif ...

  8. 【leetcode刷题笔记】Remove Duplicates from Sorted Array II

    Follow up for "Remove Duplicates":What if duplicates are allowed at most twice? For exampl ...

  9. javascript设计模式和构造函数返回值

    工厂模式 function createPerson(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = ...

  10. ONVIF协议测试工具 ONVIF Device Test Tool 29 12.12 最新版

    ONVIF协议测试工具 ONVIF Device Test Tool 29 12.12 最新版 包含文档和工具,本人亲测,好用! http://download.csdn.net/detail/li_ ...