Для чего используются базы данных, ведь есть старые добрые файлы? Чем они хуже базы данных или чем база данных лучше файлов? БД — более структурированное хранилище. Она позволяет делать транзакции, запросы и так далее. Самый простой случай: есть сервер с базой данных и несколько приложений, которые делают запросы к серверу. База данных отвечает, меняет что-то внутри себя, и всё хорошо ровно до того момента, пока нагрузка на неё не вырастает настолько, что база данных перестаёт справляться.
Если допустить, что это только нагрузка на чтение, то проблема решается репликацией. Вы можете ставить к базе данных столько реплик, сколько нужно, и все чтения пускать на реплику, а все записи — на мастер. Если же на базу данных идёт нагрузка на запись, то репликация эту проблему не решает, ведь запись должна осуществляться на все реплики. Таким образом, сколько бы вы их ни ставили, вы не уменьшите нагрузку на запись из расчёта на одну машину. Тут на помощь приходит шардинг.
Если база не держит нагрузку на запись, то шарды можно добавлять до бесконечности. Шард устроен сложнее, чем реплика, потому что нужно как-то распределить данные по таблицам или внутри таблицы, по хэшу, по range — есть множество разных вариантов. Таким образом, добавляя реплики и шарды, вы можете делить любую нагрузку на базу данных. Казалось бы, больше желать нечего, о чём дальше говорить?
Но есть проблема
…которая лежит уже не в плоскости технологий. Ваш босс, видя постоянно растущий парк серверов, начинает негодовать, потому что на это уходит много денег. Нагрузка растёт, количество запросов от пользователей растёт, и вы всё добавляете и добавляете серверы. Вы же технарь, про деньги не думаете — пусть этим занимаются финансисты. И вы говорите своему боссу: «Всё нормально. У нас бесконечно масштабируемая система. Мы добавляем серверы, и всё круто работает». А босс отвечает: «Отлично, но мы теряем деньги. Нужно что-то с этим сделать. И если мы не решим проблему, то придётся закрыть весь бизнес. Поскольку, несмотря на рост бизнеса, по базам данных и серверам мы растём с опережающей скоростью». И эту проблему должны решить именно вы, а не финансисты, потому что она лежит, возможно, в технологической плоскости. Что делать дальше? Amazon гораздо дороже. Оптимизировать? Вы уже оптимизировали все запросы.
Выходом может стать кэширование данных, которые часто селектятся. Их можно держать в каком-то кэше и постоянно возвращать оттуда, не обращаясь к многочисленным репликам и шардам.
Проблемы кэша
Отлично, проблема решена: один memcached заменяет нам целую стойку серверов-реплик. Но за всё приходится платить.
- Приложение пишет и в кэш, и в базу, которые между собой никак не реплицируются. Таким образом возникает несогласованность данных. Например, вы пишете сначала в кэш, потом в базу. По какой-то причине запись в базу не получилась: приложение упало, сеть моргнула. Затем приложение вернуло пользователю ошибку, но в кэше уже находятся другие данные. То есть в кэше одни данные, а в базе другие. Про это никто не знает, приложение продолжает работать с кэшем. И когда он перезагружается, данные теряются, потому что в базе находится другая копия.
Самое смешное, что если писать в обратном порядке, то будет происходить то же самое. В базу записали, а в кэш запись не прошла. Мы работаем со старыми данными из кэша, в базе новые данные, но про это никто не знает. Кэш перезагрузился — опять данные потеряны. То есть в обоих случаях теряется апдейт. А это говорит о том, что вы теряете некое свойство базы данных, а именно — гарантию, что записанные данные сохранены в ней навсегда, то есть коммит уже не коммит. Справиться с несогласованностью данных можно, написав умный кэш, чтобы приложение работало только с ним. Он может быть и write through, лишь бы приложение не работало с базой. Сначала кэш должен записывать полученные данные в базу, а потом в себя. Если по каким-то причинам данные в базу не записались, то и в кэш они не должны записываться. Таким образом данные будут всегда синхронны. Данные не могут не записаться в кэш, потому что кэш — это память, а в память запись всегда проходит, кроме ситуации, когда память побилась, но и в этом случае умный кэш «свалится», унеся все закэшированные данные в небытие, что плохо, но не приведет к рассинхрону данных.
Но всё равно остаётся один редкий случай, при котором данные оказываются несинхронны: приложение пишет в кэш, кэш пишет в базу, база внутри себя сделала коммит. Дальше она подтверждает кэшу успешное завершение операции, но в этот момент рвётся сеть, и кэш этого подтверждения не получает. Он считает, что данные в базу не записались, и не применяет их у себя. Но в базе они всё-таки применились. Приложение работает со старыми данными, потом кэш перезагружается — данные опять другие. Это очень редкий случай, но он возможен.
И самое главное — умный кэш никак не решает проблему шардинга. А ваш босс не любит шардинг, потому что это очень дорого, ведь нужно покупать много-много серверов. - Помимо прочего, внедрение кэша не избавляет нас от шардинга, потому что запись не ускоряется. Каждый коммит должен куда-то коммититься, причём не в кэш.
- Следующая проблема: кэш — это не база данных, а обычное key/value-хранилище. У вас теряются запросы и транзакции. Индексы и таблицы тоже теряются, но их можно с грехом пополам соорудить поверх key-value кэша. Поэтому приложение приходится упрощать и кардинально переделывать.
- Четвёртая проблема — «холодный старт». Когда кэш только поднимается, он пустой, в нём нет данных. Далее все селекты идут напрямую в базу мимо кэша, потому что в нём ещё ничего нет. Соответственно, приходится снова добавлять реплики, хотя бы не в полном объёме. Нам же нужно как-то прогревать кэш. А когда он прогревается, то в базу идёт много селектов. Соответственно, приходится держать целый ряд реплик только для прогрева кэша. Не правда ли, это выглядит достаточно расточительно? Но без этих реплик вы не сможете нормально стартовать. Рассмотрим подробнее решение этой проблемы.
Холодный старт
В своё время возникла такая идея: чтобы данные были всегда «теплыми», нужно просто не «охлаждать» их. Для этого кэш должен быть персистентным (persistence), то есть нужно хранить данные где-то на диске, и тогда всё будет нормально. Кэш будет стартовать и подгружать данные. Но тут возникло сомнение: кэш — это ОЗУ, он должен быть быстрым, а когда ему в пару даётся диск, то не будет ли он таким же медленным, как база данных? На самом деле — не будет.
Проще всего «персистить» кэш раз в N минут, целиком дампить его на диск. Этот дамп можно делать асинхронно, в фоне. Он не замедляет никакие операции, не нагружает процессор. Это позволяет многократно ускорить прогрев: когда кэш поднимается, у него уже под рукой собственный дамп с данными, он их читает линейно и очень быстро. Получается быстрее, чем с любым количеством реплик баз данных. Но не может же быть всё так легко, верно? Допустим, мы делаем дамп каждые пять минут. А если в промежутке происходит сбой, то накопившиеся с момента предыдущего дампа изменения будут потеряны. Для каких-то приложений это неважно, например для статистики, а для каких-то важно.
Вторая проблема — мы хорошо нагружаем диск, который, возможно, требуется для чего-то ещё, например для логов. Во время дампа диск будет тормозить, и происходить это будет бесконечно. Избежать этого можно, если вместо регулярных сбросов дампа вести журнал. Сразу должен возникнуть вопрос: «Как же так? Это кэш, он быстрый, а мы тут всё журналируем». На самом деле это не проблема. Если писать журнал в файл последовательно, на обычном винчестере, то скорость записи будет достигать 100 Мб/с. Допустим, средний размер транзакции 100 байт — это миллион транзакций в секунду. Очевидно, что мы никогда не упрёмся в производительность диска при журналировании кэша. Благодаря этому решается и проблема IOPS: мы нагружаем диск ровно настолько, насколько это необходимо, чтобы все данные персистились. Данные всегда свежие, мы их не теряем, при этом прогрев осуществляется быстро.
Но у журналирования есть свои недостатки. При ведении лога апдейты, которые обновляют один и тот же элемент, не группируются в одну запись. Их получается много, и при старте кэшу приходится «проигрывать» все эти записи, что может занять больше времени, чем старт с дампа. Кроме того, сам по себе лог может занимать очень много места, даже не уместиться на диск.
Для решения проблемы можно объединить оба подхода — сброс дампа и ведение лога. Почему бы нет? Мы можем дампить, то есть создавать снапшот, раз в сутки, и при этом всегда писать в лог. В снапшоте мы сохраняем ID последнего изменения. А когда нужно запустить кэш, читаем снапшот, сразу применяем его в память, дальше читаем лог, начиная с последнего изменения в снапшоте, и применяем его поверх снапшота. Всё, кэш прогрет. Это делается так же быстро, как если бы мы читали из дампа. Итак, с холодным стартом мы разобрались, давайте теперь решать остальные проблемы в нашем списке.
Остальные три проблемы
У базы данных есть такое свойство, как durability, которое обеспечивается с помощью транзакций. В БД обычно хранятся горячие и холодные данные. По крайней мере, раз мы дошли до кэша, то у нас данные точно делятся на горячие и холодные. Обычно холодных данных очень много, а горячих очень мало. Так устроена жизнь. Мы реплицируем и шардируем базу данных на много-много копий и шардов только для того, чтобы обслуживать горячие данные. Мы можем сказать себе: «Зачем мы всё это копируем? Давайте шардировать только горячие данные». Но это никак не поможет, потому что мы должны использовать ровно такое же количество серверов, ибо шардируем и реплицируем не из-за того, что данные не помещаются в память или на диск, а из-за того, что мы упираемся в CPU. То есть база не успевает обрабатывать. Таким образом, шардинг и репликация только горячих данных не решает эту проблему. И босс всё ещё злится, потому что нужно платить за всё новые серверы.
Что можно сделать? У нас есть кэш, но горячие данные в базе не дают спокойно жить, мы доставляем их реплики и шарды. Однако кэш тоже хранит данные, как и база. При желании можно сделать в нём репликацию. Что нам мешает использовать кэш как первичный источник данных? Отсутствие такой фичи, как транзакции? Можем сделать и транзакции. Тем самым мы решаем остальные три проблемы, поскольку горячие данные можно будет вообще не хранить в базе, только в кэше. Шардинг тоже становится не нужен, ведь нам не придётся резать базу данных на много серверов, кэш успешно справляется с нагрузкой, в том числе и на запись. А на запись он справляется потому, что у кэша запись работает с журналом так же быстро, как и без журнала.
Итак, в кэш можно внедрить все свойства, которые присущи базе данных. Мы так и сделали, а получившееся в результате детище назвали Tarantool. По скорости работы на чтение и запись он сопоставим с кэшем, при этом имеет все свойства базы данных, которые нам необходимы. Таким образом, мы можем отказаться от базы для хранения горячих данных. Все проблемы решены.
Возможности и особенности Tarantool
Итак, эти многочисленные холодные данные мы реплицировали и шардировали только для того, чтобы обрабатывать малочисленные горячие. Теперь холодные данные, редко запрашиваемые и изменяемые, лежат в SQL, а горячие отправляются в Tarantool. То есть Tarantool — это база для горячих данных. В итоге для большинства задач двух инстансов (мастера и реплики) более чем достаточно. Хотя можно обойтись и одним, потому что паттерн доступа к нему и RPS такой же, как у обычного кэша, несмотря на то что это база данных. Для кого-то это проблема психологического свойства: как можно отказаться от базы как от авторитетного источника хранения данных с её уютным durable с транзакциями и уйти в кэш? На самом деле, начав использовать memcached или любой другой кэш, вы уже отказались от преимуществ базы данных. Вспомните про inconsistency и потерю апдейтов. И с этой точки зрения Tarantool не только ускоряет работу и позволяет экономить деньги, он возвращает вас обратно в мир баз данных с транзакциями, запросами, индексами и прочим.
Пара слов про параллельную работу транзакций. Когда в Tarantool используется Lua, он это рассматривает как одну транзакцию: все чтения делает из памяти, а все записи передаёт во временный буфер и в самом конце одним куском пишет на диск. И пока данные пишутся, другая транзакция уже может читать из памяти старые, незакомиченные данные без всяких блокировок! Очередь из транзакций может возникнуть лишь в том случае, если будет превышена пропускная способность последовательной записи на диск.
Как мы перекладываем с горячих на холодные
Этот процесс у нас пока не автоматизирован. Анализируем логи и определяем, что данные с таким-то паттерном можно считать горячими. Например, профили пользователей — горячие, значит, мы их перекладываем в Tarantool. Понятно, что попутно зацепим и холодные, потому что часть юзеров, например, на Почту уже не ходит. Но, несмотря на перерасход, это получается эффективнее, чем при использовании MySQL. Хотя бы потому, что у Tarantool очень сильно оптимизирован memory footprint. Очень интересный факт: БД SQL всё хранит на диске, но 10–20% должно кэшироваться в памяти. Но при этом у традиционных SQL БД footprint в три-пять раз хуже чем у Tarantool, поэтому эти 20% превращаются в 100%. Получается, что при аналогичной нагрузке SQL-сервер не выигрывает даже по памяти, хотя и нагрузку эту он не держит.
Tarantool против Redis
С нашей точки зрения, есть два ключевых отличия между Tarantool и Redis.
- По нашим тестам, Tarantool быстрее процентов на 30%. Результаты тестирования представлены на сайте Tarantool и в этой статье.
- Tarantool — это база данных. Там можно писать server side скрипты на Lua. У Redis тоже есть Lua, но он однопоточный, блокирующийся, писать свои скрипты можно, но область применения их весьма ограниченна. К тому же Lua в Redis не транзакционен. В этом смысле Tarantool идеален. Он и быстрее, и позволяет использовать транзакции везде, где нужно. Нет необходимости доставать key из кэша, апдейтить и класть обратно, когда параллельно кто-то ещё может менять.
Один миллион долларов
Эта сумма — не выдумка для привлекательного заголовка, а реально сэкономленные деньги в одном из проектов Mail.Ru Group. Нам нужно было где-то хранить профили пользователей. До этого они лежали в старом хранилище, и мы размышляли, куда бы их перенести. Изначально мы рассматривали MySQL. Развернули 16 реплик и шардов MySQL, начали потихоньку дублировать в них нагрузку от профилей на чтение и запись. Профили — это маленькие порции информации (от 500 байт до килобайта), хранящие ФИО, количество отправленных писем, различные флаги и сервисные данные, которые обычно нужны на каждой странице. Эти данные часто запрашиваются и обновляются. При 1/8 от всей нашей нагрузки ферма из 16 MySQL сломалась. И это после всех оптимизаций, которые мы там сделали. После этого мы решили попробовать Tarantool. Оказалось, что он на четырёх серверах спокойно держал нагрузку, которая до этого была распределена по 128 серверам. На самом деле даже на одном сервере держал, мы поставили четыре для подстраховки. А экономия в виде 128 серверов и снижения расходов на хостинг оказалась эквивалентна миллиону долларов.
И это лишь один случай. Tarantool нашёл применение во многих наших проектах. Например, в Почте и Облаке трудится 120 инстансов Tarantool. Если бы при имеющемся уровне нагрузок там использовался MySQL, то пришлось бы ставить десятки тысяч серверов или других SQL — PostgreSQL, Oracle, чего угодно. Даже трудно оценить, в какие миллионы долларов это бы вылилось. Мораль сей басни такова — для каждой задачи нужно подбирать правильный инструмент, это позволяет экономить огромные деньги на простейшей фиче. Холодные данные надо хранить в предназначенной для этого базе данных SQL, а горячие данные, которые часто запрашиваются и часто обновляются, нужно хранить в хранилище, адаптированном для этого, коим является Tarantool.
В версии 1.7, которая сейчас находится в разработке, мы хотим сделать полностью автоматическое кластерное решение с шардингом и с репликацией типа RAFT. Следите за обновлениями!
Комментарии (87)
nikitasius
22.12.2015 16:20+12Теперь нужна статья «Как сэкономить миллион баксов с помощью nginx», начало такое: «решили мы сделать CDN, и запустили ферму из 16 апачей..».
Техничку приводить как обычно не надо, достаточно писать в стиле этой же статьи, что для определенных задачredistarantool шустрее чем mysql.
Ваш кэп!danikin
22.12.2015 16:26+3Я рад, что статья получилась настолько понятной, что я выгляжу кэпом :) Насчет технических деталей — можете задать любые вопросы, и мы вам обязательно ответим! :)
zloidemon
22.12.2015 16:48+2А почему бы и не написать? Кстати в такую свзяку можно взять lua-resty-tarantool и получить ещё больше плюсов!
danikin
22.12.2015 16:55+4Ну идея хорошая. Просто вопрос что именно написать? Если конкретные case study, то уже много статей буквально недавно вышло про Тарантул. Вот, например:
habrahabr.ru/company/mailru/blog/254727
habrahabr.ru/company/mailru/blog/272141
habrahabr.ru/company/mailru/blog/272669
habrahabr.ru/company/mailru/blog/272769
Поэтому я тут решил написать что-то более общее и кэпское :) Хотя, надо и про профили и про другие кейсы сделать статьи. Работаем над этим :)Sliver
23.12.2015 15:43+3use case: «как поднять tarantool в режиме memcached» («для чайников»)
с персистентностью и без — с плюсами и минусами.danikin
23.12.2015 16:26+1Отличная идея! Надо будет написать статью про это. Спасибо! :)
Sliver
26.12.2015 02:30И ещё хотелось бы внятного описания, для чего Тарантул подходит хорошо и для чего он не подходит.
А то по вопросам и ответам кажется, что его пытаются представить как то, что подходит вообще для всего и что работает он во всех областях лучше, чем всё остальное, что только есть. Это же не так, правда?
Хотелось бы понять область применения.danikin
26.12.2015 09:37Тарантул подходит для авторитетного хранения и обработки горячих данных. Т.е. данных, к которым много запросов, как на чтение так и на запись, и данных, которые хранятся в тарантуле как в единственном их источнике (единственном в том плане, что Тарантул — не надстройка над другой базой в виде кэша, а он сам по себе полноценная база).
Тарантул, по сути, это база данных и кэш в одном лице.
По каким признакам понять, что Тарантул вам поможет?
1. У вас данные хранятся в базе. А вы хотите, чтобы эта база обладала свойствами кэша, как то большая пропускная способность по количеству запросов, маленькое время выполнения запроса, большее количество update в секунду.
2. У вас данные хранятся в базе. База порезана на много шардов. А вы хотите, чтобы все жило в одном шарде.
3. У вас данные хранятся в кэше (memcached, redis). А вы хотите, чтобы этот кэш обладал свойствами базы, как то — персистенс, репликация, вторичные индексы, таблицы, хранимые процедуры, курсоры, рэйндж сканы, фул сканы.
4. У вас данные хранятся в базе, а сверху от базы есть кэш. В этом случае у вас есть некоторые свойства кэша (быстрая скорость работы запросов, болшая пропускная способность на чтения) и некоторые свойства базы (репликация, персистенс), но при этом вы теряете другие свойства базы, например, транзакции, хранимые процедуры, вторичные индексы, рэйнд сканы и другие свойства кэша, например, большая пропускная способность на запись, маленькое количество серверов, expire данных (если нужно), отсутствие maintenance окна. А вы хотите, чтобы ваша система обладала всеми свойствами и базы и кэша.
Каждая из ситуаций выше — это признак того, что Тарантул вам поможет. Тарантул — это база данных для горячих данных.VolCh
29.12.2015 10:04Одно из часто используемых свойств популярных РСУБД — обеспечение целостности базы путём различных ограничений, прежде всего ограничений по внешним ключам. Как у Тарантула с этим?
dimoclus
30.12.2015 06:06Решаемо через хранимые процедуры на LUA (можно реализовать почти что любой сложный SQL-запрос — вопрос лишь в вашем терпении).
Область применения у tarantool весьма специфичная, не предполагающая вот таких вот сложных use cases.
Ни в коем случае не покупайтесь на сравнения с реляционными СУБД. В лучшем случае tarantool можно сравнить с HandlerSocket в MySQL, где в «терминах грубой силы» победит MySQL (за счет multicore), а по гибкости однозначно tarantool.danikin
30.12.2015 09:39По нашему опыту и тестам Тарантул быстрее HandlerSocket, причем в разы быстрее. Тарантул даже быстрее Memcached. Вот тут полностью открытая информация по тестам, с исходниками и образами тестируемых машин: articles.rvncerr.org/how-to-chose-an-in-memory-nosql-solution-performance-measuring
Будем благодарны вам, если расскажете конкретный кейс, когда HandlerSocket побеждал Тарантул за счет грубой силы. Мы бы потрепарировали его и повыясняли, как так получилось, что Тарантул проиграл.baltazorbest
30.12.2015 10:05Ну вот например в синтетическом тесте (по циклу делаем 100к записей и 100к выборки) Redis vs Tarantool в скорости обработки insert/set и select/get, Redis Все таки выигрывал у меня. Относительно QPS пока ничего сказать не могу.
Тест проводился на Centos 7.2, Tarantool 1.6.7-558, Redis 3.0.5 + php 5.4.45 & clients redis(+redis-igbinary) & tarantool( последний из репозитория). Но тесты проводились на виртуальной машине Virtualbox с 2 ядрами и 3гб ОЗУ. Вставлялся многомерный массив.
Результаты были приблизительно такие:
Tarantool 100k insert с включенным wal_mode режим write — 10сек., с выключенным wal_mode — 7сек., primary index type=«HASH», STR
Redis: 7сек.
На чтение:
Tarantool: 7сек.
Redis: 5сек.
danikin
30.12.2015 10:19О, отличный тест, спасибо! Можно ли попросить у вас исходники теста и конфигурации Tarantool и Redis? Если да, то вот мой email: anikin@corp.mail.ru
Тест и базы работали на одной и той же машине? Результаты теста повторяются устойчиво, т.е. если раза 3-4 прогнать, то результаты примерно такие же?
Плюс, можете чуть чуть пояснить
Включенный wal, только запись
Tarantool: 10s
Redis: 7s
Выключенный wal, только запись
Tarantool: 7s
Redis: 7s
Только чтение
Tarantool: 7s
Redis: 5s
Все верно?
Т.е. у Redis включенность wal не влияет на скорость?baltazorbest
30.12.2015 10:23Да конечно, сейчас повторю тест но уже на живой машине и вышлю Вам на почту и пхп скрипт и настройки и более подробное время.
Плюс к Redis set ошибся со временем, не 7 а 5сек.
Плюс выкл./вкл. wal был выставлен у Tarantool, редис был с выключенным save.
dimoclus
30.12.2015 16:30>По нашему опыту и тестам Тарантул быстрее HandlerSocket, причем в разы быстрее
Вот здесь http://yoshinorimatsunobu.blogspot.ru/2010/10/using-mysql-as-nosql-story-for.html пишут о 750k select-ов по primary key в секунду. Честно говоря, я недоумеваю, каким образом однопоточный процесс может выиграть у многопоточного, когда в современных процессорах число логических ядер доходит до 24.danikin
30.12.2015 16:45Tarantool в пике делает 300К селектов на 1 ядре. На один физический сервер — умножайте на 24. Но в реальности больше 2 миллионов не получалось. Насчет однопоточности — его можно шардить по ядрам. Насчет handlersocket — по нашему опыту нам не получалось больше 30-40К select/sec на одной машине выжать из handlersocket.
Если у вас для вашей задачи на вашем железе получится сделать 750K select в секунду на один сервер в MySQL и при этом в эту базу будут идти конкурирующие updates и вы будете сохранять те же 750K select в секунду, то это будет супер круто и вам, конечно, Tarantool не нужен. Скажу даже больше, вы сможете конкурировать легко с Percona или другими сапортерами MySQL и озолотитесь.
Я просто к чему говорю, что бенчмарк бенчмарку — рознь. Надо всегда проверять на конкретной нагрузке для конкретной задачи и сравнивать, сравнивать и сравнивать. Чем мы собственно всегда и занимаемся. Давно бы ушли с Тарантула на что-то, если бы это что-то было бы тоже базой данных с транзакциями и server-side-scripting и работало бы реально на наших кейсах быстрее. Но пока ничего такого не нашли :-)dimoclus
30.12.2015 20:20>Tarantool в пике делает 300К селектов на 1 ядре
Это при каких условиях?
На официальном сайте тарантула есть бенчмарк http://tarantool.org/benchmark.html, показывающий в районе 100k select-ов в секунду, а вы говорите о 300k.
>На один физический сервер — умножайте на 24
А память на 24 умножать не затратно будет?
>Шардить по ядрам
А потому ходить в шарды черед прокси (кстати, многопоточную или single-threaded?) и в итоге придем к тому, что «лучше бы вовсе не шардили».
Кстати, availability (не говоря уже о запросах «посчитать что-то среди имеющихся данных) страдает от шабрдинга, т. к. возрастает вероятность недоступности одного из шардов. Плюс увеличивается время старта при использовании AVLTREE-индексов.danikin
30.12.2015 21:121. 300K в прыжке. Это суперсинтетика.
2. Если база пошардена или просто разделена по сервисам, то умножать не надо. Это же не репликация, где у каждой реплики одинаковые данные.
3. Не обязательно прокси. Можно поделить базу по сервисам так, чтобы одни сервисы ходили в один кусок, а другие в другой. Даже 100К на одном ядре достаточно для большинства сервисов, поэтому можно на одну машине помещать, грубо говоря, по отдельной сущности на ядро.
4. Шардинг привносит проблемы, это факт. Поэтому первое правило шардинга — do not shard. И именно поэтому один из главных принципов Тарантула — это добиться максимальной производительности на 1-2 ядрах (на 2, потому что он умеет разносить transaction processor и networking на разные ядра), чтобы в шардинге вообще не было необходимости кроме супервысоконагруженных сервисов. Но и про шардинг мы не забываем и в версии 1.7 планируем его зарелизить. Чтобы все шардилось не только по ядрам, но и по машинам.
5. 750КRPS на одну машину — это потрясающий результат. Тот, кто его достиг на HandlerSocket, должен использовать HandlerSocket и ничего больше. Единственное что, я бы глянул на следующее:
— как оно работает с параллельными апдейтами
— как оно работает с транзакциями (когда не просто set/get value, а какая-нибудь хранимка вызывается, и когда эта хранимка должна отработать по принципу ACID)
— очевидно, что при таких скоростях весь dataset в памяти, а значит надо посмотреть на memory footprint. MySQL не самым оптимальным образом хранит данные в памяти, у него большие оверхеды per field и per row.
И если все это устраивает, то можно и нужно юзать HandlerSocket! А если нет, то надо обратиться в сторону Redis/Tarantool и других решений.
Надо использовать правильный инструмент для работы, который наиболее эффективен для данного workload.
talanchor
22.12.2015 17:01Проблема №1 решается т.н. двухфазным коммитом: мы либо пишем и в базу и в кеш, либо никуда не пишем, а читаем потом только из кэша. Моргание сети и прочее тут не является принципиальной проблемой, т.к. это всё устранимо (в конце концов, когда-то и электричество может «моргнуть», и без репликации по разным стойкам/дц тут ничего не поделаешь).
Далее, имея двухфазный коммит для пары кэш+бд, используем персистентный кэш с включённым LRU — например тот же Redis. На выходе получаем и гарантированную запись, и быстрые чтения, и быстрый холодный старт, и как бонус — автоматическое, а не ручное разделение горячих и холодных данных, используя при этом стандартные проверенные решения — что является большим плюсом, перевешивающим заморочки с малоизвестным, пусть и навороченным софтом (я это говорю со стороны, как внешний наблюдатель — всё-таки популярность и удобство Redis'а — это тоже немаловажный фактор на фоне десятка-другого процентов скорости).
В итоге имеем решение аналогичное предложенному, плюс сокращаем работу по ручному отделению горячих данных от холодных, плюс ещё экономим память на горячих данных (у вас ведь всё лежит в памяти, несмотря на то что горячих данных там от силы половина, а то и меньше). И экономим тот же самый миллион долларов.
Если я не прав — ЧЯДНТ?danikin
22.12.2015 17:54+51. Двухфазный комит надо разрабатывать. И это на деле сложнее чем в теории. Вот можете прям в деталях пояснить как бы вы его написали? Было бы круто привести примеры кода для MySQL + Redis или еще какой-нибудь популярной связки.
Кроме того какой бы крутой не был у вас двухфазный коммит все равно в самом конце вы должны завершить этот коммит в двух местах синхронно (или оба да или оба нет), что не совсем понятно как сделать, т.е. вероятность, что данные разъедутся остается. И они точно разъедутся в самый неподходящий момент и вы не будете про это знать :)
2. Запись в базу будет идти медленно. Из-за синхронного коммита вы сможете не справиться в нагрузкой, т.к. запись будет работать не быстрее чем со скоростью MySQL.
3. Для того, чтобы MySQL держал нагрузку на запись вам придется его шардировать, т.е. уже тратите сколько то сот тысяч из этого миллиона.
Но в целом ваше решение достаточно неплохое. Если у вас нагрузка в основном на чтение, то оно вполне будет работать.
Кстати, у нас для таких профилей нагрузки есть репликация из Tarantool в MySQL. Причем сам Tarantool можно автоматом подчищать по принципу LRU, если памяти не хватает. Это решает проблему двухфазного коммита (репликация всегда рано или поздно произойдет, потому что работает через очередь). И это дает функционально ровно то решение, о котором написали вы. Просто, насколько я знаю, между MySQL и Redis'ом стандартной репликации нет.talanchor
22.12.2015 18:37+1> Вот можете прям в деталях пояснить как бы вы его написали?
Сходу нет, но с mysql и redis это не выглядит как совсем уж космическая задача. А вот поинт насчет скорости записи выглядит существенный. В обычную базу, действительно, очень быстро писать не выйдет.danikin
22.12.2015 18:40+3Ну вот стандартных механизмов по связи MySQL и Redis через двухфазный коммит нет. Уже проблема. А Тарантул реплицироваться с MySQL умеет. Т.е., даже если вы не доверяете Tarantool как авторитетному хранилищу данных (что вполне логично для новой для вас базы, даже если она 7 лет уже существует), то как раз репликация в MySQL спасает.
У нас, на самом деле, полно кейсов, когда вроде как с виду решение есть без Tarantool (мы тоже всегда его челленджим, ищем, что еще есть нового-хорошего), но начинаешь копать глубоко и понимаешь, что там нужен костыль, здесь доработка, тут будет поттормаживать, там будут данные теряться и возвращаемся к старому доброму Tarantool :)baltazorbest
22.12.2015 20:56А можно ли потом будет вытащить данные из MySQL в Tarantool? Ну вот например Tarantool данные хранятся только в памяти, перезагружаем сервер/тарантул и что бы данные считались с MySQL?
Так же чуть отклоняясь от данного поста вопрос: Можно ли хранить данные Тарантула параллельно и в памяти и на диске по принципу например Cassandra? Вопрос в том что есть база например 200гб и как то дорого выходит хранить всю это базу в памяти.danikin
23.12.2015 09:35+4Если настроена репликация между ними, то да, можно. Но в описанном вами кейсе в этом нет необходимости, потому что после перегрузки сервера/тарантула, тарантул поднимает все данные из snapshot + xlog и тут же находится в том же состоянии, что был до перезагрузки.
У Тарантула есть disk store. Он в версии 1.6 пока не считается стабильным. Но будет стабильным в 1.7.
Насчет базы в 200Г. У Тарантула очень оптимальный memory footprint. Если это 200Г в MySQL, то в Тарантуле они уже будут весить 100Г или меньше. MySQL при таком размере базы для нормальной работы требует 32Г. Кроме того, MySQL требует SSD диск, а Тарантул может работать и с SATA диском, который в разы дешевле. Плюс, Тарантуле в отличие от MySQL не обязательно иметь крутейший процессор на 16-32 ядер, ему достаточно для нормальной работы 1-2 ядра (и на 1-2 ядрах он будет быстрее чем MySQL на 16-32 ядрах). Т.е. в сумме получается, что затраты на сервер существенно дешевле. Плюс, если ваша нагрузка, к примеру 100KQPS и больше, то в случае MySQL вам придется ставить уже ферму из 10 серверов, т.е. эти 32Г превратятся в 320Г и все остальные ресурсы тоже удесетярятся. Тарантул же легко держит ту же нагрузку на одном сервере с все теми же 100Г памяти.baltazorbest
23.12.2015 09:58>тарантул поднимает все данные из snapshot + xlog
Т.е. тарантул умеет делать автоматические снапшоты во время работа?
Понятное дело что 100К QPS MySQL не вытянет (если брать 1 сервер), тут скорее был вопрос подтянет ли данные тарантул с мускула.
Спасибо за ответ.danikin
23.12.2015 13:28> Т.е. тарантул умеет делать автоматические снапшоты во время работа?
Конечно. И этот снэпшот содержит целостную копию базы на определенный момент времени. При этом снэпшоттинг никак не блокирует ни чтение, ни запись и почти не потребляет дополнительной памяти (хранить приходится лишь новую версию тех апдейтов, которые пришли во время создания снэпшота).
Тарантулу нет нуждны синкаться из MySQL при старте. Он просто все поднимает из снэпшота.
baltazorbest
23.12.2015 23:00+1Тогда более конкретный вопрос: имеем сервер с 32гб ОЗУ, имеет базу Cassandra 160гб+, по вашему мнению можно ли будет перейти в конечном счете на Tarantool на этом же одном сервере? Ваш продукт меня заинтересовал, но вот то что он Memory only в моем случае это и плюс и минус. Так же еще пару вопросов:
1) Есть ли нормальная документация по PHP connector? Со списком методов, описанием и примерами.
2) На сколько я понял, TTL (expire) все таки можно ставить tuple (ключу?)?
3) И на сколько я понял сейчас есть режим master-master? Записываем не зависимо в какую ноду.
Спасибо.danikin
24.12.2015 09:12У вас сейчас ровно один сервер? Какой примерно QPS (queries per second)? Какого рода данные там лежат? Можем, если хотите продолжить общаться в личке: anikin@corp.mail.ru :)
По вопросам 1 и 2 привлек разработчиков. Они сейчас ответят.
По вопросу 3 — да, именно так. И eventual consistency.
rtsisyk
24.12.2015 12:01+11) github.com/tarantool/tarantool-php-stubs/blob/master/src/Tarantool.php
2) Есть пакет github.com/tarantool/expirationd, который позволяет использовать какое-то из полей тапла под TTL
3) В 1.6 есть асинхиронный мастер-мастер, можно писать в любую ноду, но конфликты надо разруливать на клиенте (либо разбивать диапазоны). В 1.7 делаем дополнительный полный синхрон.baltazorbest
24.12.2015 13:58Заметил кстати что в пхп коннекторе нету метода upsert который уже есть в самой БД, можете пожалуйста подсказать на когда планируется ввести данный метод?
rtsisyk
25.12.2015 23:44Прогресс по данной фиче трекаем здесь
github.com/tarantool/tarantool-php/issues/48
ComodoHacker
22.12.2015 18:00+2С двухфазным коммитом производительность связки на запись становится равна производительности базы. От недостатка которой нам собственно и понадобился кэш.
danikin
22.12.2015 18:12+5Вот именно. Но репликация из кэша в базу будет чуть лучше чем двухфазный коммит, т.к. она частично поможет тем, что сгладит пики запросов на запись.
webdi
22.12.2015 17:36+4Простите за вопрос. Скажите, где можно почитать про Tarantool, так чтобы «совсем для начинающих»?
dedokOne
22.12.2015 19:17+2Прям для «совсем для начинающих» доки очень мало, известная беда :(
Могу посоветовать: tarantool.org/doc/getting_started.html
Если будет вопрос, можно сюда запостить groups.google.com/forum/#!forum/tarantooldanikin
22.12.2015 19:22+3Еще в группу в Фейсбуке можете всегда написать: www.facebook.com/groups/tarantoolru. Мы с удовольствием ответим на все ваши вопросы! :)
meln1k
22.12.2015 18:08А если воспользоваться подходом CQRS и писать все события в Kafka, а потом отражать изменения на кеше? Данный вариант тоже имеет право на жизнь?
danikin
22.12.2015 18:25Ну если Kafka использовать как лог изменений для кэша, из которого поднимать все данные при рестарте кэша, то вполне.
tgz
22.12.2015 18:23-9Была написана уже 28-ая пиар статья про таранатоол, но его так никто и не хотел внедрять. Даже даром.
danikin
22.12.2015 18:27+7Из крупных известных нам внедрений на пределами Mail.Ru — badoo.com, www.avito.ru, wallarm.com. Кстати, приходите к нам на Meetup 26 Января, послушаете сами представителей этих компаний :) Вот тут детали: corp.mail.ru/ru/press/events/149
DjOnline
22.12.2015 21:45Сравнивалось ли с Mysql Handlersocket/memcached plugin?
rtsisyk
22.12.2015 23:32сравнивался Тарантул с дисковым движком, mysql, postgresql и еще что-то. Как откопаем картинку, запостим.
danikin
23.12.2015 09:38Сравнивались со всеми. В частности в указанном случае в статье с профилями мы пробовали MySQL именно с HandlerSocket. И Тарантул оказался минимум на порядок производительнее.
Вот тут статья, где мы сравнили с in-memory конкурентами, включая memcached. Тарантул рвет почти всех, кроме нескольких случаев: articles.rvncerr.org/how-to-chose-an-in-memory-nosql-solution-performance-measuring
gizmondo
23.12.2015 00:16+1Делаете ли вы fsync журнала при каждом коммите?
rtsisyk
23.12.2015 09:12+2В Tarantool можно включить и такой параноидальный режим работы.
Хотя и fsync тоже толком ничего не гарантирует.
gizmondo
23.12.2015 10:50+11) При наличии BBWC — гарантирует, насколько я понимаю. Вы кстати используете такое железо?
2) Парой постов выше Вы сказали, что сравнивали тарантул с постгресом. Я надеюсь, это сравнение было тоже с fsync=off?
Вообще мой вопрос вызван тем, что описанный алгоритм персистентности ничем в целом не отличается от того, что делает тот же постгрес, но при этом обещается на два порядка лучшая производительности на запись.danikin
23.12.2015 11:16+2Тарантул пишет на диск линейно. Postgres как и MySQL/InnoDB, как и любой другой движок базы данных, основанный на B-Tree на каждую запись делает много disk seek, то замедляет все на порядок и больше.
Кроме того, Тарантул сильно свежее Postgres (в плане движка хранения) или InnoDB, поэтому там многие вещи устроены оптимальней. Ну и потом, производительность было наше главное требование, когда мы писали Тарантул. Поэтому там все очень оптимально внутри. Мы можем проводить часовые митинги на тему как убрать лишний байт или даже бит на хранение поля или как избавиться от лишнего memcpy.
Тарантул быстрее и с fsync=off и тем более с fsync=on (потому что в последнем случае у вас в B-Tree based engines будут честные многократные перемещения дисковой головки при каждой записи).gizmondo
23.12.2015 12:55Я бы хотел лучше понять утверждение про «на каждую запись делает много disk seek, то замедляет все на порядок и больше.»
Для простоты предположим что у нас есть апдейт поля, которое не входит в индекс.
И тарантул, и постгрес пишут последовательный журнал. По дефолту постгрес честно фсинкает этот файл, а тарантул нет, но допустим мы настроили одинаково.
Теперь тарантул запишет изменение у себя в памяти, а постгрес сделает fd.write (так что в вашем случае мы экономим на системном вызове). Этот write попадает в кэш ОСи, и теперь операционка и контроллер диска вольны откладывать запись на диск на сколько угодно (точнее до следующего чекпоинта), реордерить, объединять их в батч по признаку близости, etc. В любом случае изменения считаются персистентными и клиенту вернули «успех».
Где тут сики на каждую запись?danikin
23.12.2015 13:34Во-первых, перед апдейтом надо считать с диска, чтобы понять, что апдейтим. К примеру, если это UPDATE t SET k='a' WHERE k='b', то надо сначала найти, где на диске лежит ключ k. И это уже как минимум один сик, а на деле 2-3. Тарантул же имеет все уже в памяти и такой проблемы там нет.
Далаее делается уже запись на диск. Допустим эта запись на диск не попала сразу на диск, а осела в кэше. Сиков нет. Есть лишь задания в памяи ядра записать в такое-то место на диске такие-то данные. Далее, на момент чекпойнта у вас обновились 100 случайных записей, то надо сделать как ни крути 100 движений головкой диска. Ибо на больших базах данных, которые целиком не влезают в кэш и расстояния между случайными записями большие. Да, может повезет, и скажем половина из этих данных будут рядом друг с другом. Ну тогда будет 50 сиков. Большой роли это не играет.gizmondo
23.12.2015 14:02Ну, про поиск это нечестное сравнение. Мы же обсуждаем горячие данные, значит надо считать что вся база вместе с индексами влезает в память. Конечно, постгрес потребует в этом сценарии в несколько раз больше памяти на сервер и какой-нибудь скрипт прогрева, но какая разница.
Если поставить checkpoint_timeout равный частоте дампа в тарантуле и checkpoint_completion_target=0.99, то не будет ли поведение одинаковым? В смысле нам в обоих случаях надо в течении долгого времени переписать на диск (почти) весь массив данных.danikin
23.12.2015 15:33+2Вы имеете в виду ситуацию, когда все влезает в память? Тогда другое дело. Сиков на записи не будет.
checkpoint_timeout ставить огромным можно. Но тогда в памяти будут храниться еще и все изменения.
А учитывая то, что memory footprint у Тарантула сильно оптимальней, чем у Postgres, то суммарно это будет в 3-5 раз больше весить в памяти чем Тарантул.
Кроме того при коммите всего этого огромного набора данных на диск Postgres будет тормозить (фактически downtime). И это не потому что Postgres плохой, это потому что он не предназначен для такого кейса.
Плюс, см. выше, Тарантул сам по себе сильно оптимальней Postgres и MySQL даже если в последних все влезает в память.
Резюме такое:
Если поставить в 5 раз больше памяти, то, да, это будет работать и с Postgres'ом. Но будет работать все равно раз в 10 медленнее. Ну т.е. не в 100, а в 10.
Просто, еще раз, надо использовать правильный инструмент для правильной работы. Если данные холодные, запросы к ним редкие, но сложные, то реляционные базы — это идеальный инструмент. А если это хранилище профилей, сессий или любой другой горячей информации, то Тарантул идеален.
dimoclus
23.12.2015 00:57+4Вы, конечно, очень радужно все описываете, однако какой как выглядит «холодный старт» тарантула? Я так понимаю, что ему необходимо прочитать с диска снапшот, затем проиграть «хвост» операций из журнала, затем перестроить индексы. И это все время полного простоя. Допустим, снапшот с нашими данными «весит» 80 гигабайт. Чтобы просто прочитать это с диска потребуется порядка 81920 / 200 ~ 409 секунд или почти 7 минут (200 mb/s вполне достойная скорость линейного чтения?). И это не какой-то «разогрев», когда «все тормозит». Это реальный 100%-й downtime.
Кстати, насчет сохранения снапшота: правильно ли я понял, что по факту нужен двойной объем RAM? Я так понимаю, что используется fork + COW, но ведь это может стрельнуть в самый неожиданный момент.
Да, а как быть со «сложными запросами», когда нужно использовать то один, то другой индекс, да еще дополнительно отфильтровывать результаты? Вы так настаиваете на сравнении с настоящими базами данных, что невольно хочется попросить предоставить кусок lua-процедуры, реализующий join в трех таблицах, где в where-условии также участвуют поля хотя бы из двух таблиц. Я представляю себе, как это выглядит, но не представляю человека, которому SELECT… FROM… JOIN… WHERE покажется сложнее, чем портянка аналогичного lua-кода.
Ну и напоследок: что у вас делают, когда в многогигабайтной расшардированной базе появляется необходимость делать выборку по новому полю? Как добавить индекс без простоя?rtsisyk
23.12.2015 09:10+51. Можно использовать репликацию, тогда простой будет минимальный. Также у Tarantool есть режим hot standby, когда резервный сервер прямо на той же машине догоняется логами основного и, в случае проблем, занимает его место. Используется для апгрейда без простоя. Думаю автор поста расскажет подробнее.
2. Снапшот уже давно на основе MVCC в памяти без fork() и COW. Дополнительная память так или иначе требуется, объем зависит от характера нагрузки. Результаты MVCC при реальном использовании существенно лучше, чем на fork()'е.
3. Забудьте про JOIN в любой более-менее загруженном проекте. Тем более через SQL с планировщиком запросов.
Точно также как любая конференция по Java всегда начинается с рассказа о новых способах тюнинга garbage collector через настройку пяти десятков опций, так и любая конференция по той же PostgreSQL или MySQL (хорошие базы, привожу в качестве понятного примера) всегда включает в себя рассказ о «секретных» методах передачи подсказок несчастному планировщику для повышения его предсказуемости. USING INDEX и прочий не совсем SQL растёт оттуда же. SELECT… FROM… JOIN… WHERE на кластере так и вообще что-то из области ненаучной фантастики. Также хотел бы заметить, что хранить данные в нормализованном виде не всегда оправдано. В реальных задачах очень часто дешевле допустить некоторую избыточность, но при этом полностью избавиться от JOIN.
Так или иначе, мы планируем добавить SQL для задач, когда удобство использование важнее скорости и затрат на железо.
4. Изменение схемы данных в рабочем проекте — это тема для отдельнойдокторской диссертациистатьи. Скажу лишь что в Tarantool можно добавлять и удалять таблички и индексы на лету.dimoclus
23.12.2015 13:27+1>у Tarantool есть режим hot standby, когда резервный сервер прямо на той же машине догоняется логами основного
Ценой 2x по памяти?
>Забудьте про JOIN в любой более-менее загруженном проекте
Согласен. Но ведь в любом проекте рано или поздно наступает момент сбора и подсчета продуктовой статистики, где SQL начинает блистать, предоставляя возможность очень просто конструировать и выполнять сложные запросы с агрегатными функциями, join-ами, группировкой и прочими плюшками. В таких задачах хочется гибкости, а не писать руками join/avg и прочее на каждый чих.
>В реальных задачах очень часто дешевле допустить некоторую избыточность, но при этом полностью избавиться от JOIN.
Зависит от задач. Денормализация подразумевает дублирование данных, а дублирование, как правило, идет рука об руку с рассинхронизацией. И не надо забывать, что в РСУБД данные не хранятся в памяти и проблема JOIN-ов на самом деле лежит в области seek-ов по диску. Странно, что в тарантуле, где все хранится прямо в локальной оперативной памяти, могут быть какие-то проблемы с подобными запросами. Однако согласен, JOIN + WHERE + ORDER BY для результата на десятки тысяч строк ни к чему хорошему и в оперативной памяти не приведет.
Что же касается планировщика и USING INDEX… Как правило, планировщик действительно лучше знает, какие индексы использовать в сложных запросах, USING INDEX был хорош в древние времена, дабы избегать filesort'ы. Очень странно, что проводя аналогию Tarantool — *SQL, вы замалчиваете, что любой запрос в тарантуле — это явный USING INDEX.
>Скажу лишь что в Tarantool можно добавлять и удалять таблички и индексы на лету.
А как выглядит добавление индекса на лету в однопоточном сервере?rtsisyk
23.12.2015 13:43+2>у Tarantool есть режим hot standby, когда резервный сервер прямо на той же машине догоняется логами основного
Ценой 2x по памяти?
Да, именно так. Поэтому вариант с репликой мне нравится гораздо больше.
В таких задачах хочется гибкости, а не писать руками join/avg и прочее на каждый чих.
Для этого и хотим сделать SQL.
>Скажу лишь что в Tarantool можно добавлять и удалять таблички и индексы на лету.
А как выглядит добавление индекса на лету в однопоточном сервере?
У нас же файберы, кооперативная многозадачность и всё такое, никто не мешает перестраивать индекс в фоне.
Но должен признаться, далеко не все операции ALTER у нас сейчас могут быть сделаны в таком режиме. У нас есть наработки для фоновой перестройки индексов, но пока нет клиентов на данную фичу.
danikin
23.12.2015 09:42+3В дополнение к ответу выше. Мы всегда имеем горячую реплику. Если машина падает, то нагрузка автоматом уходит на реплику.
Насчет 100% downtime. Да, если ВСЕ реплики свалятся, то будет в указанном вами случае будут минуты даунтайма. 5-14 минут в зависимости от типа диска — для SSD линейное чтение — 300Mb/s, для SATA — 100Mb/s.
Но давайте вспомним про традиционные базы данных (MySQL/Postgress), они прогревают кэши хорошо если со скоростью 1-3Mb/s, и там будет 50% downtime на протяжении часов, что, согласитесь, еще хуже. Причем, в случае с MySQL вы даже ETA этого downtime предсказать не можете. Мы на это нарывались ни раз.
Насчет SQL. Да это удобнее Lua во многих случаях (но как сказал автор выше — не на heavy workloads). И именно поэтому мы сейчас в процессе прикручивания SQL к Тарантулу. Следите за обновлениями! :)dimoclus
23.12.2015 13:45>Если машина падает, то нагрузка автоматом уходит на реплику.
Можно поподробнее с этого момента? Это встроенная возможность стандартного tarantool-коннектора, или вручную кодится в каждом клиенте? И если с SELECT-ами все понятно, то как быть с модифицирующими запросами в такой схеме? Как вы избегаете split brain-а при автоматических переключениях?
>Но давайте вспомним про традиционные базы данных (MySQL/Postgress), они прогревают кэши хорошо если со скоростью 1-3Mb/s
Откуда взята такая странная цифра? Традиционным базам нужно, чтобы индексы попали в page cache, что в некоторых случаях достижимо простым cat index.MYI > /dev/null на той же самой линейной скорости чтения.
>50% downtime на протяжении часов
Это как? Кроме того, разве в наше время такой downtime не решается установкой SSD для диска с данными? Будет ли хранение данных в RAM более экономным, чем использование SSD + «традиционная РСУБД»?danikin
23.12.2015 15:47+2Отвечаю по порядку:
1. Это отличный вопрос! Прямо сейчас в стандартных коннекторах такой возможности нет. У нас есть внутренняя возможность в Mail.Ru, которую мы скоро отдадим в community. Там вся сложность в том, что это нельзя просто реализовать в коннекторе, надо делать прокси. А мы это у себя реализовали в своих собственных внутренних проксях, через которые мы из любого кода ходим в Тарантул.
Насчет update'ов. У нас есть уже в 1.6 асинхронная репликация мастер-мастер. Если вы в своем коде поддержите такую логику «не получилось в одну базу, сходили в другую», то вы сможете применять и апдейты тоже. В этой схеме будет eventual consistency и в случае конкурирующих изменений будет применяться грубо говоря случайное. Для многих задач такая логика вполне подходит.
В 1.7 у нас будет синхронный мастер-мастер на основе RAFT. Это даст возможность strong consistency, при этом с high availability, т.е. без downtime при падении машины. Разумеется, проблема network partitioning останется — если на кластере из 3 машин они разрежутся недоступной сетью на две группы 1 и 2, то та группа, где 1 машина будет недоступна ни на чтение ни на запись. Но тут уж ничего не поделаешь, CAP теорема :)
2. Это если у вас столько памяти, что вы готовы хранить индексы и в page cache и внутри базы целиком. Тогда, да. Вообще, если база почти целиком влезает в память, то все проще. Но как я написал в одном из коментов выше — даже в этом случае тарантул в разы быстрее + оптимальней в разы по памяти. Если же у вас нет лишней памяти на page cache под весь индекс или индекс целиком не влезает в память, то прогрев идет просто за счет запросов в базу, что медленно.
3. Пока база греется путем запросов в нее, то она вся стоит в диск и какую-то часть запросов не обрабатывает. 50% — это примерно то, что я знаю по нашему опыту подобных проблем эксплуатации MySQL и Postgres.
4. SSD для традиционной базы лучше чем SATA. Но все равно не дотягивает до Тарантула по производительности на порядок и больше. Хранение в RAM будет более экономной, потому что не надо много реплик (одна машина тарантула тянет такую же нагрузку как десятки машин с традиционными базами), потому что более оптимальный memory footprint, потому что не нужен SSD (SATA для Тарантула вполне достаточно). Если хотите, можете дать мне вашу конфигурацию, с количеством и спецификацией машин, с базами, которые там стоят и с QPS, а я прикину, какие бы и в каком количестве машины держали ту же самую нагрузку на Тарантуле.
Sliver
23.12.2015 16:10По п.1 (фича-реквест): раз в Тарантуле нет никаких настроек, то может быть, для разных use case и разных проблем с сетью сделать флаги на чтение типа «отдать обязательно последние данные» либо «отдать какие-нибудь доступные данные»?
Просто иногда («для статистики») бывает полезнее получить хоть что-то, чем ничего.
Ну и при ответе можно ещё рядом сообщить, что именно отдали: последнее или определённой свежести.
dimoclus
25.12.2015 02:34+1>А мы это у себя реализовали в своих собственных внутренних проксях, через которые мы из любого кода ходим в Тарантул.
Что такого можно сделать во внешней проксе, чего нельзя сделать в коннекторе?
>У нас есть уже в 1.6 асинхронная репликация мастер-мастер
Дублирование запроса на соседа — это дублирование запросов на соседа. Репликация все-таки должна оставлять данные в консистентном состоянии.
>В 1.7 у нас будет синхронный мастер-мастер на основе RAFT.
Честно говоря, я не вполне понимаю вектор развития tarantool. То он позиционируется как «мемкеш на стеродидах», то как «lua application server». Судя по рекламе, которую вы даете в своих постах, это интересная наработка, которой мало кто пользуется, потому что не знают, как. Вы лучше определитесь с нишей, соберите набор best practices, дефолтные конфигурации для типовых задач, и т. д. Вот MySQL, сколько бы его ни ругали, используют повсеместно, потому что о нем полно информации.
>Вообще, если база почти целиком влезает в память, то все проще.
Кстати, а что делать, когда данные перестают влезать в один тарантул? Есть какие-то утилиты/гайды по шардингу или отдел эксплуатации каждый раз все заново изобретает?
>Если же у вас нет лишней памяти на page cache под весь индекс или индекс целиком не влезает в память
Я же писал, что можно параллельно cat'нуть индексный файл в /dev/null в некоторых случаях. И индекс всегда меньше данных. Взять, к примеру, базу постов хабра: реляционная база, хранящая только индексы по сообщениям и постам будет потреблять на это меньше памяти, чем tarantool, также хранящий в памяти тела сообщений.
>Хранение в RAM будет более экономной, потому что не надо много реплик (одна машина тарантула тянет такую же нагрузку как десятки машин с традиционными базами)
А можно с этого момента поподробнее? «Традиционные базы» могут использовать многоядерность современного железа и, как правило, успешно обрабатывают весьма нетривиальные запросы. Для простейших запросов по индексу у MySQL есть handlersocket, который в свое время позволял выжимать десятки тысяч запросов в секунду с одной железки. Более того, если база вдруг начинала «упираться в CPU», то можно было существенно все ускорить заменой железа: даешь больше ядер, получаешь лучшую производительность без каких-либо затрат на поддержку/разработку.
Это, кстати, весьма актуальный вопрос, т. к. я не нашел бенчмарков с простенькими луашками, которые делают, скажем, простенький left join для результата из одной записи.danikin
25.12.2015 09:52> Что такого можно сделать во внешней проксе, чего нельзя сделать в коннекторе?
Прокси хранит состояние. Например, она помнит, какие сервера с этого конкретного сервера были недоступны. Кроме того, прокси помнит схему шардинга, т.е. на какой машине что живет и постоянно обновляет эту схему с центральной машины (если она недоступна — не страшно, все работает, нельзя только в этот момент доставлять новые сервера, т.е. downtime нет).
В коннектор все это можно впихнуть, но тогда не понятно, как коннекторы на одном физическом сервере будут шарить эту информацию. Можно в шаренную память, опять же, но с ней не очень удобно работать, в плане, что туда нельзя помещать развесистые объекты.
Кроме того, коннектор надо конфигурять (задать список машин, путь для лог файла (потому что надо вести лог и понимать, что успешно, а что нет)), что придется делать в клиентах, что неудобно.
В целом, у нас большой опыт по созданию fault-tolerance для различных хранилищ. Большой портал, многодесятко миллионная аудитория. Но если вы подскажите как правильно реализовать fault-tolerance в коннекторе и у вас есть такой опыт, то будем рады послушать :-)
В любом случае, мы задумались над этой темой. Будем думать как сделать fault-tolerant коннектор без прокси.
>Дублирование запроса на соседа — это дублирование запросов на соседа. Репликация все-таки должна оставлять данные в консистентном состоянии.
У нас есть на выбор 2 из 3 широко известных человечеству и часто используемых репликаций, а в 1.7 будет и третья. 3 — это я имею в виду master-slave, master-master, Paxos/RAFT. В MySQL, к примеру, есть только в стабильном виде master-slave.
Насчет консистентного состояния. Стандартный master-slave, который есть в Тарантуле и есть во всех популярных СУБД не является strong consistent. Т.е. данные на мастере поменялись, реплика этих изменений еще не видит, а клиентский код уже пошел на реплику. master-master, который дополнительно есть в танрантуле тоже не является strong consistent, но зато он позволяет создать почти 100% availability, потому что всегда есть сервер куда записать. С другой стороны он создает проблему конкурирующих апдейтов, которой нет в master-slave, но для некоторых кейсов это не страшно. Paxos/RAFT же решает эту проблему. Данные всегда consistent и всегда available за исключением случая network partitioning.
Какую схему выбирать — решаете вы. Но мы можем подсказать, в каком случае какая лучше. Если есть вопросы — обращайтесь :-)
>Честно говоря, я не вполне понимаю вектор развития tarantool. То он позиционируется как «мемкеш на стеродидах», то как «lua application server». Судя по рекламе, которую вы даете в своих постах, это интересная наработка, которой мало кто пользуется, потому что не знают, как. Вы лучше определитесь с нишей, соберите набор best practices, дефолтные конфигурации для типовых задач, и т. д. Вот MySQL, сколько бы его ни ругали, используют повсеместно, потому что о нем полно информации.
Тарантул — это СУБД и кэш в одном флаконе. Он обладает почти всеми основными фичами СУБД (транзакции, репликация, персистенс, хранимые процедуры, таблицы, вторичные индексы) и всеми свойствами кэшей (быстро работает, большой QPS, маленький latency). И это позволяет отказаться от схемы база + кэш и просто применять Тарантул. Что дает лучшую целостность данных, решает проблему холодного старта и самое главное — дает огромную пропускную способность на запись данных.
Т.е. Тарантул — это лучшая из нам известных замена база + кэш на единую базу, с, еще раз, улучшением при этом основных параметров работы (скорость, количество серверов, целостность данных).
Но мы прислушаемся к вашей критики и обязательно будем корректировать позиционирование так, чтобы оно было четкое и всем понятное. Спасибо!
>Кстати, а что делать, когда данные перестают влезать в один тарантул? Есть какие-то утилиты/гайды по шардингу или отдел эксплуатации каждый раз все заново изобретает?
Утилиты/гайды есть. Но они пока не совсем полностью отданы в community. Мы сейчас над этим работаем. Плюс, мы прямо сейчас работаем над механизмом автошардинга, чтобы можно было ставить кластер серверов, который бы автоматом бы шардился и реплицировался и был бы виден снаружи через коннектор просто как единая база.
>Я же писал, что можно параллельно cat'нуть индексный файл в /dev/null в некоторых случаях. И индекс всегда меньше данных. Взять, к примеру, базу постов хабра: реляционная база, хранящая только индексы по сообщениям и постам будет потреблять на это меньше памяти, чем tarantool, также хранящий в памяти тела сообщений.
Если индекс влезает в 50% памяти, то cat поможет отлично. Если нет — то не совсем, потому по результату работы cat весь индекс доожен попасть в буферный кэш, а потом весь индекс должен по тихоньку считаться из буферного кэша, но так чтобы во время чтения не вытеснить другие части буферного кэша на диск, которые еще предстоит считать. Т.е. если у вас памяти на машине 64G, а индекс весит, скажем 50G, то cat поможет не сильно. А если индекс весит >64G, то cat не факт, что поможет вообще.
Насчет постов хабра — тут надо смотреть. Если все данные весят, скажем 1Tb, а индексы весят, скажем 128G в базе и все работает на одном сервере, скажем MySQL, без шардинга, то тогда все круто и надо применять MySQL. Если же это все хозяйство надо шардить на, скажем 4 сервера с шардами и по 4 реплики на каждый, чтобы тянуло нагрузку и на каждом по, скажем, 32G памяти (как раз 1/4 индекса), то суммарно это будет весить в памяти на всех серверах 32*16 = 512G. В случае тарантула же можно весь 1Tb запихнуть в него на 1 машину и он потянет нагрузку. А поскльку у него memory footprint оптимальнее MySQL, то это реально будет весить 300-400Gb. Т.е. уже меньше потребляет памяти и 1 сервер вместо 16 (ну на самом деле 2 сервера, реплику-то тоже надо на всякий пожарный, но все же 2, а не 16). Это все грубые прикидки — надо смотреть как оно на самом деле.
Но мораль статьи-то простая — если все работает без шардинга и репликаций на 1-2 серверах MySQL и lanetcy устраивает, то все круто и трогать ничего не надо :-)
>А можно с этого момента поподробнее? «Традиционные базы» могут использовать многоядерность современного железа и, как правило, успешно обрабатывают весьма нетривиальные запросы. Для простейших запросов по индексу у MySQL есть handlersocket, который в свое время позволял выжимать десятки тысяч запросов в секунду с одной железки. Более того, если база вдруг начинала «упираться в CPU», то можно было существенно все ускорить заменой железа: даешь больше ядер, получаешь лучшую производительность без каких-либо затрат на поддержку/разработку.
Это все правда. Даже и спорить не буду. Просто, еще раз, если уже стоит много реплик/шардов баз данных и вы хотите сэкономить на железе + улучшить латенси, то надо смотреть в сторону тарантула. Если это 1-2 машины, пусть и мощные и вы не хотите экономить на них, или вам некуда их продать/отдать, то тогда пусть все остается на традиционной базе.
>Это, кстати, весьма актуальный вопрос, т. к. я не нашел бенчмарков с простенькими луашками, которые делают, скажем, простенький left join для результата из одной записи.
Должно работать также быстро как и выборка по key-value, если result set это 1 запись и есть индекс. Но вы правы, бенчмарков не хватает. Будем работать над этим. Спасибо! :-)
DjOnline
23.12.2015 17:34Планируется ли какое-то сжатие, в памяти или на диске, как в TokuDB? Для хранения и использования логов это было бы очень полезно.
danikin
23.12.2015 19:40+3Сжатие в disk store очень даже планируется. Кстати, скоро опубликуем тесты по disk store. Он бьет по производительности не только все B-Tree базы данных, но и новомодные движки типа TokuDB и RocksDB.
DjOnline
24.12.2015 11:17И правильно ли я понимаю, что если какие-то значения меняются по 100 раз (например цена товара, или постоянная правка описания), то на диске это будет занимать в 100 раз больше места? Есть какой ручной или фоновый режим очистки этих ненужных записей по типу mysql optimize table,
danikin
24.12.2015 13:46+1Простите, не туда написал комент. Дублирую сюда :)
Да. И поэтому есть снэпшоттинг, который раз в настроенное вами время сбрасывает всю базу на диск. После чего все transaction logs до момента снэпшота можно удалять.
Важное свойство transaction log — это то, что вероятность того, что база данных побьется (перейдет в нецелостное состояние) в случае аварийного падения сервера практически равна нулю. Ибо в снэпшоте уже есть целостное состояние базы на какой-то момент времени, а запись в transaction log происходит строго последовательно, поэтому в самом худшем случае просто откатятся несколько последних изменений (которые, кстати, применились на реплике, поэтому даже в этом случае не будет потери данных). В то время как в традиционных базах данных идет запись постоянно в table space, что в случае аварийного рестарта сервера можно привести к «фаршу» в данных. Фарш, конечно, чинится разными инструментами, но это, во-первых, downtime, а во-вторых, потеря не последних изменений, а просто рандомных данных.
Кроме всего прочего, если не так страшно потерять последние изменения, то можно transaction log выключить и делать лишь периодический снэпшот. В этом случае указанной вами проблемы не будет вообще.
Можете написать про ваш конкретный кейс (если хотите в личку: anikin@corp.mail.ru) и я вам расскажу подойдет ли Тарантул в вашем случае, и если подойдет, то как его настроить.
danikin
24.12.2015 13:45Да. И поэтому есть снэпшоттинг, который раз в настроенное вами время сбрасывает всю базу на диск. После чего все transaction logs до момента снэпшота можно удалять.
Важное свойство transaction log — это то, что вероятность того, что база данных побьется (перейдет в нецелостное состояние) в случае аварийного падения сервера практически равна нулю. Ибо в снэпшоте уже есть целостное состояние базы на какой-то момент времени, а запись в transaction log происходит строго последовательно, поэтому в самом худшем случае просто откатятся несколько последних изменений (которые, кстати, применились на реплике, поэтому даже в этом случае не будет потери данных). В то время как в традиционных базах данных идет запись постоянно в table space, что в случае аварийного рестарта сервера можно привести к «фаршу» в данных. Фарш, конечно, чинится разными инструментами, но это, во-первых, downtime, а во-вторых, потеря не последних изменений, а просто рандомных данных.
Кроме всего прочего, если не так страшно потерять последние изменения, то можно transaction log выключить и делать лишь периодический снэпшот. В этом случае указанной вами проблемы не будет вообще.
Можете написать про ваш конкретный кейс (если хотите в личку: anikin@corp.mail.ru) и я вам расскажу подойдет ли Тарантул в вашем случае, и если подойдет, то как его настроить.
nucleusv
Когда же будет надстройка SQL в Tarantool?
danikin
В процессе :) Следите за обновлениями. Например в нашей группе в Facebook: www.facebook.com/TarantoolDatabase/?fref=ts