Search
Write a publication
Pull to refresh
1
0
Николай Налбантов @NickNal

Data Engineer

Send message

Так мы узнали, что либо в Сбере нет отдела комплаенса, либо его не зовут на акселераторы)

Что-то вспомнились времена в консалтинговой фирме из недружественной страны - там бы такое сразу завернули, ибо харассмент и конфликты интересов из всех щелей

А по-человечески - идея классная, конечно)

К сожалению, это расширение недоступно в Managed PostgreSQL у большинства облачных провайдеров (AWS RDS, например)

Ну и этот табличный API выглядит немного костыльно (на мой вкус)

Одним словом, пилю под свой кейс (PG в AWS RDS + оркестрация через Airflow) собственный велосипед для автоматического партиционирования / пересборки партиций - выложу на Github в ближайшие месяцы

Но покопавшись в этой теме могу сказать, что автоматическое партиционирование в PG - дело весьма нетривиальное, неспроста его нет из коробки. Особенно весёлый кейс - когда в ключе range-партиционирования больше одного поля / выражения (и они ещё могут быть почти любого типа данных). Честно, не заглядывал ещё в исходники partman/pathman по этой теме - интересно, как они решили эту проблему

Тогда при каждом "неоптимальном" add column (например, добавление int-колонки в таблицу с text-колонками), PG пришлось бы автоматически перезаписывать всю таблицу - плохая идея для дефолтного поведения

Ну и в целом, PG среди СУБД - как Linux среди ОС: всегда делает ровно то, что запросил юзер (даже если это выстрел себе в ногу из дробовика)

Хз, но в том же абзаце в доке есть такая фраза про него

These are not intended for general-purpose use, only for use in the internal system catalogs

Будет хуже
boolean всегда занимает ровно 1 байт, а char(1) займёт 2 байта
https://www.postgresql.org/docs/current/datatype-character.html

The storage requirement for a short string (up to 126 bytes) is 1 byte plus the actual string, which includes the space padding in the case of character.

Но есть ещё скрытый системный тип "char", который займёт ровно 1 байт (по той же ссылке ниже)

The type "char" (note the quotes) is different from char(1) in that it only uses one byte of storage, and therefore can store only a single ASCII character. It is used in the system catalogs as a simplistic enumeration type

Если писать COPY TO напрямую в файл, то он будет перезаписываться

Чтобы сделать append, можно:

  • направить COPY TO в STDOUT и перенаправлять STDOUT в целевой файл

  • направить COPY TO в PROGRAM

copy (select * from table) to PROGRAM 'cat >>/tmp/log.txt'

Логирование можно делать через автономные транзакции с помощью встроенного расширения dblink (есть из коробки) - можно писать логи в ту же БД, можно писать в другие БД на том же или другом кластере PostgreSQL

Ну или любым из FDW для сброса логов в другие типы хранилищ

У postgres_fdw есть куча настроек для оптимизации DML-операций, которых нет в dblink. Зато через dblink можно делать то же самое + дёргать функции и процедуры, а в postgres_fdw это умышленно не добавляют. Ну и dblink лучше подходит для эмуляции автономных транзакций внутри одной БД

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

Я абсолютно не против того, чтобы не пихать бизнес-логику в хранимки

Но те, кто об этом пишет статьи, обычно предлагают такие альтернативы:

  • аналогичный код на Питоне / JS / PHP..., ещё и обезображенный использованием ORM

  • dbt и его аналоги, которые в точности повторяют функционал хранимок, но через богомерзкую Jinja

Ещё ни разу не видел кейса, чтобы написали: "мы выпилили хранимки, вместо них написали высокопроизводительный код на С / Java / Rust..., который работает с данными по бинарному протоколу"

А замена хранимок на dbt / функции во внешних скриптах - это тот же половой орган, только в другой руке

--
По поводу именно этой статьи: баянистый сборник мифов о хранимках от человека, который вряд ли когда-то хотя бы пробовал писать на них что-то сложное. Иначе никак не объяснить множественные посылы о проблемах с тестированием - это ж самое простое как раз

Ух ты, кто-то ещё использует MySQL в 2024 году

Ладно б ещё хотя б с MariaDB сравнивали, она и то живее и активнее поддерживает фичи из новых редакций стандарта SQL (system-versioned, bitemporal tables и т. д.)

Сейчас MySQL по функционалу уже скорее с SQLite соперничает, чем с PG

Я плохо знаком с мёртвыми языками типа Пыхи и ещё хуже с его ОРМ, открыл почитать документацию по Yii2

Ну и сразу нарвался на типичную ОРМ-логику - joinWith по умолчанию генерирует два запроса на получение датасетов: левый джойн первой таблицы со второй и выборку из второй таблицы с коррелированным подзапросом (where in (...))

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

Можно я не буду эту лажу воспроизводить?)

Именно SQL ограничен декларативной парадигмой, а сверхсложную логику пишут на языках общего назначения

