限制会减慢我的postgres查询速度

时间:2014-12-11 11:54:12

标签: postgresql limit

嗨我在单个表上有一个简单的查询,运行速度非常快,但我想查看我的结果,而LIMIT让选择速度惊人地降低。该表包含大约80万行。我在postgres 9.2。

没有LIMIT需要330ms并返回2100行

EXPLAIN SELECT * from interval where username='1228321f131084766f3b0c6e40bc5edc41d4677e' order by time desc

Sort  (cost=156599.71..156622.43 rows=45438 width=108)"
  Sort Key: "time""
  ->  Bitmap Heap Scan on "interval"  (cost=1608.05..155896.71 rows=45438 width=108)"
        Recheck Cond: ((username)::text = '1228321f131084766f3b0c6e40bc5edc41d4677e'::text)"
        ->  Bitmap Index Scan on interval_username  (cost=0.00..1605.77 rows=45438 width=0)"
              Index Cond: ((username)::text = '1228321f131084766f3b0c6e40bc5edc41d4677e'::text)

EXPLAIN ANALYZE SELECT * from interval where 
username='1228321f131084766f3b0c6e40bc5edc41d4677e' order by time desc

Sort  (cost=156599.71..156622.43 rows=45438 width=108) (actual time=1.734..1.887 rows=2131 loops=1)
  Sort Key: id
  Sort Method: quicksort  Memory: 396kB
  ->  Bitmap Heap Scan on "interval"  (cost=1608.05..155896.71 rows=45438 width=108) (actual time=0.425..0.934 rows=2131 loops=1)
        Recheck Cond: ((username)::text = '1228321f131084766f3b0c6e40bc5edc41d4677e'::text)
        ->  Bitmap Index Scan on interval_username  (cost=0.00..1605.77 rows=45438 width=0) (actual time=0.402..0.402 rows=2131 loops=1)
              Index Cond: ((username)::text = '1228321f131084766f3b0c6e40bc5edc41d4677e'::text)
Total runtime: 2.065 ms

使用LIMIT需要几分钟(我从不等待它结束)

EXPLAIN SELECT * from interval where username='1228321f131084766f3b0c6e40bc5edc41d4677e' order by time desc LIMIT 10

Limit  (cost=0.00..6693.99 rows=10 width=108)
  ->  Index Scan Backward using interval_time on "interval"  (cost=0.00..30416156.03 rows=45438 width=108)
        Filter: ((username)::text = '1228321f131084766f3b0c6e40bc5edc41d4677e'::text)

表格定义

-- Table: "interval"

-- DROP TABLE "interval";

CREATE TABLE "interval"
(
  uuid character varying(255) NOT NULL,
  deleted boolean NOT NULL,
  id bigint NOT NULL,
  "interval" bigint NOT NULL,
  "time" timestamp without time zone,
  trackerversion character varying(255),
  username character varying(255),
  CONSTRAINT interval_pkey PRIMARY KEY (uuid),
  CONSTRAINT fk_272h71b2gfyov9fwnksyditdd FOREIGN KEY (username)
      REFERENCES appuser (panelistcode) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE,
  CONSTRAINT uk_hyi5iws50qif6jwky9xcch3of UNIQUE (id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE "interval"
  OWNER TO postgres;

-- Index: interval_time

-- DROP INDEX interval_time;

CREATE INDEX interval_time
  ON "interval"
  USING btree
  ("time");

-- Index: interval_username

-- DROP INDEX interval_username;

CREATE INDEX interval_username
  ON "interval"
  USING btree
  (username COLLATE pg_catalog."default");

-- Index: interval_uuid

-- DROP INDEX interval_uuid;

CREATE INDEX interval_uuid
  ON "interval"
  USING btree
  (uuid COLLATE pg_catalog."default");

进一步的结果

SELECT n_distinct FROM pg_stats WHERE tablename='interval' AND attname='username';
n_distinct=1460

SELECT AVG(length) FROM (SELECT username, COUNT(*) AS length FROM interval GROUP BY username) as freq;
45786.022605591910

SELECT COUNT(*) FROM interval WHERE username='1228321f131084766f3b0c6e40bc5edc41d4677e';
2131

2 个答案:

答案 0 :(得分:3)

规划人员期待username' 1228321f131084766f3b0c6e40bc5edc41d4677e'行的45438行,而实际上它只有2131行,因此它认为通过向后看会找到你想要的10行通过interval_time索引。

在用户名栏上尝试increasing the stats,看看查询计划是否会发生变化。

ALTER TABLE interval ALTER COLUMN username SET STATISTICS 100;

ANALYZE interval;

您可以尝试不同的统计值,最高可达10000。

如果您对该计划仍然不满意并且肯定您可以比计划者更好并知道您在做什么,那么您可以绕过任何通过对其进行一些不改变其值的操作来轻松索引。

例如,您可以使用ORDER BY time代替ORDER BY time + '0 seconds'::interval,而不是time。这样,将绕过表中存储的{{1}}值的任何索引。对于整数值,您可以乘以* 1等。

答案 1 :(得分:2)

页面http://thebuild.com/blog/2014/11/18/when-limit-attacks/显示我可以通过使用CTE强制postgres做得更好

WITH inner_query AS (SELECT * from interval where username='7823721a3eb9243be63c6c3a13dffee44753cda6')
SELECT * FROM inner_query order by time desc LIMIT 10;