分页查询,你真的懂吗?

http://www.cnblogs.com/baochuan/p/4625262.html

  程序员代码的编写能力主要体现在思维的严谨上。有些看起来很简单的东西,里面包含很多很细的点,你能想到吗?

  今天我就简单说一下一个例子,让大家学习到新知识的同时,也养成一种思维的习惯。

问题

  有一张收藏表,里面存储的是用户和图书ID。数据量为1亿。现在要求分页获取所有用户ID(不重复),写下你的sql语句。

  表结构大致如下:

  


1

2

3

4

5

6

7

8

CREATE TABLE 收藏表(

     `id` bigint(20) unsigned NOT NULL auto_increment COMMENT ‘primary key‘,

     `uid` bigint(20) unsigned NOT NULL default 0 COMMENT ‘uid‘,<br>    `status` tinyint(3) unsigned NOT NULL default 0 COMMENT ‘status‘,

     `book_id` bigint(20) unsigned NOT NULL default 0 COMMENT ‘book Id‘,

     `create_time` int(11) unsigned not null default 0 COMMENT ‘create time‘,

      PRIMARY KEY (`id`),

      UNIQUE KEY `uid_book_id` (`uid`, `book_id`),<br>    KEY `uid_status` (`uid`, `status`)

)ENGINED=Innodb Auto_increment=1 default charset=gbk COMMENT ‘用户收藏信息‘;

三种设计

  最容易想到的第一种分页语句是(这也是我们最容易想到的语句):


1

2

select distinct uid from 收藏表 order by uid desc limit 0, 10;

select distinct uid from 收藏表 order by uid desc limit 11, 10;

  再高级点语句,第二种($last_min_uid表示上一次读到的最后一个uid):


1

2

select distinct uid from 收藏表 order by uid desc limit 10;

select distinct uid from 收藏表 where uid < $last_min_uid order by uid desc limit 10;

  最高级的方式


1

2

select uid from 收藏表 group by uid order by uid desc limit 10;

select uid from 收藏表 group by uid having uid < $last_min_uid order by uid desc limit 10;

  

  

分析

  以上三种方式都可以实现分页获取到用户ID列表,那么区别是什么?我现在就把每一种跟大家分析下。

  第一种在业务场景中,会出现丢数据的情况。——这是比较严重的情况,不予采纳。

  具体的业务场景是这样的:当你读取第5页的时候,前四页的用户id列表中,假如有一页的用户ID从库中删除掉,那么你这时读到的第5页(limit 51, 10),就是原来的第6页,你会把1页的用户ID丢失掉。

  第二种的第二条语句,通过explain分析,实际并没有命中唯一索引,而只是命中了一般索引,数据查询范围在7百万级别,故explain建议我们使用group by。——这个查询会有严重的性能问题。

+----+--------------+---------------+-------+-------------------------------------------------------------+-------------+----------+-------+------------+------------------------------------------------------------------------+
| id   | select_type | table           | type  | possible_keys                                                      | key           | key_len | ref     | rows        | Extra                                                                               |
+----+--------------+---------------+-------+-------------------------------------------------------------+---------------------+---------+------+---------+---------------------------------------------------------------------+
| 1    | SIMPLE      | ubook_room | range | uid_book_id                                                       | uid_status  | 4          | NULL | 7066423  | Using where; Using index for group-by; Using temporary; Using filesort | 
+----+--------------+---------------+-------+-------------------------------------------------------------+-------------+----------+-------+------------+------------------------------------------------------------------------+

  

  第三种explain分析,数据查询范围在12万级别(跟第二种相差一个数量级),查询性能高。

+----+---------------+------------+-------+-----------------+-----------------+---------+----------+----------+-------------+
| id   | select_type  | table       | type  | possible_keys  | key               | key_len | ref       | rows      | Extra         |
+----+---------------+------------+-------+-----------------+-----------------+---------+----------+----------+-------------+
| 1    | SIMPLE       | 收藏表      | index | NULL             | uid_book_id   | 12         | NULL  | 121719   | Using index | 
+----+---------------+------------+-------+-----------------+-----------------+---------+----------+----------+-------------+

时间: 2024-10-15 22:51:08

分页查询,你真的懂吗?的相关文章

分页查询不知你是否真正的懂和PHP的正则的应用和一些性能优化

