Comments 60
* http://use-the-index-luke.com/blog/2016-07-29/on-ubers-choice-of-databases
* https://blog.2ndquadrant.com/thoughts-on-ubers-list-of-postgres-limitations/
* http://rhaas.blogspot.ru/2016/08/ubers-move-away-from-postgresql.html
Еще крайне интересно почитать мэйлинг лист postgresql по этому поводу:
https://www.postgresql.org/message-id/579795DF.10502@commandprompt.com
Тот же самый автор — Evan Klitzke — писал об этом переходе: https://www.yumpu.com/en/document/view/53683323/migrating-uber-from-mysql-to-postgresql
Он, видимо, всё никак он не определится.
Как водится, нет абсолютно плохих или абсолютно хороших систем, у каждой есть плюсы и минусы. А на разных этапах развития бизнеса вес плюсов и минусов может меняться.
Насколько знаю, fork() на сегодняшних компьютерах не копирует всю память — а только лениво копирует только те страницы, в которых что-то изменяется. Где-то на ютубе была лекция, в которой рассказывали, что это очень быстро и эффективно и используется в некоторых базах данных для практически мгновенного создания атомарного слепка базы: делаем fork- и имеем атомарный слепок, которых потихоньку пишем на диск, и при этом основной процесс может менять свою область памяти не опасаясь изменить еще не записанные данные.
$ php -r '$a = str_repeat("ololo", 200 * 1024 * 1024); var_dump(getrusage()["ru_maxrss"]); $start = microtime(true); $pid = pcntl_fork(); if ($pid == 0) die; echo (microtime(true) - $start) . " sec\n";' int(1031604) 0.0074088573455811 sec
Если у вас резидентный набор процесса будет 100 гигабайт, то это будет уже 750 мс.
Навскидку:
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
__inline__ uint64_t rdtsc() {
uint64_t x;
__asm__ volatile ("rdtscp\n\tshl $32, %%rdx\n\tor %%rdx, %%rax" : "=a" (x) : : "rdx");
return x;
}
void test(size_t size){
char * mem = (char *) malloc(size);
uint64_t p;
for(p=0; p<size; p++){
mem[p] =(char) p%17;
}
uint64_t x = rdtsc();
pid_t pid = fork();
uint64_t diff = rdtsc() - x;
free(mem);
if (0==pid) exit(0);
printf("%" PRIu64 "M -> %" PRIu64 "\n", size/(1024*1024), diff);
}
int main(){
int i;
for(i=0; i<14; i++)
test(1024*1024LL*(1<<i) );
return 0;
}
результат(в тиках):
1M -> 378173
2M -> 421121
4M -> 429324
8M -> 438039
16M -> 447977
32M -> 477917
64M -> 554516
256M -> 980945
512M -> 1457601
1024M -> 2484345
2048M -> 4366125
4096M -> 7856287
8192M -> 15170437
Начиная с некоторого размера fork() с ростом размера время растет линейно. Когда размер памяти мал, то вероятно, работают кэши. На самом деле это может означать, что мой тест некорректен в этой части, но вцелом суть зависимости — ясна.
Что означает в этом контексте «да и производительность будет оставлять желать лучшего»? Про особенности копирования вы рассказали, очевидно, вы предполагали нечто еще.
Насколько понял из моей тестовой проги, производительность проседает после хипа 4гб: 8гиг заполнялось раз 10дольше, чем 4. Видимо, аппаратная TLB-таблица, или какая-то другая таблица заканчивается — и оно переходит на полусофтварный способ трансляции физических адресов в логические.
Только вот, с форком это уже никак не связано. Но очевидно, гигансткие хипы тормозные сами по себе. Интересно, где этот порог у ксеонов.
Кэши собственно данных, типа L0, в этом не участвуют, читать-писать основное содержимое страниц не нужно. А вот создать копию VM map процесса — задача достаточно затратная, включая карты physical maps с инкрементом счётчиков использования (вот тут наверняка кэш и насилуется).
— системе доступно 10 Гб ОЗУ
— процесс жрет 8 Гб ОЗУ
— процесс форкается
— чайлд долго пишет свой снапшот на диск
— родительский процесс достаточно быстро модифицирует свою память и в итоге свободная память в системе кончается
fork, copy-on-write поможет
популярное заблуждение, посмотрите на популярность pgbouncer — это костыль именно для решения проблемы fork и вымывания набраного бакендом кэша (например дескрипторы открытых файлов ), но pgbouncer не решает проблемы полностью, например приходиться отказываться от prepared-statement
В репликации MySQL много недостатков.
На эту тему мне очень нравится статья: https://habrahabr.ru/company/oleg-bunin/blog/313594/
https://softwareengineeringdaily.com/2016/09/09/ubers-postgres-problems-with-evan-klitzke/
вывод о недостатках при потерях быстродействия при модификациях данных входящих в кластерный индекс весьма надуманы т.к. назначение кластерного индекса физический порядок хранения данных, то есть в кластерный индекс нужно первично заложить константу, то есть данные которые не будут изменяться, при построении любого индекса предусматривающего модификации нужно запланировать необходимый процент заполнения, т.е. свободного места необходимого под быстрые модификации до процедуры реиндексации.
в противном случае все эти выводы с употреблением высокопарных научных терминов выглядят как некое самооправдание.но сработает оно только «для начальства». любой спец подвох раскусит.
а вот если постгри допускает вывод дублирующей записи при условии нарушения уникальности заданного для таблицы ключа — то это недочет критического уровня. если этот баг реально есть -значит эту субд просто нельзя использовать вообще нигде и никогда.
я полагаю что поле id определенное как примари кей в постгри предоплагает что оно уникально без каких либо дополнительных действий (как это в других, нормальных субд)
Не понял почему инженеры из Uber не воспользовались Slony или Bucardo для репликации…
Беглым поиском не смог найти информацию об объёмах данных с которыми им приходится работать, количество запросов к БД на чтение/запись. А интересно было бы знать такое :)
Думаю, следует ждать через несколько лет статью о том как они мигрировали с MySQL на PostgreSQL.
Вот для примера:
когда вы пассажир и смотрите в приложение, то вам показывается как движется автомобиль. На самом деле данные не реалтайм, а построены на фильтре Калмана. Слава богу что они додумались его использоваться. Но вместо того, чтобы открывать сокет и передавать в него данные, водительское приложение через HTTP запрос передает свои координаты, а сервер ему возвращает поллинг интервал когда сообщить в следующий раз.
В итоге получаем, что водитель уже на месте, а у пользователя по фильтру калмана водитель проехал дальше на пару километров вперед. А все потому что сервер ответил водителю передать свои координаты через 3 минуты, допустим. И это самый банальный пример.
А вы тут про guuid и инкременты.
А гуиды вам чем не угодили?
Большой ли смысл держать на одном сервере БД более сотни соединений (не говоря уже о тысячах)? TPS от этого не вырастет, зато накладные ресурсы от переключений контекстов и расхода памяти будут весьма велики (что в форках, что в потоках).
Если у вас сотни веб-серверов...
То нужно использовать PgBouncer, о чём написано в каждой доке по развертыванию постгреса в продакшене.
Accordingly, using pgbouncer to do connection pooling with Postgres has been generally successful for us. However, we have had occasional application bugs in our backend services that caused them to open more active connections (usually “idle in transaction” connections) than the services ought to be using, and these bugs have caused extended downtimes for us.
Собственно, странно зачем нужен отдельный pgBouncer, если можно было встроить это прямо в БД?
Вообщем ребят, проблема не в fork, потому что fork происходит от супервизора с минимумом памяти, проблема в LoadCriticalIndex и кучей других кэшей которые бакенд загружает при старте.
«Усиление записи» достаточно часто встречается в Интернете (в том числе на Хабре, Гиктаймс, 3dnews и др.): https://yandex.ru/search/?lr=11&msid=1489328416.72813.22886.28344&text=«усиление записи».
Мне этот термин по душе, хоть и не очень точно отражает суть.
У англичан amplification, судя по http://www.oxfordlearnersdictionaries.com/definition/english/amplification?q=amplification, имеет два основных значения: «усиление» и «добавление комментариев, пояснений». Какой смысл они имели в виду при создании термина write amplification? Мне кажется, что у них это тоже «усиление» в переносном смысле.
Как вариант, у генетиков можно позаимствовать вариант «амплификация».
Переход в Uber проплаченный — ради шумихи, стопудов.
Платили конечно не Uber, а кому-то из разработчиков.
Э-э-э, а можно поподробнее про требовали денег? Это как вообще?
Поменяли БД.
Коммерческая лицензия позволяет производителям оборудования, независимым поставщикам и реселлерам распространять коммерческие исполняемые файлы программного обеспечения MySQL с их собственным коммерческим программным обеспечением, не подвергая, что программное обеспечение с лицензией GPL и его требование распространять исходный код.[машинный перевод]
Источник.
Мне вот тоже интересно. Единственный случай при котором они имеют право требовать деньги — нарушение GPL лицензии. То есть переход на Percona ситуацию не меняет так как будет то же нарушение.
Старая тема но соображения такие:
- Да, проблеммы у Postgres есть, и Uber наткнулся только на маленькую часть. Но конкретно их проблемму вполне можно было бы сильно уменьшить если общаться с сообществом. (уже сейчас улучшения есть)
- Но главная проблемма Postrges которую никто не пытается исправить это дизайн >20 летней давности. Postgres развивается небольшими шажками без серьёзных рефакторингов из-за этого, код это ужасная лапша и архитектура сильно устарела, а у сообщество не может и не хочет что то менять. Отчасти из-за архитектуры и языка Си, развите postrges сейчас крайне сложное дело. Архитектурные пробелы это процессы, а не потоки и самопальный буфер менеджер вместо iommap. Не говоря уже про организацию tuple. На данный момент в аналитических запроссах большая часть времени уходит на процесс разбора тюплов. heap (сторадж данных) прибит гвоздями, индексы отваязаны но с ограничениями.
Простите за этот сумбур, я думаю тут надо писать целую статью, но общий вывод в том что postgres распространяется под BSD лицензией и его развитие это мягко говоря политическая игра между несколькими игроками… из-за этого реально нужные вещи просто не делаются.
PS но при этом, это хорошая СУБД и наверное одна из самых лучших, особенно что касается фичь SQL и расширябельности. Только вот насколько долго она такой останется?
Мне кажется речь в первую очередь идёт о ванильном постгресе, смотрю сейчас на доработки пгпро, пангалини и т.п.
Расхождения с ванильным постгри может совсем сильным, даже возможно отколоться от основной ветки.
Ещё Олег из постгрепро рассказывал 10 лет назад про свое виденье идеальной субд
ПостгрессПро-это ванильный постгресс+много наворочено того, что можно и так из открытых решениях взять+что-то допилили.
Но в целом в ПостгрессПро нет ничего такого значимого, чтобы его покупать.
Это мнение бывших двух архитекторов из компании Постгресс Про, которых потом перекупили в другую более крупную компанию.
В слоне когда появилась поддержка секционирования? А когда параллельная обработка запросов по секциям? А оператор merge? Особенно когда последний был уже давно введен в стандарт SQL. Я уж не говорю о проблемах обслуживания этой СУБД, особенно голова очень болит от так называемого вакуума (всё никак нормально сделать не могут и потому не редко, когда приходится пересаживаться на тот же более стабильный в этом плане мускул). Тогда как в той же MS SQL все это было реализовано более 10 лет назад.
Идеальные или близкие к ним СУБД уже есть, если не думать что open source - бесплатное решение. Не бесплатное, а открытое: со всеми вытекающими отсюда плюсами и минусами.
Сейчас уже MS SQL идёт со встроенным ИИ.
В целом меньше слушайте маркетинговые выпады и сами проверяйте.
Интересно было бы почитать обстоятельный разбор и других субд.
Я могу про OrientDB рассказать. Там, каждая нода (процесс на том или ином севере), содержит некоторое число "кластеров". Каждая нода может содержать разный набор кластеров, на этом основано партицирование и репликация. Каждая "таблица" (на самом деле класс документов) кладётся в один или несколько кластеров. Каждый кластер — 2 файла: один со страницами данных фиксированного размера, другой с маппингом идентифакаторов на номера страниц. Полный индентификатор записи (RID) имеет вид "#1:2", где первое число — номер кластера, а второе — помер записи. Именно этот идентификатор хранится в индексах и других таблицах. Благодаря распределённому конфигу, каждая нода знает какие кластеры на каких нодах лежат, так что по RID сразу понятно куда делать запрос, что даёт встроенный map-reduce. При обновлении, субд старается изменить данные на месте, если хватает места, предварительно записав, что собирается делать в WAL. Если выделенных страниц не хватает, то выделяются новые страницы, по всей видимости по принципу связанного списка. Индексы не распределённые, так что они и не реплицируются — каждая нода создаёт свои индексы по прилетевшим к ней данным. Соответственно MVCC идёт на каждой ноде свой, а уникальный индекс по партицированной базе будет уникальным лишь рамках одной ноды. OrientDB поддерживает распределённые транзакции, так что облом на одной из нод приведёт к откату транзакции, но какое-то время обновление будет доступно на тех репликах, где транзакция прошла. Короче уникальные индексы по партицированной базе лучше не делать. Рекликация, соответственно, происходит на уровне записей.
Uber — причины перехода с Postgres на MySQL