业务SQL那些事--慎用LIMIT

在业务中使用LIMIT限制SQL返回行数是很常见的事情,但如果不知道其中可能的坑或者说真正执行逻辑,就可能会使SQL执行非常慢,严重影响性能。

LIMIT OFFSET, ROW_COUNT实现分页

业务反映一条SQL执行非常慢。简单分析,例如下面的schema与SQL(演示databae:PostgreSQL):

create table t(c1 varchar(20) primary key, c2 int);

select * from t where c1 > '20150224' and c1 < '20160706' and c2 > 1 and c2 <500000000 order by c1 offset $offset limit 5000;(limit $offset, 5000)

其中offset的从0开始,5000递增,最大可以到200W。SQL执行时间就会随着offset的值增加而增加,最终达到业务不可承受的程度。

这条SQL因为主键有序所以省去了order by的SORT,但SQL访问表的时候依然至少需要访问$offset + 5000行数据,扫描行数随着offset增加而增加。而且这是至少需要访问的数据量,那么不难理解为什么SQL会随着offset变大而变慢。

业务是用这条语句实现分页功能,其分页的order c1就是表的主键。所以对于这个查询条件可能会访问大量数据的SQL应该记录last_id来实现分页。改为如下SQL,last_id初始值为'20150224',然后每次获取数据后记录最后一行的c1作为下次的last_id。

select * from t where c1 > $last_id and c1 < '20160706' and c2 > 1 and c2 <500000000 order by c1 limit 5000;

LIMIT ROW_COUNT会性能差

业务遇到一条包含有LIMIT 0, 15的SQL执行时间超过预期。简单分析,schema与SQL如下:

create table t(c1 int, c2 int, c3 int, c4 int, primary key(c1));

create index t_c2_c4_c3 on t(c2, c4, c3);

select * from t where c4 = 1 and c3 <> 9 and c2 > 1 and c2 < 1000000 order by c3, c2 offset 0 limit 15;(limit 0, 15)

业务虽然建立了索引,同时在索引前缀上有约束条件,但是由于满足约束条件的行非常多,同时order by的column不是索引ordering的前缀,所以table层依然需要访问所有满足索引条件的行,同时在过滤后进行SORT操作。Plan如下:

test=# explain verbose select * from t where c4 = 1 and c3 <> 9 and c2 > 1 and c2 < 1000000 order by c3, c2 offset 0 limit 15;
+----------------------------------------------------------------------------------------+
| QUERY PLAN |
+----------------------------------------------------------------------------------------+
| Limit (cost=8.29..8.29 rows=1 width=16) |
| Output: c1, c2, c3, c4 |
| -> Sort (cost=8.29..8.29 rows=1 width=16) |
| Output: c1, c2, c3, c4 |
| Sort Key: t.c3, t.c2 |
| -> Index Scan using t_c2_c4_c3 on public.t (cost=0.15..8.28 rows=1 width=16) |
| Output: c1, c2, c3, c4 |
| Index Cond: ((t.c2 > 1) AND (t.c2 < 1000000) AND (t.c4 = 1)) |
| Filter: (t.c3 <> 9) |
+----------------------------------------------------------------------------------------+

和业务方了解后,c3的值只有3个(0,1,9),即c3 <> 9可以改写为 c3 in (0,1)。同时由于c4是定值,考虑到其他SQL对c4列的使用,决定让业务建立index(c4, c3, c2)。在PostgreSQL中如下:

test=# explain verbose select * from t where c4 = 1 and c3 in(0,1)
and c2 > 1 and c2 < 1000000 order by c3, c2 offset 0 limit 15;
+----------------------------------------------------------------------------------+
| QUERY PLAN |
+----------------------------------------------------------------------------------+
| Limit (cost=0.15..8.28 rows=1 width=16) |
| Output: c1, c2, c3, c4 |
| -> Index Scan using t_c4_c3_c2 on public.t (cost=0.15..8.28 rows=1 width=16) |
| Output: c1, c2, c3, c4 |
| Index Cond: ((t.c4 = 1) AND (t.c2 > 1) AND (t.c2 < 1000000)) |
| Filter: (t.c3 = ANY ('{0,1}'::integer[]))|
+----------------------------------------------------------------------------------+

省去了SORT的代价,同时TABLE只需要找到索引上满足约束条件的15行数据。

不过比较遗憾演示的PostgreSQL没有能利用filter: c3 in (0,1)条件对(c4,c3,c2)生成两个查询范围(1,0,1)~(1,0,1000000)和(1,1,1)~(1,1,1000000),即"C4"=1 AND ("C3"=0 OR "C3"=1) AND "C2">1 AND "C2"<1000000。

