пятница, 17 мая 2013 г.

Сотрудничество Mail.ru и Obsidian Entertainment.



Друзья!
Ведущая студия разработчиков компании Mail.Ru Group — Allods Team — будет сотрудничать с известной американской компанией Obsidian Entertainment. Она, в свою очередь, будет работать над MMORPG Skyforge — онлайн-игрой нового поколения от Allods Team.

О сотрудничестве двух гигантов объявил лично президент Obsidian Entertainment Фергюс Уркхарт на своем выступлении в рамках Конференции разработчиков игр-2013, говорится в сообщении Mail.Ru Group. Г-н Уркхарт — одна из ключевых фигур легендарной студии Black Isle Studios. Она известна тем, что принимала участие в создании хитов золотого века CRPG — Fallout, Baldur’s Gate, Planescape: Torment, Icewind Dale и других.

«Skyforge — очень интересный и перспективный проект, и я рад, что Obsidian Entertainment принимает участие в работе над этой игрой», — заявил г-н Уркхарт. Он уверен, что сотрудничество с Allods Team станет отличным опытом. «Skyforge — самый масштабный российский проект. Теперь же к разработке Skyforge присоединятся и зарубежные эксперты, фактически создавшие стандарты жанра», — подчеркнул руководитель студии Allods Team Олег Шпильчевский.

Многопользовательская ролевая онлайн-игра Skyforge — новый проект ААА-класса студии Allods Team. Действие игры разворачивается во вселенной, где мирно уживаются достижения научно-технического прогресса и магия. Главными особенностями Skyforge станут беспрецедентный уровень графики и оригинальная система развития персонажа. Проект был впервые представлен на Конференции разработчиков игр-2012 и сразу же завоевал приз зрительских симпатий.

Нагрузочное тестирование в Skyforge (доклад)

Меня зовут Александр Акбашев. Я – QA-инженер Allods Team на проекте Skyforge. В зону моей ответственности входит организация тестирования сервера нашей игры, и именно о тестировании сервера будет данная статья. В мае я делал доклад на КРИ, который превратился в статью из двух частей. Эта статья – первая из них. 



Skyforge с высоты птичьего полета


Технологический стек Skyforge таков: клиент игры написан на C++, сервер игры разрабатывается на Java, много различных полезных утилит и скриптов написано на Python. Еще у нас есть инструменты для дизайнеров, написанные на C#, но в статье не будет больше ни слова про C#. :)

Что же такое боты и зачем они нужны?


Во время разработки проекта Аллоды Онлайн перед командой сервера встал серьезный вопрос: как проводить комплексные и нагрузочные тесты сервера? Решили написать для этого ботов – автономное клиентское приложение, эмулирующее поведение реального игрока. С тех пор, конечно, много воды утекло. Боты, используемые в Skyforge, уже мало похожи на самых первых хиленьких ботов. Назначение и инфраструктура тестов, проводимых с их использованием, также эволюционировали: теперь боты помогают нам получать огромное количество серверных характеристик для анализа. Всё благодаря тому, что на нашем проекте в процесс непрерывной интеграции включены ботовые тесты.

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

Что же собой представляют боты?


Наши боты реализованы так называемым «методом белого ящика»: у клиента, написанного на C++, удален интерфейс, а вместо него добавлен «мозг», написанный также на C++. Более того, общая база кода у клиента и ботов позволяет тестировать достаточно низкоуровневые вещи в самом клиенте.

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

Пример простейшего графа переходов: бот появляется на карте, бежит, видит монстра и убивает его. Если есть трофеи – поднимает, если нет – бежит дальше.



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

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

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

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

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

Тесты решают


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

Проверка сервера

Благодаря непрерывной интеграции, несколько раз в сутки мы собираем свежую версию сервера. Однако нет уверенности в том, хорошая вышла сборка или нет и можно ли её использовать. Тестировщики не всегда могут сразу же обновить клиент и проверить новую сборку. Для этого сейчас используется smoke test, который проверяет свежую сборку с помощью бота. Это достаточно простой бот: он заходит на сервер, ищет монстра и убивает его. Если все прошло гладко – сервер рабочий и на нем можно играть.

Репетиция главного теста

