РИТ++ 2017, HighLoad Junior
Зал Сингапур, 5 июня, 13:00
Тезисы:
http://junior.highload.ru/2017/abstracts/2702.html
Любой Full Stack Developer сегодня обязан иметь в своём арсенале опыт и умение работы хотя бы с одной популярной РСУБД. Но без понимания основ — того, какие задачи решают СУБД, как происходит работа с данными, какие есть базовые возможности для этой работы, — такие умения превращаются в воздушный замок, быстро разрушающийся при росте проекта.
Этот доклад — попытка разбудить интерес слушателей к тщательному изучению основ теории и практики реляционных баз данных и к применению всей мощи РСУБД по прямому назначению.
...
2. О чём и зачем этот доклад
БД — сердце IT-системы
РСУБД и SQL занимают лидирующие позиции.
И это надолго
Не использовать полноценно их возможности — глупость*
___________________
* Получается глупо, потому что: 1) качество хуже, 2) производительность ниже, 3) потом всё равно
придётся исправлять (возможно, уже не вам)
3. О докладчике
• ФУПМ МФТИ / ИСП РАН / «Базы данных»
• MoiKrug (2005), MirTesen (2007), Postila (2013)
• PostgreSQL-консалтинг с 2007 в России, с 2013 в Калифорнии
• #RuPostgres (http://rupostgres.org) — вторая крупнейшая митап-группа в мире
• Postgres-твиттер (3500+ подписчиков): @postgresmen
• ПК конференций HLj, BackendConf, Highload++, PgDay Russia
4. Проблемы использования SQL & РСУБД
• боязнь и незнание возможностей РСУБД/SQL:
• для контроля данных
• для манипулирования данными
• архитектурные ошибки, связанные с этими
страхами и невежеством
6. Последствия ошибки №1
1. Отсутствие Primary Key (PK):
• дубликаты, грязные данные;
• нарушение реляционной модели и вытекающие проблемы (аномалии
данных, неожиданные результаты JOIN и т.д.)
• рано или поздно приходится чистить (долго, дорого)
7. Последствия ошибки №1
1. Отсутствие Primary Key (PK):
• дубликаты, грязные данные;
• нарушение реляционной модели и вытекающие проблемы (аномалии
данных, неожиданные результаты JOIN и т.д.)
• рано или поздно приходится чистить (долго, дорого)
2. Только «суррогатный» PK, но нет Unique Key(s) (UKs):
• «скрытые» дубликаты, опять грязные данные и проблемы
• опять необходимость чистки (долго, дорого)
8. Последствия ошибки №1
1. Отсутствие Primary Key (PK):
• дубликаты, грязные данные;
• нарушение реляционной модели и вытекающие проблемы (аномалии
данных, неожиданные результаты JOIN и т.д.)
• рано или поздно приходится чистить (долго, дорого)
2. Только «суррогатный» PK, но нет Unique Key(s) (UKs):
• «скрытые» дубликаты, опять грязные данные и проблемы
• опять необходимость чистки (долго, дорого)
Числовые PK вида 1, 2, … («суррогатные») — это иллюзия
правильного проектирования, легко забыть о потенциальных
ключах!
9. Последствия ошибки №1
1. Отсутствие Primary Key (PK)
2. Только суррогатный PK,
но нет Unique Key(s) (UKs)
3. Не используются Foreign Keys (FKs):
• orphans (осиротевшие записи) → аномалии, ошибки 404, 500
• …и опять: придётся чистить!
10. Последствия ошибки №1
1. Отсутствие Primary Key (PK)
2. Только суррогатный PK, но нет Unique Key(s) (UKs)
3. Не используются Foreign Keys (FKs)
4. Не используются NOT NULLs:
• неполные данные, бессмысленные записи, аномалии
• опять. придётся. чистить.
11. Последствия ошибки №1
1. Отсутствие Primary Key (PK)
2. Только суррогатный PK, но нет Unique Key(s) (UKs)
3. Не используются Foreign Keys (FKs)
4. Не используются NOT NULLs
5. Не используются CHECK constraints:
• мусор, некорректные данные
• опять. придётся. чистить.
12. Ещё более фундаментальное — типы данных
0. Типы данных
● недостаточно жёсткие ограничения текста (text, varchar(5000) и т.п.)
легко приводят к проблемам отображения (вёрстка и т.п.)
13. Ещё более фундаментальное — типы данных
0. Типы данных
● недостаточно жёсткие ограничения текста (text, varchar(5000) и т.п.)
легко приводят к проблемам вёрстки
● varchar(250) или text превратить в varchar(50) — долго, дорого!
14. Ещё более фундаментальное — типы данных
0. Типы данных
● недостаточно жёсткие ограничения текста (text, varchar(5000) и т.п.)
легко приводят к проблемам вёрстки
● varchar(250) или text превратить в varchar(50) — долго, дорого!
● PK типа integer в больших таблицах — бомба замедленного действия:
○ максимум 2147483647 (2.1 млрд)
○ перейти на int8 (aka bigint) — долго, дорого!
15. Ещё более фундаментальное — типы данных
0. Типы данных
● недостаточно жёсткие ограничения текста (text, varchar(5000) и т.п.)
легко приводят к проблемам вёрстки
● varchar(250) или text превратить в varchar(50) — долго, дорого!
● PK типа integer в больших таблицах — бомба замедленного действия:
○ максимум 2147483647 (2.1 млрд)
○ перейти на int8 (aka bigint) — долго, дорого!
● Регистрозависимое поле email (varchar вместо citext и нет lower()):
○ аккаунты-дубликаты, жалобы «не могу войти»
16. А что если проверять данные в приложении?
Data Checks (format, constraints, etc)
in App (Ruby or Python or PHP or …)
17. А что если проверять данные в приложении? Oops
Data Checks (format, constraints, etc)
in App (Ruby or Python or PHP or …)
18. Проверка данных только в приложении — не надо так!
Data Checks (format, constraints, etc)
in App (Ruby or Python or PHP or …)
19. Проверка данных — а как правильно?
App (Ruby or Python or PHP or …)
CHECKS
20. Проверка данных — а как правильно?
App (Ruby or Python or PHP or …)
CHECKS
Контроль данных
средствами СУБД:
- чёткая типизация,
- все виды ограничений
целостности
21. Немного о JSON(b) – от неструктурированных данных
к слабоструктурированным (semi-structured)
22. Немного о JSON(b) – от неструктурированных данных
к слабоструктурированным (semi-structured)
Делаем ключ color обязательным
23. Немного о JSON(b) – от неструктурированных данных
к слабоструктурированным (semi-structured)
Делаем ключ color обязательным
Без ключа color JSON-значение не принимается
29. Роль MySQL и MariaDB в росте страхов и невежества
30. Не допускаем ошибку №1
0. Полноценно используем типы данных
a. citext для регистронезависимых сравнений
b. varchar(XX) построже
c. int8 в таблицах, которые вырастут, — сразу
d. int2 для экономии, когда знаем, что диапазон значений точно будет мал
e. timestamptz вместо timestamp
f. tstzrange, int4range, int8range, numrange + exclusion constraint
A Tour of PostgreSQL Data Types (2013), Tour de (PostgreSQL) Data Types (2017)
1. PK – обязательно
2. Максимально используем UKs (особенно если PK суррогатный — выявляем наш
“natural key” и добавляем unique index!)
3. Используем FKs
4. Используем NOT NULLs
5. Используем CHECK constraints
32. Пример 2.1: инкремент
● Начинающий РНР-программист, задача массовой отправки писем.
● Последовательный перебор по возрастанию User ID,
сохранение «указателя» на last User ID в БД.
● Ошибки отправки отдельных писем не должны останавливать процесс.
43. Пример 2.2: обновление/удаление группы строк
Полностью
Само удаление с перечислением ID
через запятую. Предварительно убрали
из массива все [keep_id]
44. Пример 2.2: более правильное решение — CTE
Используем CTE (Common Table Expressions):
50. Пример 2.2: как это делать с CTE
Вставляем данные: У природы нет плохой
погоды
(особенно с 2000-то года)
51. Пример 2.2: как это делать с CTE
Вставляем данные:
Но на самом деле есть.
Май 2017, ураган.
52. Пример 2.2: как это делать с CTE
Вставляем данные:
Ooops! INSERT вместо
UPDATE...
53. Пример 2.2: как это делать с CTE
Получили грязные данные, дубликаты:
54. Пример 2.2: как это делать с CTE
Получили грязные данные, дубликаты:
55. Пример 2.2: как это делать с CTE
UK уже не создать, поздно пить «Боржоми»:
Время собирать камни дубликаты. С помощью четырёхэтажного CTE:
Выборка данных
Правка created у тех, что
останутся + RETURNING *
Удаление лишних + RETURNING *
Анализ результата (сколько
UPDATEd, сколько DELETEd)
57. Пример 2.2: как это делать с CTE. Action!
IDs, которые оставим
58. Пример 2.2: как это делать с CTE. Action!
А это мы хотим «забрать с собой»
59. Пример 2.2: как это делать с CTE. Action!
А это мы хотим «забрать с собой»
… и забираем
60. Пример 2.2: как это делать с CTE. Action!
Так получаем IDs, которые будем удалять:
все IDs группы за вычетом keep_id
61. Пример 2.2: как это делать с CTE. Action!
Так получаем IDs, которые будем удалять:
все IDs группы за вычетом keep_id
… и вот так используем, с UNNEST, чтобы
получить список ID для удаления
62. Пример 2.2: как это делать с CTE. Action!
У UPDATE и DELETE есть RETURNING *
63. Пример 2.2: как это делать с CTE. Action!
У UPDATE и DELETE есть RETURNING *
…которые дают нам получить 2 числа
— количество затронутых строк
66. Пример 2.2: O CTE и современном SQL
Внимание: если строк удаляем/обновляем много,
может понадобиться поэтапная работа с пачками строк!
(чтобы не блокировать много и надолго; а также давать возможность отработать VACUUM-у)
– нам нужен цикл вызовов извне (что угодно: bash, pyhon, ruby, php, etc)
67. Пример 2.2: O CTE и современном SQL
О современном SQL
Максим Богук:
● «Неклассические приемы оптимизации запросов» (2014)
● How to teach an elephant to rock'n'roll (2017)
Markus Winand:
● http://use-the-index-luke.com/
● http://modern-sql.com/
Внимание: если строк удаляем/обновляем много,
может понадобиться поэтапная работа с пачками строк!
(чтобы не блокировать много и надолго; а также давать возможность отработать VACUUM-у)
– нам нужен цикл вызовов извне (что угодно: bash, pyhon, ruby, php, etc)
68. Пример 2.3: тянем данные на клиент
Часто (Machine Learning, Data Science) – вытягивание огромного количества
данных на клиент / app server для анализа (R, Python)
Имитация:
• 1 млн строк
• клиент (psql) в Германии
• сервер (Postgres 9.6) — в США
SeqScan, получение 1 числа: ~3 сек
SeqScan, получение 10000000 чисел: ~22 сек
69. MADlib: Machine Learning inside your DBMS
- A lot of ML algorithms implemented (more in each release):
- PL/Python
- Very easy and quick start to do machine learning with your Postgres data
http://madlib.incubator.apache.org/
70. MADlib: Machine Learning inside your DBMS
Source: Apache MADlib: Distributed In-Database Machine Learning for Fun and Profit
Details: MADlib Design Document
71. Data Manipulation Logic
in App (Ruby or Python or PHP or …)
Something*
* ElasticSearch, Sphix, Analytics DBMS, etc
Пример 2.4
72. Пример 2.4
Data Manipulation Logic
in App (Ruby or Python or PHP or …)
Something*
* ElasticSearch, Sphix, Analytics DBMS, etc
73. Пример 2.4
Data Manipulation Logic
in App (Ruby or Python or PHP or …)
Something*
* ElasticSearch, Sphix, Analytics DBMS, etc
74. Пример 2.4
App (Ruby or Python or PHP or …)
Something*
* ElasticSearch, Sphix, Analytics DBMS, etc
Data
Manipulation
Use:
- functions, triggers,
- Foreign Data Wrappers (FDW),
- Logical Decoding
75. Database First! Что важно помнить
● Делать вообще всё в СУБД можно, но в некоторых случаях неоправданно
○ пример: работа с внешними API с непредсказуемым временем
— оккупация CPU мастера может быть фатальной ошибкой)
76. Database First! Что важно помнить
● Делать вообще всё в СУБД можно, но в некоторых случаях неоправданно
○ пример: работа с внешними API с непредсказуемым временем
— оккупация CPU мастера может быть фатальной ошибкой)
● CTE в PostgreSQL – optimization fence
○ планировщик «не знает», какие есть возможности (индексы) у «соседнего этажа»
○ это может быть как и проблемой, так и помощью в ускорении запросов
77. Database First! Что важно помнить
● Делать вообще всё в СУБД можно, но в некоторых случаях неоправданно
○ пример: работа с внешними API с непредсказуемым временем
— оккупация CPU мастера может быть фатальной ошибкой)
● CTE в PostgreSQL – optimization fence
○ планировщик «не знает», какие есть возможности (индексы) у «соседнего этажа»
○ это может быть как и проблемой, так и помощью в ускорении запросов
● Иногда всё же очень нужна «внешняя сила»
○ пример: дробление UPDATE/DELETE на батчи. Должен кто-то вызывать нас
78. Database First! Что важно помнить
● Делать вообще всё в СУБД можно, но в некоторых случаях неоправданно
○ пример: работа с внешними API с непредсказуемым временем
— оккупация CPU мастера может быть фатальной ошибкой)
● CTE в PostgreSQL – optimization fence
○ планировщик не знает, какие есть возможности (индексы) у «соседнего этажа»
○ это может быть как и проблемой, так и помощью в ускорении запросов
● Иногда всё же очень нужна «внешняя сила»
○ пример: дробление UPDATE/DELETE на батчи. Нужны вызовы извне цикле!
● SQL- и PL/pgSQL-код важно правильно «готовить»:
○ Code Style, подсветка кода (есть плагин vim для PL/pgSQL)
○ версионирование и «миграции» (sqitch, liquibase, etc)
○ тестировать (pgtap)
○ мониторить (pg_stat_statements.track = all)
○ дебажить (простые raise notice; pl/pgsql profiler, debugger)
79. Database First! Checklist
➜ Типы данных
✓ varchar(N) построже
✓ int8 для PK больших таблиц
✓ int2, где диапазон мал
✓ timestamptz вместо timestamp
✓ tstzrange, int4range, etc
+ exclusion constraint
A Tour of PostgreSQL Data Types (2013),
Tour de (PostgreSQL) Data Types (2017)
➜ Манипуляции с данными — прежде всего с помощью средств СУБД!!!
✓ если ничего не мешает, ищем/меняем данные средствами СУБД
✓ можно ли «гонять» меньше данных по сети, поручить работу по подготовке данных СУБД?
✓ много ли строк меняются запросом? нужна ли работа частями?
✓ хранимки — не зло, PL/pgSQL — прекрасен, используем; но можно ли обойтись SQL (или SQL-функцией)?
✓ используем современный SQL: CTE, lateral join, работа с массивами, JSONb, агрегация, оконные функции
«Неклассические приемы оптимизации запросов» (2014),
How to teach an elephant to rock'n'roll (2017)
Cайты: Use-the-Index-Luke.com, Modern-SQL.com
Twitter: @postgresmen
➜ Ограничения целостности (constraints):
✓ 5 главных: PK, UK, FK, NOT NULL, CHECK;
✓ а какой у таблицы Natural Key? Создаем UK(s);
✓ интервалы/геометрия? + exclusion constraint;
✓ сложные случаи (проверка данных в нескольких
таблицах, денормализация, etc) – используем хранимки,
триггеры
PostgreSQL Documentation