Порядок стовпців у складеному індексі в PostgreSQL (і порядок запитів)


10

У мене є таблиця з рядками 50K. Це насправді таблиця PostGIS.

Запит складається з 4 частин (1 обов'язкова) (3 необов’язково)

  1. перетинна коробка (географічний прямокутник) з 4 латом, довгий (я використовую st_intersects) [Обов’язково]
  2. Діапазон дат (хв., Макс.) У полі дати
  3. Тип файлу (набір до 8 текстових значень), який зараз використовується IN (.....), але я можу зробити цю таблицю тимчасових, якщо потрібно. Я бачу, що багато людей не люблять IN.
  4. Країна (текстове значення).

Я очікую приблизно 100 - 4000 повернутих рядків

Якщо я створю складений індекс на таблиці, який стовпець я повинен використовувати спочатку. Дрібнозернисте - це, мабуть, місце (дані поширюються по всьому світу). На даний момент я маю це як індекс GIST.

Інші індекси будуть BTREE.

Моя інтуїція говорить про використання дрібнозернистих і звичайно останніх. Наприклад, існує лише близько 12 типів файлів, так що це буде дуже великі відра для індексу.

Що говорять гуру PostgreSQL та PostGIS (які знають внутрішні системи)?


ОНОВЛЕННЯ:

Дозвольте я загострити це питання.

  1. Я не хочу, щоб хтось мав виконувати ту роботу, яку я повинен робити. Я занадто багато поважаю твій час. Тож я перейду до аналізу пояснень пізніше.
  2. Я шукав лише вказівки та поради та рекомендації.
  3. Я прочитав цю чудову маленьку публікацію: https://devcenter.heroku.com/articles/postgresql-indexes#managing-and-maintain-indexes про індекси
  4. Зазвичай я створюю 4 окремі індекси (геополе, назва країни, тип файлу та дату), але те, що я хочу бачити, як би складений складений запит.

Скажіть, чи будь-яке з цих припущень невірно. (Я досить новачок в ідеї складних індексів)

  1. Порядок важливий. Виберіть в якості першого індексу той, який скоротить рядки найбільше (в моєму випадку найкраще буде розташування (географія), який є простим багатокутником або багатокутником).
  2. Іноді запити пропускають індекси. Але якщо я створять складений запит з ключем (# 1, # 2, # 3, # 4), то навіть якщо користувач створить щось, що запитує №1, №3, планувальник все одно використовуватиме єдиний складений запит, оскільки він замовляє підтримується.
  3. Зазвичай я створив би три запити BTREE і один GIST (для типу географії). PostGIS не підтримує створення сполуки з декількох типів індексів. Тому мені доведеться використовувати GIST складний індекс. Але це не повинно зашкодити речам.
  4. Якщо я створюю додаткові індекси складних чи єдиних значень, планувальник досить розумний, щоб вибрати найрозумніший .....
  5. Назва країни може містити близько 250 різних значень і, очевидно, сильно пов'язана з місцем розташування (геобокс), але якщо наступний найкращий індекс для зменшення розміру рядка - це file_type, я повинен використовувати це наступне. Я не очікую, що користувачі часто використовують країну або дату у своїх наборах запитів.
  6. Я НЕ повинен турбуватися про те, що створення складного індексу з 4 клавіш значно збільшить розмір даних індексу. Тобто, якщо індекс з одним ключем складе 90% від підвищення продуктивності, не завадить додати ще 3 елементи, щоб зробити його складнішим. І навпаки, мені справді слід створити обидва індекси. Єдиний географічний індекс, а також складений індекс, і нехай планувальник визначить, що найкраще, і він врахує розмір таблиці індексу.

Знову ж таки, я нікого не прошу розробити моє рішення, я не сподіваюся на роботу інших. Але мені потрібні речі, які документація PostGreSQL не говорить мені про реалізацію

[Причиною, поки я не маю показати EXPLAIN результату, є те, що я повинен створити цю таблицю рядків 25K з таблиці 24М рядків. Це займає більше часу, ніж я думав. Я об'єдную речі в 1000 груп елементів і дозволяю користувачеві запитувати проти таблиці рядків 25K. Але наступне моє запитання передбачає використання результатів цього запиту, щоб перейти до таблиці рядків MASTER 25M та витягнути речі, і саме там ефективність складного індексу дійсно буде HIT].


зразок запиту нижче:


SELECT
    public.product_list_meta_mv.cntry_name       AS country,
    public.product_list_meta_mv.product_producer AS producer,
    public.product_list_meta_mv.product_name     AS prod_name,
    public.product_list_meta_mv.product_type     AS ptype,
    public.product_list_meta_mv.product_size     AS size,
    ST_AsGeoJSON(public.product_list_meta_mv.the_geom, 10, 2)          AS outline
FROM
    public.product_list_meta_mv 
WHERE
    public.product_list_meta_mv.cntry_name = 'Poland' 
AND
    ST_Intersects(public.product_list_meta_mv.the_geom,
    st_geogfromtext('SRID=4326;POLYGON((21.23107910156250 51.41601562500000,
                                        18.64379882812500 51.41601562500000,
                                        18.64379882812500 48.69415283203130,
                                        21.23107910156250 48.69415283203130,
                                        21.23107910156250 51.41601562500000))')) 
AND (date >= '1/2/1900 5:00:00 AM' 
 AND date <= '2/26/2014 10:26:44 PM')
AND (public.product_list_meta_mv.product_type in
    ('CIB10','DTED0','DTED1','DTED2','CIB01','CIB05')) ;

ПОЯСНІТЬ АНАЛІЗУВАННЯ (я не вкладав жодних складних індексів, і зі швидкістю, яку я бачу, я не знаю, чи потрібно мені).

"Bitmap Heap Scan on catalog_full cat  (cost=4.33..37.49 rows=1 width=7428) (actual time=1.147..38.051 rows=35 loops=1)"
"  Recheck Cond: ('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography && outline)"
"  Filter: (((type)::text = ANY ('{CADRG,CIB10,DTED1,DTED2}'::text[])) AND (_st_distance('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography, outline, 0::double precision, false) < 1e-005::double precision))"
"  Rows Removed by Filter: 61"
"  ->  Bitmap Index Scan on catalog_full_outline_idx  (cost=0.00..4.33 rows=8 width=0) (actual time=0.401..0.401 rows=96 loops=1)"
"        Index Cond: ('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography && outline)"
"Total runtime: 38.109 ms"

EXPLAIN ANALYZE SELECT pid,product_name,type,country,date,size,cocom,description,egpl_date,ST_AsGeoJSON(outline, 10, 2) AS outline 
FROM portal.catalog_full AS cat 
WHERE ST_Intersects(st_geogfromtext('SRID=4326;POLYGON((21.2200927734375 51.38031005859375, 18.65478515625 51.38031005859375, 18.65478515625 48.7298583984375, 21.2200927734375 48.7298583984375, 21.2200927734375 51.38031005859375))'), cat.outline) 
AND (cat.type in ('CADRG','CIB10','DTED1','DTED2'))

2
Надайте фактичний запит, будь ласка.
ypercubeᵀᴹ

Чи означає "3 необов'язкові", що запит може мати 8 різних варіацій (залежно від того, активовано 2,3,4 варіанти чи ні)?
ypercubeᵀᴹ

В ЧУДЕ 4 компоненти. На St_intersects потрібно, інші можуть бути там, а вони ні. Але я хочу розібратися з тією справою, де всі вони присутні.

2
Я проголосував за перенесення питання на dba.se, це складний запит із умовами кількох діапазонів.
ypercubeᵀᴹ

1
Показати EXPLAIN ANALYZEдля запиту.
Крейг Рінгер

Відповіді:


4

У рамках своєї роботи я підтримую досить велику базу даних PostgreSQL (близько 120 гб на диску, кілька багатомільйонних таблиць) та зібрав кілька прийомів, як пришвидшити запити. Спочатку кілька коментарів до ваших припущень:

  1. Так, порядок важливий, але це лише перший, який насправді інший, решта - індекси другого класу.
  2. Я не впевнений, що це завжди використовуватиме обидва, я здогадуюсь, що планувальник запитів використовуватиме №1, а потім зробіть щось розумне з рештою.
  3. Я не маю досвіду роботи з GIST.
  4. Так, спочатку додайте всі індекси, подивіться, що найбільше використовується та що дає найкращі показники.
  5. Я б сказав, що ви спробуєте і те, і виміряти те, що працює найкраще. Спробуйте переписати sql за допомогою різних підзапитів, можливо, країну та час в одному, а потім приєднайтеся до пересічного запиту. Я не помітив жодної проблеми з продуктивністю в IN-статтях, доки IN-список не має тисячі елементів. Я здогадуюсь, що декілька різних запитів, спеціально налаштованих залежно від наявних вхідних критеріїв, дадуть найкращі результати.
  6. Я б запропонував проти створення 4-х сторонного індексу. Спробуйте створити один, а потім перевірити розмір, вони можуть вийти справді величезними. На мій досвід, чотири 1-ключові індекси були майже настільки ж швидкими, як і один чотиристоронній індекс. Трюк, який добре працює для деяких конкретних запитів, - це часткові індекси, тобто щось подібне:

    СТВОРИТИ ІНДЕКС ON table_x (key1, key2, key3) WHERE some_x_column = 'XXXX';

Я створив псевдоніми у своєму .psqlrc-файлі із запитами, щоб допомогти знайти які індекси додати чи видалити. Не соромтеся переглядати їх на GitHub: .psql

Я дуже часто використовую: seq_scans і: bigtables, а потім \ d table_name, щоб отримати детальну інформацію про таблицю. Не забудьте скинути статистику після деяких змін, виберіть pg_stat_reset ();


1
Це відмінні поради. Я взяв вашу пораду, а потім скористався цим експериментом над значно більшою таблицею, яку ми підтримуємо (43 мільйони рядків). Результати: dba.stackexchange.com/questions/61084/…
Dr.YSG

1

Я думаю, що найімовірніше, що допоможе (якщо є щось), буде додати product_type як 2-й стовпець до індексу суті. Але не знаючи, скільки рядків відповідає кожному з умов І (окремо) для ваших типових / проблемних запитів, ми можемо лише здогадуватися.

Коли я підходжу до цього, перше, що я роблю, це запустити запит у спрощеній формі, де в пункті WHERE є лише одна умова, кожна з яких виконується по черзі, під EXPLAIN ANALYZE. Подивіться як на оцінені рядки, так і на фактичні рядки для кожного.


дивіться моє оновлення вище, але я думаю, ви даєте мені хороший результат, подумайте про впорядкування індексів, за допомогою яких швидше скорочується вихід рядків. Це так?
Dr.YSG
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.