Я запускаю приложение Python Pyramid на сервере CentOS, используя uWSGI и nginx. Я использую SQLAlchemy в качестве ORM, MySQLdb в качестве API и MySQL в качестве базы данных. Сайт еще не запущен, поэтому единственный трафик - это я и некоторые другие сотрудники компании. Мы приобрели некоторые данные для заполнения базы данных, поэтому самая большая (и наиболее часто запрашиваемая) таблица составляет ~ 150 000 строк.
Вчера я быстро открыл четыре новые вкладки сайта и получил пару ошибок 502 Bad Gateway. Я посмотрел в журнале uWSGI и обнаружил следующее:
sqlalchemy.exc.OperationalError: (OperationalError) (2006, 'MySQL server has gone away') 'SELECT ge...
Важное примечание: эта ошибка не связана с MySQL wait_timeout. Был там, сделал это.
Я задавался вопросом, была ли проблема вызвана одновременными запросами, обслуживаемыми одновременно. Я сделал себя тестером нагрузки бедного человека:
for i in {1..10}; do (curl -o /dev/null http://domain.com &); done;
Конечно, в этих десяти запросах, по крайней мере, один вызовет ошибку 2006 года, а зачастую и больше. Иногда ошибки становятся еще более странными, например:
sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column 'table.id'"
Когда столбец наиболее определенно существует и отлично работает на всех других идентичных запросах. Или этот:
sqlalchemy.exc.ResourceClosedError: This result object does not return rows. It has been closed automatically.
Когда снова все работало нормально для всех остальных запросов.
Для дальнейшей проверки того, что проблема возникла из-за одновременных подключений к базе данных, я установил uWSGI для одного рабочего и отключил многопоточность, заставляя запросы обрабатываться по одному за раз. Конечно же, проблемы исчезли.
В попытке найти проблему я настроил журнал ошибок для MySQL. За исключением некоторых уведомлений при запуске MySQL, он остается пустым.
Вот мой конфиг MySQL:
[mysqld]
default-storage-engine = myisam
key_buffer = 1M
query_cache_size = 1M
query_cache_limit = 128k
max_connections=25
thread_cache=1
skip-innodb
query_cache_min_res_unit=0
tmp_table_size = 1M
max_heap_table_size = 1M
table_cache=256
concurrent_insert=2
max_allowed_packet = 1M
sort_buffer_size = 64K
read_buffer_size = 256K
read_rnd_buffer_size = 256K
net_buffer_length = 2K
thread_stack = 64K
innodb_file_per_table=1
log-error=/var/log/mysql/error.log
Тяжелое гугление по ошибке выявило мало, но предложило увеличить max_allowed_packet. Я увеличил его до 100M и перезапустил MySQL, но это совсем не помогло.
Подводя итог: параллельные соединения с MySQL вызывают 2006, 'MySQL server has gone away'
и некоторые другие странные ошибки. В журнале ошибок MySQL нет ничего уместного.
Я работал над этим часами и не добился никакого прогресса. Кто-нибудь может мне помочь?
Ответы:
Я также столкнулся с этим и нашел причину и исправить.
Причина этого заключается в том, что плагин Python UWSGI (или, скорее всего, все плагины UWSGI) fork () новых рабочих после загрузки приложения в родительский. В результате дочерние элементы наследуют все ресурсы (включая файловые дескрипторы, такие как соединение db) от родительского.
Вы можете кратко прочитать об этом в вики uwsgi :
И, как вы, возможно, знаете, соединения и курсоры Python для mysqldb не являются поточно-ориентированными, если вы явно не защитите их. Поэтому несколько процессов (например, рабочие uwsgi), использующие одно и то же соединение / курсор mysql, одновременно повредят его.
В моем случае (для Золотого API короля Артура ) это работало нормально, когда я создавал соединение MySQL для каждого запроса в области видимости другого модуля, но когда я хотел, чтобы постоянные соединения помогали с производительностью, я переместил соединение с базой данных и курсор в глобальную область видимости в родительский модуль. В результате мои связи наступали друг на друга, как ваши.
Чтобы исправить это, добавьте ключевое слово "lazy" (или параметр командной строки --lazy) в вашу конфигурацию uwsgi. В результате приложение будет разветвлено заново для каждого дочернего элемента вместо того, чтобы разветвляться от родителя и делиться соединением (и наступать на него в некоторый момент, так что сервер MySQL принудительно закрывает его из-за поврежденного запроса в некоторый момент).
Наконец, если вам нужен способ сделать это без изменения конфигурации uwsgi, вы можете использовать декоратор @postfork для правильного создания нового соединения с базой данных сразу после разрыва рабочего процесса. Вы можете прочитать об этом здесь .
Из вашего наблюдения я вижу, что вы уже переключились на pgsql, но вот ответ, чтобы вы могли лучше спать по ночам и для любого, как вы, и я пытаюсь найти ответ на этот вопрос!
PS Как только я понял проблему (курсор был поврежден из-за того, что работники наступали друг на друга), но не понял, что такое fork () и --lazy, я подумал о создании своего собственного пула, в котором работники могли бы " проверьте «соединение mysql из пула в глобальной области», а затем «вернитесь обратно» непосредственно перед выходом из application (), однако, вероятно, гораздо лучше использовать --lazy, если загрузка вашего веб-приложения / приложения меняется настолько, что вы постоянно создание новых работников. Даже тогда я мог бы предпочесть --lazy, потому что он значительно чище, чем реализация собственного пула соединений с БД.
редактировать: вот более подробное описание этой проблемы + решение, так как не хватает информации для других, кто столкнулся с ней: http://tns.u13.net/?p=190
источник
ping
и другихmysqladmin
запросах. Вероятно, это потому, что я пытался удалить базу данных из оболочки ... но она продолжала выдавать ошибку "сервер ушел" для этой команды. В любом случае, спасибо!