Pull to refresh

Comments 191

Разные условия — разные инструменты. Другое дело, что переход с Mysql на PostgreSQL у Uber-а был, мягко говоря, не сильно обдуманный. Тут уж надо выбирать — или шашечки (они же хайп), или ехать (здравый смысл при выборе софта и не только).

Здесь параллельно поддерживались обе СУБД. Так что тут как такового перехода вообще не будет.

Сразу вспоминается как Gitlab случайно удалил свою продакш-базу и положил весь сервис. Переход на psql от этого не спасет, так что пусть не торопятся)
UFO just landed and posted this here
так, что бы можно было использовать любую СУБД легким щелком переключаясь между ними.

Вот только это всегда идет ценой немалой потери производительности из-за невозможностии использовать специфичные для конкретной субд оптимизации
Вот только это всегда идет ценой немалой потери производительности из-за невозможностии использовать специфичные для конкретной субд оптимизации

Как-то слишком категорично. Очень многие фреймворки и орм позволяют выбирать «драйверы» для конкретных бд (со специфичными оптимизациями под капотом), при этом не меняя сами абстракции с которыми вы работате в коде.
Про закон дырявых абстракций слышали? В случае с СУБД — он особенно жёстко работает: чтобы ваша программа быстро работала вам нужно учитывать её особенности ещё на этапе создания схемы хранени данных. Никакие «драйверы» вам не помогут, если вы заложитесь на какую-то операцию, которая в одной СУБД делается быстро, с индексами, а в другой — только полным сканом всей базы.

Пример вот прямо здесь же, в этой статье, обсуждался: PostgreSQL умеет искать по подстроке в текстовых полях с использованием индекса, а MySQL — нет, только прочёсывать всю базу. И что ваши «драйвера» могут с этим сделать?

Обычно там не оптимизации, а особенности синтаксиса, которые хорошо прячутся за абстракциями. Оптимизации же, обычно, или недоступны под абстракциями, или абстракции сильно текут при их использовании и код становится зависимым от конкретной СУБД или даже её версии.

БД это просто хранилище данных
Но ведь БД уже давно не только хранилище данных. Если так считать, то можно и в текстовом файлике данные хранить :)
UFO just landed and posted this here
Да, мы тоже так делали. Без всяких SQL-запросов просто 2-3 if'а в коде, и перелопачивается инфа проще и быстрее :)
А что начнется, когда потребуется конкурентный доступ…
А если захочется немного оптимизировать доступ и добавить индексный файл для поиска, управлять кэшированием.
ОЙ! Кажется мы пишем новую СУБД :)
UFO just landed and posted this here
Знаете, вторая сторона правды в том, что запросы ORM всё равно приходится как-то оптимизировать в случае более-менее приличной нагрузки, а железом и деньгами можно залить далеко не всё.
железом и деньгами можно залить далеко не всё.

Вы даже не представляете сколько таки можно и таки заливается железом и деньгами.
Я видел такие чудеса! Там где можно написать просто и красиво и использовать один простенький сервак и монолитное приложение легко справится. Люди ставят несколько кластеров раскидывают десяток криво написанных и несбалансированных микросервисов без какого-либо вменяемого мониторинга и без тестирования (никакого! ни юнит ни интнграционных, ни нагрузочных). Сверху ставятся рэндомно настроенные системы кэширования и CDN. И потом активно сосутся деньги на борьбу с «большой нагрузкой». Поверьте, деньгами можно залить все, особенно собственную некомпетентность. Как вы сможете объяснить некомпетентному авторитарному руководителю отдела с такими же как он некомпетентными админами и девелоперами, что вся их работа просто результат их же некомпетентности? они все получают зарплаты и премии, а язык технических аргументов не понимают и не собираются разбираться что и как можно улучшить. Им это не нужно — им проще и понятнее запросить бюджет на пару десятков новых серверов и нанять еще десяток людей чтобы все это «поддерживать».

Извините, к ORM мой коммент вообще не имеет отношения, ни в ту ни у другую сторону, просто немного наболело. Просто поверьте деньгами заливается порой так много, что волосы встают дыбом и теряется дар речи.
Да я вас понимаю, что вы. Просто конкретно сегодня серебряной пулей объявлен ORM (ну, судя по тому, что крутится вокруг моих знакомых), а так — картина как раз именно такая. Только это… «Лить деньги» и «Залить деньгами» это немного разные критерии, на мой взгляд. Вот вы как раз описываете историю про «лить деньги», а «залить» — это забыть на 3/5/7 (ну, если сильно повезло) лет. И вот этого как раз и не происходит.
«Лить деньги» и «Залить деньгами» это немного разные критерии, на мой взгляд

Поддержу. Алгоритмическую сложность не зальёшь. Я лично видел O(nˆ3), где можно обойтись O(n*log(n)). И в таких случаях никакой кластер-хуястер не поможет xD
Ну, точнее поможет на пару месяцев, пока нагрузка низкая и медленно растёт.

во во, я видел как одна фирма реализовала простейший алгоритм вида: изображение с камеры минус фон и если слишком много пикселей больше некоторого порога то останавливаем станок (оператор руку в область плазменой резки засунул) на кластере из серверов каждому отдавалась свой кусочек изображения 50 мегапиксельного), но моя реализация управилась в реалтайме на микроконтроллере. Они его реализовали не простым сравнением кадров с ресайзом кадра хотябы до 1 мегаписеля (хотя ноготь занимает около 5% экрана, ладонь половину, да и область резки 20х20см, и зачем туда 50 мегаписелей и камера за миллионы рублей?), нет они вычисляли оптический поток новомодным кодеком AV-1 всех 50 мегапикселей с макс качеством, с максимальными настройками предикторов движений и граней и тд. Всё равно им не удавалось залить это деньгами, серверными шкафами и превзнемоганиями — оно попросту глючило (на краях разрезки) и было неотзывчиво что жесть (пинг до минуты).

Кем он объявлен? У вас устаревшая информация: актуальная серебрянная пуля — CQRS+ES :)

уже не актуально. теперь serverless в тренде :)
нужно делать продукты так, что бы можно было использовать любую СУБД легким щелком переключаясь между ними

Пишу я проект на пострге… и в постргре есть функционал, который позволяет мне ускорить запрос в два раза, но этого функционала нет в других субд или в какой то конкретной субд. Получается, из ваших слов, я не должен его использовать потому что «потом» если я вдруг захочу заменить его на что то другое щелчком пальца, у меня возникнут проблемы?
Извините, я не понял, что вы хотите до меня донести. Как этот класс решит озвученную мной проблему?
Класс лишь пример. Вообще я указывал не на весь класс, а на функцию реализующую FIND_IN_SET в различных базах данных. И в ней видно что oci8, pdo_oracle бросает exception, предупреждая что данный оператор не поддерживается.
Вот я и хочу донести, если выпишите универсальную систему, вы можете использовать функционал
который позволяет мне ускорить запрос в два раза
но там где это не поддерживается вы его просто не используете.

Отвечая на ваш вопрос: вы можете использовать любой функционал, но система должна быть отказоустойчивой в случае отсутствия данного функционала. В вашем случае: если база не пострге, не используем функции ускорения запросов.
Система не обязана быть отказоустойчивой при смене бд. Если я изначально для себя решил, что мне нужен просто сайт на постргре и я не планирую переезд на другую платформу — это мое право.
Не говоря о том, что кинутые в последствии эксепшены при смене базы, это не решение самой проблемы. Мое личное мнение, что проще писать все на чистом постргре и затем если когда то это понадобится один раз сесть и переписать все узкие места, чем поддерживать все это.
Вы сами себе противоречите «я не планирую переезд на другую платформу» хотя изначально было «если я вдруг захочу заменить его на что то другое щелчком пальца».