Срочно устраивайтесь в Databricks, а то они зачем-то Spark Dataframe API трансформируют на Spark SQL, а не наоборот, дилетанты!1! И всякие Iceberg, Hudi и другие профессиональные инструменты для работы с данными почему-то продолжают развиваться в парадигме SQL в первую очередь.

Ну и с прикладной стороны, по секрету скажу, куча "сверхсложной логики" по работе с данными в крупнейших банках и телеком-провайдерах написана в БД - это проще, производительнее, надёжнее

Если в вашем пузыре клепания CRUD-сайтиков никто не умеет в SQL на профессиональном уровне, это не значит, что он чем-то ограничен) Вполне себе Тьюринг-полный язык программирования

ORM в следствии своей парадигмы, толкает на написание работы с данными внутри кода, что приводит цельности логики и его дальнейшему рефакторингу.

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

Логика кода от его обфускации в ORM или без него не должна меняться или становиться более или менее цельной

То же самое с рефакторингом, только SQL переписывать, тестировать и профилировать куда проще

Вопрос - зачем вообще делать маппинг отношений в объекты в приложении?)

Мне кажется, что мне вас есть чем приятно удивить.

Страшно представить, как какая-нибудь SQLAlchemy справится с разветвлённой иерархией ролей в PostgreSQL, когда она на джойнах нескольких таблиц и базовых DML-операциях начинает спотыкаться)

Даже не хочу знать)

Чтобы всем было наглядно видно, что эту мешанину будет сложно поддерживать

Я принципиально не понимаю как кривую и функционально неполную абстракцию над кодом может быть проще поддерживать, чем чистый, максимально explicit код, тем более такой простой, как SQL)

Не вижу от этого особой пользы для стандартных задач

Ну это ожидаемо)
К счастью, современные ОРМ уже залатали 99% дыр в безопасности, которыми они посвистывали ещё лет 5-7 назад. А сливы данных из-за такого отношения к инфобезу уже к ORM не относятся
Вот если б ORM делали бы вид, что ещё и ролевые модели в БД умеют генерировать, вот тогда б точно приехали))

P.S. завтра утром до ноута доберусь, может накидаю какие-то варианты, раз так сильно просите)

Господи, ну вы что, не представляете генерацию параметризованной SQL-лапши без ORM? Это ж просто строка, её можно вертеть как угодно через условия в переменных и if / циклы и т. д. Если не требуется джойнить managers - да просто не генерируй соответствующий кусок строки, и всё))

Нужно переиспользовать шаблон логики? Не беда, человечество давно придумало шаблонизаторы (Jinja, Twig и т.п.) и пользовательские функции на языках, встроенных в БД (что даёт не только более чистый и стабильный код и на стороне приложения, и на стороне базы, но ещё и более безопасный код - см. например security definer-функции в PostgreSQL).

Единственное, что действительно необходимо для работы с БД из приложения - драйвер для подключения к этой БД (примеры для Python - psycopg / asyncpg, cx-oracle, pyodbc и т.п.)

С телефона я сейчас печатать код не буду, 1 мая, увы, не за рабочим местом)

Но тут такой примитив, что даже ORM не должен налажать, наплодив n + 1 проблему, избыточные джойны и отбитые списки в where. Хотя я не уверен) Да и не факт, что вы сами знаете как этот запрос сейчас у вас фактически выполняется)

Тут не один запрос, а простейшая параметрическая кодогенерация. Это всегда решается на уровне приложения или процедурного языка (PL/SQL, T-SQL, PL/pgSQL). ORM для этого не обязателен, на любом языке программирования из коробки можно собрать такой же запрос, в т.ч. с защитой от SQL Injection

А где тег "вредные советы", "сарказм"?

Использование ORM и по смыслу (напиши мне SQL-запрос на основе команды на другом языке), и по качеству результата (ниже плинтуса) эквивалентно использованию ChatGPT для генерации SQL-запросов

Это стандартные объёмы транзакций небольшой-средней локальной платёжной системы. Или базы данных платформы с чатами / соцсети тоже не самого большого масштаба. Или телеметрии с датчиков крупного автопарка / завода

Ничего сверхъестественного в 2024 году в этом нет

Запретить погромистам менять структуру кода в новых версиях

Сломал обратную совместимость - к тебе выезжает Github-патруль и ломает палец

Так вижу

база по умолчанию собирает данные о распределении относительно всего лишь 100 значений (параметр default_statistics_target).

Это некорректная формулировка
Статистика собирается по 300 * default_statistics_target кортежам с такого же количества страниц. Т. е. , по дефолту, 300 * 100 = 30000 строк
И ещё default_statistics_target определяет количество корзин в гистограмме значений, тут без всякого множителя

См. комментарий в исходниках по поводу числа 300

Information

Rating
Does not participate
Location
Краснодар, Краснодарский край, Россия
Works in
Date of birth
Registered
Activity

Specialization

Database Developer, Database Architect
Lead
From 500,000 ₽
SQL
PostgreSQL
DWH
Greenplum
ClickHouse
Apache Airflow
ETL
Neo4J
Database
Python