一.不废话太多  直接进入例子. 1  问题: 有一张收藏表,里面存储的是用户和图书ID.数据量为1亿.现在要求分页获取所有用户ID(不重复),写下你的sql语句. 表结构大致如下:           CREATE TABLE 收藏表(              `id` bigint(20) unsigned NOT NULL auto_increment COMMENT 'primary key',              `uid` bigint(20) unsigned NOT NU

MongoDB 分页查询的方法及性能

这篇文章着重的讲讲MongoDB的分页查询 传统的SQL分页 传统的sql分页,所有的方案几乎是绕不开 row_number的,对于需要各种排序,复杂查询的场景,row_number就是杀手锏.另外,针对现在的web很流行的poll/push加载分 页的方式,一般会利用时间戳来实现分页. 这两种分页可以说前者是通用的,连Linq生成的分页都是row_number,可想而知它多通用.后者是无论是性能和复杂程度都是最好的,因为只要简单 的一个时间戳即可. MongoDB分页 进入到Mongo的思路,

C#MongoDB 分页查询的方法及性能

传统的SQL分页 传统的sql分页,所有的方案几乎是绕不开row_number的,对于需要各种排序,复杂查询的场景,row_number就是杀手锏.另外,针对现在的web很流行的poll/push加载分页的方式,一般会利用时间戳来实现分页. 这两种分页可以说前者是通用的,连Linq生成的分页都是row_number,可想而知它多通用.后者是无论是性能和复杂程度都是最好的,因为只要简单的一个时间戳即可. MongoDB分页 进入到Mongo的思路,分页其实并不难,那难得是什么?其实倒也没啥,看明白

分页查询的那些坑和各种技巧

背景 从上周开始我就一直在做数据清洗的工作,这次算是体会到了什么叫做“抛开数据量谈实现就是耍流氓”. 我设计方案和调试代码连接的都是日常环境的数据库,里面的单表数据量在百级,无论我怎么实现都是瞬间洗完.到了性能测试的时候用的就是性能库,双 11 之前@W君做性能测试的时候,往里面写入了 2000W 的数据,足够我战个痛快. 深坑 一开始的时候,分页查询用的是 limit 子句,SQL 语句形态如下. select * from table where xxx in (1,2,3) order b

Mybatis+SpringMVC实现分页查询(附源码)

Maven+Mybatis+Spring+SpringMVC实现分页查询(附源码) 一.项目搭建 关于项目搭建,小宝鸽以前写过一篇Spirng+SpringMVC+Maven+Mybatis+MySQL项目搭建,这篇文章提供了详细的搭建过程,而且提供了源码下载,接下来的将在这个源码的基础上继续开发.所以建议各位猿友可以把猿友下载一下. 二.分页插件的介绍 博主采用的插件是PageHelper这个插件,使用起来十分方便.该插件支持以下数据库: Oracle Mysql MariaDB SQLite

AspNet.WebAPI.OData.ODataPQ实现WebAPI的分页查询服务-(个人拙笔)

AspNet.WebAPI.OData.ODataPQ 这是针对 Asp.net WebAPI OData 协议下,查询分页.或者是说 本人在使用Asp.Net webAPI 做服务接口时写的一个分页查询服务支持的扩展库. 它支持 MS Asp.Net WebAPI OData 协议下获取记录总数进行分页操作. 其实,分页真的简单,简单得不能再简单了.分页总数,每页大小.就够了,但是怎么都找不到 Asp.Net WebAPI OData 协议下获取总记录数的例子 或者找到例子,按照做了N遍,就是

MySQL大数据量分页查询方法及其优化

方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N 适应场景: 适用于数据量较少的情况(元组百/千级) 原因/缺点: 全表扫描,速度会很慢 且 有的数据库结果集返回不稳定(如某次返回1,2,3,另外的一次返回2,1,3). Limit限制的是从结果集的M位置处取出N条输出,其余抛弃. 方法2: 建立主键或唯一索引, 利用索引(假设每页10条) 语句样式: MySQL中,可用如下方法: SELECT * FRO

Hibernate5.1+Sqlserver2000分页查询

前几天改到一个bug:从MS SQLserver上面同步表结构并且采集数据写入其他库.然后用的核心技术是用的Hibernate. 其中bug出在SQLServer2000版本上.排查下来发现2000版本真的是一个让人头疼的数据库. 驱动jar包不兼容:hibernate5.1分页查询也不能用.系统表也与其他版本的天差地别. 一.驱动问题 一开始上网查询,发现大家都推荐用JTDS驱动.但是JTDS貌似不能与官方的Hibernate兼容,需要使用第三方Hibernate. 不然Hibernate在建

Oracle分页查询

一.利用rownum,无order by(最优方案) 如下例查询出来5003行数据,然后扔掉了前面5000行,返回后面的300行.经过测试,此方法成本最低,只嵌套一层,速度最快!即使查询的数据量再大,也几乎不受影响,速度依然. SELECT * FROM (SELECT ROWNUM AS rowno, t.* FROM XXX t WHERE hire_date BETWEEN TO_DATE ('20060501', 'yyyymmdd') AND TO_DATE ('20060731',

QBC查询、离线条件查询(DetachedCriteric)和分页查询模版

一.QBC检索步骤 QBC检索步骤: 1.调用Session的createCriteria()方法创建一个Criteria对象. 2.设定查询条件.Expression类提供了一系列用于设定查询条件的静态方法, 这些静态方法都返回Criterion实例,每个Criterion实例代表一个查询条件. Criteria的add()方法用于加入查询条件. 3.调用Criteria的list()方法执行查询语句.该方法返回List类型的查询结果,在 List集合中存放了符合查询条件的持久化对象. 比较运