SlideShare a Scribd company logo
Возможно ли подружить
Django-ORM и PostgreSQL?
Что нужно знать современному python-разработчику, чтобы быстро и
эффективно разрабатывать высоконагруженные web-проекты на
Django и PostgreSQL.
Чуркин Олег @ Rambler&Co
Как мы используем ORM в связке с СУБД?
• Создание и изменение схемы данных (описание моделей,
миграции схемы и данных)
• Манипуляция данными (QuerySet’ы и соответствующие SQL-
запросы)
А теперь поговорим о нюансах.
Contraints (ограничения) в Django ORM
• Не поддерживается CHECK: CHECK (price >= minimum_price)
• Не поддерживается FOREIGN KEY ON DELETE (UPDATE) – Django
обрабатывает подобные ограничения на уровне питоновского
кода, что может привести к огромному количеству DELETE
запросов при каскадном удалении.
Что делать:
• Указывать неподдерживаемые ограничения в миграциях
• Обязательно проставлять on_delete параметр в конструкторах
полей.
Indexes (индексы) в Django ORM
• Умеет создавать только индекс по умолчанию (btree).
• Не поддерживается конкурентное (CONCURRENTLY) создание и
удаление индексов.
• Не поддерживаются частичные (PARTIAL) индексы:
CREATE INDEX idx_name ON table (pubdate) WHERE project_id = 1;
Что делать:
• Создавать нужные индексы в миграциях, кроме конкурентных.
• Ждать Django 1.10, где можно будет вручную управлять
транзакциями в миграции (Migration.atomic) и использовать
CREATE INDEX CONCURRENTLY.
Почему изменения схемы данных могут быть
небезопасны?
• Некоторые запросы ALTER TABLE берут ACCESS EXCLUSIVE LOCK,
которая блокирует таблицу на чтение и запись.
• Изменение схемы может быть заблокировано запросом в
статусе IDLE IN TRANSACTION (ABORTED).
• VACUUM FULL полностью блокирует таблицу.
• CREATE (DROP) INDEX блокирует таблицу на запись.
https://postgrespro.ru/doc/explicit-locking.html#LOCKING-TABLES
Памятка по небезопасным миграциям
Проблема Решение
CREATE (DROP) INDEX
CREATE (DROP) INDEX
CONCURRENTLY
Изменение типа данных в
колонке
Добавляем новую колонку,
меняем код, чтобы он
использовал обе, удаляем
старую колонку
Добавление колонки со
значением по умолчанию
Отдельно добавляем колонку и
отдельно проставляем ей
значение по умолчанию и
заполняем этим значением
Памятка по небезопасным миграциям
Проблема Решение
Добавление новой NOT NULL
колонки
Лучше даже не думать об этом.
Добавление колонки с UNIQUE-
ограничением
Добавляем новую колонку,
создаем уникальный индекс
конкурентно, создаем
ограничение на основе индекса
VACUUM FULL
Лучше посмотреть в сторону
pg_repack
На что стоит обратить внимание Django-
разработчикам
• db_index=True вызывает неконкурентный CREATE INDEX.
• CREATE INDEX CONCURRENTLY нельзя выполнить в транзакции,
поэтому ждем Djano 1.10.
• Не выключайте autocommit, следите, чтобы транзакции всегда
заканчивались commit или rollback.
• Лучше разбивать большие миграции на несколько маленьких.
• После созданияудаления индексов необходимо выполнить
ANALYZE TABLE.
Как лучше провести миграцию схемы?
• Отдельно от кода проекта (иногда в три этапа)
• Завершаем фоновые задачи с медленными запросами
• tail –f uwsg_production_log | grep –С 10 HARAKIRI!!!
• pg_activity
• New Relic
PG_ACTIVITY
New Relic
Классические ошибки советы для
Django-разрабочиков
• Экономьте ресурсы: минимизируйте количество запросов и
оптимизируйте их по скорости.
• Экономьте память приложения и сетевой трафик.
• Попробуйте сначала написать SQL, а потом воспроизвести этот
запрос через ORM.
• БД всегда быстрее чем Python: агрегацию, сортировку и
удаление дубликатов выгоднее выполнять на уровне СУБД.
• Используйте транзакции 
Анилизируем количество и качество
SQL-запросов
• Django: логгирование в Django через логгер django.db.backends
• Django: django-debug-toolbar илии django-devserver
• PostgreSQL: log_min_duration_statement
• PostgreSQL: pg_stat_statements
• PostgreSQL: EXPLAIN (ANALYZE, BUFFERS)
• PgBadger
Пишите запрос (QuerySet) максимально
соответствующий вашей задаче
• Не забывайте про select_related и prefetch_related.
• В циклах используйте iterator для экономия памяти.
• Запрашивайте только необходимые поля через: values,
values_list, only, defer.
• Используйте exists() вместо count().
• Не используйте order_by(‘?’)
Стандартная задача: выбрать TOP-N
элементов в каждой группе
Пример: для каждой темы получить N последних опубликованных
статей (сортируем по дате, группируем по теме)
• Для каждой темы сделаем по одному запросу на новости: N + 1
• Один запрос с подзапросом в WHERE.
• Оконные функции (WINDOW FUNCTIONS)
• LATERAL JOIN
Простое решение: N+1 запросов
for category in categories: latest_articles_by_category[category] =
( Article.objects .filter(category=category) .order_by('-
date_published')[:N])
Придется сделать по одному запросу к БД на каждую группу, сработает
относительно быстро для небольшого количества групп.
Проблем у Django ORM с таким решением не возникает.
Window functions to the rescue!
PosgreSQL 9.1: оконные функции (window functions) –
позволяют выполнять вычисления над строками, которые
находятся в "окне", т.е. над строками перед и после текущей
строки результата запроса.
SELECT * from ( SELECT a.*, row_number() OVER (PARTITION
BY a.category_id
ORDER BY a.date_published DESC) as row FROM article AS a
JOIN category AS c ON a.category_id = c.id AND c.id
IN (1, 2, 3, 4) ) as s WHERE s.row <= N;
В Django-ORM нет поддержки оконных
функций и явных подзапросов
articles_subquery = ( Article.objects .filter(category_id__in=(1, 2, 3, 4))
.annotate(row=RawSQL( 'ROW_NUMBER() OVER (PARTITION BY %s ORDER BY %s
DESC)', [Article._meta.get_field('category').column,
Article._meta.get_field('date_published').column] )) )
articles = Article.objects.raw(
'SELECT * FROM ({}) AS s WHERE s.row <= %s'
.format(articles_subquery.query), [5])
И самый быстрый вариант: LATERAL JOIN
PosgreSQL 9.3: LATERAL JOIN – выполняет второй подзапрос в
поле FROM для каждого элемента из первого подзапроса.
SELECT sa.* FROM (SELECT * FROM category WHERE id IN (1, 2, 3, 4))
as c JOIN LATERAL ( SELECT * FROM article as a WHERE
a.category_id = c.id ORDER BY a.date_published DESC, a.id DESC LIMIT
5 ) as sa ON TRUE;
Классическая паджинация
В Django реализована в виде класса Paginator, проблемы:
• COUNT (Execution time: 7145.938 ms)
• OFFSET X – БД нужно прочитать с диска X записей после
соответствующей сортировки; неверный список элементов,
если новый элемент был вставлен на страницу, которую уже
запросили.
Решение: KEYSET паджинация
• Запоминаем идентификаторы первого (FIRST_TIMESTAMP) и
последнего (LAST_TIMESTAMP) элемента на странице, например
это может быть дата публикации.
• Чтобы получить следующую страницу используем конструкцию
WHERE table.date > LAST_TIMESTAMP ORDER BY date ASC LIMIT
<PAGE SIZE>
• Для предыдущей страницы: WHERE table.date <
FIRST_TIMESTAMP ORDER BY date DESC LIMIT <PAGE SIZE>
KEYSET паджинация
Плюсы:
• Работает ощутимо быстрее на большом количестве данных.
• Правильно обрабатывает изменение предыдущих страниц при
добавлении новых элементов.
Минусы:
• Нельзя перейти на произвольную страницу, без
предварительной подготовки данных, поэтому хорошо
применима только для «бесконечного скроллинга».
• Нет полноценной реализации для Django, django-infinite-scroll-
pagination – слишком примитивен и давно не обновляется.
EAV vs JSONB
Непростые решения в реляционных СУБД.
• А что если нам требуется динамически изменять схему данных?
Возможность добавить произвольное поле в таблицу из
админки.
• В нашем интернет магазине тысячи товаров, у которых набор
полей может отличаться.
• Небольшой «тюнинг» для объектов в БД – слишком много
булевых колонок.
Entity-Attribute-Value
Старейший и очень распространенный паттерн. Состоит из трех
таблиц:
Entity-Attribute-Value
Плюсы:
• Есть хорошая реализация для Django: django-eav
• Знакомая реляционная семантика
Минусы:
• Значения атрибутов не типизированы
• Сложно задать обязательные аттрибуты (NOT NULL)
• Любые сложные запросы превращаются в нагромождение
JOIN’ов
• Производительность на большом количестве данных
JSONB (PostgreSQL 9.4+)
Плюсы:
• Проще и понятней.
• Быстрее чем EAV, особенно с GIN индексом.
• Данные + индексы занимают меньше места (раза в 3)
• Поддерживает хранение различных типов данных внутри JSON
формата, а не только строки.
Минусы:
• Поддерживаются не все инструменты и подходы, которые мы
используем для реляционных данных (не все индексы и
ограничения, ограниченный функционал запросов).
• Неочевидный синтаксис запросов (->>, ->, @>, ?&, ?|).
JSONB на стероидах
JSQUERY extension: новые возможности по поиску данных в jsonb:
• Поиск элемента в массиве
• Поиск массивов с определенным размером
• Поиск во вложенных элементах через wildcards
• Добавляет возможность использовать CHECK CONSTRAINT!
И многое другое: https://github.com/postgrespro/jsquery
Но, не все так просто: приходится создавать дополнительное
поле с типом данных jsquery.
JSONB в Django-ORM
В версии 1.9 доступно JSONField, поддеживает основные
операции:
• contains
• contained_by
• has_key
• has_any_keys
• has_keys
• values
Запросы jsquery не поддерживаются.
Дружить или нет? Вот в чем вопрос.
• ORM не поощряет использование сложного SQL.
• Ограниченная работа с схемой данных.
• Встроенные инструменты используют известные, но не самые
быстрые подходы к обработке данных.
• Используемые вами приложения тоже не особо заморачиваются
с оптимизацией.
Мой вердикт: только приятельские отношения. Быстрая
разработка и простота – это прекрасно, но на больших данных
(более 100 000 записей а таблицах) вы уже почувствуете
дискомфорт.
Спасибо! Вопросы?
Ad

