MySQL LIMIT 子句
MySQL LIMIT 子句是控制 SELECT 语句返回行数的重要工具。通过指定从结果集中获取的最大行数,它可以让你处理数据子集,尤其是在涉及大表的情况下。该功能可提高查询性能,并通过只获取必要的行来优化资源使用。
MySQL LIMIT 子句的语法
MySQL 中的 LIMIT 子句接受一个或两个参数:偏移量和计数。这两个参数都应该是非负整数。
·偏移量参数表示要从结果集中返回的第一条记录的位置,决定了在返回记录之前要跳过的记录数。
·计数参数指定要从结果集中检索的最大行数,为要返回的行数设置限制。
使用两个参数时,第一个参数代表偏移量,第二个参数代表计数。这样就可以从结果集中检索特定范围的记录。但如果只使用一个参数,则表示从结果集开始返回的记录数。
LIMIT 子句的基本语法如下:
SELECT column1, column2, ... FROM table_name LIMIT offset, count;
如何在查询中使用 ORDER BY 和 LIMIT 子句
在对大数据集进行排序的交互式应用程序中,MySQL ORDER BY 带 LIMIT 是 ORDER BY 最常见的用法。在许多网站上,你会发现热门标签、最近注册的用户等,这通常需要在后端使用带 LIMIT 的 ORDER BY。一般来说,这种 ORDER BY 类型看起来像 SELECT ..... WHERE [conditions] ORDER BY [sort] LIMIT N, M。
确保使用索引。在执行带 LIMIT 的 ORDER BY 时,不对整个结果集进行扫描和排序是非常重要的,因此必须使用索引--在这种情况下,将启动索引范围扫描,并在产生所需数量的记录后立即停止查询执行。
MySQL LIMIT 子句示例
下面我们来看几个如何使用 MySQL LIMIT 子句检索特定结果的示例,包括创建日期、类别 ID 和在多列上运行查询。
使用 MySQL LIMIT 10 按 "date_created"和 "category_id"查找结果集
例如,如果我执行 SELECT * FROM sites ORDER BY date_created DESC LIMIT 10,我将使用 (date_created) 上的索引快速获得结果集。
现在,如果我使用类似 SELECT * FROM sites WHERE category_id=5 ORDER BY date_created DESC LIMIT 10 的方法;
在这种情况下,通过 date_created 索引也可以工作,但它可能不是最有效的--如果是一个罕见的类别,可能要扫描表的大部分内容才能找到 10 条记录。因此,以 (category_id, date_created) 为索引会更好。
让我们来看看更复杂的情况: SELECT * FROM sites WHERE category_id in (5,10,12) ORDER BY date_created DESC LIMIT 10;
尽管看起来与前一个案例非常相似,但由于列表中有多个 category_id 值,因此不能直接使用(category_id、date_created)上的索引,这一点有很大不同。单独使用 date_created 索引仍然有效。从性能角度来看,使用 UNION 解决方法是不错的(尽管有点难看)。
在多列查询中使用 MySQL LIMIT
如果应用可以在许多不同列上执行搜索,但选择性却不尽如人意,该怎么办?各种社交网络和交友网站就是此类查询的完美示例。
SELECT FROM people where gender='m' and age between 18 and 28 and country_id=5 and city_id=345 order by last_online desc limit 10;
限制因素可能有很多,而且都是可选的。这是一个很难解决的问题,可以开发高端的定制搜索解决方案,但如果我们坚持使用简单的 MySQL,在大多数选择性列上使用多个索引将是提高此类查询性能的一个好主意。
例如,可以在(gender,last_online)和(country_id,city_id,last_online)上建立索引,前者假定大多数人会指定性别,后者假定大多数情况下会指定这些。在这种情况下,需要对实际运行的查询和数据选择性进行仔细研究,才能得出一套好的索引,将来也可能需要进行调整。
如果没有通过索引解析的完整 WHERE 子句,则需要注意的主要问题是需要扫描多少行才能解析 order by(这可以在慢查询日志中或通过检查 Hander 统计数据找到)。如果只需检查 50 条记录就能提供 10 行结果集,那么情况还算不错。但如果是 5000 行,则可能需要重新考虑索引。
此外,请注意:根据特定的常量和其他因素,为提供结果集而扫描的记录数量将是动态的。
例如,在我们的示例中,如果我们只使用 (last_online) 索引并查找来自美国的人,那么如果这个国家很小,或者来自这个国家的成员很少(例如斯洛文尼亚),我们可能很快就能找到十个人。
在上面的示例中,我们按最后一列排序。事实上,如果按前导列排序,索引也可用于 ORDER BY。但请注意,用于按列排序的列之后的列不能用于限制结果集。例如
key(a,b,c) SELECT * FROM tbl WHERE c=5 ORDER BY a,b limit 10 - 在这种情况下,可以使用索引中的前两列来满足order by,但索引不会帮助检查 c=5(除非是索引覆盖的查询)。在 (c,a,b) 上建立索引对上述查询更有效。
使用MySQL limit子句的最佳实践
不要按表达式排序
我想这是显而易见的--表达式或函数会阻止索引在 "按......排序 "时的使用。
按驱动表中的列排序
如果使用带 ORDER BY ... LIMIT 的 JOIN,则应尽量使排序列位于驱动表中。有时,这意味着要打破规范化,并在其他表中重复 ORDER BY 要使用的列。
下面是一个 ORDER BY 由第二个表完成的示例,它需要文件排序:
mysql> explain select test.i from test, test t where test.k=5 and test.i=t.k order by t.k limit 5; +----+-------------+-------+------+---------------+------+---------+-------------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-------------+------+---------------------------------+ | 1 | SIMPLE | test | ref | PRIMARY,k | k | 4 | const | 1 | Using temporary; Using filesort | | 1 | SIMPLE | t | ref | k | k | 4 | test.test.i | 1 | Using where; Using index | +----+-------------+-------+------+---------------+------+---------+-------------+------+---------------------------------+ 2 rows in set (0.00 sec)
但是,如果第一张表的访问类型是 "const"或 "system",那么它就会从连接执行中有效地移除(替换为常量),这样,即使 ORDER BY 由第二张表完成,也可以对其进行优化:
mysql> explain select test.i from test, test t where test.i=5 and test.k=t.k order by t.k limit 5; +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | test | const | PRIMARY,k | PRIMARY | 4 | const | 1 | | | 1 | SIMPLE | t | ref | k | k | 4 | const | 1 | Using index | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ 2 rows in set (0.01 sec)
这里的区别是:“i”是主键,而“k”只是索引列。
注意:在某些情况下,即使可以使用索引来执行带 JOIN 的 ORDER BY,MySQL 仍然无法使用它,因为优化器还不够智能,无法检测到这种情况:
mysql> explain select test.i from test, test t where test.k=5 and test.i=t.k order by test.k,t.j limit 5; +----+-------------+-------+------+---------------+------+---------+-------------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-------------+------+---------------------------------+ | 1 | SIMPLE | test | ref | PRIMARY,k | k | 4 | const | 1 | Using temporary; Using filesort | | 1 | SIMPLE | t | ref | k | k | 4 | test.test.i | 1 | Using where; Using index | +----+-------------+-------+------+---------------+------+---------+-------------+------+---------------------------------+ 2 rows in set (0.00 sec)
在这种情况下,表上有索引 (k,j),因此可以在每个表上使用索引来优化排序,或者至少可以对第二个表的每个 t.k=const 值使用局部排序。但这并没有做到。
单个方向排序
如果使用 ORDER BY col1, col2,可以使用索引进行优化。如果使用 ORDER BY col1 DESC、col2 DESC,情况也一样,但如果使用 ORDER BY col1、col2 DESC,MySQL 就必须使用文件排序。经典的解决方案是建立适当排序的索引(按 col1 升序,按 col2 降序),但 MySQL 目前还做不到。目前可以使用的变通方法是单独列保存反向值,这样就可以用 ORDER BY col1, col2_reverse 代替。
小心大的 LIMIT
如果需要对前几条记录进行排序,那么使用索引排序的效率会很高,即使需要进行一些额外的过滤,因此通过索引扫描的记录也会比 LIMIT 要求的多。但是,如果处理的 LIMIT 查询偏移量较大,效率就会受到影响。LIMIT 1000,10 可能比 LIMIT 0,10 慢得多。诚然,大多数用户不会浏览超过 10 页的结果。但是,搜索引擎机器人很可能会这样做。在我的项目中,我就看到过机器人在查看 200 多页。此外,许多网站都没有注意到这一点,这就为发起 DOS 攻击提供了一个非常简单的任务--从很少的连接中获取大量的请求,这就足够了。如果你不做其他任何事情,请确保你阻止了页码过大的请求。
在某些情况下,例如,如果结果是静态的,可能需要预先计算结果,以便查询它们的位置。因此,您可以用 WHERE 位置在 1000 和 1009 之间来代替 LIMIT 1000,10 查询,这样对任何位置(只要有索引)都有相同的效率。
必要时强制索引
在某些情况下,MySQL 优化器可能更倾向于使用不同的索引,它具有更好的选择性或只是更好的估计,而不是支持你进行排序的索引。例如,在查询 SELECT * FROM people WHERE country_id=5 and city_id=6 order by last_online desc limit 10 时,如果在(country_id,city_id)上有索引,在(country_id,last_online)上也有索引,那么即使会导致文件排序,也可能会选择第一个索引。
要解决这个问题,要么扩展你的索引,这样 MySQL 优化器就不必在更好的排序和更好的查找之间做出选择,要么使用 FORCE INDEX 强制它使用适当的索引。
对于 "SELECT ... WHERE [conditions] ORDER BY [sort] LIMIT N "这样的查询,优化器可能会选择索引来解决 ORDER BY,而不是在 WHERE 子句中的列上使用索引。在 MySQL 5.7 中修复了一个bug。
bug:https://bugs.mysql.com/bug.php?id=73837
对于将 ORDER BY 与 LIMIT 结合在一起的查询,优化器可能会切换到适用于 ORDER BY 的索引。在某些情况下,切换的决定基于启发式而非成本。现在,优化器会统一根据成本来决定是否切换。当切换会导致查询读取整个索引或索引的大部分来查找符合条件的行时,这将会带来更好的性能。
使用降序索引
MySQL 8.0 引入了降序索引,按降序存储索引的键值。降序索引可以按正向顺序扫描,这样效率更高。如果查询混合了 ASC 和 DESC,那么如果列上的索引也使用了相应的升序和降序混合列,优化器就可以使用该索引:
SELECT * FROM test ORDER BY k DESC, j ASC;
如果 k 是降序列,j 是升序列,优化器可以在 (k, j) 上使用索引。如果 k 是升序,j 是降序,优化器还可以在这些列上使用索引(使用后向扫描)。
如果多条记录的 ORDER BY 列中的值相同,服务器可按任意顺序返回这些记录。如果必须确保有 LIMIT 和无 LIMIT 时的记录顺序相同,可在 ORDER BY 子句中包含额外的列,使顺序具有确定性。
标签:ORDER,索引,LIMIT,MySQL,test,id From: https://www.cnblogs.com/abclife/p/18160034