Потеря соединения с сервером MySQL во время запроса по случайным простым запросам
Я использую ruby в стеке rails, наш сервер mysql является отдельным, но размещается на том же сайте, что и наши серверы приложений. (Мы попытались заменить его на другой сервер MySQL с двойной спецификацией, но улучшения не было.
в рабочее время мы получаем несколько таких запросов без какого-либо конкретного запроса.
ActiveRecord::StatementInvalid: Mysql2::Error: Lost connection to MySQL server during query
большинство неудачных запросов действительно просты, и, похоже, между одним запросом и другим нет никакой закономерности. Все это началось, когда я обновил Rails 4.1 до 4.2.
Я в растерянности относительно того, что попробовать. Наш сервер баз данных занимает менее 5% процессорного времени в течение дня. Я получаю отчеты об ошибках от пользователей, у которых случайные взаимодействия заканчиваются неудачей из-за этого, так что это не запросы, которые выполнялись часами или что-то в этом роде, конечно, когда они повторяют ту же самую вещь, это работает.
Наши серверы настроены на cloud66.
Итак, вкратце: наш сервер mysql по какой-то причине отключается, но не из-за нехватки ресурсов, это также совершенно новый сервер, когда мы мигрировали с другого сервера, когда возникла эта проблема.
это также случается со мной на localhost при разработке функций, поэтому я не верю, что это проблема загрузки.
Мы запускаем следующее:
- рубин 2.2.5
- рельсы 4.2.6
- mysql2 0.4.8
ОБНОВЛЕНИЕ: согласно первому ответу ниже я увеличил нашу переменную max_connections до 500 прошлой ночью, и подтвердил увеличение с помощьюshow global variables like 'max_connections';
Я все еще получаю сброшенное соединение, первое сегодня было сброшено всего несколько минут назад....ActiveRecord::StatementInvalid: Mysql2::Error: Lost connection to MySQL server during query
Я побежал select * from information_schema.processlist;
и я получил 36 строк назад. Значит ли это, что на моих серверах приложений в тот момент было 36 подключений? или процесс может быть несколькими соединениями?
ОБНОВЛЕНИЕ: я только установил net_read_timeout = 60 (это было 30 раньше), я посмотрю, поможет ли это
ОБНОВЛЕНИЕ: Это не помогло, я все еще ищу решение...
Вот мой Database.yml с удаленными учетными данными.
production:
adapter: mysql2
encoding: utf8
host: localhost
database:
username:
password:
port: 3306
reconnect: true
Последнее обновление: мы решили эту проблему, найдя способ достичь наших целей без разветвления. Но причиной проблемы стала разветвленность.
7 ответов
Соединение с MySQL может быть прервано несколькими способами, но я бы порекомендовал пересмотреть ответ Марио Карриона, так как это очень мудрый ответ.
Кажется вероятным, что соединение нарушено, потому что оно используется совместно с другими процессами, вызывая ошибки протокола связи...
... это может легко произойти, если пул соединений привязан к процессу, что, как я полагаю, в ActiveRecord, означает, что одно и то же соединение может быть "проверено" несколько раз одновременно в разных процессах.
Решение состоит в том, что соединения с базой данных должны быть установлены только ПОСЛЕ fork
выписка на сервере приложений.
Я не уверен, какой сервер вы используете, но если вы используете warmup
особенность - нет.
Если вы выполняете какие-либо вызовы базы данных до первого запроса сети - не делайте.
Любое из этих действий может потенциально инициализировать пул соединений до fork
Это происходит, в результате чего пул соединений MySQL распределяется между процессами, а система блокировки - нет.
Я не говорю, что это единственная возможная причина проблемы, как говорит @sloth-jr, есть и другие варианты... но большинство из них, по вашему описанию, менее вероятно.
Примечание:
Я запустил команду select * from information_schema.processlist; и я получил 36 строк назад. Означает ли это, что на моих серверах приложений в тот момент было 36 подключений? или процесс может быть несколькими соединениями?
Каждый процесс может содержать несколько соединений. В вашем случае у вас может быть до 500X36 соединений. (см. редактирование)
В общем, количество соединений в пуле часто может быть таким же, как количество потоков в каждом процессе (оно не должно быть меньше количества потоков, иначе конфликт может замедлить работу). Иногда полезно добавить еще несколько, в зависимости от вашего приложения.
РЕДАКТИРОВАТЬ:
Я прошу прощения за игнорирование того факта, что счетчик процессов ссылался на данные MySQL, а не на данные приложения.
Число процессов, которое вы показали, представляет собой данные сервера MySQL, которые, кажется, используют схему ввода-вывода для каждого потока. Данные "Процесс" фактически учитывают активные соединения, а не фактические процессы или потоки (хотя они также должны преобразовываться в число потоков).
Это означает, что из 500 возможных подключений на процессы приложения (т. Е. Если вы используете для своего приложения 8 процессов, это будет 8X500=4000 разрешенных подключений), ваше приложение пока открыло только 36 подключений.
Это указывает на ошибку тайм-аута. Обычно это общий ресурс или ошибка соединения.
Я бы проверил ваш конфиг MySQL для максимальных соединений на консоли MySQL:
show global variables like 'max_connections';
И убедитесь, что количество пулов соединений, используемых Rails database.yml, меньше этого:
pool: 10
Обратите внимание, что database.yml отражает количество соединений, которые будут объединены одним процессом Rails. Если у вас есть несколько процессов или другие серверы, такие как Sidekiq, вам нужно добавить их вместе.
Увеличьте max_connections, если необходимо, в конфигурации вашего сервера MySQL (my.cnf), предполагая, что ваш комплект справится с этим.
[mysqld]
max_connections = 100
Обратите внимание, что другие вещи тоже могут блокировать, например, открывать файлы, но смотреть на соединения - хорошая отправная точка.
Вы также можете отслеживать активные запросы:
select * from information_schema.processlist;
а также мониторинг медленного журнала MySQL.
Одной из проблем может быть длительная команда обновления. Если у вас медленная команда, которая влияет на множество записей (например, на всю таблицу), она может блокировать даже самые простые запросы. Это означает, что вы можете увидеть тайм-аут случайных запросов, но если вы проверите состояние MySQL, настоящая причина - другой длительный запрос.
Вещи, которые вы не упомянули, но вы должны посмотреть:
- Вы используете единорога? Если да, вы подключаетесь и отключаетесь в
after_fork
а такжеbefore_fork
? - Является
reconnect: true
установить в вашей конфигурации database.yml?
Если у вас включен кеш запросов, сбросьте его, и он должен работать.
RESET QUERY CACHE;
На первый взгляд кажется, что ваш веб-сервер поддерживает сеансы mysql открытыми, и иногда пользователь сталкивается с таймаутом. Попробуйте отключить поддержку сеансов mysql. Это будет боров, но вы используете только 5% ...
другие советы:
Включите mysql "Slow Query Log" и посмотрите.
написать короткий сценарий, который извлекает и регистрирует список процессов mysql каждую минуту и перепроверять журнал с таймаутами
посмотрите на размер пула в вашем подключении к БД или установите его! http://guides.rubyonrails.org/configuring.html должен быть равен максимальному количеству подключений, которое любит иметь mysql!
Удачи!
Узнайте, ограничена ли ваша база данных с точки зрения нескольких соединений. Поскольку обычно предполагается, что база данных SQL имеет более одного активного соединения. (Обратитесь к вашему сетевому провайдеру)
Не могли бы вы опубликовать некоторые из ваших запросов? В документации MySQL есть что сказать об этом: https://dev.mysql.com/doc/refman/5.7/en/error-lost-connection.html TL; DR:
- Проблемы с сетью; Периодически обновляются ли какие-либо из ваших блоков или возникают другие ошибки сетевого подключения (netstat / ss), тайм-ауты брандмауэра и т. д. Не знаете, как ваши хосты управляются с помощью cloud66....
- Тайм-аут запроса Это может произойти, если у вас есть резервные копии команд за инструкциями блокировки (например, изменения / блокировки резервных копий в таблицах MyISAM). Насколько просты ваши запросы? Нет картезианских продуктов в игре? Объясните запрос может помочь.
- Превышение MAX_PACKET_SIZE. Вы храните фотографии, видео и т. Д.?
Здесь есть много возможностей, и без дополнительной информации будет трудно определить это.
Сначала посмотрите на mysql_error.log, а затем вернитесь с сервера БД обратно к вашему приложению.
ОБНОВЛЕНИЕ: это не сработало.
Вот решение, особая благодарность @Myst за то, что он указал на то, что разветвление может вызвать проблемы, у меня не было никакой идеи взглянуть на этот конкретный код. Поскольку ошибки казались случайными, потому что мы разветвлялись таким образом в нескольких местах.
Оказывается, когда я разветвлял процессы, rails использовал одно и то же соединение с базой данных для всех разветвленных процессов. Это создавало ситуацию, когда один из процессов (родительский процесс?) Прерывал соединение с базой данных, оставшийся процесс имел бы свое соединение прерываться.
Решением было изменить этот код:
def recalculate_completion
Process.fork do
if self.course
self.course.user_groups.includes(user:[:events]).each do |ug|
ug.recalculate_completion
end
end
end
end
в этот код:
def recalculate_completion
ActiveRecord::Base.remove_connection
Process.fork do
ActiveRecord::Base.establish_connection
if self.course
self.course.user_groups.includes(user:[:events]).each do |ug|
ug.recalculate_completion
end
end
ActiveRecord::Base.remove_connection
end
ActiveRecord::Base.establish_connection
end
Внесение этого изменения остановило ошибки на наших серверах, и теперь все работает хорошо. Если у кого-то есть какая-либо дополнительная информация о том, почему это работает, я был бы рад услышать это, поскольку я хотел бы иметь более глубокое понимание этого.
Редактировать: оказывается, это тоже не сработало.... у нас все еще были разорваны соединения, но не так много.