オリジナル:最近、古いコードのいくつかからMySQL OperationalErrorsを取得し始めましたが、問題をトレースバックできないようです。以前は動作していたので、何かを壊したのはソフトウェアの更新であると考えました。私はpython 2.7 with Django runfcgi with nginxを使用しています。ここに私の元のコードがあります:
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)
_
MySQLドキュメント に従って、クライアントがサーバーに質問を送信できない場合、おそらくサーバー自体が接続を閉じたため、エラーメッセージが表示されます。最も一般的なケースでは、サーバーは8時間(デフォルト)後にアイドル接続を閉じます。これはサーバー側で構成可能です。
MySQLドキュメント は、他の考えられる原因をいくつか示しており、それらがあなたの状況に合っているかどうかを調べる価値があります。
すべての関数でconnect()
を呼び出す代わりに(これにより、不必要に新しい接続が作成される可能性があります)、接続オブジェクトでping()
メソッドを使用して調査します。これは、自動再接続を試行するオプションを使用して接続をテストします。オンラインでping()
メソッドの まともなドキュメント を見つけるのに苦労しましたが、 この質問 の答えが役立つかもしれません。
再接続が暗黙的なロールバックを引き起こすように見えるため、トランザクションを処理する場合、自動再接続は危険な場合があります(自動再接続がMySQLdb実装の機能ではない主な理由のようです)。
「OperationalError:(2006、 'MySQL server is gone away')」と表示される場合は、大きすぎるクエリを発行していることが原因です。これは、たとえば、セッションを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を再起動するか、方法がわからない場合はマシンを再起動する必要があります。
私もこの問題に苦労しています。 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 その問題のようです。
あるスレッドで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(...)
このエラーは、MySQLが切断の理由を報告せず、ただ消えてしまうため、不可解です。
この種の切断には多くの原因があるようです。私が見つけたのは、クエリ文字列が大きすぎると、サーバーが切断されることです。これはおそらくmax_allowed_packets
設定。
まず、MySQLセッションとグローバル環境のwait_timeout
およびinteractive_timeout
値を確認する必要があります。次に、クライアントはこれらの環境値以下でサーバーに再接続する必要があります。
私にとって、これはデバッグモードで起こっていました。
そこで、デバッグモードで永続的接続を試し、リンクをチェックアウトします。 Django-ドキュメンテーション-データベース-永続的接続 。
設定で:
'default': {
'ENGINE': 'Django.db.backends.mysql',
'NAME': 'dbname',
'USER': 'root',
'PASSWORD': 'root',
'Host': 'localhost',
'PORT': '3306',
'CONN_MAX_AGE': None
},
この問題が発生し、構成を変更するオプションがありませんでした。最後に、問題が私の50000レコードループで49500レコードを発生していることがわかりました。これは、2番目のデータベースにヒットするために(かなり前に試した後)再試行した時間だからです。
そこで、コードを変更して、数千レコードごとに2番目のデータベースに再度アクセスし(非常に小さなテーブルのcount()を使用)、それを修正しました。間違いなく「ping」またはデータベースにアクセスする他の手段も機能します。
私の意見では、このような警告に関する一般的な問題は、アプリケーションがMySQLのwait_timeout値に達したという事実です。
私が開発したPythonFLASKアプリでも同じ問題が発生し、非常に簡単に解決しました。
追伸:私はMySQL 5.7.14を使用していました
$ grep timeout /etc/mysql/mysql.conf.d/mysqld.cnf
# https://support.rackspace.com/how-to/how-to-change-the-mysql-timeout-on-a-server/
# wait = timeout for application session (tdm)
# inteactive = timeout for keyboard session (terminal)
# 7 days = 604800s / 4 hours = 14400s
wait_timeout = 604800
interactive_timeout = 14400
1つの重要な観察: MySQLバッチモードで変数を検索する場合、値はそのまま表示されます。しかし、「SHOW VARIABLES LIKE 'wait%';」を実行するとまたは「SHOW VARIABLES LIKE 'interactive%';」、「interactive_timeout」に設定された値は両方の変数に表示されますが、理由はわかりませんが、実際には「/ etc」の各変数に設定された値/mysql/mysql.conf.d/mysqld.cnf 'は、MySQLプロセスによって尊重されます。
よろしく!
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()
このコードは何歳ですか? Django少なくとも.96以降、設定でデータベースが定義されています。他に考えられるのはmulti-dbのサポートだけです。
特定のビューに特別なDBが必要な場合でも、おそらく設定で定義する方が良いと思います。
これは、メインスレッドから子スレッドにコピーされたDB接続が原因である可能性があります。 Pythonのマルチプロセッシングライブラリを使用してさまざまなプロセスを生成すると、同じエラーが発生しました。接続オブジェクトは、フォーク中にプロセス間でコピーされ、子スレッドでDB呼び出しを行うとMySQL OperationalErrorsにつながります。
これを解決するための良いリファレンスがあります: Djangoマルチプロセッシングとデータベース接続