2014-12-11 4 views
1

Привет, у меня есть простой запрос на одну таблицу, которая выполняется довольно быстро, но я хочу опубликовать свои результаты, а LIMIT замедляет выбор невероятно. Таблица содержит около 80 миллионов строк. Я на postgres 9.2.Предел замедляет мой запрос postgres

Без 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 занимает несколько minuts (я никогда не ждал его до конца)

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 
+0

Не могли бы вы показать нам результаты EXPLAIN ANALYZE? –

+1

Что такое значение 'n_distinct' для этого столбца? 'ВЫБРАТЬ n_distinct FROM pg_stats WHERE имя_таблицы =«интервал»И AttName =«имя пользователя»,' –

+0

@kouber n_distinct = 1460 – wutzebaer

ответ

2

Планировщик ожидает 45438 строк для username «1228321f131084766f3b0c6e40bc5edc41d4677e», в то время как в действительности есть только 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 и т.д.

+0

привет, я попробовал 100 и 1000, но ничего не произошло ... но затем 10000 сделал трюк! и он изменил план запроса, есть ли у вас ссылка на хорошую документацию по этой настройке? – wutzebaer

+0

hm, но с пользователем с 10030 вводами он возвращается к старому тарифному плану, любая идея почему? – wutzebaer

+0

Является ли запрос более эффективным в таком случае, или планировщик по-прежнему не прав? –

1

страница http://thebuild.com/blog/2014/11/18/when-limit-attacks/ показал, что я мог заставить Postgres сделать лучше с помощью КТР

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

Так или иначе, просто умножая значение.Я отредактировал ответ соответственно, включая также ссылку на статистику. –

Смежные вопросы