跨库分页查询终极指南-分库分表 (跨库分页的四种方案)
概述
随着数据库中数据量日益剧增,不得进行分库分表,在分库后将数据分布到不同的数据库实例(甚至物理机器)上,以达到降低数据量,提升系统的处理能力。但是这种架构也带来其他问题,比如本文要讲解的跨库分页查询。全局查询法
以
test
表为例,其中有数据
[1,2,3,4,5,6,7,8]
。在单库的情况下,查询第 2 页数据,每页显示 2 条,语句如下:
sql
select from test order by id limit 2 offset 2
数据返回
[3,4]
。
数据切分后,如果执行同样的语句,可能会出现问题。例如,在节点 1 上执行此语句,返回
[6,8]
,在节点 2 上返回
[5,7]
。然后根据各节点返回的数据进行排序,取前 2 条,返回
[5,6]
,显然与实际结果不符。因此,需要对 SQL 语句进行如下改写:
sql
select from test order by id limit 0 offset 4
然后根据各节点返回的数据,再进行排序,筛选出第 2 页的 2 条。
优点:
保证结果准确性
缺点:
禁止跳页查询
各节点需要返回更多的数据,增加网络开销
禁止跳页查询法
对于数据量庞大、翻页数众多的情况,很多产品并不提供直接跳到指定页面的功能,而只提供下一页功能。这个小小的业务折衷可以极大地降低技术方案的复杂度。 假设
db1
中的值为
[2、4、6、8]
,
db2
中的值为
[1、3、5、7]
,根据
id
进行排序,返回对应的条数,在内存中对各个节点返回的数据进行排序,得到需要的数据。执行以下语句,查询第一页数据,返回结果集为
[1,2]
:
sql
select from test where id>0 order by id limit 2
相比以前的方案,此方案在查询第二页时,需要根据上一页
id
的最大值
id_max
(第一页的最大
id_max
为 2)作为第二页的最小值,语句如下:
sql
select from test where id>2 order by id limit 2
这样,每个节点不再需要返回 4 页数据,只需要返回与第一页一样页数的数据。可以看到,通过对业务的折衷,性能得到极大的提升。
优点:
简化实现
减少网络开销
缺点:
无法跳页查询
允许数据精度损失查询法
本方案使用
patitionkey
进行分库,在数据量较大、数据分布足够随机的情况下,各分库所有非
patitionkey
属性在各个分库上的数据分布,统计概率情况是一致的。例如,在
uid
随机的情况下,使用
uid
取模分两库,
db0
和
db1
:
![精度损失查询法示意图](image.png)
利用这一原理,如上图要查询全局第 2 页数据,
limit2offset2
可改写为
limit1offset1
,每个分库偏移 1(一半),获取 1 条数据(半页),得到的数据集并集,即结果为
[3,4]
。基本可以认为这是全局数据
limit2offset2
的数据,当然实际返回的数据并不完全准确。
根据实际业务经验,用户很少查询第 100 页的网页、帖子、邮件等数据。这一页数据的精准性损失,业务上往往是可以接受的,但技术方案的复杂度便大大降低了,既不需要返回更多的数据,也不需要进行服务内存排序了。
优点:
实现简单
网络开销小
缺点:
可能出现数据精度损失
无法跳页查询
终极大招——二次查询法
以上介绍的方案或多或少都有一定缺点,那么有没有一种方式能够满足业务需要,也能满足性能要求呢?答案是肯定的,那就是二次查询法。 此方案相较于前三个方案理解起来相对复杂一些。为了方便说明,先以单库为例。以下单一库中保存用户年龄数据,1 到 30 岁,总共 30 条。如果要查询: sql select from T order by age limit 5 offset 10 会返回以下粉色标识数据,即
[11-15]
,请记住此结果,下面会讲解如何在分库情况下查询出相同结果。
![单一库数据集](image2.png)
将以上所有数据进行拆分打散存放到 3 个分库中,如下:
![分库数据集](image3.png)
如上文介绍,在单一库中查询
limit5offset10
,返回了
[11-15]
结果。那么,如何在以上三个分库全局查询
limit5offset10
呢?
第一步:语句改写
将
select from T order by age limit 5 offset 10
改写为:
sql
select from T order by age limit 15 offset 10
第二步:分库查询
对每个分库执行改写后的语句,得到如下结果:
分库 1:
[1,2,3,4,5]
分库 2:
[6,7,8,9,10]
分库 3:
[11,12,13,14,15]
第三步:合并结果
将各分库返回的结果数据集合并,形成一个大结果集
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
。
第四步:分页
对合并后的结果集进行分页,取第 2 页的数据,即
[11-15]
。
优点:
保证结果准确性
支持跳页查询
网络开销小
缺点:
实现相对复杂
总结
本文介绍了四种跨库分页查询方案,每种方案都有其优缺点。根据业务场景和性能要求,可选择适合的方案。 | 方案 | 优点 | 缺点 | |---|---|---| | 全局查询法 | 保证结果准确性 | 禁止跳页查询,网络开销大 | | 禁止跳页查询法 | 简化实现,减少网络开销 | 无法跳页查询 | | 允许数据精度损失查询法 | 实现简单,网络开销小 | 可能出现数据精度损失,无法跳页查询 | | 二次查询法 | 保证结果准确性,支持跳页查询,网络开销小 | 实现相对复杂 |MySQL如何实现分库分表,如何提高查询效率
本人没有做过电商平台,但了解其中的道道,今天闲来无事,说说其中的道道。 下边我要开始表演了。 在大型电商网站中,随着业务的增多,数据库中的数据量也是与日俱增,这时候就要将数据库进行分库分表了。 1、如何分库分表?两种解决方案:垂直拆分、水平拆分垂直拆分:根据业务进行拆分,比如可以将一张表中的多个字段拆成两张表,一张是不经常更改的,一张是经常改的。 水平拆分:即根据表来进行分割:比如user表可以拆分为user0,、user1、user2、user3、user4等2、分库分表之后如何实现联合查询?可以使用第三方中间件来实现,比如:mycat、shading-jdbc原理解析:当客户端发送一条sql查询:select * from user;此时中间件会根据有几个子表,拆分成多个语句:select * from user1;select * from user2;select * from user3等多条语句查询,然后将查询的结果返回给中间件,然后汇总给客户端。 这些语句是并发执行的,所以效率会很高哦。 MySQL如何实现分库分表,如何提高查询效率标签:romhandle更改水平mysqlhand方案字段效率
MySQL数据库性能优化之分区分表分库
分表是分散数据库压力的好方法。 分表,最直白的意思,就是将一个表结构分为多个表,然后,可以再同一个库里,也可以放到不同的库。 当然,首先要知道什么情况下,才需要分表。 个人觉得单表记录条数达到百万到千万级别时就要使用分表了。 分表的分类 **1、纵向分表** 将本来可以在同一个表的内容,人为划分为多个表。 (所谓的本来,是指按照关系型数据库的第三范式要求,是应该在同一个表的。 ) 分表理由:根据数据的活跃度进行分离,(因为不同活跃的数据,处理方式是不同的) 案例: 对于一个博客系统,文章标题,作者,分类,创建时间等,是变化频率慢,查询次数多,而且最好有很好的实时性的数据,我们把它叫做冷数据。 而博客的浏览量,回复数等,类似的统计信息,或者别的变化频率比较高的数据,我们把它叫做活跃数据。 所以,在进行数据库结构设计的时候,就应该考虑分表,首先是纵向分表的处理。 这样纵向分表后: 首先存储引擎的使用不同,冷数据使用MyIsam 可以有更好的查询数据。 活跃数据,可以使用Innodb ,可以有更好的更新速度。 其次,对冷数据进行更多的从库配置,因为更多的操作时查询,这样来加快查询速度。 对热数据,可以相对有更多的主库的横向分表处理。 其实,对于一些特殊的活跃数据,也可以考虑使用memcache ,redis之类的缓存,等累计到一定量再去更新数据库。 或者mongodb 一类的nosql 数据库,这里只是举例,就先不说这个。 **2、横向分表** 字面意思,就可以看出来,是把大的表结构,横向切割为同样结构的不同表,如,用户信息表,user_1,user_2等。 表结构是完全一样,但是,根据某些特定的规则来划分的表,如根据用户ID来取模划分。 分表理由:根据数据量的规模来划分,保证单表的容量不会太大,从而来保证单表的查询等处理能力。 案例:同上面的例子,博客系统。 当博客的量达到很大时候,就应该采取横向分割来降低每个单表的压力,来提升性能。 例如博客的冷数据表,假如分为100个表,当同时有100万个用户在浏览时,如果是单表的话,会进行100万次请求,而现在分表后,就可能是每个表进行1万个数据的请求(因为,不可能绝对的平均,只是假设),这样压力就降低了很多很多。 延伸:为什么要分表和分区? 日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表。 这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能会更加糟糕。 分表和表分区的目的就是减少数据库的负担,提高数据库的效率,通常点来讲就是提高表的增删改查效率。 什么是分表? 分表是将一个大表按照一定的规则分解成多张具有独立存储空间的实体表,我们可以称为子表,每个表都对应三个文件,MYD数据文件,索引文件,表结构文件。 这些子表可以分布在同一块磁盘上,也可以在不同的机器上。 app读写的时候根据事先定义好的规则得到对应的子表名,然后去操作它。 什么是分区? 分区和分表相似,都是按照规则分解表。 不同在于分表将大表分解为若干个独立的实体表,而分区是将数据分段划分在多个位置存放,可以是同一块磁盘也可以在不同的机器。 分区后,表面上还是一张表,但数据散列到多个位置了。 app读写的时候操作的还是大表名字,db自动去组织分区的数据。 **MySQL分表和分区有什么联系呢?** 1、都能提高mysql的性高,在高并发状态下都有一个良好的表现。 2、分表和分区不矛盾,可以相互配合的,对于那些大访问量,并且表数据比较多的表,我们可以采取分表和分区结合的方式(如果merge这种分表方式,不能和分区配合的话,可以用其他的分表试),访问量不大,但是表数据很多的表,我们可以采取分区的方式等。 3、分表技术是比较麻烦的,需要手动去创建子表,app服务端读写时候需要计算子表名。 采用merge好一些,但也要创建子表和配置子表间的union关系。 4、表分区相对于分表,操作方便,不需要创建子表。 我们知道对于大型的互联网应用,数据库单表的数据量可能达到千万甚至上亿级别,同时面临这高并发的压力。 Master-Slave结构只能对数据库的读能力进行扩展,写操作还是集中在Master中,Master并不能无限制的挂接Slave库,如果需要对数据库的吞吐能力进行进一步的扩展,可以考虑采用分库分表的策略。 **1、分表** 在分表之前,首先要选中合适的分表策略(以哪个字典为分表字段,需要将数据分为多少张表),使数据能够均衡的分布在多张表中,并且不影响正常的查询。 在企业级应用中,往往使用org_id(组织主键)做为分表字段,在互联网应用中往往是userid。 在确定分表策略后,当数据进行存储及查询时,需要确定到哪张表里去查找数据, 数据存放的数据表 = 分表字段的内容 % 分表数量 **2、分库** 分表能够解决单表数据量过大带来的查询效率下降的问题,但是不能给数据库的并发访问带来质的提升,面对高并发的写访问,当Master无法承担高并发的写入请求时,不管如何扩展Slave服务器,都没有意义了。 我们通过对数据库进行拆分,来提高数据库的写入能力,即所谓的分库。 分库采用对关键字取模的方式,对数据库进行路由。 数据存放的数据库=分库字段的内容%数据库的数量 **3、即分表又分库** 数据库分表可以解决单表海量数据的查询性能问题,分库可以解决单台数据库的并发访问压力问题。 当数据库同时面临海量数据存储和高并发访问的时候,需要同时采取分表和分库策略。 一般分表分库策略如下: 中间变量 = 关键字%(数据库数量*单库数据表数量) 库 = 取整(中间变量/单库数据表数量) 表 = (中间变量%单库数据表数量) 实例: 1、分库分表 很明显,一个主表(也就是很重要的表,例如用户表)无限制的增长势必严重影响性能,分库与分表是一个很不错的解决途径,也就是性能优化途径,现在的案例是我们有一个1000多万条记录的用户表members,查询起来非常之慢,同事的做法是将其散列到100个表中,分别从members0到members99,然后根据mid分发记录到这些表中,牛逼的代码大概是这样子: 复制代码 代码如下: <?php for($i=0;$i< 100; $i++ ){ //echo CREATE TABLE {$i} LIKE ; echo INSERT INTO members{$i} SELECT * FROM members WHERE mid%100={$i}; } ?> 2、不停机修改mysql表结构 同样还是members表,前期设计的表结构不尽合理,随着数据库不断运行,其冗余数据也是增长巨大,同事使用了下面的方法来处理: 先创建一个临时表: /*创建临时表*/ CREATE TABLE members_tmp LIKE members 然后修改members_tmp的表结构为新结构,接着使用上面那个for循环来导出数据,因为1000万的数据一次性导出是不对的,mid是主键,一个区间一个区间的导,基本是一次导出5万条吧,这里略去了 接着重命名将新表替换上去: /*这是个颇为经典的语句哈*/ RENAME TABLE members TO members_bak,members_tmp TO members; 就是这样,基本可以做到无损失,无需停机更新表结构,但实际上RENAME期间表是被锁死的,所以选择在线少的时候操作是一个技巧。 经过这个操作,使得原先8G多的表,一下子变成了2G多。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。