错误2006:MySQL服务器消失了


8

我正在使用uWSGI和nginx在CentOS服务器上运行Python Pyramid应用程序。我使用SQLAlchemy作为ORM,使用MySQLdb作为API,并使用MySQL作为数据库。该站点尚未上线,因此唯一的访问量是我和公司的其他一些员工。我们购买了一些数据来填充数据库,因此最大(也是最经常查询)的表是〜150,000行。

昨天我快速连续打开了网站的四个新标签,然后又收到了502错误的网关错误。我查看了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_pa​​cket。我将其增加到100M,然后重新启动MySQL,但这根本没有帮助。

总结: 与MySQL的并发连接会导致2006, 'MySQL server has gone away'其他一些奇怪的错误。MySQL的错误日志中没有任何相关性。

我已经在这工作了几个小时,并且没有取得任何进展。有人可以帮我吗?


当您处理并发请求时,每个线程(或进程或其他对象)是否都建立自己的数据库连接?
DerfK 2012年

每个进程都有一个由SQLAlchemy管理的连接池,因此每个请求都应具有自己的连接。
塞隆·鲁恩

另一个注意事项:负载测试不会对我的本地开发服务器造成任何问题,该服务器是服务器的Waitress,数据库是MySQL。
塞隆·鲁恩

Answers:


18

我也遇到了这个问题,并找到了原因并解决。

发生这种情况的原因是,在将应用程序加载到父级后,python uwsgi插件(或更可能是所有uwsgi插件)会fork()new worker。结果,子级从父级继承所有资源(包括文件描述符,如db连接)。

您可以在uwsgi Wiki上简要了解此内容:

uWSGI尽可能尝试在写入时滥用fork()复制。默认情况下,它将在加载应用程序后派生。如果您不希望这种行为,请使用--lazy选项。启用它,将指示uWSGI在每个工作人员的fork()之后加载应用程序

如您所知,Python的mysqldb连接和游标不是线程安全的,除非您明确保护它们。因此,同时使用同一mysql连接/游标的多个进程(例如uwsgi worker)将破坏它。

以我为例(对于亚瑟王的黄金 API),当我在另一个模块的作用域中创建每个请求的MySQL连接时,此方法工作正常,但是当我想要持久性连接以提高性能时,我将数据库连接和光标移至了全局范围父模块。结果,我的联系就像您一样互相加紧。

解决此问题的方法是在uwsgi配置中添加“ lazy”关键字(或--lazy命令行选项)。结果,将为每个子代重新创建应用程序,而不是从父代分支并共享连接(并在某个时候踩到它,以使MySQL服务器由于某个时候的请求损坏而强制关闭)。

最后,如果您想要一种无需修改uwsgi配置即可执行此操作的方法,则可以在派生工作进程后立即使用@postfork装饰器来正确创建新的数据库连接。您可以在这里阅读有关内容。

我从您的后续工作中了解到您已经切换到了pgsql,但这是答案,因此您可以在晚上睡个好觉,对于像您这样的任何人,我都想找到答案!

PS一旦我了解了这个问题(由于工人们互相踩踏而使光标损坏),但是我没有意识到关于fork()和--lazy的问题,我正在考虑实现自己的池,工人们将“从全局范围的池中检出“ mysql连接”,然后在退出application()之前“检入”,但是使用--lazy可能要好得多,除非您的Web /应用程序负载变化足以使您不断创造新的工人。即使那样,我还是会偏爱--lazy,因为它比实现自己的数据库连接池要干净得多。

编辑:这是此问题+解决方案的更详尽的文章,因为对于遇到此问题的其他人来说,缺少有关此问题的信息:http : //tns.u13.net/?p=190


知道是什么原因引起的,这绝对是一件好事。谢谢!
塞隆·鲁恩

只是把那个帖子扔出去就是我也遇到了同样的问题,您的解决方案已解决它:)谢谢!
MasterGberry

“因此,同时使用相同的mysql连接/游标的多个进程(例如uwsgi worker)将破坏它。” 这很有启发性。我有两个连接打开到本地的同一数据库(一个来自外壳,另一个来自wsgi应用程序),并收到此错误。数据库正在向ping其他mysqladmin请求报告自己的状态。可能是因为我试图从外壳中删除数据库...但是它一直使该命令出现“服务器已消失”错误。还是谢谢你!
Brian Peterson

你救了我的命。
ech
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.