В целом и автор, комментарием что вы процитировали, прав и вы. У него мыльтиплатформенная система, он учитывает её особенности. У вас она завязана на конкретную базу, вы учитываете только её особенности.

Поэтому если, как вы написали в первом посте, вы хотите изменить базу щелчком пальца, то вы должны сразу писать что-то мультиплатформенное. Если, как вы написали потом, вам нужен сайт на постргре, то уже как вам удобно.
Хорошо, понял вас, согласен. Надеюсь мне не придется поддерживать такую систему))
Если честно, мне вообще сложно придумать ситуацию, когда может понадобиться писать проект под несколько бд с возможностью быстрого переключения между ними))
Разве что если вы делаете какой то продукт для клиентов, что бы они могли сами выбрать бд.
Вы опять противоречите себе, причём в этот раз в пределах одного комментария!
мне вообще сложно придумать ситуацию, когда может понадобиться писать проект под несколько бд
вы делаете какой то продукт для клиентов, что бы они могли сами выбрать бд.
Почему противоречу, мне пришлось напрячься что бы его придумать…
расширяйте, я не против)
А зачем всех этих перечислять, если, собственно, вся статья о компании, выпускающей «какой то продукт для клиентов, что бы они могли сами выбрать бд»?
Перечислил первых кто пришёл в голову.
Учавствовал в разработке проекта под 4 СУБД — Oracle, IBM Informix, PostgreSQL, MS SQL Server. Заказчик еще запускал систему на MySQL, хотя мы предупреждали, что с ним она не тестировалась. Т. к. писалось все на Java, мы просто использовали Hibernate и HQL для запросов, без использования фич конкретных СУБД.
UFO just landed and posted this here
Справедливости ради, это цена за универсальность. В oracle подобное реализовать возможно процедурой.

На мой взгляд, так не бывает. Если у вас есть возможность на ровном месте уменьшить время отклика в 2 раза, то или у вас где-то в цикле стоит sleep, или вы получаете рост производительности по одним параметрам за счет просадки производительности по другим параметрам. Грубо говоря, уменьшили время работы запроса — потратили оперативную память.


Если произвести анализ, за счет чего происходит улучшение, то, как правило, можно обнаружить, что в большинстве топовых продуктов такая оптимизация уже есть, только устроена немного по-другому, или можно самому ее реализовать.

Это только на ваш взгляд так не бывает. Наверное, вы не работали с планами запросов и не пробовали использовать хинты/менять запрос для изменения этого плана. Или вообще не представляете как работает запрос, что некоторые СУБД кэшируют план и т.д. Самый банальный пример с алгоритмами соединения таблиц, когда планировщик выбирает, например, Nested Loops вместо Hash или Merge Join

И что же такое есть в одной СУБД, чего нет в другой?

1) Например, хинты. Насколько я помню, в Postgre их не завозят, в Oracle они в комментах, в MS SQL они часть синтаксиса.
2) В Postgre табличные выражения, насколько помню, указывают планировщику указывают на то, что эту часть нужно материализовать во время выполнения запроса (или как-то так, я с Postgre не работал). А в Oracle и MS SQL они не влияют на план запроса.
3) MySQL вроде как не поддерживает оператор MERGE из стандарта SQL 2008 года. Есть аналоги, но не полностью покрывают функционал.
4) MySQL не поддерживает транзакционность DDL-операций. Из-за этого сложности с накатом миграций.

Продолжать можно до бесконечности

Это все про удобство. Ничего из этого выигрыша времени выполнения запроса в 2 раза не дает.


MS: Because the SQL Server query optimizer typically selects the best execution plan for a query, we recommend only using hints as a last resort for experienced developers and database administrators.


Oracle: test by means of hints, but use other techniques to manage the SQL execution plans, such as SQL Tuning advisor and SQL Plan Baselines.

Ключевое слово здесь «typically». А неправильно выбранный план даст просадку не в два раза, а в сто.

Так не пользуйтесь хинтами и не будет у вас просадки в 100 раз. Именно про это же и пишут производители.

У вас слишком упрощённое восприятие предметной области. План может сломаться из-за кучи различных факторов: неправильная статистика, сильная фрагментация индекса, сам индекс был оптимальным, а может стать неоптимальным. И в каждом из этих случаев тупо положившись на оптимизатор легко получить деградацию на порядки. И простого решения уровня «не используйте хинты» здесь нет.
И, разумеется, хинты — это не про «про удобство». Это про боль, страдания, ненависть. Но никак не про удобство.

У вас есть примеры, когда запрос без хинта работал нормально, а потом резко деградировал на порядки, причем, если бы в нем был хинт, то деградации бы не произошло?

Разумеется. Вы, вероятно, просто не работали с большими базами данных)

Смотря, что считать большими. Те базы, с которыми я работал, я большими не считаю.

Разумеется. Чуть ли ни штатная ситуация даже со небольшими данными, порядка лишь миллионов записей, когда оптимизатор не справляется с оценкой мощности подветвей запросов даже средней (с виду) сложности.
Вот вам наглядный пример из моей практики:
Приложение посылает к базе MS SQL несколько раз запрос, в котором в области WHERE идет фильтрация в каждом отдельном случае по разному значению идентификатора. В запросе накручено много всего (его генерирует ORM). Но, обычно, он выполняется быстро (в зависимости от кол-ва возвращаемых строк — от 0 до двух минут). И тут иногда случаются дикие просадки, запрос висит по часу.

Пошел разбираться — вначале к серверу приходили запросы по идентификатору, по которому одна-две записи. SQL Server генерировал план запроса с использованием Nested Loops для джойнов. И кэшировал этот план. А потом к нему приходил запрос, по которому данных в несколько тысяч раз больше. Но план брался из кэша — не оптимальный.

Это лечится, например, хинтами. Тут два варианта, либо явно указывать алгоритм соединения таблиц в проблемном месте. Либо указывать запросу, чтоб он строил план без учета закэшированного. Так как я работал через ORM то я просто упростил запрос, но в ином случае, возможно, использовал бы хинт.
T-SQL, разбор xml на сервере.
С хинтом и без хинта время разбора большой ( пара сотен мб ) xml отличается на 2 порядка, пока шли небольшие xml — проблема не была видна.
на всякий случай, хинт:
OPTION ( OPTIMIZE FOR ( xml = NULL ))
Как раз время разбора xml ни от какого хинта зависеть не может. Не там тормоза ищете.

Не "время разбора", а план запроса, и общее время этого самого запроса. Наверняка же та же самая проблема что и в комментарии выше: кешируется план, составленный для xml из пары записей, а потом прилетает xml из пары миллионов записей и все.

Это уже теплее, но у автора коммента, на который я отвечал (вы же прочли?), подозрение конкретно на время разбора xml, и отвечал я именно на это. Ну и сравните:
Не там тормоза ищете
Не «время разбора», а план запроса
Да, возможно кривой план.
Но с хинтом и без скорость работы процедуры различается на 2 порядка.
Так для этого и нужно читать оценочные мощности ветвей в плане: для пустого xml (при optimize for(xml=null)) оптимизатор предполагает пустой результат разбора, но поскольку нулевые оценочные мощности он никогда не ставит — оценочная мощность этой подветви будет равна одной строке. Соответственно, может меняться и остальной план из этой оценки, вот и вся магия.
так не бывает

