Лучшие практики оптимизации производительности чтения в наборах реплик
Улучшите чтение в наборе реплик MongoDB с помощью предпочтений чтения, уровня чтения, мониторинга задержки, индексации и контроля тайм-аутов.
Лучшие практики оптимизации производительности чтения в наборах реплик
Наборы реплик MongoDB обеспечивают высокую доступность, но не делают чтение автоматически быстрее. Если ваше приложение отправляет каждый запрос на первичный узел, использует слабые индексы или читает с отстающих вторичных узлов, пользователи почувствуют это как медленные страницы и устаревшие данные.
Хорошая производительность чтения достигается за счет выбора правильного предпочтения чтения, соответствия уровня чтения требуемой согласованности, отслеживания задержки репликации и первоочередного исправления медленных запросов.
Понимание пути чтения в наборах реплик
В стандартной конфигурации набора реплик один участник назначается первичным, обрабатывающим все записи. Остальные участники являются вторичными, которые асинхронно реплицируют данные с первичного. Чтения приложения могут направляться на первичный узел или распределяться по вторичным в зависимости от конфигурации.
Оптимизация чтения означает балансирование между необходимостью немедленной согласованности данных (что часто требует чтения с первичного узла) и желанием снизить нагрузку на первичный узел (путем чтения с вторичных).
1. Стратегическое использование уровней чтения
Уровень чтения определяет степень согласованности данных, необходимую для операций чтения. Установка излишне строгого уровня чтения, когда достаточно ослабленного, является частой причиной задержки чтения, так как может заставить операцию ждать подтверждения от нескольких узлов.
Доступные уровни чтения
MongoDB предлагает несколько уровней чтения, каждый из которых обеспечивает компромисс между задержкой и долговечностью/согласованностью:
| Уровень чтения | Описание | Случай использования |
|---|---|---|
majority |
Возвращает данные, подтвержденные как зафиксированные большинством голосующих узлов. Стандартный по умолчанию. | Чтения общего назначения, требующие высокой долговечности. |
local |
Возвращает последние данные, доступные на узле, с которого производится чтение, независимо от подтверждения записи. | Чтения, которые могут допускать некоторую устаревшую информацию (например, счетчики на панели управления). |
linearizable |
Читает с первичного узла и отражает все записи, подтвержденные до начала чтения. Требует readConcern: "linearizable" и уровня записи majority для соответствующих записей. |
Редкие чтения, которые должны наблюдать последнее подтвержденное состояние, например, проверки владения блокировкой. |
Совет по оптимизации: использование по умолчанию local или majority
Для некритичных чтений (например, загрузка редко обновляемых конфигурационных данных или кэшированных результатов) используйте уровень чтения local на вторичных узлах. Это позволяет избежать задержек синхронизации.
Пример: Установка уровня чтения на уровне сессии
// Установка уровня чтения 'local' для этой конкретной сессии
const session = mongoClient.startSession({ readConcern: { level: "local" } });
// Операция поиска с использованием сессии
db.collection('mydata').find().session(session).toArray();
Предупреждение: Чтение с уровнем
localна вторичном узле может вернуть устаревшие данные относительно первичного узла.
2. Распределение чтений по вторичным узлам
По умолчанию MongoDB направляет чтения на первичный узел. Чтобы масштабировать пропускную способность чтения, необходимо явно направлять чтения на вторичные узлы с помощью настроек Предпочтения чтения.
Понимание предпочтения чтения
Предпочтение чтения определяет, какие участники набора реплик могут удовлетворять запросы на чтение и в каком порядке они должны выбираться.
Распространенные предпочтения чтения включают:
primary: (По умолчанию) Только первичный узел.primaryPreferred: Сначала пробует первичный узел; переключается на вторичный, если первичный недоступен.secondary: Только вторичные узлы. Если вторичные узлы недоступны, операция завершается ошибкой.secondaryPreferred: Предпочитает вторичные узлы; переключается на первичный, если вторичные недоступны.nearest: Выбирает участника (первичного или вторичного) с наименьшей сетевой задержкой до клиента.
Совет по оптимизации: Использование secondaryPreferred или nearest
Для большинства приложений с интенсивным чтением использование secondaryPreferred позволяет распределить нагрузку запросов по всем доступным вторичным узлам, значительно снижая нагрузку на первичный узел.
Если у вас есть географически распределенные серверы приложений, nearest часто является лучшим выбором, так как минимизирует сетевую задержку для клиента, даже если иногда обращается к первичному узлу.
Пример: Подключение с secondaryPreferred
При подключении драйвера приложения укажите предпочтение чтения:
const uri = "mongodb://host1,host2,host3/?replicaSet=rs0&readPreference=secondaryPreferred";
// Или с использованием опций подключения в настройках драйвера
const options = {
readPreference: "secondaryPreferred"
};
3. Управление синхронизацией и задержкой вторичных узлов
Если вы направляете чтения на вторичные узлы, производительность этих чтений полностью зависит от того, насколько быстро вторичные узлы успевают за первичным. Высокая задержка репликации означает, что вторичные узлы обслуживают устаревшие данные, или, если задержка слишком велика, чтения могут завершаться ошибкой или тайм-аутом.
Мониторинг задержки репликации
Всегда отслеживайте разницу optime между первичным и вторичными узлами. rs.status() показывает состояние репликации для каждого участника, а управляемые инструменты, такие как MongoDB Atlas, Cloud Manager или Ops Manager, могут предупреждать о задержке.
rs.status().members.map(m => ({
name: m.name,
stateStr: m.stateStr,
optimeDate: m.optimeDate
}))
Влияние уровня записи на производительность вторичных узлов
Хотя эта статья посвящена чтению, высокие настройки уровня записи могут косвенно влиять на производительность чтения, замедляя первичный узел, что, в свою очередь, приводит к еще большему отставанию вторичных узлов.
Например, требование w: "majority" означает, что клиент не получает подтверждение, пока запись не достигнет большинства голосующих узлов, содержащих данные. Если вторичные узлы медленные из-за нагрузки на диск или сеть, задержка записи приложения может возрасти, и те же перегруженные вторичные узлы могут также обслуживать медленные чтения.
Лучшая практика для уровня записи (косвенная оптимизация чтения): Не снижайте уровень записи только для того, чтобы чтения выглядели быстрее. Выбирайте уровень записи на основе требований к долговечности, затем устраняйте причину задержки: медленные диски, перегруженные вторичные узлы, недостаточный размер oplog, проблемы с сетью или запросы, конкурирующие с репликацией.
4. Индексация и оптимизация запросов
Никакие настройки конфигурации не могут преодолеть плохо написанный запрос. Основополагающим принципом быстрого чтения остается надежная индексация.
Ключевые соображения по индексации
- Покрытые запросы: Проектируйте запросы, которые могут быть полностью удовлетворены индексом без извлечения документов с диска. Это самые быстрые возможные чтения.
- Соответствие индексов: Убедитесь, что индексы соответствуют полям, используемым в ваших предложениях
find(),sort()иprojection(). - Избегайте сканирования коллекций: Всегда проверяйте в профилировщике запросов, что операции чтения используют индексы (
IXSCAN), а не выполняют полное сканирование коллекции (COLLSCAN).
Настройка тайм-аутов запросов
Если приложение обращается к сильно отстающему вторичному узлу, запрос может превысить тайм-аут. Настройте разумные тайм-ауты в вашем приложении, чтобы корректно обрабатывать временную задержку, возможно, переключаясь на первичный узел или повторяя попытку позже, вместо того чтобы зависать на неопределенный срок.
Резюме шагов по оптимизации чтения
Чтобы достичь оптимальной производительности чтения в вашем наборе реплик MongoDB, выполните следующие практические шаги:
- Определите типы чтения: Классифицируйте чтения на те, которым нужны свежие данные с первичного узла, и те, которые могут допускать конечную согласованность с вторичных узлов.
- Настройте предпочтение чтения: Установите строку подключения или параметры сессии для использования
secondaryPreferredилиnearestдля большей части трафика приложения. - Мониторьте задержку: Непрерывно отслеживайте задержку репликации с помощью
rs.status(), метрик драйвера или вашей платформы мониторинга. Если задержка постоянно высока, исследуйте проблемы с оборудованием вторичных узлов или сетью. - Пересмотрите уровни записи: Убедитесь, что уровни записи не замедляют чрезмерно первичный узел, что лишает вторичные узлы свежих данных.
- Тщательно индексируйте: Проверьте, что все часто выполняемые пути чтения используют эффективные индексы.
Масштабирование чтения в наборах реплик работает лучше всего, когда вы честны в отношении устаревания данных. Отправляйте критически важные для пользователя чтения на первичный узел, когда они должны быть актуальными, используйте вторичные узлы для аналитики или панелей управления, которые могут допускать задержку, и продолжайте измерять планы запросов и состояние репликации по мере изменения трафика.