Ответы пользователя по тегу PostgreSQL
  • Корректна ли будет такая организация Docker-контейнеров?

    @rPman
    Зачем ты разделяешь части приложения по доккер образам?

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

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

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

    @rPman
    для такой задачи в базу данных записывают не boolean, а время на момент записи, а в select запросе делают
    select now()-start_time<=3 часа from ....
    Ответ написан
    Комментировать
  • Как защититься от двойного списания в многопоточном приложении?

    @rPman
    Блокировку во время траты уже сказали, но бывает что процесс может длиться достаточно долго, чтобы пользователь в соседнем окошке не смог совершить параллельно оплату (у него будет все висеть), поэтому блокировки реализуют программно

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

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

    @rPman
    значит узкое место почти наверняка диск.

    Пальцем в небо, файловая система на которой таблеспейсы лежат какая? случайно не cow (btrfs/zfs/xfs)? с ними отвратительно работают базы данных, так как частые записи в файл генерируют сильную фрагментацию. В этом случае перед тяжелой обработкой хотя бы дефрагментируй файлы базы и отключи cow фичу на таблеспейсах

    неплохим тюнингом может оказаться (на выбор):
    * разместить базу в ram диске (буквально, залить на сервер в облаке, обработать данные, залить назад, работая напрямую с таблеспейсами, но версия софта должна совпадать до последней цифры)
    * разместить базу целиком на ssd (даже если это будет потребительский и дешевый)
    * добавить в систему ssd кеш для hdd с помощью например bcache (включенный на запись), правда для линейной обработки базы это может дать мало пользы, но вообще это неплохой способ на порядок поднять производительность за дешево (в одном месте я использовал фичу virtualbox со снапшотами в файл, есть и у kvm, когда последующие записи шли не на исходный образ а на другой диск, и он ssd)
    * разместить таблеспейс для индексов (а может и каждую таблицу отдельно) на другом физическом устройстве (hdd, ssd или даже в ram), требования к размеру тут обычно низкие, ключевое слово - исключить последовательные чтения/записи на одно устройство.
    * разместить журнал (например ext4) на ssd диск (хватит пары гигабайт) или по хардкору даже отключить его (очень опасно, можно получить кашу из данных при сбое питания, но как временное решение пока идет долгая операция, при наличии всех бакапов, оправдано) - наименьшая оптимизация, но при частых мелких записях это заметно
    Ответ написан
  • Как проще и правильнее перенести одну запись из бэкапа?

    @rPman
    Если бакап достаточно свежий, можно сравнить бакапы (построчно каждую таблицу но эта операция достаточно простая и скрипт можно сгенирировать на основании структуры, вытаскивая ее из ddl, да тут структура то и не нужна), если искать разницу что именно удалено (то что есть в бакапе но нет в текущей базе, проверку делать по полю id (тот что primary key, ddl нужен именно чтобы выявлять составные), базы можно развернуть на одном сервере) то можно получить вполне готовый срез записей, просмотреть их глазками будет гораздо проще чем всю базу целиком.

    посмотри pgdiff

    Чем меньше расстояние в изменениях между бакапом и текущей базе, тем меньше лишних записей получится в результате.
    Ответ написан
    Комментировать
  • Как сделать поиск с лишним словом в postgresql?

    @rPman
    замени слово которое не важно на символ % и условие должно быть where field like 'xxx % yyy'
    Ответ написан
  • Как синхронизировать базы данных Test и Production?

    @rPman
    Правильно - исключить 'ручную' работу в процессе переноса релизных изменений в продакшен базу.
    Т.е. задача разработчика делать скрипты, которые будут приводить базу данных предыдущей версии к следующей, при этом это не только обновление структуры (это кстати можно сделать автоматически, сравнив базы разных версий, гуглить ddl diff, к примеру для postgres это pgadmin shema diff) но и данные, например наполнение новых полей данными или к примеру в старой версии поле было текстовое в 'свободном формате', а в новой на его основе целая структура (вырожденный пример - был адрес текстовой строкой а стал чуть ли не целым ФИАС).

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

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

    Тестовая база может быть репликой базы, ей не обязательно быть с самыми свежими данными (хотя если апдейт - фикс бага для данных, которые ввели недавно пользователи - то нужен оперативный бакап).
    p.s. Для эффективного создания реплик продакшн базы можно использовать комбинацию master-slave репликации и снапшоты файловой системы (slave базу можно кратковременно приостанавливать, создавать снапшот файловой системы и запускать из него копию базы, первая же slave реплика должна продолжать работать, накапливая изменения с продакшен)
    Ответ написан
    Комментировать
  • Как происходит разрешение коллизий в хэшах PostgreSQL?

    @rPman
    Хеш индекс для базы данных не требует уникальности хеша (т.е. это может быть 2-байтовый хеш с 65536 количеством значений для базы в миллион строк на каждое значение будет много коллизий), это механизм нужен больше чтобы поделить трудоемкость сканирования всей таблицы на возможное количество значений хеша, т.е. в индексе для записей с равным хешем запрос пройдет по всем таким (если у хеша коллизия для id равным 1 и 10 то в таблице индекса у этого хеша будет 2 записи и при поиске нужной обе они будут просканированы).

    тут или тут чуток про это есть
    Ответ написан
  • Позволяют ли партишены делать такое?

    @rPman
    Вставка данных без индексов должна работать максимально быстро, у вас что, на чтение очень много запросов?
    p.s. диск хоть ssd?

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

    table_base_latest куда бы попадали данные только за последние 365 дней, а остальные а остальные складирвоались без в какой-нибудь table_base_tail который бы содержал все оставшиеся данные?
    на сколько я знаю штатный инструмент это не умеет, ведь данные придется как то перемещать между партициями

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

    Имхо 'овчинка не стоит выделки', точнее очень мало случаев когда геморой реализации даст адекватное повышение производительности.
    Ответ написан
    4 комментария
  • Как правильно работать с большим количеством данных?

    @rPman
    Избавляйся от ... where fld like '%...%' это самые плохие по скорости запросы, хотя не всегда это возможно без увеличения размера базы, вообще полнотекстовый поиск можно отдавать на откуп стороннему приложению (это может расширить возможности фильтрации), считай это еще одной формой индекса, хотя лучше данные по другому хранить. Я встречал ситуации когда в varchar хранили значения справочника из десятка значений, но делали по нему like %% что грузило базу прилично, когда как сделать простой комбобокс на порядок эффективнее и удобнее - поиск по подстроке лучше по справочнику делать на клиенте.

    Без индексов скорее всего никуда, если в запросе идет фильтрация по полю, то если не создать на него индекс, этот запрос и будет создавать нагрузку

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

    Частный случай прогноза, не делать select ... limit ... на каждую страницу, а при смене фильтра запрашивать весь диапазон, но только идентификаторы, сохранив их либо в таблице на сервере либо на бакэенде, генерирующим запросы вида select ... id in (...), последнее если количество записей не больше тысяч. Кстати именно постраничная навигация с прогруженным списком id самая шустрая

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

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

    p.s. диск на сервере hdd или ssd? если не хватает денег на большой ssd, настрой маленький ssd как кеш к медленному hdd (linux bcache), даже когда данные полностью не влезают в кеш, это дает заметный прирост, потому что hdd начинает менее случайно двигаться, плюс добавляется буферизация записи (данные записываются на ssd и лежат там пока hdd не освободится), так же дает эффект ускорения записи вынос журнала файловой системы (если ext4) на ssd (требуется пара гигабайт от силы).

    Осторожно, если высокие требования к надежности хранения, кеширующий ssd в режиме записи лучше пусть будет хотя бы raid1, так как иначе при смерти ssd починить данные, размазанные по диску и кешу будет очень сложно. Для кеша только для чтения это не так критично (при смерти ssd сервер просто упадет, не убивая данные).

    Но мне больше нравится резервирование на лету (настроить онлайн репликацию, пусть и на слабый, бакап сервер)
    Ответ написан
    1 комментарий
  • Как упаковать Postgress в бинарник?

    @rPman
    смени postgres на sqlite

    sqlite это база данных внутри файла, не требует администрирования и установки, очень функциональная и достаточно шустрая

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

    @rPman
    Если речь о 'высоконагруженном' сервисе, то лучше все же собрать запрос в один, тем более с помощью к примеру union можно это сделать не добавив накладных расходов

    Каждый запрос это куча телодвижений и ожиданий, которые сразу вылезают когда запросов сотни и тысячи в пределах одной ноды, зачем закладывать это с самого начала когда оптимизация ничего не стоит. Да я знаю про преждевременную оптимизацию но...
    Ответ написан
    Комментировать
  • Можно ли пропускать некоторые id?

    @rPman
    Будет запускать много телеграмм ботов. Хотел чтобы они работали на порте=id. Но не все порты свободны.

    Правильное решение - составные идентификаторы.

    Т.е. идентификатором должны являться одновременно пара портов - id записи и id порта (или устройства).

    Существует практика, когда в один числовой идентификатор запихивают два, самый простой способ - для каждой новой записи увеличивать значение идентификатора не на 1 а на N, где N - максимальное количество устройств (в вашем случае ботов), пусть и с запасом. Т.е. если номер порта P то идентификатор будет равен P+N*seq, пока количество устройств меньше N они не пересекутся и простым делением по модулю N можно из идентификатора извлечь P

    У вас postgres он поддерживает сиквенсы
    Ответ написан
    Комментировать
  • Насколько хорошо/оптимально использовать хранимую процедуру для полинга?

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

    Если база данных в своей основе не позволяет нужны вам функционал, как бы вы не извращались, делать его придется снаружи. И лучше чтобы это было 'userspace' а не ядро базы данных или ее расширение, так как стоимость поддержки результата растет экспоненциально.

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

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

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

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

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

    p.s. разделение задач по разным таблицам в зависимости от длины интервала никак на производительность не повлияют, только усложнят алгоритм
    Ответ написан
  • Как организовать пагинацию, если БД и поисковая машина - это раздельные сервисы?

    @rPman
    Добавление тысяч идентификаторов в sql запрос вида where in (...) это плохая практика, что получается, если вы сначала заполняете in nmemory таблицу идентификаторами (из полнотекстового запроса внешней базы), а потом уже делаете фильтрацию по вашим атрибутам из sql базы, приджойнив это временную табличку?

    зы, пагинацию делать только через - получаете список id всех записей (отфильтрованные и отсортированные как надо), сохранив во временной таблице по сессии пользователя, а затем уже подгружаете данные окнами. Никаких limit для тяжелых запросов!

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

    И конечно кеширование (не нужно кешировать совсем уж конечный результат, но промежуточный, создающий наибольшие накладные расходы - имеет смысл), всего и вся, память стоит гораздо дешевле процессорного времени.
    Ответ написан
    Комментировать
  • По каким критериям выбирать локаль для базы данных postgresql?

    @rPman
    Мультиязычный - без вариантов выбирайте подмножество unicode лучше utf8 или тот же utf 16/32 (последний крут и даже имеет мизерное преимущество по скорости обработки вне базы данных но значительно больший оверхед по занимаемому месту на диске)

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

    А еще с utf8 клиентским приложениям работать проще всего, так как поддержка лучше. Если у вас windows (не web) у вас может быть соблазн использовать ее нативную кодировку UTF-16LE,.. не поддавайтесь на провокации ;)
    Ответ написан
  • Как задать уникальность поля совместно с полем из другой таблицы?

    @rPman
    Универсальный способ для любых БД и выкрутасов, что вы придумаете - создаете таблицу с полем и индексом в нужном вам формате, и заполняете ее триггером. Если фейлится индекс этой таблицы, сфейлится и запрос на изменение подчиненных таблиц, только что сообщение об ошибке будет 'не привычное'.
    Ответ написан
    2 комментария
  • Как хранить транзакции?

    @rPman
    обычно это 2 таблицы: кошельки и переводы (1-м), в некоторых случаях добавляют еще связь кошелька с пользователем (м-1), если у вас не просто валюта а платежка - добавьте понятие счет (предложение заплатить, кстати это самая сложная сущность, в зависимости от ваших хотелок может вырасти в мегаструктуру, типа всякие escrow или регулярные автоплатежи), все остальное кодируется в полях этих таблиц.

    Криптовалюты пошли по другому пути и взяли за правило - пусть все есть список транзакций, а транзакция это код - и вокруг этого наворачивают что хочешь (правда через задницу иногда получается зато универсально). Т.е. там вообще только одна таблица - транзакция, все остальное это кеши (например список непотраченных выходов восстанавливается ресканом списка транзакций)
    Ответ написан
    Комментировать
  • Возможно ли реализовать запуск bash по факту инсерта в БД?

    @rPman
    не надо ничего вызывать , и по крону не надо сканировать базу, достаточно на таблицу вызывать sql LISTEN и pg_get_notify из бакэнда.

    p.s. вариант с bash в тригере плох еще тем, что он отработает даже если транзакция будет отменена по какой-либо причине.
    Ответ написан
    Комментировать