Poniższe sprzężenie ma bardzo różne szacunki wierszy podczas wykonywania łączenia na partycjach w porównaniu do łączenia całej tabeli:
CREATE TABLE m_data.ga_session (
session_id BIGINT NOT NULL,
visitor_id BIGINT NOT NULL,
transaction_id TEXT,
timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
day_id INTEGER NOT NULL,
[...]
device_category TEXT NOT NULL,
[...]
operating_system TEXT
);
Dla wszystkich partycji:
CREATE TABLE IF NOT EXISTS m_data.ga_session_20170127 ( CHECK (day_id = 20170127) ) INHERITS (m_data.ga_session);
-- the identifier are theoretically invalid, but they get truncated to 63 chars and nevertheless work
CREATE INDEX IF NOT EXISTS "ga_session__m_tmp.normalize_device_category(ga_session.device_category)" on m_data.ga_session_20170127 USING btree (m_tmp.normalize_device_category(device_category)) ;
CREATE INDEX IF NOT EXISTS "ga_session__m_tmp.normalize_operating_system(operating_system)" on m_data.ga_session_20170127 USING btree (m_tmp.normalize_operating_system(operating_system)) ;
ANALYZE m_data.ga_session_20170127;
EXPLAIN analyse
SELECT *
FROM m_data.ga_session_20170127 ga_session
JOIN m_dim_next.device ON
device.device_category_name = m_tmp.normalize_device_category(ga_session.device_category)
AND device.operating_system_name = m_tmp.normalize_operating_system(ga_session.operating_system);
Statystyki dla tych indeksów na partycjach są widoczne:
SELECT * FROM pg_stats WHERE tablename ilike 'ga_session_20170127%';
schemaname |tablename |attname |inherited |null_frac |avg_width |n_distinct
-----------|----------------------------------------------------------------|---------------------------|----------|------------|----------|-------------
m_data |ga_session_20170127__m_tmp.normalize_device_category(device_cat |normalize_device_category |false |0 |10 |3
m_data |ga_session_20170127__m_tmp.normalize_operating_system(operating |normalize_operating_system |false |0 |7 |14
To (ze statystykami dotyczącymi indeksu na partycji) skutkuje następującymi (dokładnymi) szacunkami planu zapytań: oszacowano 80146, faktycznie 77503
Hash Join (cost=1.95..6103.53 rows=80146 width=262) (actual time=0.121..117.204 rows=77503 loops=1)
Hash Cond: ((COALESCE(initcap(ga_session.device_category), 'Unknown'::text) = device.device_category_name) AND (COALESCE(replace(ga_session.operating_system, '(not set)'::text, 'Unknown'::text), 'Unknown'::text) = device.operating_system_name))
-> Seq Scan on ga_session_20170127 ga_session (cost=0.00..2975.03 rows=77503 width=224) (actual time=0.010..9.203 rows=77503 loops=1)
-> Hash (cost=1.38..1.38 rows=38 width=38) (actual time=0.064..0.064 rows=38 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 11kB
-> Seq Scan on device (cost=0.00..1.38 rows=38 width=38) (actual time=0.006..0.019 rows=38 loops=1)
Planning time: 1.460 ms
Execution time: 120.098 ms
To, co nie działa, to sprzężenie w całej tabeli, które szacuje całkowicie błędną liczbę wierszy (832 oszacowane w porównaniu z 876237 rzeczywistymi).
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hash Join (cost=1.95..60056.78 rows=832 width=262) (actual time=0.037..1065.778 rows=876237 loops=1)
Hash Cond: ((COALESCE(initcap(ga_session.device_category), 'Unknown'::text) = device.device_category_name) AND (COALESCE(replace(ga_session.operating_system, '(not set)'::text, 'Unknown'::text), 'Unknown'::text) = device.operating_system_name))
-> Append (cost=0.00..33759.37 rows=876238 width=225) (actual time=0.005..132.070 rows=876237 loops=1)
-> Seq Scan on ga_session (cost=0.00..0.00 rows=1 width=319) (actual time=0.000..0.000 rows=0 loops=1)
-> Seq Scan on ga_session_20170125 ga_session_1 (cost=0.00..3648.38 rows=94438 width=226) (actual time=0.005..10.606 rows=94438 loops=1)
-> Seq Scan on ga_session_20170126 ga_session_2 (cost=0.00..3185.81 rows=82581 width=225) (actual time=0.014..8.982 rows=82581 loops=1)
-> Seq Scan on ga_session_20170127 ga_session_3 (cost=0.00..2975.03 rows=77503 width=224) (actual time=0.002..8.797 rows=77503 loops=1)
-> Seq Scan on ga_session_20170128 ga_session_4 (cost=0.00..2936.83 rows=76083 width=225) (actual time=0.003..7.873 rows=76083 loops=1)
-> Seq Scan on ga_session_20170129 ga_session_5 (cost=0.00..3716.18 rows=96618 width=224) (actual time=0.002..9.318 rows=96618 loops=1)
-> Seq Scan on ga_session_20170130 ga_session_6 (cost=0.00..3833.19 rows=99619 width=224) (actual time=0.002..9.453 rows=99619 loops=1)
-> Seq Scan on ga_session_20170131 ga_session_7 (cost=0.00..3488.79 rows=90579 width=225) (actual time=0.002..8.298 rows=90579 loops=1)
-> Seq Scan on ga_session_20170201 ga_session_8 (cost=0.00..3615.58 rows=93958 width=224) (actual time=0.002..9.199 rows=93958 loops=1)
-> Seq Scan on ga_session_20170202 ga_session_9 (cost=0.00..3286.56 rows=85256 width=224) (actual time=0.006..8.021 rows=85256 loops=1)
-> Seq Scan on ga_session_20170203 ga_session_10 (cost=0.00..3073.02 rows=79602 width=225) (actual time=0.002..7.727 rows=79602 loops=1)
-> Hash (cost=1.38..1.38 rows=38 width=38) (actual time=0.016..0.016 rows=38 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 11kB
-> Seq Scan on device (cost=0.00..1.38 rows=38 width=38) (actual time=0.002..0.004 rows=38 loops=1)
Planning time: 1.017 ms
Execution time: 1090.213 ms
To z kolei skutkuje nieprawidłowymi wyborami złączeń (zagnieżdżone pętle), gdy użycie tego sprzężenia skutkuje większą liczbą złączeń (nie pokazano tutaj).
Właściwie miałem niepoprawne oszacowania wierszy również na partycjach przed ANALYSE
ponownym uruchomieniem na partycjach, więc wygląda na to, że planista zapytań nie bierze pod uwagę statystyk opartych na indeksach podczas korzystania z całej tabeli.
Czy jest jakiś sposób, aby planista zapytań zbierał statystyki na poziomie tabeli nadrzędnej lub wziął pod uwagę statystyki poszczególnych partycji podczas tworzenia planu zapytań?
źródło
Odpowiedzi:
Upewnij się, że nie tylko partycje są indeksowane, ale także tabela główna jest indeksowana w ten sam sposób i ANALIZA.
Może to spowodować, że planista będzie uwzględniał oszacowania oparte na indeksie na jednej partycji, ale zignoruje je na poziomie tabeli głównej.
Jeśli brakuje indeksu wyrażeń lub statystyk dla tabeli głównej, planista nie jest w stanie wywnioskować liczności złączeń z tego warunku - nawet jeśli ma doskonałe statystyki dla partycji.
To tylko przypuszczenie, ponieważ nie dostarczyłeś pełnego schematu. Proszę daj mi znać, czy to pomogło.
źródło