Как стать автором
Обновить

Комментарии 22

Добавили форматированный вывод в селектах.


Почему мне это напомнило релизнотес sql сервера Watcom или чего-то подобного… 30-и летней давности :0
А вы не могли бы пояснить:

Разработчики Tarantool в своё время как про преимущество свой разработки говорили про однопоточность. Что если, мол, нужно распараллеливать — то просто запускайте отдельные СУБД на каждом ядре. И шардингом распараллеливайте. Говорили, что причиной выбора однопоточности явилось упрощение обработки транзакций, что нет блокировок, все проходит быстро.

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

С третьей стороны — про производительность высочайшую.

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

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

Почему так как я говорю? Вот почему:

m.habr.com/ru/company/selectel/blog/521168
Если мы начинаем писать на диск маленькими порциями с обязательным проталкиванием данных из кэша, с ожиданием реального завершения операции записи — то при записывании транзакции по одном — получаем крайне низкую скорость записи. Чуть ли не в 1000 раз меньше, чем при записи большими порциями.

Записывать транзакции пачками мы не можем, так как обработка однопоточная. Блокировок и версионирования нет. Значит, отдельные записи на диск будут мааааленькими, размером в одну транзакцию, это считанные байты зачастую.

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

Асинхронная репликация тоже не давала таких гарантий.

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

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

Я так подозреваю что вы хоть и выполняете транзакции в 1 поток, но при записи блокируете их пачками, в ожидании завершения записи? Разумеется в предположении, что включена синхронная запись на диск и синхронная репликация.
Записывать транзакции пачками мы не можем, так как обработка однопоточная.

А в чем тут проблема? Однопоточно копим пачку, потом ее записываем.

А в чем тут проблема? Однопоточно копим пачку, потом ее записываем.


А транзакция из потока при этому уже подтверждена или или еще не подтверждена?

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

То есть подтверждение идет пачкой — значит клиенты получают информацию о коммитах всех своих транзакций тоже пачкой.

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

Ну да, пишем пачку танзакций в WAL, после этого пачка транзакций считается подтвержденной.


А параллельность во входящем сетевом соединении от клиентов всё же поддерживается Тарантулом?

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

Но в целом не вижу проблем, например, в Go


Мы говорим о вполне конкретном Тарантуле.

Как там на входе происходит?

Транзакция №1 начата, данные изменены, ожидает записи.
Транзакция №2 начата, данные изменены, ожидает записи.
Транзакция №3 начата, данные изменены, ожидает записи.
Последовательность гарантирована?
Затем пачкой записываем 3 штуки.
Ожидаем записи.
Подтверждаем клиенту транзакцию №1
Подтверждаем клиенту транзакцию №2
Подтверждаем клиенту транзакцию №3

Так?

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

Вопрос на самом деле интересный, подтверждение клиенту уходит при записи на диск или при добавлении в буффер?

Такие вещи обычно пишут в документации. Вот тут вполне расписано, как это работает
www.tarantool.io/en/doc/latest/dev_guide/internals/file_formats
— A message is sent to the WAL writer running in a separate thread, requesting that the change be recorded in the WAL. The instance switches to work on the next request until the write is acknowledged.
— On success, a confirmation is sent to the client. On failure, a rollback procedure is begun.

Все просто и никто не блокирует весь сервер пока там транзакция ляжет на диск.
— On success, a confirmation is sent to the client. On failure, a rollback procedure is begun.

Все просто и никто не блокирует весь сервер пока там транзакция ляжет на диск.


А как быть с зависимыми данными?
Если транзакция №2 использует то, что навычисляла транзакция №1, а транзакция №1 откатывается?

В этой статье обещают Serializable…

А как транзакция может увидеть то, что еще не закомичено в базу? Транзакция 2 увидит то, что есть реально в базе, а не то, что навычисляла транзакция 1. А дальше вопрос в конфликте между ними при коммите.
А как быть с зависимыми данными?
Если транзакция №2 использует то, что навычисляла транзакция №1, а транзакция №1 откатывается?

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

В случае движка memtx (в оперативной памяти) — тут транзакции выполняются от создания до начала коммита без переключения корутин. То есть увидеть данные друг друга в процессе выполнения не могут. В версии 2.6 теперь они могут прерываться, но добавили изоляцию и откаты как в виниле.

Но речь то о другом — пока вы не получили подтверждения вы не можете быть уверенными что данные точно сохранены. И транзакция всё это время ждет в незавершенном состоянии. Что не вяжется с производительностью.

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

При записи на диск в случае асинхронной репликации, и при успешной репликации + записи коммита на диск в случае синхронной.
Последовательность гарантирована?

Да.

Затем пачкой записываем 3 штуки.
Ожидаем записи.
Подтверждаем клиенту транзакцию №1
Подтверждаем клиенту транзакцию №2
Подтверждаем клиенту транзакцию №3
Так?

Да.

То есть по достижению момента записи (момента постановки в очередь за запись) происходит переключение одно-потока на следующую транзакцию?
При этом в целом сервер однопоточен?

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

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

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

То есть подтверждение идет пачкой — значит клиенты получают информацию о коммитах всех своих транзакций тоже пачкой.