У нас есть ночной тест, который идет 8 часов и позволяет собрать очень много ценной информации. К сожалению, итерации теста очень дорогие, и, чтобы не пропускать запуски этого теста из-за ошибок, которые можно исправить за 5 минут, проводится отдельный дневной тест. Он запускается на той же версии кода и тех же данных, что и ночной, но всего на час. Таким образом, в течение дня проводятся репетиции ночного запуска. При необходимости мы успеваем исправить различные ошибки до конца рабочего дня. Прохождение часового теста на 99% гарантирует прохождение теста ночного.

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

Проверка контента

Контент Skyforge достаточно объемный. У нас очень много объектов игрового мира. Поэтому, когда запускается ночной тест, не всегда понятно, какие элементы дизайна приводят к дополнительной нагрузке. Не всегда понятно, почему на одном гейммеханическом сервере с определенным набором карт все хорошо, а на другом и с другим набором карт все плохо. Карта ли виновата, количество игроков или фаза луны GC? В результате были добавлены отдельные тесты по картам. Они позволяют сравнивать игру в стерильных условиях. Например, если взять три карты и прогнать их на одинаковых версиях кода и данных, мы получим разные сравнительные характеристики и температурные карты, на которых видно, где могут возникнуть критические места. Эти тесты, наверное, самое полезное, что у нас есть для профилирования игрового контента.


Пример температурной карты (на шкале – некий performance rating, вычисляемый в ботах)

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

Самый главный тест

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

Заключение


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

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

Надеюсь, данная заметка про ботов была интересна сообществу. Спасибо за внимание!

Видео


Ниже представлено видео  с конференции КРИ-2013

Это вторая статья, посвященная тестированию сервера Skyforge. На всякий случай напоминаю, что Skyforge – это MMORPG, сервер которой рассчитан на сотни тысяч игроков и написан на Java.
В отличие от первой части, где речь идет о роли ботов, эта статья рассказывает о нагрузочном тестировании и метриках. 




Нагрузочное тестирование


Читатели Хабра знают, что нагрузочное тестирование – это сбор показателей производительности программного обеспечения с целью проверки соответствия требованиям.

Наши требования к подобным тестам достаточно просты: нагрузка на сервере в «боевых» условиях должна быть в пределах нормы, а user experience не должен страдать. Поэтому при организации нагрузочного тестирования нужно в первую очередь определить, какие условия считать «боевыми». Например, для нас это означает следующее: на двух серверах игровой механики, одном сервере баз данных и одном сервере с разными вспомогательными сервисами резвятся 5000 игроков одновременно. Во вторую очередь нужно определить, какая нагрузка считается нормальной. Мы считаем, что сервер справляется с нагрузкой, если он проводит в обработке меньше 20 миллисекунд за серверный «тик».

У нас сервисно-ориентированная архитектура — это значит, что топология сервисов во время теста должна совпадать с топологией в «боевых» условиях. Объем контента тоже должен быть приближен к объему, который будет на «боевых». В общем, нагрузочное тестирование – это production в миниатюре, где вместо реальных пользователей – боты.

Организация тестирования


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

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

Ночной ботовый тест


Основной нагрузочный тест, во время которого проверяется весь контент, идет ночью в течение 8 часов. Почему выбрана именно такая продолжительность? Экспериментально было замечено, что самые серьезные ошибки возникают на рубеже 4,5–6 часов, и чтобы их найти, мы просто вынуждены проводить такие длительные тесты. Именно на этот интервал приходится FullGC-пауза (подробнее об этом явлении), борьба с которой также является целью тестов. В наших планах реализовать постоянные тесты длительностью 56 часов в течение выходных. Но пока, к сожалению, это только планы.

Сервера


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

Важнейший этап нагрузочного тестирования – выбор и подготовка серверов для тестов. Так как у нас нет боевых серверов, мы выбирали максимально приближенные к тем, которые будут на момент релиза игры. Иначе следовало бы использовать сервера, аналогичные «боевым».
Сервера должны быть настроены именно так, как будут использоваться в «боевом» режиме. К слову, мы рассматриваем возможность использования технологии Thread Affinity, которая позволяет закрепить отдельные процессорные ядра за конкретными потоками, например за потоками игровой механики. И если эта технология «выстрелит», это будет означать, что данная настройка должна быть включена при проведении нагрузочного тестирования. Иначе поведение сервера под нагрузкой в тестовом окружении и в реальности будет значительно отличаться.

