Последнее изменение: 11:08:19 13 июня 2019
Эта статья посвящена записи оптимизации производительности игрового сервера. Методы, используемые здесь, включают: MongoDB (движок MMAPv1), PHP.
С увеличением числа импортируемых игр количество документов в одной коллекции превысило 400 Вт. Часто встречаются карточки с отзывами игроков. Особенно после миграции сервера (с 16 Г 8 ядер на 8 Г 4 ядер), Carton стал более серьезным и начал исследовать проблему.
- используйте первую команду
topдля просмотра общей ситуации, в это время загрузка процессора невелика.%waЭто соотношение поддерживается на уровне около 40%. Предварительное заключение состоит в том, что дисковый ввод-вывод слишком высок. - Используйте
iotopКоманда просматривает статистику ввода-вывода с точки зрения детализации процесса и обнаруживает, что процесс MongoDB считывает данные на полной скорости. Используйте собственную команду MongoDB
mongostat, поля обнаружениясбоевдлятся до 200, что означает, что число сбоев доступа в секунду достигает 200, то есть данные извлекаются из физической памяти и помещаются в подкачку.Поскольку пространство подкачки не задано, он не может передать команду
vmstat, чтобы проверить, выполняется ли манипуляция подкачкой- Выполните в оболочке Mongo
db.currentOp()Подтвердите, что существует большое количество операций, которые в настоящее время выполняются слишком долго
На данный момент проблема в основном решена: большое количество запросов (разумных или нет) заставляет MongoDB непрерывно выполнять операции ввода-вывода с диска, и из-за меньшего объема памяти (по сравнению с предыдущими 16G) запрашиваемые данные кэша постоянно удаляются из памяти.
Уменьшение размера одной коллекции
Этот шаг в основном направлен на несколько очень больших коллекций в библиотеке, и данные в этих коллекциях не важны и их легко удалить.
Здесь мы возьмем в качестве примера таблицу магазинов, в которой сохраняются данные различных магазинов для каждого игрока. После удаления данных незарегистрированных игроков в течение более чем N дней размер коллекции уменьшается с 244 до 3G.
За счет уменьшения размера коллекции можно не только повысить эффективность запросов, но и ускорить ежедневное резервное копирование базы данных.
Медленный Анализ Журнала
Необходимо включить медленные журналы
profile=1 slowms=300
Подтвердите все медленные журналы один за другим и проанализируйте проблемы с выполнением инструкций
use xxx;
db.system.profile.find({}, {}, 20).sort({millis:-1});На данном этапе основное внимание уделяется подтверждению поля статистики выполнения( execstatus ) на промежуточном этапе( этап ) Это полномасштабное сканирование (COLLSCAN), которое является самым большим убийцей производительности.
Добавление/Изменение Индекса
Благодаря медленному анализу журнала установлено, что большинство полных сканирований таблиц связано с:
- Регулярная статистика рейтингов
- Игровой логике необходимо обновить все соответствующие документы в определенных коллекциях
- …
Для того чтобы решить эти проблемы, мы можем увеличить индекс.
Пример 1: Рейтинг игроков
// Query statement
db.User.find({gm:0}, {}, 100).sort({Lv:-1, Exp:-1});
// Remove old index and add composite index
db.User.createIndex({Lv:-1, Exp:-1}, {background:true});
db.User.dropIndex({Lv:-1})Необходимо добавить индекс производственной среды {background: true} В противном случае во время индексации возникнет большая перегрузка.
Кроме того, перед удалением старого индекса не забудьте создать новый индекс, чтобы избежать большого количества медленных запросов.
примите объясните("allPlansExecution") Как может видеть анализатор запросов, начальным этапом в настоящее время является IXSCAN, который сканирует индекс.
Пример 2. Обработка заголовка игрока
// Query statement
db.User.find({TitleData:{$exists:true}});
// Increase sparse index
db.createIndex({TitleData:1}, {sparse:true, background:true});Используется разреженная индексация, поскольку у большинства игроков нет титулов.( Данные заголовка Поля). При использовании разреженного индекса индексируются только документы с этим полем. Для сравнения, Пользователь В коллекции по умолчанию _id_ Размер индекса 138 МБ, только что установленный разреженный индекс Title Data_1 Размер составляет всего 8 КБ (минимальный размер).
Изменить Инструкцию Запроса
Поскольку код проекта многоручный, а некоторые люди неопытны, проблемы с производительностью не учитываются при написании кода.
Поэтому нам нужно изменить часть кода сервера, что является тяжелой работой, по одному изменять, относится к оптимизации бизнес-кода.
Пример 1: Проверка игроков
// Original Query Statement: Reward for Full Service
db.User.find({});
// Modified: Screen logged in only the last 30 days, using the existing index {LastVisit:-1}
Db. User. find ({LastVisit: {$gt: timestamp 30 days ago})Пример 2: Информация о членстве в Гильдии
// Original Query Statement: Search for a specified guild member in the User collection
db.User.find({GuildId:xx});
// Modified: Get guild member data one by one using Guild Members list already in Guild collection
db.Guild.find({Id:xx}, {Id:1, GuildMembers:1}, 1);
db.User.find({Id:{$in: [xx, xx, xx]}})Таймер добавить блокировку
Объем данных на раннем сервере невелик, таймер каждой минуты может работать плавно в течение 1 минуты, но как только медленный запрос (до оптимизации было 10 минут), последний таймер не сработал, снова появился следующий таймер, большое количество медленных операторов запроса, скопившихся в MongoDB, привело к тому, что вся база данных была утащена вниз, прямо в снег. Авария. Это самая прямая причина для обратной связи игроков с Картоном.
Хотя после описанной выше оптимизации запрос не будет выполняться более одной минуты, возможно, что несколько запросов будут накапливаться более одной минуты.
Чтобы избежать укладки сценариев таймера, необходима блокировка, чтобы избежать проблем.
Конкретные схемы блокировки включают:
- memcached
- редис
Все очень просто.
Избегайте тайм-аута клиента
Таймер обычно используется для выполнения некоторых трудоемких операций. В дополнение к вышеупомянутой проблеме блокировки существует еще одна, которую нельзя игнорировать: Тайм-аут клиента .
Некоторые операции с MongoDB в PHP по умолчанию занимают 30 секунд, например find() Как только операция превышает 30 секунд, возникает “исключение тайм-аута”, но оператор все равно выполняется в экземпляре MongoDB.
Поскольку задача синхронизации не завершена, следующий таймер продолжит выполнять ту же операцию, когда она наступит.
Решение простое. Возьмем в качестве примера PHP-код
$mongo->selectCollection('xx')->find([...])->timeout(-1);- Замена механизма хранения: Замените MMAPv1 проводным триггером
- С помощью кластера (или простого ведущего устройства) экспорт данных и резервное копирование данных выполняются непосредственно из библиотеки. Кроме того, код логики на стороне сервера изменяется, и некоторые медленные запросы применяются к подчиненной библиотеке (в основном нет).