Да легко: в моём случае пересадка на постгрес с переписыванием кучи запросов (много insert into select… group by, insert on duplicate key) в эквивалентные дала ускорение в три раза (с полутора минут до 30 секунд). Памяти постгрес жрал «стандартные» 12 метров на всё про всё.
Да, какие-то запросы в постгресе выполняются медленнее (например, вставка в таблицы с большими первичными ключами ака наивные счётчики просмотров или запросы содержащие not in), но мне из постгреса с настройками «для калькулятора» в среднем удаётся выжать намного больше, чем из мускуля который когда-то долго настраивали под железо.

В старших редакциях MS SQL Server можно создавать материализованные (индексированные) представления, и они будут автоматически подхватываться для некоторых запросов, выступая как что-то вроде индекса.


А в младших редакциях того же самого MS SQL Server, вроде SQL Server Express, такой возможности нет. Более того, даже прямая выборка из материализованного представления не использует это самое представление, если забыть указать ему with(noexpand)!


Вот тут-то и выходит, что на каком-нибудь простейшем select count_big(*) from foo where bar = 'baz' разница в производительности будет хоть в 1000 раз, хоть в 10000 — в зависимости от того, чему это count_big(*) равно.


И получается выбор: либо писать запросы строго для enterprise-редакции, либо писать более сложные запросы для любых редакций sql server — либо денормализовывать схему БД просто из-за того, что в природе существует MySQL, где аналогичной функциональности нету.

Ещё веселее, когда нужно реализовать одинаковый функционал на 2000, 2008 и 2016 SQL Server — потому что они есть у заказчиков и менять никто ничего не будет.
Смотришь, в какие граблевилосипеды разворачивается быстрый код и структура БД при переходе с MSSQL 2016 на 2000 и становится грустно.
Незнаю как в MSSQL но в Oracle и PostgreSQL materialized view, это вьюшка со своим стораджем, что позволяет, например, строить сложные индексы по нескольким полям разных таблиц
Для таких случаев придумали стратегии: если мы на постгресе, то используем оптимизированный запрос, если нет — делаем медленно.
Именно это говорит дядюшка Боб в Чистой Архитектуре. Мол, архитектуру надо делать такую, чтобы вопрос о выборе конкретной технологии можно было откладывать как можно дольше.
В большом серьезном проекте это вряд ли получится, иначе придется изначально завязываться на то что одни и те же данные могут храниться в десятков разных типов баз данных (key-value, sql, bigdata, текстовые файлы, mongodb) и интегрироваться десятком разных способов (rest, soap, css архивы, message broker и т.д.). Это ограничит возможный функционал базы данных и интеграции (то есть никаких сложных sql запросов или аутинтификации от soup) и сильно усложнит приложение.

Скорее нужно как можно раньше ограничить какие технологии могут быть потенциально использованы в будущем в вашем проекте, а какие нет (в смысле, понять что забивать гвозди микроскопом вы никогда точно не будете).
Если вы делаете простой микросервис, который просто сохранает и возвращает данные из sql базы, то при изменении требований с sql базы на mongoDB и интеграции с rest на soup намного проще выкинуть старый сервис и написать новый микросервис с нуля, чем пытаться изначально сделать максимально универсальную архитектуру.

Он предлагает абстрагировать бизнес-логику от систем хранения, это да. Но почему мало кто замечает, что он в этой книге постоянно упирает на экономику процесса и связанную с этим разумную достаточность?


Здесь же явно проблемы не с абстракциями, а с экономикой. Существенное сокращение затрат для продуктовой компании вполне может окупить отвал не критичного количества принципиальных пользователей именно mysql. Абстракции сами по себе стоят денег, чего уж говорить о конкретных реализациях!


Да и "универсальный" код (это если не делать отдельных реализаций), зачастую, сильно связывает руки. Знаете, я был счастлив, когда в январе узнал, что мне не надо больше оглядываться на ms sql 2000. А ведь у меня не было под него отдельной реализации какой-нибудь абстракции, только необходимость избегать использования нескольких полезных возможностей.


Так что пусть себе отказываются. Поддерживаю. О себе тоже иногда надо заботиться.

UFO just landed and posted this here
Ахаха, вы бы комментарии аккуратнее писали. Вас понял как раз в противоположном смысле.

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

В Энтерпрайзе много данных. Очень много. И они часто лежат в самых разных местах: какие-то попадают на внешний ftp, какие то лежат в другой бд, третьи — это действия пользователей в ERP системах. И для сборщиков всего этого пишется много etl'ов. Затем имея все это в бд, тебе надо это все пересчитывать, используя наверное хранимые процедуры. Посчитанные данные надо показать пользователю, например через power bi, который очень любит базу ms sql. И я уже молчу про тонну триггеров, которые аналитики вешают на таблицы. И я замечу, что все это может делать аналитик, который просто хорошо знает SQL.
И вот как все это переключать на другую БД щелчком пальцев, и самое главное Зачем? Вопрос открытый.

каким образом логика работы с данными может быть зависеть от системы хранения.

Логика не зависит, зато зависит реализация этой логики. Это тоже самое сказать, что логика работы с данными не зависит от языка программирования.
Могу привести свой пример из почти «кровавого энтерпрайза»
Есть ERP-система, в которой невозможно изменить логику в абсолютно любом месте. Например, расчет каких-то значений, или контроль ввода данных, или пересчет значений в других таблицах. И единственный выход в данной ситуации писать бизнес-логику на триггерах в SQL-таблице.
Совсем стандартный случай, когда в самих триггерах фиксируется кто и когда отредактировал запись в БД, тк клиентский не протоколирует изменения.
Штатного функционала в ERP-системе на программирование логики на этапах before-insert, after-insert нету и не предвидится. В тех местах, где она доступна, пишется на таком псевдо-языке, что хочется убиться об стену. sad but true.
В другой ERP-системе видел примеры, когда XML-сообщения парсились функциями из SQL, тк в самой системе для работы с XML почти ничего нет.
Еще пример, например, есть расписание сотрудника с обедами и техническими перерывами, со сменами в разное время, и вот это вот все. Вся информация раскидана по разным таблицам, типовые дни в одном месте, перерывы в другом, праздники в третьем, и тд. И как всегда, неожиданно, нужно сгенерить некий табель рабочего времени сотрудников.
Есть несколько вариантов решения этой, казалось бы, стандартной задачи. Обратиться к разработчику этой ЕРП-системы, ответ: функционал будет в следующей версии, когда-нибудь. В течение семи лет разработчик так и не сделал этот функционал.
Второй вариант, написать на SQL хранимую процедуру, которая по некоему алгоритму обойдет все эти таблицы, и запишет результат в некоторую временную таблицу. Сделать простой select к произвольной таблице и выдать пользователю ERP-система умеет.

Так тут дело в том, что код ERP менять нельзя, а не в том, что база лучше.

UFO just landed and posted this here
в каком месте располагать обработку логики работы приложения
Мое мнение в процедурах БД удобнее, поскольку СУБД и заточена под обработку данных

Так она же для обработки данных заточена, а не для написания логики. Языки программирования заточены на описание логики, поэтому надо располагать в приложении.

Но ведь логика работает с данными.
UFO just landed and posted this here

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

UFO just landed and posted this here

Понятно, что можно реализовать на хранимках (хотя в разных SQL будет разная боль). Но вот что работа столь же удобна как с обычным кодом просто не верю. Одно версионирование кода процедур ни разу не встречал нормальной рализации, с историей, с ветвлением, со слиянием ветвей и т. п. Плюс в тех SQL с которыми плотно работал не было средств нормальной группировки хранимок, по сути они все разделяют одно пространство имён.

UFO just landed and posted this here

Текст процедуры или команда типа CREATE PROCEDURE? Или кто-то из SQL начился синкать своё хранилище процедур с ФС на клиенте?


Группировать хранимки можно префиксами

Вот сразу вспомнилась молодость, C и PHP 3. Не согласен, что всего лишь вопрос привычки, это вопрос удобства.

UFO just landed and posted this here
Первое сохранение может быть CREATE, далее ALTER.

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


Еще может быть при развертывании сначала проверка на существование процедуры, если ее нет CREATE пустышки, далее ALTER.

Я не про развёртывание, а про разработку с локальной БД. Пришёл на работу, открыл IDE, изменил файл, сохранил и могу эту процедуру вызвать. Скрипты развёртывания или проверка существования мне не нужны в окне редактирования кода.


И откатывание на предыдущую версию лишь частный случай переключения между версиями.


Сейчас мы работаем прямо на боевом сервере

А как синхронизируете работу? Одному нужно пару строк в процедуру добавить вначале, другому в конец.

UFO just landed and posted this here

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


Можно представить такую ситуацию: на таблице висит триггер, вызывающий хранимку. В какой-то момент в таблицу добавляется новое поле, и отдельный скрипт проставляет ему правильное значение, вызывая срабатывание триггера.


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

UFO just landed and posted this here

Да со связкой-то все понятно, это очевидно. Проблема вот в чем: для обновления версии схемы с 3й до 5й вам могут понадобиться хранимки 4й версии.


Иными словами, история некоторых хранимок имеет значение. Именно в этом их принципиальное отличие от кода.

UFO just landed and posted this here

Не занимаются. Но могут участвовать, прямо или косвенно.

UFO just landed and posted this here

Ага, но способов проверить что все сделано как надо — нету. А значит, спящие ошибки будут потихоньку накапливаться.

UFO just landed and posted this here

Чтобы что-то отловить на тестах — нужно воспроизводимое окружение, а его-то как раз получить с таким подходом (миграции данных — отдельно, хранимки — отдельно) не получается.

UFO just landed and posted this here

Нет, не то же самое: миграции данных не могут зависеть от кода, и это не чье-то пожелание, а реально существующее ограничение.


Плюс, как я уже говорил, с запутанным кодом работать проще чем с запутанными хранимками.


Плюс кода выходит попросту меньше: особенности языка позволяют не повторять одно и то же кучу раз.

UFO just landed and posted this here
Вы можете накатить миграции, а код ваш оставить старым и приложение поломается.

Да, но БД при этом окажется в валидном состоянии, все ошибки изолированы в приложении.


Если то же самое сделать в схеме с миграциями и хранимками — то БД может оказаться в поврежденном состоянии, и хорошо если еще это окажется сразу же замечено.


Сохраненные процедуры и функции для того и сохраненные, чтобы не повторять одно и тоже.

В SQL нет обобщенного программирования и метапрограммирования (ну, теоретически есть exec, но он дает write-only код), поэтому иногда именно что приходится повторять одно и то же, с небольшими вариациями.

UFO just landed and posted this here

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

UFO just landed and posted this here

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


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


Кстати, насчет WHERE: простая перестановка ключевых слов позволяет заметно сократить число подобных ошибок. И это тоже плюс для ORM.

UFO just landed and posted this here

Потому что в одном случае миграция БД от версии 3 до версии 5 дает такой же результат, что и миграция от версии 3 к версии 4, а потом к версии 5.


А в другом случае результат может оказаться разным.

UFO just landed and posted this here

Почитайте комментарии выше.


Версия 3 содержит некоторую версию хранимки X (далее X3)


Версия 4 содержит другую версию хранимки X (далее X4)


Версия 5 содержит миграцию Y, которая, в том числе, запускает X


Теперь, если мигрировать 3-4-5, то миграция Y запустит хранимку X4. А если мигрировать 3-5 — то есть два варианта:


  1. обновления хранимок — тоже миграции. Все в порядке, миграция Y все еще запускает хранимку X4.


  2. обновления хранимок происходят после всех миграций. Вот тут уже проблема: миграция Y теперь запустит хранимку X3.


UFO just landed and posted this here
Я не могу понять, зачем миграция должна запускать хранимую процедуру?

Так случилось. Если это не ограничено технически — значит, рано или поздно это случится.


И если вы допускаете, что миграция 5 может стартовать после миграции 3, значит вы должны обеспечить механизмы для этого. Например, вы обеспечиваете сначала запуск миграции 4, потом уже запускаете миграцию 5.

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


Причем тут бизнес логика в процедурах?

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

UFO just landed and posted this here

Ну вот есть у меня ветка задачи, когда-то от основной ветки отрезанная. Я сделал свои процедуры или изменил сществующие, пускай с десяток изменений — проверил всё работает. Подмерживаю основную ветку, чтобы проверить, что последние изменения не ломают мою задачу, а мои изменения не ломают их. За это время в основной ветке с десяток процедур добавилось, а ещё три десятка изменилось. Всего их, например, под 1000 (аналог сотни объектов с десятком методов в каждом — несложная система). Как мне сделать эту проверку?


Смотреть какие файлы изменились/создались за время последнего синка с основной веткой и накатывать их по одному? Сносить всю базу и разворачивать её с нуля?


Сделал задачу, отправил её на код-ревью, переключаюсь на другую. В базе у меня версии от предудщей задачи, как мне переключиться на версию основно ветки — удалять новые процедуры ручками по памяти или делать дифф vcs, накатывать старые версии так же по памяти лили диффу, или нактывть новую базу?


На основная суть: процедура это код в виде текстового файла

Все более-менее рабочие тулзы и подходы по использованию VCS для SQL базируются на том, что под контролем VCS хранятся не коды процедур, а команды на создание или обновление процедур (возможно создание с предшествующим удалением), которые уже содержать имя и код процедуры. Императивный подход, а не декларативный. Мы редактируем не процедуру, а команду на её создание или обновление. А если, не дай бог, для раскатывания процедур применяются миграции, то мы и не редактируем процедуру, а на кажды чих создаём новую команду на изменение, н уи удаление.

UFO just landed and posted this here
Вы реально работали с системами, где бизнес логика работает в хранимых процедурах?

Я вот работал.

А как вы проверяете сейчас? Все тоже самое.

Просто проверяю. Рабочий каталог под контролем VCS или прямо содержит исполняемый код (скриптовые языки) или рабочий каталог компилируется в него, а компилятор — чистая функция от исходников по сути. Состояние проекта полностью лежит под контролем VCS, нет ничего (ну, почти ничего) внешнего, у чего было бы своё состояние, от которого зависит воспроизводимость запусков.


Пока не появляется SQL или типа того, когда у нас нет ни желаемого состояния в проекте, ни удобных (в идеале прозрачных) механизмов приведения к нему. Если мы храним процедуры или схему данных в виде явных или завуалированных команд CRATE/ALTER/DROP/..., то мы лишь надеемся, что эти команды будут исполнены на том состоянии базы, на котором они исполнились у нас. Со схемой данных более-менее решено путём декларативного описания таблиц, связей и т. п. и генерацией команд для приведения текущего состояния (реально текущего, физически запрашивается текущая схема у СУБД) к желаемому. С самими данными сложнее, а с процедурами более-менее работающего способа нет, кроме как дропнуть все и создать заново при каждой смене ветки, мерже, пулле. Уж не знаю почему, но реализуют генераторы команд CRATE/ALTER/DROP для процедур и триггеров, которые бы сравнивали текущее состояние процедур в базе и их текстовое представление в файлах.