Recommended

PPTX
Оптимизация работы с данными в мобильных приложениях / Святослав Иванов, Артё...
Ontico
 
PDF
Aлександр Зайцев, LifeStreet
Ontico
 
PPTX
Аналитическая инфраструктура оптимизации рекламной сети (Александр Зайцев)
Ontico
 
PDF
NewSQL: SQL никуда не уходит / Константин Осипов (tarantool.org)
Ontico
 
PDF
Обзор перспективных баз данных для highload / Юрий Насретдинов
Ontico
 
PDF
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
Ontico
 
PDF
Функциональное тестирование высоконагруженных проектов / Илья Пастушков (2ГИС)
Ontico
 
PDF
"Новые возможности MySQL 5.7"
Badoo Development
 
PDF
NoSQL внутри SQL: приземленные вопросы практического применения / Дмитрий До...
Ontico
 
PDF
Асинхронная репликация без цензуры: архитектурные проблемы MySQL, или почему ...
Oleg Tsarev
 
PPTX
Переезжаем на Yandex ClickHouse / Александр Зайцев (LifeStreet)
Ontico
 
PPTX
2014.12.23 Александр Андреев, Parallels
Nikolay Samokhvalov
 
PDF
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".
Badoo Development
 
PDF
Aleksey Yeschenko "Моделирование данных с помощью CQL3". Выступление на Cassa...
it-people
 
