pgsql添加limit变慢
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pgsql添加limit变慢相关的知识,希望对你有一定的参考价值。
为什么mysql分页用limit会越来越慢2021-07-25 10:35:17 作者:程序员阿牛
在mysql中limit可以实现快速分页,但是如果数据到了几百万时我们的limit必须优化才能有效的合理的实现分页了,否则可能卡死你的服务器
阿牛新入职了一家新公司,第一个任务是根据条件导出订单表中的数据到文件中,阿牛心想:这也太简单了,于是很快写好了如下语句,并且告诉测试自己的代码是免测产品。
语句如下:
select * from orders where name=‘lilei' and create_time>'2020-01-01 00:00:00' limit start,end
没想到上线一段时间后,生产开始预警,显示这条sql为慢SQL,执行时间50多秒,严重影响到了业务。
阿牛赶紧请教大佬猿猿帮忙查找原因,猿猿很快就帮其解决了,并且给阿牛做了以下实验:
一、测试实验
mysql分页直接用limit start, count分页语句:
select * from product limit start, count
当起始页较小时,查询没有性能问题,我们分别看下从10, 100, 1000, 10000开始分页的执行时间(每页取20条),如下:
select * from product limit 10, 20 0.016秒
select * from product limit 100, 20 0.016秒
select * from product limit 1000, 20 0.047秒
select * from product limit 10000, 20 0.094秒
我们已经看出随着起始记录的增加,时间也随着增大, 这说明分页语句limit跟起始页码是有很大关系的,
那么我们把起始记录改为40w看下(也就是记录的一半左右)
select * from product limit 400000, 20 3.229秒
再看我们获取最后一页记录的时间
select * from product limit 866613, 20 37.44秒
像这种分页最大的页码页显然这种时间是无法忍受的。
从中我们也能总结出两件事情:
limit语句的查询时间与起始记录的位置成正比。
mysql的limit语句是很方便,但是对记录很多的表并不适合直接使用。
二、 对limit分页问题的性能优化方法
2.1 利用表的覆盖索引来加速分页查询
我们都知道,利用了索引查询的语句中如果只包含了那个索引列(覆盖索引),那么这种情况会查询很快。
因为利用索引查找有优化算法,且数据就在查询索引上面,不用再去找相关的数据地址了,这样节省了很多时间。
另外Mysql中也有相关的索引缓存,在并发高的时候利用缓存就效果更好了。
在我们的例子中,我们知道id字段是主键,自然就包含了默认的主键索引。现在让我们看看利用覆盖索引的查询效果如何:
这次我们之间查询最后一页的数据(利用覆盖索引,只包含id列),如下:
select id from product limit 866613, 20
查询时间为0.2秒,相对于查询了所有列的37.44秒,提升了大概100多倍的速度。
那么如果我们也要查询所有列,有两种方法,
2.2 利用 id>=的形式:
SELECT * FROM product
WHERE ID > =(select id from product limit 866613, 1) limit 20
查询时间为0.2秒,简直是一个质的飞跃啊。
2.3 利用join
SELECT * FROM product a
JOIN (select id from product limit 866613, 20) b ON a.ID = b.id
总结:
是不是认为我没说理由,原因就是使用select * 的情况下直接用limit 600000,10 扫描的是约60万条数据,并且是需要回表60W次,也就是说大部分性能都耗在随机访问上,到头来只用到10条数据,如果先查出来ID,再关联去查询记录,就会快很多,因为索引查找符合条件的ID很快,然后再回表10次。就可以拿到我们想要的数据。
到此这篇关于为什么MySQL分页用limit会越来越慢的文章就介绍到这了,更多相关MySQL分页limit慢内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 参考技术A 您好,您的问题很有意思,PostgreSQL添加limit变慢的原因可能有很多,但是最常见的原因是因为您使用了不正确的索引。如果您的查询中使用了索引,但是查询的结果集仍然很大,那么PostgreSQL就会在查询完成后再执行limit操作,这样就会导致查询变慢。另外,如果您的查询中没有使用索引,那么PostgreSQL就会扫描整个表,然后再执行limit操作,这也会导致查询变慢。因此,为了解决PostgreSQL添加limit变慢的问题,您需要检查您的查询是否使用了正确的索引,如果没有,您可以尝试创建索引来提高查询性能。
使用 PostgreSQL PL/pgSQL 在 For 循环中添加月份
【中文标题】使用 PostgreSQL PL/pgSQL 在 For 循环中添加月份【英文标题】:Add Months in a For Loop using PostgreSQL PL/pgSQL 【发布时间】:2021-09-15 15:25:03 【问题描述】:我正在学习 for 循环并尝试使用 PostgreSQL 创建一个 PL/pgSQL 块,该块将生成一个付款时间表。此循环运行 12 个月,直到余额为 0,并且每个月都会显示到期日期。
这是我目前所拥有的,但月份部分没有添加到 for 循环中,它只添加了一次。
DO $$
DECLARE
start_date DATE := '01-Jan-2021';
payment_due_date DATE;
due_date VARCHAR(25);
mon_pay_amt NUMERIC := 10;
total_mon_amt NUMERIC := 12;
bal_num NUMERIC :=0;
BEGIN
bal_num := mon_pay_amt * total_mon_amt;
due_date := start_date;
RAISE NOTICE 'Total Amount due in 12 Months: %', to_char(bal_num, '$999.99');
FOR i IN 1..total_mon_amt LOOP
bal_num := bal_num - mon_pay_amt;
payment_due_date := start_date + INTERVAL '1 MONTH';
RAISE NOTICE 'Payment Number: %', i ||' '|| 'Due Date: ' ||payment_due_date|| ' Payment Amount : ' || to_char(mon_pay_amt, '$99.99') ||' '||
'Balance ' || to_char(bal_num, '$999.99');
END LOOP;
END $$;
【问题讨论】:
那是因为你不断将'1 month'
添加到start_date
,它被固定为'01-Jan-2021'。您需要在循环中使用i
计数器。比如:start_date + INTERVAL i::varchar || ' ' || MONTH'
.
那么你有答案了吗?
【参考方案1】:
会这样工作:
DO
$$
DECLARE
start_date date := '2021-01-01'; -- always use ISO date format
payment_due_date date := start_date;
mon_pay_amt numeric := 10;
mon_pay_amt_text text := to_char(mon_pay_amt, '$90.99');
total_mon_amt int := 12;
bal_num numeric := mon_pay_amt * total_mon_amt;
BEGIN
RAISE NOTICE 'Total Amount due in 12 Months: %', to_char(bal_num, '$999.99');
FOR i IN 1 .. total_mon_amt LOOP
bal_num := bal_num - mon_pay_amt;
RAISE NOTICE '%', format('Payment Number: %s Due Date: %s Payment Amount: %s Balance %s'
, i
, payment_due_date
, mon_pay_amt_text
, to_char(bal_num, '$990.99'));
payment_due_date := payment_due_date + interval '1 month'; -- increment after?
END LOOP;
END
$$;
但考虑使用 generate_series()
而不是循环的基于集合的方法:
DO
$$
DECLARE
start_date date := '2021-01-01';
payment_due_date date := start_date;
mon_pay_amt numeric := 10;
mon_pay_amt_text text := to_char(mon_pay_amt, '$99.99');
total_mon_amt int := 12;
bal_num numeric := mon_pay_amt * total_mon_amt;
BEGIN
RAISE NOTICE '%'
, (SELECT 'Total Amount due in 12 Months: ' || to_char(bal_num, '$999.99') || E'\n'
|| string_agg(format('Payment Number: %s Due Date: %s Payment Amount: %s Balance: %s'
, i
, payment_due_date + interval '1 month' * (i-1)
, mon_pay_amt_text
, to_char(bal_num - mon_pay_amt * i, '$990.99'))
, E'\n')
FROM generate_series(1, total_mon_amt) i
);
END
$$;
【讨论】:
以上是关于pgsql添加limit变慢的主要内容,如果未能解决你的问题,请参考以下文章
为啥 MySQL 查询在使用 LIMIT 和 Order BY 时会变慢?