Также нужно помнить, что на современных серверах есть режимы работы «зеленая среда» или «экономия электричества». Рекомендую отключить их сразу, выставляя процессоры на полную производительность, потому что «в бою» серверам будет не до отдыха, а чинить несуществующие проблемы производительности во время теста в экорежиме – это плохая затея.

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

Полный доступ подразумевает также, что вы там один. Если вы проводите нагрузочное тестирование, нужно убедиться, что на сервере нет вашего коллеги, занятого тем же самым, а также выяснить, не происходит ли backup базы данных. Нужно быть на 100% уверенным, что вы там один.

Снимаемая статистика


Самый простой и самый наглядный способ проанализировать данные по нагрузке – это визуализировать их. Мы используем для построения графиков библиотеку Highcharts, которая уверенно вытеснила jqPlot. Давайте посмотрим на примеры.

График нагрузки




Подобный график я вижу каждое утро. Он позволяет отслеживать нагрузку. Нагрузка на сервере – это величина, равная отношению времени в миллисекундах, проведенному в обработке данных за 1 «тик» сервера, к 20 миллисекундам. Если на графике показатель больше единицы (больше нормы), значит, все плохо, если меньше – все хорошо. 

График использования памяти




Это общий график использования памяти. Он позволяет приблизительно оценить работу «сборщика мусора».

График работы GC




Наверное, это один из наиболее важных графиков для Java-серверов. Вряд ли игроки не заметят паузы во время полной сборки мусора. На нем по оси Y отмечена продолжительность той или иной фазы работы сборщика мусора.
Ключи, которые мы используем для сбора данных о работе GC

График остановок в Safepoint




Safepoint – это точка в Java Virtual Machine, где происходит остановка каждый раз, когда нужно собрать stack trace или провести сборку мусора. Подробнее о safepoints можно почитать здесь. На данном графике изображено, сколько миллисекунд за 1 минуту сервер проводит в этих точках.

График датабазных операций




А вот прекрасные «шарфики», которые были сделаны по заказу Randll, чей доклад о базах данных вы могли прочесть ранее. Эти «шарфики» позволяют оценивать, какие database-операции и в каком количестве у нас выполняются.

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

Для этих же целей с помощью API Yourkit'a мы сделали автоматическое снятие дампа памяти и профиля нагрузки в конце теста. Сейчас они анализируются только в ручном режиме, но планируется автоматизировать и этот процесс.

Вишенка


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



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

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

Проблемы


У нагрузочных ночных тестов очень дорогие итерации. Стоимость каждого теста – день. Конечно, мы прикладываем все усилия, чтобы тест проходил каждую ночь, но в реальности успешно он проходит реже, а итерации становятся еще дороже. Любая поломка в любом узле инфраструктуры способна сорвать проведение теста.

Заключение


Регулярное нагрузочное тестирование с применением всевозможных статистик – это лучшее средство для здорового сна разработчиков. Сейчас я не представляю, как можно жить без такого рода тестирования, потому что благодаря ему каждое утро я вижу, где у нас полный «аврал» могут возникнуть проблемы. Это помогает нам двигаться в правильную сторону. Спасибо за внимание!

Базы данных в онлайн играх. От Аллодов до Skyforge

Когда говорят про разработку игр, обычно речь идет о шейдерах, графике, AI и т.д. Крайне редко затрагивается серверная часть игровых проектов, а ещё реже — базы данных. Исправим это досадное недоразумение: сегодня я расскажу о нашем опыте работы с базами данных, который мы приобрели в ходе разработки Аллодов Онлайн и нашего нового проекта Skyforge. Обе эти игры — клиентские MMORPG. В первой зарегистрировано несколько миллионов игроков. Вторая разрабатывается студией в строжайшей секретности в недрах Allods Team. 

Меня зовут Андрей Фролов. Я ведущий программист Allods Team и работаю в команде сервера. Мой опыт разработки — почти 10 лет, но в игры я попал только в октябре 2009. В коллективе я уже больше трёх лет, с марта 2010. Начинал работу на Аллодах Онлайн, а сейчас на Skyforge. Занимаюсь всем, что так или иначе связано с сервером Skyforge и базами данных. В этой статье я расскажу о базах данных в онлайн-играх на примере Аллодов и Skyforge.



