Драйвер PHP MongoDB открывает много соединений по медленному запросу
Мы встречаемся со странным поведением в PHP-драйвере MongoDB (v1.3) при выполнении медленного запроса. Драйвер, похоже, продолжает открывать соединения, когда запросы медленные, и я не до конца понимаю, почему. Может быть, у вас, ребята, есть некоторые предложения здесь.
Вот несколько фактов:
- Сайт и база данных работают на одном сервере Ubuntu 13.04
- Сервер высокого класса 8-ядерный сервер 16 ГБ ОЗУ
- MongoDB v2.2.4
- Сайт работает на PHP 5.4
- Apache 2 в качестве веб-сервера
- PHP Mongo драйвер 1.3.x (должен быть самым новым 1.3)
- Сайт использует Doctrine ODM
- Веб-сайт имеет от 50 до 100 одновременных пользователей в любой момент
- ulimit открытые файлы (ulimit nofile) = 64000
Один раз в день истекает запись Memcache и выполняется медленный запрос. Это приводит к тому, что PHP открывает до 800 подключений к MongoDB (обычно у нас есть 10 открытых подключений согласно журналам). Наш веб-сайт почти полностью Memcached, поэтому наша база данных не имеет никакой другой значительной нагрузки. При 800 открытых соединениях веб-сайт сначала загружается за 30 секунд, а затем выдает несколько типов исключений MongoExceptions (слишком много подключений / сокетов).
Это уродливый запрос с группой. Чтобы быть совершенно ясным, мы понимаем, что этот запрос медленный и идиотский, и мы удаляем этот запрос сегодня. Просто не понятно, почему он портит весь сайт. Мы используем Doctrine как уровень абстракции, но это фактический запрос к базе данных на 200 000 документов (3 поля на документ: id/product/date) в соответствии с журналами:
{"group":true,"keys":{"product":1},"initial":{"count":0},"reduce":"function (obj, prev) { prev.count++; }","options":[],"db":"Orders","collection":"History"}
После выполнения запроса его результаты записываются в Memcache на 24 часа. Таким образом, все новые запросы получают его из Memcache, а не из MongoDB. Но, тем не менее, он устанавливает около 800 соединений, проблема не решается сама собой, и через некоторое время веб-сайт больше не отвечает. Открытие этих 800 соединений занимает около 10 минут.
Это похоже на типичное состояние гонки. Запрос не выглядит достаточно тяжелым, чтобы вызвать гонку на этом сервере с этой нагрузкой. Я имею в виду, такое чувство, что не должно.
Итак, вопросы:
- Почему PHP продолжает открывать так много соединений?
- Почему MongoDB не может справиться с этим (это не должно быть большой проблемой, верно?)
- Любые другие предложения о том, что мы должны делать?
- Должен ли я установить таймауты на соединения и запросы, чтобы решить эту проблему или это что-то еще?
Я спрашиваю об этом потому, что наш сайт растет очень быстро, и мы ожидаем, что в будущем будет больше трафика и нагрузки на MongoDB.
Заранее большое спасибо!
1 ответ
Учитывая, что вы вызываете group
Команда вместо выполнения основного запроса на чтение, вы также можете бороться с интерпретатором JavaScript в MongoDB 2.2. Только в версии 2.4 интерпретатор JavaScript был улучшен для поддержки одновременного выполнения. Если каждая из этих групповых операций требует оценки JS (по крайней мере, для reduce
функция), вы смотрите на широко распространенное ресурсное голодание.
У меня нет никакого объяснения исключений "слишком много соединений". Даже 800 одновременных подключений значительно ниже предела MongoDB в 20 000 (примечание: это удаляется для 2.6 в SERVER-8943).
Одна идея реорганизовать ваше приложение и избежать group
условием гонки было бы использование одного документа в качестве блокировки для процесса PHP для пересчета результата и пополнения кэша. С помощью findAndModify
, вы могли бы иметь один документ с некоторой строкой _id
(например "Order.History group") и другое active
поле. Когда процесс PHP получает ошибку в кеше и ему нужно пересчитать результат, он может сначала попытаться выполнить findAndModify
и найти подходящий _id
где active
является false
, обновление active
в true
в том же атомном режиме. Только после получения этого документа блокировки он может продолжить group
команда. Другие процессы PHP, которые не могут найти документ блокировки (потому что active
не будет false
) можно поручить немного поспать, вернуть устаревшие данные или отменить веб-запрос.