业务SQL那些事--慎用LIMIT的更多相关文章

  1. pagehelper使用有误导致sql多了一个limit

    接口测试报告中发现时不时有一个接口报错,但再跑一次又没有这个报错了.报错信息是sql异常,sql中有两个limit.查看后台代码和XXmapper.xml,发现确实只有一个limit.一开始开发以为是 ...

  2. SQL中TOP,LIMIT,ROWNUM的用法

    SQL SERVER/MS Access的Select Top的用法: Select TOP number|percent table_columname FROM tablename MySQL/O ...

  3. (5) 电商场景下的常见业务SQL处理

    1. 如何对评论进行分页展示 一般情况下都是这样写 SELECT customer_id,title,content FROM product_comment WHERE audit_status = ...

  4. 正确使用索引(sql优化),limit分页优化,执行计划,慢日志查询

    查看表相关命令 - 查看表结构   desc 表名- 查看生成表的SQL   show create table 表名- 查看索引   show index from  表名 使用索引和不使用索引 由 ...

  5. 业务需求那些事,使用WCF如何实现业务需求!

    最近遇到一个新项目,需要与硬件结合,进行读取信息并保存在数据库中.业务要求也在昨天发布一个问题,当然感谢许多园内的朋友出谋划策,截图有真相! 关于这个问题,我做了如下假设.目前处于测试状态,代码比较简 ...

  6. 4W条人才表循环处理业务sql优化过程

    场景: 使用windows服务定时更新合同数据:执行存储过程(pas_RefreshContractStatus),但存储过程里面有一个需要更新4W条人才表循环处理业务 问题: 循环更新4W条人才表状 ...

  7. sql语句查询,limit与order by 同时出现,应该order by在前面

    eg:select orderid,status,createtime from orders where appid = :appId and userid = :userId order by c ...

  8. pageHelper的使用步骤,省略sql语句中的limit

    1.引架包.注意版本问题 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId& ...

  9. 业务SQL优化

    1,个人开户报表统计 优化前语句,执行时间80多秒 SELECT a.DA AS f_da, a.account_name AS f_account_name, a.sex AS f_sex, a.n ...

随机推荐

  1. mysql--使用left join条件查询时加where条件的问题

    场景:为一个活动添加指导文件,每一个活动会对应多种指导文件类型,进入每一个活动的设置指导文件页面后所呈现的指导文件类型相同,查询时,使用指导文件类型表LEFT JOIN指导文件表,要求指导文件类型全部 ...

  2. 【PHP】PHP基本语法

    一.什么是PHP? a)    定义:PHP就是超文本预处理器 b)    超文本:我们前边8天学习的内容其实就是超文本内容 c)    预处理器:相当于牛奶在工厂加工的过程,我们虽然不可见,但是我们 ...

  3. 谁给你说的 Ubuntu 不可以输入中文

    文章更新于:2020-04-04 按照惯例,需要的文件附上链接放在文首 文件名:sogoupinyin_2.3.1.0112_amd64.deb 文件大小:25.5 MB 下载链接:https://w ...

  4. 「一闻秒懂」你了解goroutine和channel吗?

    开源库「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 背景介绍 大家都知道进程是操作系统资源分配的基本单位,有独立的内存空间,线程可以共享同一个进 ...

  5. Python常见数据结构-字符串

    字符串基本特点 用引号括起来,单引号双引号均可,使用三个引号创建多行字符串. 字符串不可变. Python3直接支持Unicode编码. Python允许空字符串存在,不含任何字符且长度为0. 字符串 ...

  6. 如何用 Python 绘制玫瑰图等常见疫情图

    新冠疫情已经持续好几个月了,目前,我国疫情已经基本控制住了,而欧美国家正处于爆发期,我们会看到很多网站都提供了多种疫情统计图,今天我们使用 Python 的 pyecharts 框架来绘制一些比较常见 ...

  7. ES6新特性箭头函数和常用function()对比

    // 无参 var fn1 = function() {} var fn1 = () => {} // 单个参数 var fn2 = function(a) {} var fn2 = a =&g ...

  8. stand up meeting 12/10/2015

    part 组员 今日工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云 修改了详细释义的自动换行功能:设计并完成了背景图片的切换功能    6 完成单词释义热度排序    6 PDF Reade ...

  9. C - Max Sum Plus Plus HDU - 1024

    用二位数组dp[i][j]记录组数为i,前j个数字的最大子段和. 转移方程: dp[i][j],考虑第j个数,第j个数可以并到前面那一组,此时dp[i][j]=dp[i][j-1]+arr[j],第j ...

  10. MRCTF Ezpop_Revenge小记

    前言 一道typecho1.2的反序列化,顺便记录一下踩的坑 www.zip获得源码,结构大致如下 flag.php需要ssrf,如果成功会写入session 拿到源码直接去网上先找了一下有没有现成的 ...