Звучит так: взял код процедуры из контроля версий, доработал, положил обратно в контроль версий.
И называется это: «Редактирование процедуры».

Согласитесь, что это не реадктирование процедуры по факту у вас, если я правильно понял, а редактирование запроса создания/редактирования процедуры?


Как протестировать изменения, если другие разработчики внесли тоже изменения? Это же общая задача при разработке. Все аналогично.

У меня вопрос не как протестировать, а как привести процедуры базы к ожидаемому состоянию. Вот сейчас в базе у меня 1000 процедур с мастер-ветки (1000 файлов с CREATE OR REPLACE PROCEDURE), я переключаюсь в ветку, где 1010 таких файлов, причём 20 новых, 10 удалено, а 30 изменено. Я ожидаю, что после чекаута в эту ветку у меня будет 1010 процедур в базе, причём физически на сервер уйдёт 60 запросов. Что мне нужно сделать?

UFO just landed and posted this here
то и обрабатывать данные удобно в процедурах СУБД

Обрабатывать, возможно, быстрее. В некоторых случаях. А удобнее в коде, где есть средства переиспользования кода, управления выполнением, возможность использовать дополнительные сервисы типа произвольного кеширования, и т.п. Я поддерживаемость имел в виду.


зачем для этого данные тащить куда то в приложение, что бы там обработать

Затем что там удобнее управлять кодом обработки.


Кстати, интересен был бы практический пример, какая логика описывается языком программирования в приложении?

Ну собственно любая. Проверка прав доступа на выполнение действия например. Работал с базой, где в каждой процедуре был скопирован один и тот же код проверки прав с выдачей нужного сообщения, с соответствующими исправлениями в каждой копии названий таблиц и переменных, и соответствующими же проблемами, когда исправлено не все и не везде, особенно когда в вызываемую функцию добавился новый параметр. Да и вообще любая, связанная с наследованием и частичным переопределением в наследниках. Сложить A и B конечно везде несложно.

Что мешало разработчику создать отдельную функцию check_operation_ACL($user_id, $table_name, $operation_id)
которая бы запускалась в нужных хранимках.
SQL точно так же умеет «переиспользование кода»
Я поддерживаемость имел в виду.

К сожалению, в моей реальности чаще проще поддерживать логику в самом SQL, тк разработчик ERP-системы не в состоянии сделать функционал редактируемой администратором логики в нужных местах.
Плюс встроенный в ERP язык программирования не умеет многие вещи.
Если про поддерживаемость имеется ввиду версионность кода, и кто когда что сломал, то это одинаковая организационная проблема, какой сохранять код, или из SQL, или из конфигуратора системы в отдельный репозиторий.

А переделать исходный код (при наличии доступа к нему) — еще проще, чем переделать логику в SQL.

А если приложение работает в режиме толстого клиента? Попробуйте заменить EXE-файл на терминальном сервере, на котором работает 50 пользователей, и еще на на паре сотне отдельно стоящих компьютеров.

Если автообновление есть и работает — никаких проблем. Если же нет — ну и кто тут виноват?

Не так выразился, нельзя заменить exe-файл, пока запущен хоть один экземпляр программы, сначала всех выгнать из программы, потом обновлять. И так несколько раз в течение рабочего дня.
Это не проще, чем переделать логику в SQL.

А почему не у каждого своя копия программы, с обновлением при запуске?

У каждого способа есть свой фатальный недостаток и куча нюансов. Нюансов системы лицензирования, изменился путь программы — новое рабочее место. Нюансы системы безопасности, запуск exe откуда-либо кроме program files и windir запрещен. Права на запись в program files тоже закрыты, значит нужно как-то заранее каждому создавать свою копию программы с директорией, и потом всю эту кучу отдельно обновлять.
Нюансов системы лицензирования

Если у вас есть доступ к исходному коду (в т.ч. на изменение) — то это ваша программа. Неужели так трудно самим себе правильную лицензию оформить? :-)


запуск exe откуда-либо кроме program files и windir запрещен

Но при этом к единственной копии СУБД админский доступ есть у любого разработчика? А доступ на запись — у любого пользователя (пусть и частично защищенный слоем хранимок)? Ну ок...

Так для этого трехзвенную архитектуру используют, это и позволяет писать логику на языке программирования. Веб-приложение это именно такой вариант. Зачем разрешать пользователю напрямую в базу коннектиться.

Она так и запускалась, только с разными переменными, часть которых предварительно вычислялась другими процедурами, и проверялся результат, по которому выдавалось специализированное для данной процедуры сообщение об ошибке. В веб-приложении это выносится куда-нибудь в базовый контроллер или в настройки движка проверки ролей, и для всех методов контроллера работает автоматически.


Поддерживаемость в любом смысле, касающемся управления кодом.

Что именно мешает придти к точно такому же бардаку в самом веб-приложении? Для этих мест роли могли считаться так, а в других местах по-другому. Это все вопросы организации внесения изменений и работы с legacy-кодом.

А для веб-приложения есть IDE, в которой можно сравнительно легко разобраться что и откуда растет.


Для SQL же я ничего столь же мощного не встречал.

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

Так вроде и для СУБД разрешения можно контролировать внутренними механизмами, без всяких процедур.


GRANT <permission> [ ,...n ] ON   
    [ OBJECT :: ][ schema_name ]. object_name [ ( column [ ,...n ] ) ]  
    TO <database_principal> [ ,...n ]   
    [ WITH GRANT OPTION ]  
    [ AS <database_principal> ]

Вот это вот всё. А то делают GRANT ALL, а потом начинают извращаться с движками проверки ролей в тридевятом царстве, а сами данные тем временем висят голым задом по system@system, и утекают при первой же возможности.

Имеются ввиду права на бизнес-обьекты. Например, может ли пользователь редактировать объект созданным другим пользователем, или может ли простой менеджер отредактировать документ уже подписанный начальником отдела. Физически записи лежат же в одной таблице.

Можно ведь сделать Updatable View, а доступа к самой таблице не давать в принципе. Если совсем строго, можно Updatable View делать на базе Readable View, и к ним иметь разные разрешения на чтение и запись, потому что если умеет обновлять, то должен уметь и читать, а обратное уже неверно.


Не работает такое разве что в платформах, где несколько клиентов на одной базе, и у каждого свои понятия, кому и сколько можно. Ну и представлений писать придётся немало, конечно, под каждую категорию ролей.

А если роли непредопределённые и пользователь их конфигурит сам? Генератор вьюшек писать?

Если роли неопределённые, тогда у вас нет ролей. Но, в принципе, разрешения в СУБД можно выдавать и отдельным пользователям, насколько я помню, только это более муторно.


А если пользователь настраивает разрешения сам, то мы находимся в else моего предыдущего сообщения, и определение разрешений средствами СУБД уже не очень хорошо работает само по себе. Зато у нас есть одно из двух решений:


  • Разрешения тоже являются данными, и их некоторые серверы разрешений и вне SQL плохо понимают, нужен будет такой, который поймёт. При этом в самой базе скорее всего придётся-таки устроить free-for-all, где все читают всё, и надеяться что БД не скомпрометируют.
  • Мы можем пользователям сделать красивый интерфейс для работы с нативными разрешениями СУБД, но опять же ценой вывешивания этого интерфейса фактически в открытый доступ, под какой-нибудь ролью суперадминистратора. И иметь ассоциации ролей с какими-нибудь данными, чтобы не было возможности менять чужие разрешения, а только свои.