PPTX
Зачем нужна Scala?
Vasil Remeniuk
 
PDF
PostgreSQL worst practices / Илья Космодемьянский (Data Egret)
Ontico
 
PDF
Очереди и блокировки. Теория и практика / Александр Календарев (ad1.ru)
Ontico
 
PDF
Константин Осипов
CodeFest
 
PDF
Производительность параметрического поиска на основе опенсорс-платформы
Yandex
 
PPTX
Micro orm для жизни. Кожевников Дмитрий D2D Just.NET
Dev2Dev
 
PDF
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Dev2Dev
 
PPTX
Система обработки бизнес-логики server-side приложения на Groovy
Regn
 
PDF
Принципы и приёмы обработки очередей / Константин Осипов (Mail.ru)
Ontico
 
PDF
Оптимизация high-contention write в PostgreSQL / Александр Коротков, Олег Бар...
Ontico
 
PDF
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ontico
 
PPT
Как поддерживать и развивать пачку "похожих" проектов. Кластер или конгломера...
Ontico
 
PDF
Cоциальный граф "Одноклассников" в myTarget
Oleg Tsarev
 
PDF
"Распределенные" вычисления на мобильных платформах. Зачем еще нужен "металли...
Ontico
 
ODP
Python & Web: От простого к сложному
Ilya Bolkhovsky
 
