Your SlideShare is downloading. ×
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

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

3,612
views

Published on


0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
3,612
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
26
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Денормализованное хранение данных в PostgreSQL 9.2 Александр Коротков
  • 2. Преимущества денормализации• При правильном использовании – повышение производительности• Упрощение SQL-запросов• При хранении документов – меньше изменений в модели данных
  • 3. Хранение данных в массивах
  • 4. Хранение данных в массивах - плюсы• Исчезает JOIN в SQL запросе – быстрее извлекаются данные• При использовании GIN и GiST индексов – быстрый поиск по значению массива• Более простые SQL запросы
  • 5. Хранение данных в массивах - минусы• Нет готовой поддержки со стороны ORM• Большие массивы и частые апдейты – большой overhead из-за MVCC
  • 6. Пример: использование промежуточной таблицы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;
  • 7. План запроса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
  • 8. Пример: использование массиваSELECT *FROM recordingWHERE tags @> {jazz}::text[];
  • 9. План запроса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
  • 10. Массивы в качестве внешних ключей• Давно просили• В 9.2 скорее всего будет (патч в статусе ready for committer)
  • 11. Массивы в качестве внешних ключей• Ключевое слово EACH перед именем столбца• Новый действия ON DELETE и ON UPDATE – EACH CASCADE и EACH SET NULL
  • 12. Пример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);
  • 13. Массивы и планировщик• До 9.2 – константные оценки селективности для операторов &&, @>, <@. Планировщик “слеп”.• В 9.2 – сбор специфичной статистики для массивов. Более адекватные планы.
  • 14. ПримерSELECT *FROM artist_credit ac JOIN recording r ON ac.id = r.artist_creditWHERE ac.artist_ids && {40}::int[];
  • 15. До 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
  • 16. 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.024rows=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
  • 17. Как это работает Cобирается следующая статистика:• Самые частые элементы массивов• Их частоты• Гистограмма числа уникальных элементов Можно посмотреть в pg_stats.
  • 18. Использование JSON
  • 19. Встроенная поддержка JSON в 9.2• Тип json и фукнции row_to_json и array_to_json.• Извлекать данные из JSON нечем – остается только собирать в нём ответы.
  • 20. Встроенная поддержка JSON в 9.2• Можно собирать JSON-объект на стороне СУБД• Проще обработка результатов запроса• Меньше размер ответа/число запросов
  • 21. ПримерSELECT row_to_json(x)FROM (SELECT f.*, (SELECT array_agg(a.*) FROM film_actor fa JOIN actor a ONfa.actor_id = a.actor_id WHERE fa.film_id = f.film_id) AS actors FROM film f LIMIT 1) x
  • 22. Результат{ "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"} ... ]}
  • 23. Модуль-расширение PL/v8• Javascript, как процедурный язык для PostgreSQL• На основе движка v8 от Google• Можно делать любые манипуляции с JSON-данными
  • 24. PL/v8 – индексирование• Пишем JS-функцию, которая извлекает то, что нужно: значение или массив• Строим expression index• Делаем поиск по этому expression
  • 25. Пример: хранимый документ{ "title":"DOZEN LION", "description":"A Taut Drama of a Cat And a Girl who must Defeat aFrisbee in The Canadian Rockies", "release_year":2006, "rental_rate":4.99, "rating":"NC-17", "actors":["NATALIE HOPKINS","CAMERON WRAY","JADA RYDER","BENHARRIS","LAURA BRODY","KENNETH HOFFMAN"], "categories":["Documentary"]}
  • 26. Пример: функция извлечения массиваCREATE OR REPLACE FUNCTIONget_text_array(key text, data text)RETURNS text[] AS $$ return JSON.parse(data)[key];$$ LANGUAGE plv8 IMMUTABLE STRICT;
  • 27. Пример: функция извлечения числаCREATE OR REPLACE FUNCTIONget_float(key text, data text)RETURNS float AS $$ return JSON.parse(data)[key];$$ LANGUAGE plv8 IMMUTABLE STRICT;
  • 28. Пример: индексыCREATE INDEX film_json_actors_idx ONfilm_json USING gin(get_text_array(actors, data));CREATE INDEX film_json_rental_rate_idxON film_json(get_float(rental_rate, data));
  • 29. Пример: поисковый запросSELECT dataFROM film_jsonWHERE get_text_array(actors, data) @> {MARY KEITEL}::text[] AND get_float(rental_rate, data) BETWEEN 4.9 AND 5.0;
  • 30. Пример: план запроса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
  • 31. PL/v8 - ограничения• JSON хранится как текст• Каждый раз приходится делать JSON.parse• Нет универсального индекса для документов
  • 32. Диапазонные типы (range types)
  • 33. Range types (диапазонные типы)• Пара, задающая верхнюю и нижнюю границы диапазона• Различные виды интервалов (a,b), (a,b], [a,b), [a,b], (-∞,b), (-∞,b+, (a,+∞), *a,+∞), (-∞;+∞), Ø (“empty”)
  • 34. Применение range types• Темпоральные данные (хранение интервала актуальности данных)• Данные с точностью
  • 35. Индексирование range types• Btree индекс поддерживает операторы >, <, =. Как правило, не слишком полезен.• GiST поддерживает &&, @>, <@ и т.д.• Можно не хранить данные как range, а просто строить expression index.
  • 36. Пример: таблицаCREATE TABLE price ( actual_from timestamp, actual_to timestamp, value float, product_id integer,);
  • 37. Пример: запросSELECT *FROM priceWHERE 2012-03-29::timestamp >= actual_from AND 2012-03-29::timestamp < actual_to;
  • 38. Пример: план запроса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
  • 39. Пример: создание индексаCREATE INDEXprice_actual_from_actual_to_idxON price (actual_from, actual_to);
  • 40. Пример: план запроса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
  • 41. Пример: создание индексаCREATE INDEX price_actual_time_idxON priceUSING gist(tsrange(actual_from,actual_to));
  • 42. Пример: запросSELECT *FROM priceWHERE tsrange(actual_from, actual_to) @> 2012-03-29::timestamp;
  • 43. Пример: план запроса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
  • 44. Перспективы развития• Универсальное индексирование для JSON• Сбор статистики для hstore, JSON и т.д.• GiST индексы для массивов разных типов, не только integer
  • 45. Спасибо за внимание!