При этом ни для одного из вами озвученных вариантов требований нет простого пути позволить пользователям решать "может ли пользователь редактировать объект, созданный другим пользователем, или может ли простой менеджер отредактировать документ уже подписанный начальником отдела". Если вьюшки мы считаем моветоном, тогда единственный более-менее реалистичный механизм работы здесь — это императивные проверки в клиенте, где пользователю будет дан чекбокс, оные проверки выключающий, и на этом его конфигурение будет закончено. Проверки, вероятно, нужно будет выразить как некий DSL, чтобы его понимал и фронт, и сервер, либо дублировать несколько раз. На UI это нужно для отзывчивости, и на сервере для надёжности, потому что полное доверие UI в итоге обходится дорого.


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

Если роли неопределённые, тогда у вас нет ролей.

Не неопределённые, а неПРЕДопределённые, максимум три роли при первом запуске: анонимус, суперадмин и зарегистрировнній пользователь.


Но, в принципе, разрешения в СУБД можно выдавать и отдельным пользователям, насколько я помню, только это более муторно.

Угу, завести в базе 100500 пользователей и апп-сервер будет держать соединение для каждого.


Кстати, а чем плох генератор вьюшек? Ну, кроме того, что его, гипотетически, надо писать?

100500 пользователей, 100500 вьюшек для каждой таблицы? Ну пускай не для каждой, джойны сделаем к тому, что и так джоинится. Ну сдесяток тоно выйдет. Миллион вьюшек?


А если "вьюшки" из релиза в релиз не меняются, зачем их генерировать каждый раз, не проще ли будет поместить их сразу в СУБД?

Они редко меняются, но постоянно создаются — пользователи-то регистрируются.

Угу, завести в базе 100500 пользователей и апп-сервер будет держать соединение для каждого.

А это разве хуже чем если каждый пользователь фактически с базой общается под аккаунтом администратора, где по факту имеет все разрешения на чтение и запись (и хорошо если и не на все ddl-операции тоже, до кучи), и только ваша прослойка с ещё одним сервером контроля разрешений сохраняет ваши данные от компрометации. То есть, доступ ко всем всем вашим данным можно получить если узнать, под каким аккаунтом ваш аппсервер общался с базой, а такой аккаунт у всех один.


100500 пользователей, 100500 вьюшек для каждой таблицы

Ну если у вас требования такие, что ж поделать. Не руками ведь вам их писать, вы генератор сделали. Если хотите ограничить число сущностей — улучшите генератор, чтоб вьюшки были параметризованы, например, пользователем, и/или отслеживайте (случайные) совпадения по условиям, ведь запросы у такого числа людей редко уникальны. А раз условия не уникальны, можно оптимизировать и выдать двум пользователям одну и ту же вьюшку(но параметризовать пользователем всё равно, во имя справедливости). И тогда их будет может быть уже 50250, а может и того меньше.


Кстати, а как вы предложите решать эту задачу без вьюшек? Ну, в смысле, у вас вместо 100500 вьюшек будет 402000 уникальных, сгенерированных вашим data-store в рантайме, запросов? Или есть какие-то мне неизвестные средства, о которых я просто не подумал?

А это разве хуже чем если каждый пользователь фактически с базой общается под аккаунтом администратора

Обычно всё же не администратора, а только с доступом к данным, без прав изменения схемы и т. п.


и только ваша прослойка с ещё одним сервером контроля разрешений сохраняет ваши данные от компрометации

Ну да.


То есть, доступ ко всем всем вашим данным можно получить если узнать, под каким аккаунтом ваш аппсервер общался с базой, а такой аккаунт у всех один.

Ну или узнать под каким аккаунтом DBA логинится или скрипты изменения схемы. Секреты надо хранить одинаково.


Кстати, а как вы предложите решать эту задачу без вьюшек? Ну, в смысле, у вас вместо 100500 вьюшек будет 402000 уникальных, сгенерированных вашим data-store в рантайме, запросов?

Зависит от драйвера СУБД, скорее всего ему на вход будут приходить параметризированные запросы под юзера для прав, которые легко разрулить на уровне SQL запроса, а остальніе будут проверяться либо до запроса к основным данным, либо после, либо и до, и после.

Обычно всё же не администратора, а только с доступом к данным, без прав изменения схемы

Так неважно, что нет прав изменения схемы, ведь там разрешения — это такие же данные, а не часть схемы. Следовательно, при взломе любому пользователю возможно "нарисовать" расширенные права доступа.

Разрешения на изменение схемы — часть схемы. А данные о доступах к данным могут храниться совсем в другой СУБД или вообще не в СУБД.

Где заканчивается обработка данных и начинается логика?
Вот здесь логика или обработка?
update clients set description=(
case when balance < 100500 then 'злостный должник'
else 'должник' end)
where balance < 0

Уже столько раз наталкивался, что очередной ORM, вместо указанного выше запроса делает так:

request1:
 select * from clients where balance < 0
...
request2:
update clients set description='должник'
where clients_id in (сотни значений)

а иногда вообще хочется плакать горькими слезами от select * from clients
потому, что программист в параметрах Linq написал IEnumerable вместо IQueryable…
По факту у нас спор остроконечников и тупоконечников.
Вот здесь логика или обработка?

Я имел в виду поддерживаемость кода, а не логическое разделение)


Уже столько раз наталкивался, что очередной ORM, вместо указанного выше запроса делает так

При правильном подходе такого запроса вообще не будет, потому что это можно проверять при построении view и отображать на языке пользователя.


Но такая замена это конечно неправильное поведение.

Конкретно данный пример был упрощен до предела. Конечно, там будет какой-нибудь set debt_status=15 и в условии where может быть нагромождение exist(select blabla)
И таких статусов у клиента штук 20, и пишутся в отдельную таблицу с историей. И есть вьюха на текущий статус из этой истории.
И в некоторых случаях VIEW значительно замедляет конечные запросы, например, а теперь из базы в пару десятков миллионов клиентов вывести количество должников по состоянию на начало года, и плюс эти должники должны быть с каким-нибудь другим статусом, или вообще потерявшим этот статус в прошедшем году.

А зачем тогда VIEW, если замедляет? Делаем просто SELECT * FROM clients WHERE balance < 0 и выводим в представлении как нужно. Поиск по истории это сама по себе непростая задача, ORM тут ни при чем. Будет что-то вроде SELECT * FROM clients WHERE id IN (SELECT client_id FROM history WHERE ...), и тут как раз удобнее разбить на 2 запроса с offset/limit в первом, в IN будет не более limit идентификаторов.

Вы привели очень частный случай. Почему-то в моей практике так делать приходится довольно редко...


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

Странно, довольно часто приходится бороться с заказчиком, который хочет поставить атрибут архивный для документов или клиентов. Начинаешь его пытать, а зачем, а какие критерии, а там выясняется всякие детали, типа, клиенты по которым не было движения больше трех лет, чтобы не попадали в общий список поиска и тд… или чтобы клиенты, которые заключили документы в пятницу 13, и родились в год козы, в общем списке подсвечивались цветом с кодом 666. А запихаешь такое во вьюху, простой select top 50 columns from clients left join view начинает дико тормозить.

