原文:我最近开始从我的一些旧代码中获取MySQL OperationalErrors,似乎无法追溯问题。由于它之前的工作,我认为它可能是一个破坏了一些东西的软件更新。我正在使用带有nginx的django runfcgi的python 2.7。这是我的原始代码:
views.py
DBNAME = "test"
DBIP = "localhost"
DBUSER = "django"
DBPASS = "password"
db = MySQLdb.connect(DBIP,DBUSER,DBPASS,DBNAME)
cursor = db.cursor()
def list(request):
statement = "SELECT item from table where selected = 1"
cursor.execute(statement)
results = cursor.fetchall()
我尝试过以下操作,但它仍无效:
views.py
class DB:
conn = None
DBNAME = "test"
DBIP = "localhost"
DBUSER = "django"
DBPASS = "password"
def connect(self):
self.conn = MySQLdb.connect(DBIP,DBUSER,DBPASS,DBNAME)
def cursor(self):
try:
return self.conn.cursor()
except (AttributeError, MySQLdb.OperationalError):
self.connect()
return self.conn.cursor()
db = DB()
cursor = db.cursor()
def list(request):
cursor = db.cursor()
statement = "SELECT item from table where selected = 1"
cursor.execute(statement)
results = cursor.fetchall()
目前,我唯一的解决方法是在每个使用mysql的函数中执行MySQLdb.connect()
。另外我注意到当使用django的manage.py runserver
时,我不会遇到这个问题,而nginx会抛出这些错误。我怀疑我是否超时连接因为list()
在启动服务器的几秒钟内被调用。我正在使用的软件是否有任何更新会导致此故障/是否有任何修复?
编辑:我意识到我最近写了一个中间件来守护一个函数,这就是问题的原因。但是,我无法弄清楚为什么。这是中间件的代码
def process_request_handler(sender, **kwargs):
t = threading.Thread(target=dispatch.execute,
args=[kwargs['nodes'],kwargs['callback']],
kwargs={})
t.setDaemon(True)
t.start()
return
process_request.connect(process_request_handler)
答案 0 :(得分:42)
有时,如果您看到“OperationalError:(2006,'MySQL服务器已经消失')”,那是因为您发出的查询太大了。例如,如果您将会话存储在MySQL中,并且您尝试在会话中放置一些非常大的内容,就会发生这种情况。要解决此问题,您需要增加MySQL中max_allowed_packet设置的值。
默认值为1048576。
因此,请查看默认值的当前值,运行以下SQL:
select @@max_allowed_packet;
要临时设置新值,请运行以下SQL:
set global max_allowed_packet=10485760;
要更加永久地解决问题,请创建至少包含以下内容的/etc/my.cnf文件:
[mysqld]
max_allowed_packet = 16M
编辑/etc/my.cnf后,如果您不知道如何,则需要重启MySQL或重启机器。
答案 1 :(得分:34)
根据MySQL documentation,当客户端无法向服务器发送问题时,会引发您的错误消息,很可能是因为服务器本身已关闭连接。在最常见的情况下,服务器将在(默认)8小时后关闭空闲连接。这可以在服务器端配置。
MySQL documentation提供了许多其他可能的原因,可能值得研究以确定它们是否适合您的情况。
在每个函数中调用connect()
的替代方法(最终可能会不必要地创建新连接)将在连接对象上使用ping()
方法进行调查;这可以尝试自动重新连接,从而测试连接。我努力在线找到ping()
方法的decent documentation,但this question的答案可能有所帮助。
注意,处理事务时自动重新连接可能很危险,因为重新连接会导致隐式回滚(并且似乎是自动连接不是MySQLdb实现功能的主要原因)。
答案 2 :(得分:1)
检查是否允许在一个线程中创建mysql连接对象,然后在另一个线程中使用它。
如果它被禁止,请使用threading.Local进行每线程连接:
class Db(threading.local):
""" thread-local db object """
con = None
def __init__(self, ...options...):
super(Db, self).__init__()
self.con = MySQLdb.connect(...options...)
db1 = Db(...)
def test():
"""safe to run from any thread"""
cursor = db.con.cursor()
cursor.execute(...)
答案 3 :(得分:1)
我也一直在努力解决这个问题。我不喜欢在mysqlserver上增加超时的想法。与CONNECTION_MAX_AGE
自动重新连接无法正常工作。不幸的是,我最终包装了查询数据库的每个方法,如此
def do_db( callback, *arg, **args):
try:
return callback(*arg, **args)
except (OperationalError, InterfaceError) as e: # Connection has gone away, fiter it with message or error code if you could catch another errors
connection.close()
return callback(*arg, **args)
do_db(User.objects.get, id=123) # instead of User.objects.get(id=123)
正如您所看到的,我更倾向于捕获异常,而不是每次查询数据库之前对其进行ping操作。因为捕获异常是一种罕见的情况。我希望django能够自动重新连接,但他们似乎refused发出了这个问题。
答案 4 :(得分:0)
这段代码多大了?自从至少0.96以来,Django已经在设置中定义了数据库。我能想到的另一件事是多数据库支持,它改变了一些东西,但即使是1.1或1.2。
即使你需要一个特殊的数据库用于某些视图,我认为你可能最好在设置中定义它。
答案 5 :(得分:0)
SQLAlchemy现在有一篇很好的文章,说明如何使用ping来对你的连接的新鲜感持悲观态度:
http://docs.sqlalchemy.org/en/latest/core/pooling.html#disconnect-handling-pessimistic
从那里开始,
from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy.pool import Pool
@event.listens_for(Pool, "checkout")
def ping_connection(dbapi_connection, connection_record, connection_proxy):
cursor = dbapi_connection.cursor()
try:
cursor.execute("SELECT 1")
except:
# optional - dispose the whole pool
# instead of invalidating one at a time
# connection_proxy._pool.dispose()
# raise DisconnectionError - pool will try
# connecting again up to three times before raising.
raise exc.DisconnectionError()
cursor.close()
进行测试以确保上述工作:
from sqlalchemy import create_engine
e = create_engine("mysql://scott:tiger@localhost/test", echo_pool=True)
c1 = e.connect()
c2 = e.connect()
c3 = e.connect()
c1.close()
c2.close()
c3.close()
# pool size is now three.
print "Restart the server"
raw_input()
for i in xrange(10):
c = e.connect()
print c.execute("select 1").fetchall()
c.close()
答案 6 :(得分:0)
我遇到了这个问题,但没有更改配置的选项。我终于发现问题是在我的50000记录循环中发生了49500条记录,因为这是我再次尝试(在很久以前尝试过之后)打到我的第二个数据库的时间。
所以我改变了我的代码,以便每隔几千条记录,我再次触摸第二个数据库(使用一个非常小的表的count()),并修复它。毫无疑问" ping"或其他一些触摸数据库的方法也可以。
答案 7 :(得分:0)
在我看来,关于此类警告的必然常见问题是您的应用程序已达到MySQL的wait_timeout值。
我在开发的PythonFLASK应用程序中遇到了同样的问题,我很容易解决。
P.S:我使用的是MySQL 5.7.14
var i =0;
function init(){
if(i<10){
createNumbers(process);
i++;
requestAnimationFrame(init);
}
}
一个重要的观察结果:如果您通过MySQL批处理模式搜索变量,则值将按原样显示。但如果你执行“SHOW VARIABLES LIKE'等待%';”或者“SHOW VARIABLES LIKE'interactive%';”,为'interactive_timeout'配置的值将出现在两个变量中,我不知道为什么,但事实是,为'/ etc中的每个变量配置的值/mysql/mysql.conf.d/mysqld.cnf',将受到MySQL进程的尊重。
问候!
答案 8 :(得分:0)
这个错误很神秘,因为MySQL没有报告它断开连接的原因,它只是消失了。
这种断线似乎有很多原因。我刚刚发现,如果查询字符串太大,服务器将断开连接。这可能与max_allowed_packets
设置有关。
答案 9 :(得分:0)
首先,你应该确保MySQL会话&amp;全球环境wait_timeout
和interactive_timeout
值。其次,您的客户应尝试在这些环境值下方重新连接到服务器。
答案 10 :(得分:0)
这可能是由于DB连接已从主线程复制到子线程中。使用python的多处理库生成不同的进程时,我遇到了相同的错误。连接对象在派生期间在进程之间复制,并且在子线程中进行数据库调用时会导致MySQL OperationalErrors。
这里是解决此问题的很好的参考:Django multiprocessing and database connections
答案 11 :(得分:0)
对我来说,这是在调试模式下发生的。
所以我在调试模式下尝试了持久连接,请检查链接:Django - Documentation - Databases - Persistent connections。
在设置中:
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dbname',
'USER': 'root',
'PASSWORD': 'root',
'HOST': 'localhost',
'PORT': '3306',
'CONN_MAX_AGE': None
},
答案 12 :(得分:0)
当您在不去数据库的耗时操作之后尝试使用连接时,可能会发生此错误。由于该连接有一段时间未使用,因此命中 MySQL 超时,并静默断开连接。
您可以尝试在耗时的非 DB 操作之后调用 close_old_connections()
,以便在连接不可用时打开新连接。请注意,如果您有交易,请勿使用 close_old_connections()
。