PDF
20070329 Phpconf2007 Training
Nikolay Samokhvalov
 

More Related Content

What's hot (20)

PDF
NoSQL внутри SQL: приземленные вопросы практического применения / Дмитрий До...
Ontico
 
PDF
Асинхронная репликация без цензуры: архитектурные проблемы MySQL, или почему ...
Oleg Tsarev
 
PPTX
Переезжаем на Yandex ClickHouse / Александр Зайцев (LifeStreet)
Ontico
 
PPTX
2014.12.23 Александр Андреев, Parallels
Nikolay Samokhvalov
 
PDF
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".
Badoo Development
 
PDF
Aleksey Yeschenko "Моделирование данных с помощью CQL3". Выступление на Cassa...
it-people
 
PPTX
Зачем нужна Scala?
Vasil Remeniuk
 
PDF
PostgreSQL worst practices / Илья Космодемьянский (Data Egret)
Ontico
 
PDF
Очереди и блокировки. Теория и практика / Александр Календарев (ad1.ru)
Ontico
 
PDF
Константин Осипов
CodeFest
 
PDF
Производительность параметрического поиска на основе опенсорс-платформы
Yandex
 
PPTX
Micro orm для жизни. Кожевников Дмитрий D2D Just.NET
Dev2Dev
 
PDF
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Dev2Dev
 
PPTX
Система обработки бизнес-логики server-side приложения на Groovy
Regn
 
PDF
Принципы и приёмы обработки очередей / Константин Осипов (Mail.ru)
Ontico
 
PDF
Оптимизация high-contention write в PostgreSQL / Александр Коротков, Олег Бар...
Ontico
 
PDF
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ontico
 
PPT
Как поддерживать и развивать пачку "похожих" проектов. Кластер или конгломера...
Ontico
 
PDF
Cоциальный граф "Одноклассников" в myTarget
Oleg Tsarev
 
PDF
"Распределенные" вычисления на мобильных платформах. Зачем еще нужен "металли...
Ontico
 
NoSQL внутри SQL: приземленные вопросы практического применения / Дмитрий До...
Ontico
 
Асинхронная репликация без цензуры: архитектурные проблемы MySQL, или почему ...
Oleg Tsarev
 
Переезжаем на Yandex ClickHouse / Александр Зайцев (LifeStreet)
Ontico
 
2014.12.23 Александр Андреев, Parallels
Nikolay Samokhvalov
 
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".
Badoo Development
 
Aleksey Yeschenko "Моделирование данных с помощью CQL3". Выступление на Cassa...
it-people
 
Зачем нужна Scala?
Vasil Remeniuk
 
PostgreSQL worst practices / Илья Космодемьянский (Data Egret)
Ontico
 
Очереди и блокировки. Теория и практика / Александр Календарев (ad1.ru)
Ontico
 
Константин Осипов
CodeFest
 
Производительность параметрического поиска на основе опенсорс-платформы
Yandex
 
Micro orm для жизни. Кожевников Дмитрий D2D Just.NET
Dev2Dev
 
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Dev2Dev
 
Система обработки бизнес-логики server-side приложения на Groovy
Regn
 
Принципы и приёмы обработки очередей / Константин Осипов (Mail.ru)
Ontico
 
Оптимизация high-contention write в PostgreSQL / Александр Коротков, Олег Бар...
Ontico
 
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ontico
 
Как поддерживать и развивать пачку "похожих" проектов. Кластер или конгломера...
Ontico
 
Cоциальный граф "Одноклассников" в myTarget
Oleg Tsarev
 
"Распределенные" вычисления на мобильных платформах. Зачем еще нужен "металли...
Ontico
 

Viewers also liked (20)

ODP
Python & Web: От простого к сложному
Ilya Bolkhovsky
 
PDF
20070329 Phpconf2007 Training
Nikolay Samokhvalov
 
PDF
Как мы делаем раскладку Календаря@Mail.Ru
Vladimir Rudnyh
 
PDF
SQL. Django, начало
pelid
 
PDF
Нагрузочное тестирование с помощью locust.io
Grigory Petrov
 
PDF
#RuPostges в Yandex, эпизод 3. Что же нового в PostgreSQL 9.6
Nikolay Samokhvalov
 
PDF
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюрин
pgdayrussia
 
PDF
Рефакторинг монолита в микросервисы на Go
Andrew Minkin
 
PDF
Владимир Бородин - PostgreSQL
Yandex
 
PDF
DJANGO-REST-FRAMEWORK: AWESOME WEB-BROWSABLE WEB APIS
Fernando Rocha
 
PPTX
Парсер: что? зачем? как?
STEP Computer Academy (Zaporozhye)
 
PDF
Django rest framework tips and tricks
xordoquy
 
PDF
Куликовский Дмитрий - Работа системного администратора сегодня
Yandex
 
PDF
Адаптивная оптимизация запросов в реляционных СУБД / Олег Иванов (Postgres Pr...
Ontico
 
PDF
Поиск совпадений и дедупликация в потоке / Леонид Юрьев (Positive Technologies)
Ontico
 
PDF
Django Rest Framework and React and Redux, Oh My!
Eric Palakovich Carr
 
PDF
django+python + nginx + gunicorn
hojung kang
 
PDF
Framework Battle: Django vs Flask vs Chalice
STEP Computer Academy (Zaporozhye)
 
PDF
История успеха Яндекс.Почты с PostgreSQL / Владимир Бородин (Яндекс)
Ontico
 
PPTX
Suturing techniques involved in dental surgery
Hasanain Alani
 
Python & Web: От простого к сложному
Ilya Bolkhovsky
 
20070329 Phpconf2007 Training
Nikolay Samokhvalov
 
Как мы делаем раскладку Календаря@Mail.Ru
Vladimir Rudnyh
 
SQL. Django, начало
pelid
 
Нагрузочное тестирование с помощью locust.io
Grigory Petrov
 
#RuPostges в Yandex, эпизод 3. Что же нового в PostgreSQL 9.6
Nikolay Samokhvalov
 
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюрин
pgdayrussia
 
Рефакторинг монолита в микросервисы на Go
Andrew Minkin
 
Владимир Бородин - PostgreSQL
Yandex
 
DJANGO-REST-FRAMEWORK: AWESOME WEB-BROWSABLE WEB APIS
Fernando Rocha
 
Парсер: что? зачем? как?
STEP Computer Academy (Zaporozhye)
 
Django rest framework tips and tricks
xordoquy
 
Куликовский Дмитрий - Работа системного администратора сегодня
Yandex
 
Адаптивная оптимизация запросов в реляционных СУБД / Олег Иванов (Postgres Pr...
Ontico
 
Поиск совпадений и дедупликация в потоке / Леонид Юрьев (Positive Technologies)
Ontico
 
Django Rest Framework and React and Redux, Oh My!
Eric Palakovich Carr
 
django+python + nginx + gunicorn
hojung kang
 
Framework Battle: Django vs Flask vs Chalice
STEP Computer Academy (Zaporozhye)
 
История успеха Яндекс.Почты с PostgreSQL / Владимир Бородин (Яндекс)
Ontico
 
Suturing techniques involved in dental surgery
Hasanain Alani
 
Ad

Similar to django-and-postgresql (20)

PDF
Web осень 2013 лекция 6
Technopark
 
PPT
Производительность в Django
MoscowDjango
 
PDF
Лекция #7. Django ORM
Яковенко Кирилл
 
PPTX
Python Meetup
iQSpace
 
PDF
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
it-people
 
PDF
Making of external DSL for Django ORM - Павел Петлинский, Rambler&Co
it-people
 
PPT
Введение в Django
Илья Барышев
 
PDF
11 - Web-технологии. Работа с СУБД
Roman Brovko
 
PDF
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
Nikolay Samokhvalov
 
PDF
Как читать и интерпретировать вывод команды EXPLAIN
Alexey Ermakov
 
PDF
12 - Web-технологии. Django модели
Roman Brovko
 
PDF
Артур Чеканов «Microframeworks» (Python Meetup)
DataArt
 
PDF
Народные средства оптимизации PostgreSQL
Nikolay Pisarev
 
PDF
Database First! О распространённых ошибках использования РСУБД
Nikolay Samokhvalov
 
PDF
Database First! О распространённых ошибках использования РСУБД / Николай Само...
Ontico
 
PDF
PostgreSQL performance recipes
Alexey Ermakov
 
PDF
Дмитрий Прокофьев: Эволюция системы синхронизации данных между сервисами
it-people
 
PDF
noBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов Николай
Ontico
 
PDF
#noBackend, или Как выжить в эпоху толстеющих клиентов
Nikolay Samokhvalov
 
PDF
Не все базы данных одинаково полезны
Sergey Xek
 
Web осень 2013 лекция 6
Technopark
 
Производительность в Django
MoscowDjango
 
Лекция #7. Django ORM
Яковенко Кирилл
 
Python Meetup
iQSpace
 
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
it-people
 
Making of external DSL for Django ORM - Павел Петлинский, Rambler&Co
it-people
 
Введение в Django
Илья Барышев
 
11 - Web-технологии. Работа с СУБД
Roman Brovko
 
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
Nikolay Samokhvalov
 
Как читать и интерпретировать вывод команды EXPLAIN
Alexey Ermakov
 
12 - Web-технологии. Django модели
Roman Brovko
 
Артур Чеканов «Microframeworks» (Python Meetup)
DataArt
 
Народные средства оптимизации PostgreSQL
Nikolay Pisarev
 
Database First! О распространённых ошибках использования РСУБД
Nikolay Samokhvalov
 
Database First! О распространённых ошибках использования РСУБД / Николай Само...
Ontico
 
PostgreSQL performance recipes
Alexey Ermakov
 
Дмитрий Прокофьев: Эволюция системы синхронизации данных между сервисами
it-people
 
noBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов Николай
Ontico
 
#noBackend, или Как выжить в эпоху толстеющих клиентов
Nikolay Samokhvalov
 
Не все базы данных одинаково полезны
Sergey Xek
 
Ad

django-and-postgresql

  • 1. Возможно ли подружить Django-ORM и PostgreSQL? Что нужно знать современному python-разработчику, чтобы быстро и эффективно разрабатывать высоконагруженные web-проекты на Django и PostgreSQL. Чуркин Олег @ Rambler&Co
  • 2. Как мы используем ORM в связке с СУБД? • Создание и изменение схемы данных (описание моделей, миграции схемы и данных) • Манипуляция данными (QuerySet’ы и соответствующие SQL- запросы) А теперь поговорим о нюансах.
  • 3. Contraints (ограничения) в Django ORM • Не поддерживается CHECK: CHECK (price >= minimum_price) • Не поддерживается FOREIGN KEY ON DELETE (UPDATE) – Django обрабатывает подобные ограничения на уровне питоновского кода, что может привести к огромному количеству DELETE запросов при каскадном удалении. Что делать: • Указывать неподдерживаемые ограничения в миграциях • Обязательно проставлять on_delete параметр в конструкторах полей.
  • 4. Indexes (индексы) в Django ORM • Умеет создавать только индекс по умолчанию (btree). • Не поддерживается конкурентное (CONCURRENTLY) создание и удаление индексов. • Не поддерживаются частичные (PARTIAL) индексы: CREATE INDEX idx_name ON table (pubdate) WHERE project_id = 1; Что делать: • Создавать нужные индексы в миграциях, кроме конкурентных. • Ждать Django 1.10, где можно будет вручную управлять транзакциями в миграции (Migration.atomic) и использовать CREATE INDEX CONCURRENTLY.
  • 5. Почему изменения схемы данных могут быть небезопасны? • Некоторые запросы ALTER TABLE берут ACCESS EXCLUSIVE LOCK, которая блокирует таблицу на чтение и запись. • Изменение схемы может быть заблокировано запросом в статусе IDLE IN TRANSACTION (ABORTED). • VACUUM FULL полностью блокирует таблицу. • CREATE (DROP) INDEX блокирует таблицу на запись. https://postgrespro.ru/doc/explicit-locking.html#LOCKING-TABLES
  • 6. Памятка по небезопасным миграциям Проблема Решение CREATE (DROP) INDEX CREATE (DROP) INDEX CONCURRENTLY Изменение типа данных в колонке Добавляем новую колонку, меняем код, чтобы он использовал обе, удаляем старую колонку Добавление колонки со значением по умолчанию Отдельно добавляем колонку и отдельно проставляем ей значение по умолчанию и заполняем этим значением
  • 7. Памятка по небезопасным миграциям Проблема Решение Добавление новой NOT NULL колонки Лучше даже не думать об этом. Добавление колонки с UNIQUE- ограничением Добавляем новую колонку, создаем уникальный индекс конкурентно, создаем ограничение на основе индекса VACUUM FULL Лучше посмотреть в сторону pg_repack
  • 8. На что стоит обратить внимание Django- разработчикам • db_index=True вызывает неконкурентный CREATE INDEX. • CREATE INDEX CONCURRENTLY нельзя выполнить в транзакции, поэтому ждем Djano 1.10. • Не выключайте autocommit, следите, чтобы транзакции всегда заканчивались commit или rollback. • Лучше разбивать большие миграции на несколько маленьких. • После созданияудаления индексов необходимо выполнить ANALYZE TABLE.
  • 9. Как лучше провести миграцию схемы? • Отдельно от кода проекта (иногда в три этапа) • Завершаем фоновые задачи с медленными запросами • tail –f uwsg_production_log | grep –С 10 HARAKIRI!!! • pg_activity • New Relic
  • 12. Классические ошибки советы для Django-разрабочиков • Экономьте ресурсы: минимизируйте количество запросов и оптимизируйте их по скорости. • Экономьте память приложения и сетевой трафик. • Попробуйте сначала написать SQL, а потом воспроизвести этот запрос через ORM. • БД всегда быстрее чем Python: агрегацию, сортировку и удаление дубликатов выгоднее выполнять на уровне СУБД. • Используйте транзакции 
  • 13. Анилизируем количество и качество SQL-запросов • Django: логгирование в Django через логгер django.db.backends • Django: django-debug-toolbar илии django-devserver • PostgreSQL: log_min_duration_statement • PostgreSQL: pg_stat_statements • PostgreSQL: EXPLAIN (ANALYZE, BUFFERS) • PgBadger
  • 14. Пишите запрос (QuerySet) максимально соответствующий вашей задаче • Не забывайте про select_related и prefetch_related. • В циклах используйте iterator для экономия памяти. • Запрашивайте только необходимые поля через: values, values_list, only, defer. • Используйте exists() вместо count(). • Не используйте order_by(‘?’)
  • 15. Стандартная задача: выбрать TOP-N элементов в каждой группе Пример: для каждой темы получить N последних опубликованных статей (сортируем по дате, группируем по теме) • Для каждой темы сделаем по одному запросу на новости: N + 1 • Один запрос с подзапросом в WHERE. • Оконные функции (WINDOW FUNCTIONS) • LATERAL JOIN
  • 16. Простое решение: N+1 запросов for category in categories: latest_articles_by_category[category] = ( Article.objects .filter(category=category) .order_by('- date_published')[:N]) Придется сделать по одному запросу к БД на каждую группу, сработает относительно быстро для небольшого количества групп. Проблем у Django ORM с таким решением не возникает.
  • 17. Window functions to the rescue! PosgreSQL 9.1: оконные функции (window functions) – позволяют выполнять вычисления над строками, которые находятся в "окне", т.е. над строками перед и после текущей строки результата запроса. SELECT * from ( SELECT a.*, row_number() OVER (PARTITION BY a.category_id ORDER BY a.date_published DESC) as row FROM article AS a JOIN category AS c ON a.category_id = c.id AND c.id IN (1, 2, 3, 4) ) as s WHERE s.row <= N;
  • 18. В Django-ORM нет поддержки оконных функций и явных подзапросов articles_subquery = ( Article.objects .filter(category_id__in=(1, 2, 3, 4)) .annotate(row=RawSQL( 'ROW_NUMBER() OVER (PARTITION BY %s ORDER BY %s DESC)', [Article._meta.get_field('category').column, Article._meta.get_field('date_published').column] )) ) articles = Article.objects.raw( 'SELECT * FROM ({}) AS s WHERE s.row <= %s' .format(articles_subquery.query), [5])
  • 19. И самый быстрый вариант: LATERAL JOIN PosgreSQL 9.3: LATERAL JOIN – выполняет второй подзапрос в поле FROM для каждого элемента из первого подзапроса. SELECT sa.* FROM (SELECT * FROM category WHERE id IN (1, 2, 3, 4)) as c JOIN LATERAL ( SELECT * FROM article as a WHERE a.category_id = c.id ORDER BY a.date_published DESC, a.id DESC LIMIT 5 ) as sa ON TRUE;
  • 20. Классическая паджинация В Django реализована в виде класса Paginator, проблемы: • COUNT (Execution time: 7145.938 ms) • OFFSET X – БД нужно прочитать с диска X записей после соответствующей сортировки; неверный список элементов, если новый элемент был вставлен на страницу, которую уже запросили.
  • 21. Решение: KEYSET паджинация • Запоминаем идентификаторы первого (FIRST_TIMESTAMP) и последнего (LAST_TIMESTAMP) элемента на странице, например это может быть дата публикации. • Чтобы получить следующую страницу используем конструкцию WHERE table.date > LAST_TIMESTAMP ORDER BY date ASC LIMIT <PAGE SIZE> • Для предыдущей страницы: WHERE table.date < FIRST_TIMESTAMP ORDER BY date DESC LIMIT <PAGE SIZE>
  • 22. KEYSET паджинация Плюсы: • Работает ощутимо быстрее на большом количестве данных. • Правильно обрабатывает изменение предыдущих страниц при добавлении новых элементов. Минусы: • Нельзя перейти на произвольную страницу, без предварительной подготовки данных, поэтому хорошо применима только для «бесконечного скроллинга». • Нет полноценной реализации для Django, django-infinite-scroll- pagination – слишком примитивен и давно не обновляется.
  • 23. EAV vs JSONB Непростые решения в реляционных СУБД. • А что если нам требуется динамически изменять схему данных? Возможность добавить произвольное поле в таблицу из админки. • В нашем интернет магазине тысячи товаров, у которых набор полей может отличаться. • Небольшой «тюнинг» для объектов в БД – слишком много булевых колонок.
  • 24. Entity-Attribute-Value Старейший и очень распространенный паттерн. Состоит из трех таблиц:
  • 25. Entity-Attribute-Value Плюсы: • Есть хорошая реализация для Django: django-eav • Знакомая реляционная семантика Минусы: • Значения атрибутов не типизированы • Сложно задать обязательные аттрибуты (NOT NULL) • Любые сложные запросы превращаются в нагромождение JOIN’ов • Производительность на большом количестве данных
  • 26. JSONB (PostgreSQL 9.4+) Плюсы: • Проще и понятней. • Быстрее чем EAV, особенно с GIN индексом. • Данные + индексы занимают меньше места (раза в 3) • Поддерживает хранение различных типов данных внутри JSON формата, а не только строки. Минусы: • Поддерживаются не все инструменты и подходы, которые мы используем для реляционных данных (не все индексы и ограничения, ограниченный функционал запросов). • Неочевидный синтаксис запросов (->>, ->, @>, ?&, ?|).
  • 27. JSONB на стероидах JSQUERY extension: новые возможности по поиску данных в jsonb: • Поиск элемента в массиве • Поиск массивов с определенным размером • Поиск во вложенных элементах через wildcards • Добавляет возможность использовать CHECK CONSTRAINT! И многое другое: https://github.com/postgrespro/jsquery Но, не все так просто: приходится создавать дополнительное поле с типом данных jsquery.
  • 28. JSONB в Django-ORM В версии 1.9 доступно JSONField, поддеживает основные операции: • contains • contained_by • has_key • has_any_keys • has_keys • values Запросы jsquery не поддерживаются.
  • 29. Дружить или нет? Вот в чем вопрос. • ORM не поощряет использование сложного SQL. • Ограниченная работа с схемой данных. • Встроенные инструменты используют известные, но не самые быстрые подходы к обработке данных. • Используемые вами приложения тоже не особо заморачиваются с оптимизацией. Мой вердикт: только приятельские отношения. Быстрая разработка и простота – это прекрасно, но на больших данных (более 100 000 записей а таблицах) вы уже почувствуете дискомфорт.