Если вы не очень любите читать, предлагаю пролистать статью до конца и посмотреть видеозапись моего доклада с Конференции разработчиков игр. Тем же, кто останется в посте, положен бонус — важное дополнение к докладу в виде рассказа о гибриде NoSQL-JSON и реляционной модели данных.

Эволюция


Игровая база — это типичная OLTP-система (много маленьких и коротких транзакций). Но использование баз данных в играх несколько отличается от их использования в вебе, банках и прочем энтерпрайзе. Во-первых, это связано с тем, что модель данных в играх существенно сложнее, чем в банках. Во-вторых, большинство программистов в геймдеве вышли из сурового мира C++, прихватив с собой бороду и любовь к бинарной запаковке. Абсолютно все они, если им надо сохранить персонажа на диск, первым делом хотят сериализовать его в файл. Именно так всё и начиналось в Аллодах Онлайн. Программисты сделали файловое хранилище, но быстро одумались и переписали всё под MySQL. Проект успешно запустили, люди играли, опыт копился.

Что у нас было в Аллодах:

  • Java, MySQL
  • Шарды. И каждый из них был рассчитан на некое ограниченное число игроков, находящихся онлайн
  • Это количество игроков выдавало примерно 200 транзакций в секунду
  • Сервис, который работает с базой, был однотредовым, т.к. этого было достаточно для такого количества транзакций


Через несколько лет стартовал Skyforge. У Skyforge были совсем другие требования, и поэтому пришлось пересмотреть наш подход к работе с базами.

Вот эти требования:

  • У нас больше нет шардов. У нас один большой единый мир
  • Мы рассчитываем наш сервер на 100000 игроков, находящихся онлайн, а возможно и больше
  • По нашим прикидкам, эти игроки должны выдавать более 7000 транзакций в секунду
  • Мы всё так же пишем на Java, но с MySQL мы перешли на PostgreSQL


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

Архитектура


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



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

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


Аватар как кэш


Эта нехитрая схема имеет один важный момент. Наш аватар нужен для работоспособности игровой механики, но как побочный эффект он же фактически является кэшем над базой данных. Все запросы вида «Покажи мне предметы этого игрока» или «А где находится аватар Василий?» обслуживаются этим аватаром. Когда игрок входит в игру, мы загружаем его аватара, и он живёт до тех пор, пока игрок онлайн. Такой нехитрый трюк позволяет снять с базы большую часть запросов на чтение и даже часть запросов на запись.

Мы делим все данные игрока на две категории:

  • Неважные данные, потерю которых игрок может пережить. К ним относятся позиция на карте, уровень здоровья и т.п. Такие данные мы «накапливаем» у аватара и периодически, а также один раз при выходе из игры скидываем в базу.
  • Важные данные, потеря которых будет для игрока болезненной. К ним относятся предметы, деньги, квесты и подобные вещи. С этими данными всё гораздо сложнее. Мы стараемся сделать так, чтобы игрок не потерял эти данные никогда, т.к. на них он потратил очень много времени и сил. Поэтому их надо сохранять в базу синхронно. Именно сохранение важных данных и создаёт основную нагрузку на нашу базу.


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

  • Сервер игровой механики присылает запрос к сервису базы данных «взять предмет ХХХ».
  • Сервер БД выполняет необходимые проверки (достаточно ли в сумке места, не нужно ли «застекать» эту вещь и так далее). После этого он сохраняет обновлённое состояние сумки аватара в базу.
  • Только если сохранение прошло успешно, аватару отсылается обновление состояния его сумки. Аватар, в свою очередь, отправляет обновления в игровой клиент. В результате игрок увидит, что у него появился предмет, только тогда, когда предмет надёжно сохранён в базу.


PostgreSQL


В Skyforge мы отказались от MySQL по совокупности причин, перечисленных ниже.

  • В MySQL все фичи размазаны по различным движкам хранения. Что-то было в InnoDB, что-то в MyISAM, что-то в движке MEMORY. Это сильно усложняло жизнь.
  • В MySQL сломан механизм распределённых транзакций, который нам очень хотелось использовать. Разработчики MySQL обещали его починить только к шестой версии, которой нет ещё даже в планах.
  • В MySQL был сломан механизм группового коммита. Его починили в версии 5.5, и этот пункт уже не актуален.
  • В MySQL на самом деле довольно много багов, странно работающих фич и весьма ограниченный оптимизатор запросов.