Да, пачкой. В одном потоке. Я написал в предыдущем ответе. У нас поток один главный, но в нем много корутин. Когда пачка транзакций закончит запись, все корутины, ждущие эти транзакции, по очереди отработают в главном потоке.

В чем же тогда однопоточность? Что сама обработка данных идет в один поток?

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

А параллельность во входящем сетевом соединении от клиентов всё же поддерживается Тарантулом?

Вопрос не понял. Читать из одного соединения параллельно — небезопасно в целом, как метод программирования вообще. Для чтения из сети и записи в нее у нас отдельный поток. Он читает сокеты, парсит запросы, отправляет их в главный поток, там они выполняются в корутинах, и потом сетевой поток пишет ответы обратно в сеть. Поток на все соединения один, но как показывает практика, этого более чем достаточно даже для тысяч соединений, в 100% CPU он практически не бывает. Это все возможно, если работа с памятью в нем организована эффективно, как в Тарантуле.
Не записывать на диск для завершения каждой транзакции мы не можем — так как иначе не получим гарантию надежного сохранения данных.

не специалист по бд, но разве синхронная репликация только в ОЗУ не надежнее отсутствия репликации вообще, но ожидания физической записи на диск?
не специалист по бд, но разве синхронная репликация только в ОЗУ не надежнее отсутствия репликации вообще, но ожидания физической записи на диск?


В принципе без разницы откуда вы получили подтверждение что запись успешно завершена — от локального диска или от удаленного сервера.

Но речь то о другом — пока вы не получили подтверждения вы не можете быть уверенными что данные точно сохранены. И транзакция всё это время ждет в незавершенном состоянии. Что не вяжется с производительностью.

Спасибо за интересные вопросы!

Разработчики Tarantool в своё время как про преимущество свой разработки говорили про однопоточность.

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

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

Все верно.

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

Это не прям таки преимущество. Это норма. Без надежности хранения база не лучше будет, чем std::map или Lua-таблица.

С третьей стороны — про производительность высочайшую. Эти вещи уже противоречат.

Почему? То есть если база не теряет данные, то она не может быть производительной? Или потому что пользователю доступен только один поток выполнения? Ни то, ни другое, честно говоря, не вижу, как связано с невозможностью писать код ядра базы эффективно.

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

Я наверное попрошу определить, что такое «высочайшая прозводительность». Я видимо не до конца понимаю.

Теперь добавляется синхронная репликация. Что тоже не увеличивает производительность.

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

А где же истина?

А где тут кто-то лгал или что-то утаивал? Цитату, если можно. Если это что-то в документации, то это баг документации — поправим. Там часто проскакивает всякое.

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

Почему так как я говорю? Вот почему:
m.habr.com/ru/company/selectel/blog/521168
Если мы начинаем писать на диск маленькими порциями с обязательным проталкиванием данных из кэша, с ожиданием реального завершения операции записи — то при записывании транзакции по одном — получаем крайне низкую скорость записи. Чуть ли не в 1000 раз меньше, чем при записи большими порциями.

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

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

Я тут еще уточню, что значит «запросы будут ждать». Возможно вас это смущает. Это разумеется не ждать вида while (!finished) sleep(1);. Запросы все выполняются в корутинах в главном потоке, так что когда запрос ждет коммита транзакции, его корутина просто перестает ставиться на исполнение, пока запись на диск/сеть не кончится. Остальные корутины работают, как ни в чем не бывало.

Записывать транзакции пачками мы не можем, так как обработка однопоточная.

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

Блокировок и версионирования нет. Значит, отдельные записи на диск будут мааааленькими, размером в одну транзакцию, это считанные байты зачастую.
Это как связано, не понял? Размер записей на диск и блокировки.

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

Верно. Поэтому пишем. Но я не вижу, как это запрещает писать на диск сразу много транзакций.

Асинхронная репликация тоже не давала таких гарантий.

Давала те же, в плане записи на диск. Проблема в том, что записи на диск, хоть 500 раз с fsync включенным, не дает гарантий против потери данных. Синхронная в дополнение дает еще и запись на реплики, что такие гарантии тоже не дает на 100% (весь кластер может уничтожить бомба, например), но увеличивает сохранность данных существенно.

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

Они исполняются параллельно как раз. Пока одни транзакции ждут записи или репликации, другие уже могут работать дальше.

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

Почти. Я выше уже расписал, но тут еще раз продублирую. Транзакции пишутся и реплицируются пачками. Но они не блокируют другие транзакции. Другие могут дальше делать свои insert/select/replace и тд, пока не позовут commit. И уже тогда будут ждать в другом потоке, пока их на диск запишут и в сеть, если надо. Главный поток не блокируется никогда, и всегда делает следующие транзакции.
Действительно долгожданный релиз, осталось выяснить что с производительностью теперь.

А как вы проверяли, что всё работает корректно при сетевых ошибках, перекосах времени и прочем? Jepsen-репорта я точно не видел :)

Но мы планируем такой отчет сделать.
Состав команды успел смениться почти полностью, включая CTO.

То есть совсем другие люди и совсем другой взгляд?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий