Мелкая питонячая радость #8: мелкие удовольствия для работы с БД

в 9:19, , рубрики: python, мелкая питонячая радость

Беглый опрос коллег на моем текущем проекте показал, что при словах "ORM и работа с БД" в подавляющем большинстве случаев звучат слова "Алхимия" и "Django ORM". Знания этих двух слов, в общем, достаточно, чтобы писать чистый, аккуратный и рабочий код. Но расширение инженерного кругозора пока еще никому не вредило, поэтому сегодня мы добавим в нашу картину мира несколько (возможно, до этого дня незнакомых) классных штук для работы с БД.

Мелкая питонячая радость #8: мелкие удовольствия для работы с БД - 1

Yoyo

Сегодня любая ORM идет со внутренней системой миграции БД. Простой и классный подход, когда ORM следит за структурой таблиц, в общем, всех устраивает. Но у этого решения случая, когда пользоваться им неудобно:

  • Есть парни, которые фигачат запросы в БД, минуя ORM. При этом используется что-то типа asyncpg и небольшая самописная нашлепка для упрощения составления запросов.

    Почему эти парни отказываются от удобств ORM? Да потому что любая обертка для БД сжирает некоторое количество системных ресурсов, а этим парням нужно писать высокопроизводительный код. ORM они выкинули, а базу мигрировать им как-то надо.

  • Встроенные миграторы могут иметь свои специфические взгляды на построение связей и индексирование записей. Порой победить эти воззрения ORM на структуру таблиц не удается, приходится самому нашлепывать исправления запросов.

В обоих этих случаях удобно примененять миграции, составленных вручную — мы не опираемся на модели ORM, а втупую руками набиваем необходимые SQL инструкции и кормим их в простенький мигратор, который последовательно применяет изменения структуры к БД.

На выходе получается чистая, понятная и полностью управляемая структура таблиц, составленная с умом.

Для такого вот ручного подхода существует мигратор схем БД yoyo.

pip install yoyo-migrations

Дальше вся работа по управлению миграциями идет с помощью исполняемого файла yoyo

yoyo new ./migrations -m "Add column to foo"

Команда создает файлик, в который можно вписать одну или несколько инструкций для миграции схемы

from yoyo import step
steps = [
   step("CREATE TABLE foo (id INT, bar VARCHAR(20), PRIMARY KEY (id))",
        "DROP TABLE foo"),
]

После этого миграции можно катить на базу

yoyo apply --database postgresql://scott:tiger@localhost/db ./migrations

Все просто, как полено. И при этом у вас абсолютно полный контроль над тем, как выглядит БД.

Минусов у такого подхода два

  • За набором полей в таблицах и их параметрами придется следить ручками. Каждое изменение приводит к необходимости писать ALTER TABLE самому, теряется возможность мигрировать все в один клик.
  • Мерджить такие миграции тоже придется всегда руками и головой. Это, конечно, лишний труд. Но на практике конфликты и сложные мерджи миграций встречаются редко.

Peewee

image

Маленькая и не самая популярная ORM (хотя про нее писали здесь уже не один раз), которая, тем не менее, имеет свою аудиторию.
Peewee создана быть максимально тупой и простой оберткой для БД, с максимально понятным механизмом выполнения запросов и легко читаемым кодом.

Users.select().where(
   Users.user_id == user_id
).get()

Несмотря на простоту, минималистичность и малое количество кода, в peewee есть все, что нужно для вменяемой работы.

  • Адекватная производительность (хотя и не самая большая скорость выполнения запросов)
  • Все необходимые плюшки — различные наборы полей, связи между сущностями, пулы коннектов, наборы плагинов и расширений.
  • Есть даже более-менее вменяемая асинхронщина, добавляемая сторонним модулем peewee_async.

Pony ORM

Pony — легендарная своей скоростью обертка. Слой работы с БД, написанный с помощью этой ORM, может по скорости в разы надрать зад другим решениям. Магии в ее скорости нет, есть очень грамотная политика кеширования запросов в базы, кучи оптимизаций и хитрости с кодом. В сумме это приводит к тому что Пони жарит с конской скоростью.

Минусом можно назвать синтаксис запросов в этой ORM — он весьма специфический, с применением генераторов, лямбд и прочих языковых плюшек Питона.

query = select(c for c in Customer
               if sum(o.total_price for o in c.orders) > 1000)

Product.select().order_by(lambda p: desc(sum(p.order_items.quantity))).first()

Такой подход требует определенного слома мозга.

Tortoise ORM

Мелкая питонячая радость #8: мелкие удовольствия для работы с БД - 3

Копаясь в разных питонячих репо, я нашел сборник тестов разных ORM с замерами по скорости. Помимо уже упомянутой Pony ORM в списке самых шустрых оказалась некая Tortoise ORM. Понятное дело, что результаты тесты зависят от того, кто тесты пишет и как их потом запускает, но посмотреть поближе на эту штуку надо.

Tortoise — относительно молодой проект, который пока что находится в стадии активной разработки. Хорошая производительность этой библиотеки объясняется тем, что ORM не содержит ниего лишнего и из коробки заточена под асинхронщину. А еще в ней предполагается использование uvloop, который работает быстрее, чем родные питонячие циклы эвентов.

ORM эта еще слишком сыра для применения в бою (например, пока даже не реализованы пулы коннектов), но посматривать за развитием этой либы стоит. Если у разработчиков все пойдет нормально — в ближайший год мы получим действительно быструю обертку для БД с хорошей скоростью и без странного синтаксиса в стиле Pony.

Автор: Andrey Korchak

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js