Ну так есть же куча вариантов как с такими поступать:


  1. "сырой" запрос либо хранимка;
  2. ставим признак заранее, при сохранении;
  3. добавляем условие чтобы не ставить признак повторно — и вот уже нет ничего страшного "обычном" ORM-подходе, ведь каждый объект будет обработан не более одного раза, а на 1 раз много ресурсов не потребуется.
2) этот атрибут бизнес может придумать в произвольный момент времени.
3) статус может измениться в произвольный момент времени, даже из-за изменения критериев расчета атрибута. Например, с 2019 года мы любим всех клиентов, родившихся в год козы, а в прошлом году мы их ненавидели.
Я утрирую конечно, но ¯\_(ツ)_/¯
статус может измениться в произвольный момент времени, даже из-за изменения критериев расчета атрибута

В таких условиях никакой update работать корректно не будет. Тут только сложный и тормозной select поможет — а вот его-то как раз ORM составлять помогает :-)

По такому запросу все автоматом становятся злостными, ибо 0 явно меньше чем 100500

Надо правильные ORM для таких задач выбирать. EF хорош писать сайты быстро, но про скорость забудьте, ее придется тюнать.
На linq2db это пишется с полпинка:

var obligors = from c in db.Clients
   where c.Balance < 0
   select c;

obligors
    .Set(p => p.Description, p => p.balance < -100500 ? "злостный должник" : "должник")
    .Update();

Можно таким же способом их удалить
obligors.Delete();
или вставить в другую таблицу не пишучи SQL.
obligors.Insert(db.Others);

Все происходит на сервере, ничего на клиент не тянется.
[совсем offtop]
и будет раз в сутки
Это все-таки не дело. Один вопрос с вами ругаться, другой — в РО загонять.
Поправил, как мог.
[/совсем offtop]

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


Иногда этого общего подмножества достаточно. Иногда — нет (и пример данной статьи говорит именно об этом).

Конечно периодически идут рассмотрения перехода на другую СУБД. Это всего выходит очень дорого с неопределенным результатом.

Собственно потому и дорого с неопределнным результатом, что надо перписывать практически весь код взаимодействия с СУБД, если вы выжимаете из текущей всё. ORM, query builders или вручную написанніе запросы — не суть, суть используете вы специфику конкретной СУБД или абстрагированы от неё, пускай даже абстракция уровня "запросы пишем ручками, но из специфичных штук используем исключительно особенности синтаксиса"

UFO just landed and posted this here

Это очень специфические продукты, в которых нужно иметь возможность менять бд щелчком пальцев.


Для чего вообще может потребоваться менять бд? Либо если текущая перестала поддерживаться, либо если конкуренты сделали прорыв в производительности. Чтобы избежать первого, нужно использовать популярную надёжную бд, вроде постгреса. Второе, по моему, просто невозможно в реляционных бд — они уже полвека пилятся, всё что можно было действительно сильно оптимизировать в движке — уже оптимизировали.

Ещё вариант: у конкурентов была лучшая производительность и другие плюсы на одних кейсах и какие-то минусы на других. На старте проекта эти минусы были значимыми, а те кейсы не предвидились. Со временем ситуация поменялась, и, например, на MySQL уже новые бизнес-требования как функциональные, так и не очень не реализовать без ОГРОМНЫХ костылей типа создания для частичных или функциональных индексов отдельных столбцов, заполняющихся по триггерам, а то и целых таблиц. Тоже с материализованным представлениями и т. п.

Ну, давайте поговорим о том, какое это «просто хранилище» и об использовании пространственных функций PostGIS, например. Замените их чем-нибудь эффективно.
Это реалистично только для определённого класса задач. Но, далеко, не во всех случаях. Реляционные СУБД — это не только хранение данных, но и стандартизированный доступ к данным, поддержка логической целостности данных и много чего ещё. «просто хранилище данных» — несколько наивный взгляд. Если в приложении реально нужно только хранилище — то хоть СУБД, хоть простые файлы — один чёрт. «Использовать любую БД», «использовать любую ОС», «использовать любой ЯП», «использовать любого программиста» — и т.п. — все эти абстракции работают только до определённого предела и накладывают массу ограничений на продукт. Те, кто укладывается в это «прокрустово ложе» — вполне счастливы, но это точно не всем подходит.
UFO just landed and posted this here
Я конечно заранее извиняюсь, но по моему должно быть стыдно, среди существенных минусов перечислять такие вещи как:
С MySQL не получалось эффективно поддерживать вложенные группы

Эта проблема решена в mysql 8 с введение WITH, а значит можно было только повысить требование к версии.

MySQL не добавит тип столбца TEXT, если не задать параметр length

ИМХО вообще мелочь, больше похожая на придирки к синтаксическому сахару.

p.s. если у кого то есть конструктивные возражения с аргументами, с радостью выслушаю.
посгрес дохнет под серьезной нагрузкой из-за тормозных транзакций и сборки мусора, но mysql/innodb еще печальней с транзакциями. попробуйте сделать innodb табличку без индексов и в двух параллельных транзакциях записать по записи, а потом в одной из транзакций удалить свою же запись. вывалится с deadlock
dba.stackexchange.com/questions/238756/mysql-innodb-trying-to-lock-uncommitted-row-from-parallel-transaction-deadlock

при фулскане оно пытается наложить локи на все, даже на не незакомиченные записи.
Момент, но я писал про эффективность поддерживать вложенные группы и параметр length, и именно про это ждал комментарии(если кто то решит что я не прав по этому поводу), и только. По поводу всего остального я знаю)
UFO just landed and posted this here
что бы воочию увидеть как mysql пытается поставить локи на незакомиченные записи соседней транзакции.
что касается индексов, то во первых в оракле мне не приходит в голову индексировать абсолютно каждое поле. во вторых повторюсь — дело в фуллскане, а не индексах. индекс лишь снижает вероятность, но не отменяют фулскан. например у меня лезли дедлоки на таблице с индексом, но записей было мало и mysql применял фуллскан не смотря на индекс. по идеи в запросах где вычитывается более половины записей таблицы тоже фулскан должен быть.
Хах, я тоже так думал. Пока один знакомый в одной из топовых организаций страны не пожаловался, что их хранилище данных — это просто ворох таблиц без связей и индексов, и что поэтому у них постоянно что-то теряется, что-то дублируется. Потом встречался с сокурсником из топового интегратора, который участвовал в проектировании этого хранилища на позиции архитектора, и на мой вопрос он ответил: «Ну а что, мы всегда так делаем, под индексами там данные не зальёшь/не изменишь — это оптимизация». У меня из одного вопроса «почему?» осталось два: «а зачем тогда оно такое надо» и «за что они получают астрономические деньги».
что бы получить опыт и осознать почему технологии бигдаты неизбежны с какого-то объема. если речь о хранилище то ты просто не зальешь за вменяемый промежуток времени данные с врубленными FK и индексами. а всякие скоринги и фрауд детекшены уже не нужны спустя несколько часов. потому сначала отключают FK и индексы при заливке, а потом переезжают на хадупы где индексов и FK нет в принципе, а валидация связей — часть ETL процесса интеграции данных в хранилище. если вырубать FK и вовсе не отслеживать зависимости то да, болото данных гарантированно.
но это про хранилища, тут же у меня скорее речь про oltp, где у mysql умудряются сталкивается транзакции, между которыми ничего общего.
Я вас немного расстрою, с вашего позволения: в любой СУБД легко, не понимая базовой матчасти (блокировки/уровни изоляции), создать подобную ситуацию. У вас ровно этот пример.
давай. попробуй расстроить. я поржу.
в любой версионной субд есть row level блокирование и подобные ситуации, где сталкиваются транзакции не имеющие ничего общего не возможны. уровни изолированности транзакций и блокировки разводят транзакции затрагивающие общие данные, а в примере у каждой транзакции своя запись, не имеющая никакого отношения к другим транзакциям.
Странный ответ. Я пишу про блокировки/уровни изоляции. Вы мне отвечаете про блокировки/уровни изоляции (версионные), подтверждая написанное, но выглядит это так, как будто вы с чем-то спорите.
чего не ясного? уровни изоляции описывают допустимые коллизии транзакций, обращающиеся к одним и тем же наборам данных. ключевое — одни и те же наборы, я же показываю что у mysql все на столько плохо, что коллизии возникают даже у транзакций, что обрабатывают разный набор данных. уровни изоляции тут не причем.
уровни изоляции тут не причем
Именно от уровня изоляции зависят блокировки. С каким-нибудь read committed, например, вы тут получите блокировку на странице данных, где оказались ваши разные наборы данных, и на этом удивленно повиснете.
ты путаешь эскалацию блокировок с уровнями изолированности. эскалация блокировок никакого отношения не имеет ни к уровням изолированности, ни к mysql. mysql утверждает что у него row level блокировки. но врут.
RC с row level блокировками не должен накладывать какие либо блокировки на данные, не имеющие никакого отношения к этой транзакции. то что на странице посторонние данные, не нужные транзакции не повод их блокировать.
Это вообще не эскалация. Эскалация — это когда блокировок ключей на странице достаточно много. При хранении в куче это «by design».

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

