45
Денормализованное хранение данных в PostgreSQL 9.2 Александр Коротков

Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

  • Upload
    ontico

  • View
    3.998

  • Download
    8

Embed Size (px)

Citation preview

Page 1: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Денормализованное хранение данных в PostgreSQL 9.2

Александр Коротков

Page 2: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Преимущества денормализации

• При правильном использовании – повышение производительности

• Упрощение SQL-запросов• При хранении документов – меньше

изменений в модели данных

Page 3: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Хранение данных в массивах

Page 4: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Хранение данных в массивах - плюсы

• Исчезает JOIN в SQL запросе – быстрее извлекаются данные

• При использовании GIN и GiST индексов – быстрый поиск по значению массива

• Более простые SQL запросы

Page 5: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Хранение данных в массивах - минусы

• Нет готовой поддержки со стороны ORM

• Большие массивы и частые апдейты – большой overhead из-за MVCC

Page 6: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: использованиепромежуточной таблицы

SELECT r.*FROM recording r JOIN recording_tag rt ON r.id =

rt.recording JOIN tag t ON rt.tag = t.idWHERE t.name = 'jazz';

Page 7: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

План запросаNested Loop (cost=0.00..377.00 rows=21 width=124) (actual time=0.203. -> Nested Loop (cost=0.00..21.47 rows=21 width=4) (actual time=0.1 -> Index Scan using tag_name_idx on tag t (cost=0.00..8.31 rows= Index Cond: ((name)::text = 'jazz'::text) -> Index Only Scan using recording_tag_tag_recording_idx on recor Index Cond: (tag = t.id) Heap Fetches: 0 -> Index Scan using recording_pkey on recording r (cost=0.00..16.9 Index Cond: (id = rt.recording)Total runtime: 214.631 ms

Page 8: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: использование массива

SELECT *FROM recording WHERE tags @> '{jazz}'::text[];

Page 9: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

План запроса

Bitmap Heap Scan on recording (cost=107.43..27372.8 Recheck Cond: (tags @> '{jazz}'::text[]) -> Bitmap Index Scan on recording_tags_idx (cost Index Cond: (tags @> '{jazz}'::text[])Total runtime: 49.235 ms

Page 10: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Массивы в качестве внешних ключей

• Давно просили• В 9.2 скорее всего будет (патч в

статусе ready for committer)

Page 11: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Массивы в качестве внешних ключей

• Ключевое слово EACH перед именем столбца

• Новый действия ON DELETE и ON UPDATE – EACH CASCADE и EACH SET NULL

Page 12: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

ПримерCREATE TABLE film ( id serial, title text NOT NULL, ... actor_ids integer[], FOREIGN KEY (EACH actor_ids) REFERENCES actor (id) ON DELETE EACH CASCADE ON UPDATE EACH CASCADE);

Page 13: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Массивы и планировщик• До 9.2 – константные оценки селективности

для операторов &&, @>, <@. Планировщик “слеп”.

• В 9.2 – сбор специфичной статистики для массивов. Более адекватные планы.

Page 14: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

ПримерSELECT *FROM artist_credit ac JOIN recording r ON ac.id = r.artist_credit

WHERE ac.artist_ids && '{40}'::int[];

Page 15: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

До PostgreSQL 9.2Hash Join (cost=6031.33..369386.51 rows=55296 width=175) (actual time Hash Cond: (r.artist_credit = ac.id) -> Seq Scan on recording r (cost=0.00..293679.83 rows=11059583 wid -> Hash (cost=5996.72..5996.72 rows=2769 width=47) (actual time=0. -> Bitmap Heap Scan on artist_credit ac (cost=69.80..5996.72 Recheck Cond: (artist_ids && '{40}'::integer[]) -> Bitmap Index Scan on artist_credit_artist_ids_idx (cost=0.00..69.11 rows=2769 width=0) (actual time=0.050..0.050 rows=6loops=1) Index Cond: (artist_ids && '{40}'::integer[])Total runtime: 48455.56 ms

Page 16: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

PostgreSQL 9.2Nested Loop (cost=16.21..20984.78 rows=559 width=171) (actual -> Bitmap Heap Scan on artist_credit ac (cost=16.21..122.58 Recheck Cond: (artist_ids && '{40}'::integer[]) -> Bitmap Index Scan on artist_credit_artist_ids_idx (cost=0.00..16.21 rows=28 width=0) (actual time=0.024..0.024 rows=6 loops=1) Index Cond: (artist_ids && '{40}'::integer[]) -> Index Scan using recording_artist_credit_idx on recording Index Cond: (artist_credit = ac.id)Total runtime: 6.338 ms

Page 17: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Как это работаетCобирается следующая статистика:

• Самые частые элементы массивов• Их частоты• Гистограмма числа уникальных элементов

Можно посмотреть в pg_stats.

Page 18: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Использование JSON

Page 19: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Встроенная поддержка JSON в 9.2

• Тип json и фукнции row_to_json и array_to_json.• Извлекать данные из JSON нечем –

остается только собирать в нём ответы.

Page 20: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Встроенная поддержка JSON в 9.2

• Можно собирать JSON-объект на стороне СУБД

• Проще обработка результатов запроса• Меньше размер ответа/число запросов

Page 21: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

ПримерSELECT row_to_json(x)FROM (SELECT f.*, (SELECT array_agg(a.*) FROM film_actor fa JOIN actor a ON fa.actor_id = a.actor_id WHERE fa.film_id = f.film_id) AS actors FROM film f LIMIT 1) x

Page 22: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Результат{ "film_id":511, "title":"LAWRENCE LOVE", ... "actors":[ {"actor_id":91, "first_name":"CHRISTOPHER“, "last_name":"BERRY“, "last_update":"2006-02-15 09:34:33”}, {"actor_id":101, "first_name":"SUSAN", "last_name":"DAVIS", "last_update":"2006-02-15 09:34:33"} ... ]}

Page 23: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Модуль-расширение PL/v8• Javascript, как процедурный язык для

PostgreSQL• На основе движка v8 от Google• Можно делать любые манипуляции с

JSON-данными

Page 24: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

PL/v8 – индексирование• Пишем JS-функцию, которая извлекает

то, что нужно: значение или массив• Строим expression index• Делаем поиск по этому expression

Page 25: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: хранимый документ{ "title":"DOZEN LION", "description":"A Taut Drama of a Cat And a Girl who must Defeat a Frisbee in The Canadian Rockies", "release_year":2006, "rental_rate":4.99, "rating":"NC-17", "actors":["NATALIE HOPKINS","CAMERON WRAY","JADA RYDER","BEN HARRIS","LAURA BRODY","KENNETH HOFFMAN"], "categories":["Documentary"]}

Page 26: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: функция извлечения массиваCREATE OR REPLACE FUNCTIONget_text_array(key text, data text)

RETURNS text[] AS $$ return JSON.parse(data)[key];$$ LANGUAGE plv8 IMMUTABLE STRICT;

Page 27: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: функция извлечения числаCREATE OR REPLACE FUNCTIONget_float(key text, data text)RETURNS float AS $$ return JSON.parse(data)[key];$$ LANGUAGE plv8 IMMUTABLE STRICT;

Page 28: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: индексыCREATE INDEX film_json_actors_idx ON film_json USING gin (get_text_array('actors', data));CREATE INDEX film_json_rental_rate_idx ON film_json(get_float('rental_rate', data));

Page 29: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: поисковый запросSELECT dataFROM film_jsonWHERE get_text_array('actors', data) @> '{MARY KEITEL}'::text[] AND get_float('rental_rate', data) BETWEEN 4.9 AND 5.0;

Page 30: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: план запросаBitmap Heap Scan on film_json (cost=20.92..60.10 rows=13 width Recheck Cond: ((get_text_array('actors'::text, data) @> '{"MA -> BitmapAnd (cost=20.92..20.92 rows=13 width=0) (actual ti -> Bitmap Index Scan on film_json_actors_idx (cost=0.00.. Index Cond: (get_text_array('actors'::text, data) @> -> Bitmap Index Scan on film_json_rental_rate_idx (cost=0 Index Cond: ((get_float('rental_rate'::text, data) >=Total runtime: 0.490 ms

Page 31: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

PL/v8 - ограничения• JSON хранится как текст• Каждый раз приходится делать

JSON.parse• Нет универсального индекса для

документов

Page 32: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Диапазонные типы (range types)

Page 33: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Range types (диапазонные типы)• Пара, задающая верхнюю и нижнюю

границы диапазона• Различные виды интервалов (a,b), (a,b],

[a,b), [a,b], (-∞,b), (-∞,b], (a,+∞), [a,+∞), (-∞;+∞), Ø (“empty”)

Page 34: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Применение range types

• Темпоральные данные (хранение интервала актуальности данных)• Данные с точностью

Page 35: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Индексирование range types• Btree индекс поддерживает операторы >, <,

=. Как правило, не слишком полезен.• GiST поддерживает &&, @>, <@ и т.д.• Можно не хранить данные как range, а

просто строить expression index.

Page 36: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: таблицаCREATE TABLE price ( actual_from timestamp, actual_to timestamp, value float, product_id integer,);

Page 37: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: запросSELECT *FROM price WHERE '2012-03-29'::timestamp >= actual_from AND

'2012-03-29'::timestamp < actual_to;

Page 38: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: план запроса

Seq Scan on price (cost=0.00..204053.83 rows Filter: (('2012-03-29 00:00:00'::timestamp Rows Removed by Filter: 9995049Total runtime: 2601.073 ms

Page 39: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: создание индексаCREATE INDEX price_actual_from_actual_to_idxON price (actual_from, actual_to);

Page 40: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: план запроса

Bitmap Heap Scan on price (cost=127071.99..2 Recheck Cond: (('2012-03-29 00:00:00'::time -> Bitmap Index Scan on price_actual_from_ Index Cond: (('2012-03-29 00:00:00'::Total runtime: 566.923 ms

Page 41: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: создание индексаCREATE INDEX price_actual_time_idxON priceUSING gist(tsrange(actual_from, actual_to));

Page 42: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: запросSELECT *FROM price WHERE tsrange(actual_from, actual_to) @> '2012-03-29'::timestamp;

Page 43: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Пример: план запроса

Bitmap Heap Scan on price (cost=464.57..25929.50 rows=10 Recheck Cond: (tsrange(actual_from, actual_to) @> '2012 -> Bitmap Index Scan on price_actual_time_idx (cost=0 Index Cond: (tsrange(actual_from, actual_to) @> 'Total runtime: 80.287 ms

Page 44: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Перспективы развития• Универсальное индексирование для JSON• Сбор статистики для hstore, JSON и т.д.• GiST индексы для массивов разных типов,

не только integer

Page 45: Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)

Спасибо за внимание!