PostgreSQL решал все эти проблемы, взамен давая только проблему с автовакуумом. Базу NoSQL мы решили не брать, т.к. у нас очень высокие требования к консистентности данных, а ни одна в мире NoSQL-база не может консистентно и транзакционно переложить предмет от одного аватара другому. Eventual consistency в этом случае нас не очень устраивал, т.к. это сильно портит game experience.

Гибридная схема данных


То, что мы используем PostgreSQL, ещё не значит, что мы должны хранить данные в реляционном виде. Реляционную базу можно использовать в качестве key-value storage

Полностью реляционная модель нас не устраивает, т.к. содержит в себе несколько узких мест, критичных для производительности. Например, у нас есть игрок, а у него есть квесты. Игрок может выполнить сотни квестов, и при входе в игру нам надо будет их все показать. Если пользоваться реляционной моделью, придётся делать запрос на выдачу сотни строк из базы, а это медленно. С другой стороны, нереляционная модель имеет множество минусов: отсутствие констрейнтов, невозможность обновить данные частично и т.п.

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

Выглядит это примерно так:

# select * from avatar limit 1;
id | 144115188075857124
position |
{"point":{"x":7402.2793,"y":6080.2197,"z":51.42402},"yaw":0.0,"map":"id:132646944","isLocal":false,"isValid":true}
death_descriptor | {"deathTime":-1,"respawnTime":-1,"sparkReturnDelay":-1,"recentDeathTimesArray":[]}
health | 1250
mana_descriptor | {"mana":{"8":300}}
avatar_client_info | \x
character_race_class_res_id | 26209282
character_sex_res_id | 550995
last_online_time | 1371814800726


Такая схема позволяет нам использовать все бонусы реляционной модели и компенсировать её узкие места нереляционными вставками на JSON. Кроме того, PostgreSQL 9.3 позволяет делать запросы по JSON. Таким образом, мы получаем эдакий коктейль два в одном — PostgreSQL и MongoDB по цене PostgreSQL.

Virtual shards


Чтобы справиться с нагрузкой на запись, мы шардируем нашу базу данных по аккаунтам. Для этого мы в ID сущности кодируем номер шарда, на котором живёт аватар, и аккаунт.

ID состоит из двух частей: первый байт — номер шарда, остальные — ID сущности внутри шарда.

long id = <shard_id> <account_id>

В игре существует несколько десятков сервисов баз данных. Каждый из них однотредовый и работает со своей маленькой базой. Несколько таких маленьких баз мы помещаем на один физический сервис. Такой подход используют многие интернет-гиганты, называется он virtual shards и решает проблему перебалансировки. Допустим, у нас есть два физических сервера, и на них лежит по 15 маленьких шардов. Если у нас вдруг появилось много пользователей и всё стало тормозить, мы просто покупаем ещё один сервер и перекладываем на него по 5 шардов со старых серверов. И вместо схемы 2х15 мы очень просто получаем схему 3х10. Таким образом, можно очень просто осуществлять перебалансировку без необходимости распиливать данные внутри базы данных.

SSD


Настоящей серебряной пулей в борьбе за перформанс для нас стал SSD. Твердотельные диски позволили нам записывать тысячи транзакций в секунду на диск без дорогостоящих RAID-массивов. И, что для нас очень важно, мы записываем данные на диск синхронно, без отложенных коммитов и отключения fsync.

Небольшое отступление. Наша игровая база данных на самом деле не очень большая, около 200 ГБ, и поэтому может уместиться на один SSD. Но если подойти к проблеме производительности базы с умом, с помощью SSD можно ускорить и бóльшие базы. На SSD достаточно положить файлы WAL PostgreSQL, в которые идёт основная нагрузка на запись, а остальные данные можно разместить на более медленных дисках. В результате от использования SSD может выиграть практически любой проект!

Перекрёстная реплика


Есть четыре факта, комбинация которых привела нас к интересному решению.

  • PostgreSQL предоставляет очень интересную фичу — синхронную репликацию.
  • Большой прирост производительности от SSD позволяет несколько расслабиться и не приносить надёжность в жертву скорости.
  • Для резервирования и других служебных целей каждому серверу БД необходим сервер-реплика.
  • Накат реплики тратит существенно меньше ресурсов, чем выполнение тех же самых операций на мастере. Ресурсы репличного сервера практически всегда простаивают.


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



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

Видео

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

Related Posts Plugin for WordPress, Blogger...