Мы разве знакомы?

вы придется заслужить «пониманием базовой матчасти» (тм)
когда блокируется больше строк, чем необходимо это эскалация
Я выше написал, что такое эскалация. Поучите базовую матчасть, пожалуйста. Начать можно отсюда:
Lock escalation is the process of converting many fine-grained locks (such as row or page locks) into table locks.
До понимания базовых терминов, извините, говорить не о чем.
матчасть я выучил 20 лет назад, 15 лет как сертифицированный ocp. так что дальнейшее разжевывание лишь за деньги. конвертация row level блокировки в page lock приводит блокировке посторонних строк, не имеющих отношения к транзакции. эскалация никакого отношения к уровням изолированности не имеет.
С каким-нибудь read committed, например, вы тут получите блокировку на странице данных

RC требует блокировать лишь необходимые транзакции строки. получить блокировку целой страницы можно исключительно в результате эскалации, которая к уровням изолированности транзакций параллельна.
15 лет как сертифицированный ocp
Вы мне предлагаете начать обсуждение уровня ценности сертификатов над уровнем плинтуса? Откажусь, пожалуй, даже несмотря на такую подачу.

конвертация row level блокировки в page lock приводит блокировке посторонних строк, не имеющих отношения к транзакции
Ну вот, хоть что-то полезное по ссылке вычитали. Вот только никакого отношения эта конвертация (которая и есть та самая эскалация) к описываемому случаю страничной блокировки не имеет.

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

не имеет и тебе доступно разжевал почему. уровни изоляции и эскалация абсолютно не пересекающиеся понятия. т.е. вот тут ты несешь дважды пургу
С каким-нибудь read committed, например, вы тут получите блокировку на странице данных

а вот тут трижды
Все-таки читать про блокировки. Подсказка для поиска: страничная блокировка при вставке в кучу без блокировки ключей.

в mysql нет страничных блокировок, при вставке ставится next-key lock только если есть ключи. но в моем примере нет ключей.
прежде чем нести пургу, хотя ознакомься хотя бы какие типы блокировок сущетсвуют в mysql.

Для новичка, который притянул сюда не имеющую отношение к происходящему эскалацию блокировок только потому, что еще вчера не знал даже, что это такое, откуда берется и как связана с блокировками — причем так и не понимающего всего этого и сейчас, что только что и продемонстировавшего — в-общем, для абсолютного новичка в теме, не понимающих даже самых базовых вещей, вы слишком неприлично брызгаете слюной. Продолжать, пожалуй, побрезгую, да и бессмысленно.
MySQL не добавит тип столбца TEXT, если не задать параметр length

Тут даже перевод корявый («столбец типа TEXT» был бы логичнее), а в недрах обсуждения выясняется, что проблема в индексировании столбца и неэффективности индекса при обилии префиксов «uploads/-/system/» в значениях (а это уже не хуже).
С MySQL не получалось эффективно поддерживать вложенные группы

Опять же, тут боль в эффективности (2,7 секунды заката солнца вручную против 10 миллисекунд через WITH), но про 8-й мускуль знают, но почему-то игнорируют.
Объяснение что трудно поддерживать обе БД понимаю, принимаю. Но также и понимаю что при отказе от фишек Postgres можно было бы жить дальше. Вопрос в том, сколько производительности потеряется при этом? 3%? 20%? Мне трудно оценить потерю производительности при использовании Mysql вместо Postgres для GitLab. Хотелось бы увидеть конкретные цифры. Ведь Вы же проводили измерения, правда? Почему же тогда цифр нет в статье?
Хотелось бы увидеть конкретные цифры. Ведь Вы же проводили измерения, правда? Почему же тогда цифр нет в статье?

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

Original author: Kenny Johnston

Цифры есть по количеству пользователей. Если никому не нужна mysql, то зачем его поддерживать, тратить силы и ресурсы?

А где хотя бы эти цифры, по количеству пользователей? Я по ссылке прошёл и не нашёл. Я плохо искал?

Вообще Mysql гораздо более распространённая база данных и менее требовательная к ресурсам. То, что у большинства стоит Postgres вызвано тем, что разработчики GitLab давно и настоятельно рекомендуют переходить на Postgres.

«В июле 2017 GitLab предупредил, что прекратит поддержку MySQL» — после этого предупреждения всё ещё есть люди на Mysql. Это может значить только одно — Mysql очень сильно востребован.

Отказ от Mysql — слабый ход со стороны GitLab
А где хотя бы эти цифры, по количеству пользователей?
Вот в статье:
Тренд достаточно устойчивый, к примеру в последнее время на 1200 установок с MySQL было порядка 110000 установок на PostgreSQL.
Смотря что вы вкладываете в понятие «производительность». Конечному пользователю особо и неважно, например, будет git pull происходить 12 секунд, или, например, 15. Процентов потеряли много, но на что они в данном случае влияют? А если никому эти проценты особо не важны, чего бы не сэкономить деньги?
А если никому эти проценты особо не важны, чего бы не сэкономить деньги?

Да, вот там прирост по времени 25% потому что не особо важно, и вон там тоже не особо важно было, потому там тоже +30%. А потом сидишь как дурак на компьютере за очень много денег и ждёшь когда очередная программа через дебри экономности продерётся и наконец-то среагирует на клик.


Я к чему. Сами по себе 3 секунды может и не много. Но вы правда верите, что только в единственном месте будет такой прирост? Или что он в будущем не станет ещё больше? Или что, как я выше писал, не сложится с аналогичным приростом ещё откуда-нибудь после рефакторинга?

Отказ от фишек постгри (и мускуля тоже :) по сути использование пересечения фишек мускуля и постгри) лишь уменьшит количество ресурсов на поддержку двух баз, поддерживать всё равно надо будет две, кроме фишек есть ещё просто нюансы синтаксиса, или фишки, которые не отключаются

Интересно а в сторону MariaDB смотрели? Там по моим ощущениям с фишками намного проще.
Sign up to leave a comment.