ho le seguenti tabelle:Come posso aumentare la velocità della mia istruzione select Postgres?
CREATE TABLE views (
view_id bigint NOT NULL,
usr_id bigint,
ip inet,
referer_id bigint,
country_id integer,
validated smallint,
completed smallint,
value numeric
);
ALTER TABLE ONLY views
ADD CONSTRAINT "Views_pkey" PRIMARY KEY (view_id);
CREATE TABLE country (
country_id integer NOT NULL,
country character varying(2)
);
ALTER TABLE ONLY country
ADD CONSTRAINT country_pkey PRIMARY KEY (country_id);
CREATE TABLE file_id_view_id (
file_id bigint,
view_id bigint,
created_ts timestamp without time zone
);
CREATE TABLE file_owner (
file_id bigint NOT NULL,
owner_id bigint
);
ALTER TABLE ONLY file_owner
ADD CONSTRAINT owner_table_pkey PRIMARY KEY (file_id);
CREATE TABLE referer (
referer_id bigint NOT NULL,
referer character varying(255)
);
ALTER TABLE ONLY referer
ADD CONSTRAINT referer_pkey PRIMARY KEY (referer_id);
La tabella views
e file_id_view_id
hanno circa 340M righe ogni. Ogni ora aumenteranno entrambe di righe 600K.
La tabella file_owner
ha 75K righe e aumenterà ogni ora mediante righe.
La tabella country
ha righe e cambia raramente.
La tabella referer
ha righe e modifiche raramente.
Il mio obiettivo è quello di essere in grado di eseguire una ricerca del tipo:
SELECT Count(ft.*) AS total_views,
(Count(ft.*) - SUM(ft.valid)) AS invalid_views,
SUM(ft.valid) AS valid_views,
SUM(ft.values) AS VALUES,
ft.day AS day,
(CASE
WHEN r.referer IS NULL THEN 'Unknown'
ELSE r.referer
END) AS referer,
(CASE
WHEN c.country IS NULL THEN 'Unknown'
ELSE c.country
END) AS country
FROM country c
right join (referer r
right join (SELECT v.validated AS valid,
v.value AS VALUES,
vf.day AS day,
vf.view_id AS view_id,
v.referer_id AS referer_id,
v.country_id AS country_id
FROM VIEWS v,
(SELECT view_id,
fivi.created_ts :: timestamp :: DATE AS
day
FROM file_id_view_id fivi
join (SELECT file_id
FROM file_owner
WHERE owner_id = 75
GROUP BY file_id) fo
ON (fo.file_id = fivi.file_id)
WHERE (fivi.created_ts BETWEEN
'2015-11-01' AND '2015-12-01')
GROUP BY view_id,
day) vf
WHERE v.view_id = vf.view_id) ft
ON (ft.referer_id = r.referer_id))
ON (ft.country_id = c.country_id)
GROUP BY day,
referer,
country;
produrre:
total_views | invalid_views | valid_views | values | day | referer | country
------------+---------------+-------------+--------+------------+-----------------+---------
Quando si esegue tale query con EXPLAIN ANALYZE
quanto segue viene prodotto:
GroupAggregate (cost=38893491.99..40443007.61 rows=182295955 width=52) (actual time=183725.696..205882.889 rows=172 loops=1)
Group Key: ((fivi.created_ts)::date), r.referer, c.country
-> Sort (cost=38893491.99..38984639.97 rows=182295955 width=52) (actual time=183725.655..200899.098 rows=8390217 loops=1)
Sort Key: ((fivi.created_ts)::date), r.referer, c.country
Sort Method: external merge Disk: 420192kB
-> Hash Left Join (cost=16340128.88..24989809.75 rows=182295955 width=52) (actual time=23399.900..104337.332 rows=8390217 loops=1)
Hash Cond: (v.country_id = c.country_id)
-> Hash Left Join (cost=16340125.36..24800637.72 rows=182295955 width=49) (actual time=23399.782..102534.655 rows=8390217 loops=1)
Hash Cond: (v.referer_id = r.referer_id)
-> Merge Join (cost=16340033.52..24051874.62 rows=182295955 width=29) (actual time=23397.410..99955.000 rows=8390217 loops=1)
Merge Cond: (fivi.view_id = v.view_id)
-> Group (cost=16340033.41..16716038.36 rows=182295955 width=16) (actual time=23397.298..30454.444 rows=8390217 loops=1)
Group Key: fivi.view_id, ((fivi.created_ts)::date)
-> Sort (cost=16340033.41..16434985.73 rows=189904653 width=16) (actual time=23397.294..28165.729 rows=8390217 loops=1)
Sort Key: fivi.view_id, ((fivi.created_ts)::date)
Sort Method: external merge Disk: 180392kB
-> Nested Loop (cost=6530.43..8799350.01 rows=189904653 width=16) (actual time=63.123..15131.956 rows=8390217 loops=1)
-> HashAggregate (cost=6530.31..6659.62 rows=43104 width=8) (actual time=62.983..90.331 rows=43887 loops=1)
Group Key: file_owner.file_id
-> Bitmap Heap Scan on file_owner (cost=342.90..6508.76 rows=43104 width=8) (actual time=5.407..50.779 rows=43887 loops=1)
Recheck Cond: (owner_id = 75)
Heap Blocks: exact=5904
-> Bitmap Index Scan on owner_id_index (cost=0.00..340.74 rows=43104 width=0) (actual time=4.327..4.327 rows=45576 loops=1)
Index Cond: (owner_id = 75)
-> Index Scan using file_id_view_id_indexing on file_id_view_id fivi (cost=0.11..188.56 rows=4406 width=24) (actual time=0.122..0.306 rows=191 loops=43887)
Index Cond: (file_id = file_owner.file_id)
Filter: ((created_ts >= '2015-11-01 00:00:00'::timestamp without time zone) AND (created_ts <= '2015-12-01 00:00:00'::timestamp without time zone))
Rows Removed by Filter: 184
-> Index Scan using "Views_pkey" on views v (cost=0.11..5981433.17 rows=338958763 width=25) (actual time=0.088..46804.757 rows=213018702 loops=1)
-> Hash (cost=68.77..68.77 rows=6591 width=28) (actual time=2.344..2.344 rows=6495 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 410kB
-> Seq Scan on referer r (cost=0.00..68.77 rows=6591 width=28) (actual time=0.006..1.156 rows=6495 loops=1)
-> Hash (cost=2.70..2.70 rows=233 width=7) (actual time=0.078..0.078 rows=233 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> Seq Scan on country c (cost=0.00..2.70 rows=233 width=7) (actual time=0.005..0.042 rows=233 loops=1)
Planning time: 1.015 ms
Execution time: 206034.660 ms
(37 rows)
Pianificare su explain.depesz.com: http://explain.depesz.com/s/OiN
206s tempo di esecuzione.
Alcune cose da notare,
PostgreSQL Version 9,4
ho regolato la configurazione come segue:
- shared_buffers = 30GB
- work_mem = 32MB
- random_page_cost = 2.0
- cpu_tuple_cost = 0.0030
- cpu_index_tuple_cost = 0.0010
- cpu_operator_cost = 0.0005
- effective_cache_size = 52 GB
esistono attualmente i seguenti indici:
- CREATE INDEX country_index su Country UTILIZZO btree (paese);
- CREATE INDICE created_ts_index ON file_id_view_id UTILIZZO btree (created_ts);
- CREATE INDEX file_id_created_ts_index ON file_id_view_id UTILIZZO btree (created_ts, file_id);
- CREATE INDEX file_id_view_id_indexing ON file_id_view_id UTILIZZO btree (id_file);
- CREATE INDEX owner_id_file_id_index ON file_owner USING btree (id_file, id_proprietà);
- CREATE INDEX owner_id_index ON file_owner USING btree (owner_id);
- CREATE INDEX referer_index ON referer UTILIZZO btree (referer);
La query precedente stava usando un id proprietario che è stato raccolto conservativamente, alcune query possono provocare 1/3 della tabella file_id_view_id essendo unite con vista.
La modifica della struttura dati è una ultima località. In questa fase un tale cambiamento deve essere dovuto a serie preoccupazioni.
Il db può essere considerato come letto solo se necessario, i dati in fase di scrittura vengono eseguiti su base oraria e viene dato ampio spazio a Postgres dopo ogni scrittura. Al momento attuale durante un 600 K ogni ora scrive il db restituisce 1100 (ciò è dovuto ad altri motivi oltre al costo degli inserti). C'è molto spazio per aggiungere ulteriori indici se aumentasse la velocità di lettura, la velocità di lettura è la priorità.
Le specifiche hardware sono:
CPU: http://ark.intel.com/products/83356/Intel-Xeon-Processor-E5-2630-v3-20M-Cache-2_40-GHz
RAM: 128GB
STOCCAGGIO: 1.5TB PCIE SSD
Come posso ottimizzare sia il mio database o la query in modo che io possa recuperare le informazioni che mi servono fuori dal db in un ragionevole lasso di tempo?
Cosa posso fare per ottimizzare il mio attuale design?
Credo che Postgres e l'hardware su cui è in esecuzione abbiano la capacità di funzionare molto meglio di quanto sia attualmente.
UPDATE
ho provato:
- analizzare le tabelle, non ha influenzato le prestazioni.
- Aumentare il work_mem, questo ha comportato un aumento della velocità a 116 s.
- Dipendendo dal pianificatore di query di Postgres evitando i sottoselezionamenti, questa performance ha effetti negativi.
- Dividi look prima della mano, questo non ha apparentemente alcun effetto positivo/negativo.
Qualcuno ha qualche tabella di ristrutturazione dell'esperienza così grande? È fattibile? Ci vorranno giorni, ore (stimando ovviamente)?
Sto considerando la de-normalizzazione del database, dal momento che in questo metodo verrà utilizzato solo il riferimento. La mia unica preoccupazione è che se le righe da 100M dovessero essere chiamate da una tabella con un owner_id indicizzato, sarebbe abbastanza veloce o dovrei ancora affrontare gli stessi problemi di prestazioni? Oserei andare in un modo, quindi tornare indietro.
L'altra soluzione che sto cercando è @ ivan.panasuik suggerimento, raggruppare tutti i dati del giorno in un'altra tabella, poiché una volta che il giorno è passato, tali informazioni sono costanti e non devono essere modificate o aggiornate. Tuttavia, non sono sicuro di come implementarlo senza problemi - dovrei avere interrogazioni che eseguono i dati mentre gli inserimenti sono in attesa e catturare i giorni il più velocemente possibile? Da quel momento in poi hai un trigger?
Le stime non sono proprio così accurate.Hai analizzato le tabelle coinvolte? Hai anche due tipi piuttosto grandi che sono fatti su disco. Puoi provare ad aumentare drasticamente work_mem per quella query_, ad es. 'set work_mem = '512MB'' o anche' set work_mem =' 1GB'' –
Ero sotto l'impressione Postgres analizzerà automaticamente le tabelle, dovrei farlo anche manualmente? Quando dici _quella query_ vuoi dire che esiste un modo specifico per impostare work_mem per una singola query? –
It _should_ lo fa automaticamente, ma a volte (ad es. Dopo un carico iniziale) non calcia abbastanza velocemente. Le affermazioni che ho mostrato eseguendo _before_ la tua query cambieranno 'work_mem' per la sessione corrente: http://www.postgresql.org/docs/current/static/sql-set.html –