Beschreibung
PostgreSQL 9.6 unter Linux, tags_tmp
Tabellengröße ~ 30 GB (10 Millionen Zeilen), tags
ist a text[]
und hat nur 6 Werte.
tags_tmp(id int, tags text[], maker_date timestamp, value text)
id tags maker_date value
1 {a,b,c} 2016-11-09 This is test
2 {a} 2016-11-08 This is test
3 {b,c} 2016-11-07 This is test
4 {c} 2016-11-06 This is test
5 {d} 2016-11-05 This is test
Ich brauche auf Daten mit Filter abzurufen tags
sowie order by
auf maker_date desc
. Kann ich für beide tags & maker_date desc
Spalten einen Index erstellen ?
Wenn nicht, könnten Sie andere Ideen vorschlagen?
Abfragebeispiel
select id, tags, maker_date, value
from tags_tmp
where tags && array['a','b']
order by maker_date desc
limit 5 offset 0
SQL-Code:
create index idx1 on tags_tmp using gin (tags);
create index idx2 on tags_tmp using btree(maker_date desc);
explain (analyse on, costs on, verbose)
select id, tags, maker_date, value
from tags_tmp
where tags && array['funny','inspiration']
order by maker_date desc
limit 5 offset 0 ;
Ergebnis erklären:
Limit (cost=233469.63..233469.65 rows=5 width=116) (actual time=801.482..801.483 rows=5 loops=1)
Output: id, tags, maker_date, value
-> Sort (cost=233469.63..234714.22 rows=497833 width=116) (actual time=801.481..801.481 rows=5 loops=1)
Output: id, tags, maker_date, value
Sort Key: tags_tmp.maker_date DESC
Sort Method: top-N heapsort Memory: 25kB
-> Bitmap Heap Scan on public.tags_tmp (cost=6486.58..225200.81 rows=497833 width=116) (actual time=212.982..696.650 rows=366392 loops=1)
Output: id, tags, maker_date, value
Recheck Cond: (tags_tmp.tags && '{funny,inspiration}'::text[])
Heap Blocks: exact=120034
-> Bitmap Index Scan on idx1 (cost=0.00..6362.12 rows=497882 width=0) (actual time=171.742..171.742 rows=722612 loops=1)
Index Cond: (tags_tmp.tags && '{funny,inspiration}'::text[])
Planning time: 0.185 ms
Execution time: 802.128 ms
Mehr Informationen
Ich habe mit der Verwendung eines Teilindex für nur ein Tag getestet, natürlich ist es schneller. Aber ich habe viele Tags , zum Beispiel : create index idx_tmp on tags_tmp using btree (maker_date desc) where (tags && array['tag1') or tags && array['tag2'] or ... or tags && array['tag6']
. Und ich habe zwischen tags && array['tag1']
und getestet 'tag1' = any(tags)
, die Leistung ist gleich.
text[]
hat nur 6 Werte =a, b, c, d, e, f
. Zum Beispiel:tags={a,b,c}, tags={a}, tags={a,c}, tags={a,b,c,d,e,f}, tags={b,f}
und so weiter. Aber es kann keinen Wert habeng->z, A-Z
und so weiter.create table tags_tmp(id int primary key not null, tags text[] not null, maker_date timestamp not null, value text)
In Bezug auf
distinct
Array-Werte nimmt das,tags
was enthält,a
20% Tabellenzeilenwhere 'a' = any(tags)
, b = 20%where 'b' = any(tags)
, c = 20%where 'c' = any(tags)
, d = 20%where 'd' = any(tags)
, e = 10%where 'e' = any(tags)
, f = 10%where 'f' = any(tags)
.Darüber hinaus
(tags, maker_date)
ist nicht eindeutig.Diese Tabelle ist nicht schreibgeschützt.
Es ist
sort on timestamp
, aber mein Beispiel zeigt Daten, tut mir leid.
Aktuelle Situation: tags = 'a' or tags = 'b' or tags = 'c'
und mehr
(1) Mit GIN index
oder Konvertieren text[] to int[]
sowie Konvertieren text[] to int
und mehr wird der Bitmap-Index für mehrere Tags verwendet. Schließlich entschied ich mich nach dem Testen, eine alte Lösung zu verwenden und OR
in viele UNION
Klauseln zu wechseln , die jeweils UNION
die Anzahl der Daten begrenzen. Natürlich werde ich partial index
für jeden Tag einen Wert erstellen, den ich mit (1) oben kombinieren kann. In Bezug auf OFFSET
wird WHERE
stattdessen eine oder mehrere Bedingungen in Klausel verwendet.
Beispiel
EXPLAIN (ANALYSE ON, costs ON, VERBOSE)
SELECT rs.*
FROM (
(SELECT tags,
id,
maker_date
FROM tags_tmp
WHERE 'a' = any(tags)
AND maker_date <= '2016-03-28 05:43:57.779528'::TIMESTAMP
ORDER BY maker_date DESC LIMIT 5)
UNION
(SELECT tags,
id,
maker_date
FROM tags_tmp
WHERE 'b' = any(tags)
AND maker_date <= '2016-03-28 05:43:57.779528'::TIMESTAMP
ORDER BY maker_date DESC LIMIT 5)
UNION
(SELECT tags,
id,
maker_date
FROM tags_tmp
WHERE 'c' = any(tags)
AND maker_date <= '2016-03-28 05:43:57.779528'::TIMESTAMP
ORDER BY maker_date DESC LIMIT 5)) rs
ORDER BY rs.maker_date DESC LIMIT 5 ;
a:2016-11-09
,b:2016-11-09
,c:2016-11-09
als Baumknoten und alle von ihnen Fügen Sie einen Zeiger auf die Zeile hinzu#1
. MongoDB unterstützt tatsächlich zusammengesetzte Multikey-Indizes ... PostgreSQL tut dies leider nicht, und das ist sehr ärgerlich. Sie müssten eine separate Tabelle mitid_ref | tag | date
erstellen, um einen ähnlichen B-Baum zu erstellen.