私は最近Django 1.4からDjango 1.7にアップグレードしました。一部のスクリプトで次のエラーメッセージが表示されるため、時々:
OperationalError: (2006, 'MySQL server has gone away')
スクリプトは非常に長く、または継続的に実行されるタスクであり、数分間データベースと通信しないフェーズが含まれる可能性があるため、接続がタイムアウトします。ただし、Djangoは自動的に接続を再確立するように見えたため、アップグレードする前は問題ありませんでした。現在は、タスクが途中で停止して失敗することがよくあります。
誰が何が変更され、どのように修正できるか知っていますか?
それはおそらくそのチケット/修正に関連していますか? https://code.djangoproject.com/ticket/2146
どうもありがとう!
このような動作の理由は、Django 1.6で導入されたデータベースへの永続的な接続です。
接続タイムアウトエラーを防ぐには、CONN_MAX_AGE
のsettings.py
を、MySQL構成(wait_timeout
)のmy.cnf
より小さい値に設定する必要があります。その場合、Djangoは、MySQLがスローする前に接続を再開する必要があることを検出します。MySQL5.7のデフォルト値は28800秒です。
settings.py
:
DATABASES = {
'default': {
'ENGINE': 'Django.db.backends.mysql',
'CONN_MAX_AGE': 3600,
<other params here>
}
}
ドキュメント: https://docs.djangoproject.com/en/1.7/ref/settings/#conn-max-age
my.cnf
:
wait_timeout = 28800
ドキュメント: https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_wait_timeout
実行中のバックグラウンドプロセスrqworker
があります。これは、いくつかのユーザーアクション後にいくつかのデータを更新するために個別のジョブを実行します。
_wait_timeout
_秒以上ユーザーの操作がなかった場合は、常にOperationalError: (2006, 'MySQL server has gone away')
を取得します。 MySQLのCONN_MAX_AGEを_wait_timeout
_より小さく設定した場合でも、.
私が理解しているように、CONN_MAX_AGEの変更は、Djangoがチェックされ、このタイムアウトによって接続を自動的に閉じる場合に役立ちます。しかし、Django 1.7.xは、それぞれの前と後にチェックしますリクエストのみ( Django/db /init。py#L101-L112 を参照)。
Django ticket 15119 で説明されているように、Djangoは、すべてのクエリを実行する前に接続が有効かどうかを確認するためにpingを実行していたことがわかります。この動作は- commit 282b2f4 。
Django開発者は https://code.djangoproject.com/ticket/21597#comment:29 でこのようなすべての質問に1つの短い答えを出しました
したがって、私のrqworker
プロセスは、新しいジョブごとに接続自体を検証する必要があります。 (注:接続を閉じると、Djangoが新しい接続を作成します)。
Djangoジョブごとにリクエストアプローチを使用し、すべてのジョブの前後にDjango.db.close_old_connections()
を呼び出します。そして、はい、_CONN_MAX_AGE
_はMySQLより小さい_wait_timeout
_、DjangoはDjango.db.close_old_connections()
メソッドの_MySQL server has gone away
_かどうかをチェックしないため。
Django 1.9:私は継続的にDjango Shellを nix画面 で実行していました)== 48時間以上無人のままでした。それに戻り、_<some_model>.objects.filter
_を実行し、OperationalError: (2006, 'MySQL server has gone away')
をスローしました
簡単なimport Django.db; Django.db.close_old_connections()
が私にとってはトリックです。
Django Docs for 1.9 にclose_old_connections()
のドキュメントが見つかりませんでしたが、ここに Django Codebaseの実装への直接リンク)がありますGithubで
Django 1.6では、(mysqlの)wait_timeoutが渡されると、DBアクセスが原因で(2006、 'MySQLサーバーがなくなった')エラーが発生します。これはDjango 1.5.1には当てはまりませんでした
Djangoコードを実行するワーカーを使用しているときに(gearmanを使用して)このエラーに気付きました。
再現するには:
/etc/mysql/my.cnfを編集してタイムアウトを低い値に設定します。[mysqld]の下に次を追加します
wait_timeout = 10
interactive_timeout = 10
その後
%python manage.py Shell
>>> # access DB
>>> import Django.contrib.auth.models
>>> print list(Django.contrib.auth.models.User.objects.all())
>>> import time
>>> time.sleep(15)
>>> print list(Django.contrib.auth.models.User.objects.all())
ここでエラーが発生します。
私がウェブで見つけた簡単な解決策は、アクセスする前にDjango.db.close_connection()を呼び出すことです
>>> import Django.db
>>> Django.db.close_connection()
>>> print list(Django.contrib.auth.models.User.objects.all())
大丈夫です。
私たちもこれに気づきました。上記のCONN_MAX_AGEをMySQL/MariaDBのwait_timeoutよりも小さい値に設定することの答えは、Webの場合に機能します。
実行時間の長いタスクの場合、これは機能しないようです。代わりに、それをラップし、実行時間の長いタスクの1つが実行されるたびに接続を閉じます。
これを独自のカスタムプールと組み合わせます。そのままにするか、そのままにします-デフォルトDjango 1はゼロ制御です-本番環境では好きではありません。接続が多すぎるDBを強制終了する前にサーバーを強制終了するように最大プールを設定します。使用タスクのデコレータとして:
@close_db_connection()
def task_do_something():
print 'Hello'
'''
Created on Dec 23, 2017
@author: Kevin
'''
from functools import wraps
def close_db_connection(ExceptionToCheck=Exception, raise_exception=False, notify=False):
"""Close the database connection when we're finished, Django will have to get a new one..."""
def deco_wrap(f):
@wraps(f)
def f_wrap(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
raise e
finally:
from Django.db import connection;
connection.close();
return f_wrap
return deco_wrap
おそらくタイムアウトが問題になるかもしれませんが、非常に大きなBLOBフィールドを書き込もうとしたときにこの問題に遭遇しました。 mysql構成ファイルで許可される最大パケットサイズを増やすことで解決しました...
max_allowed_packet = 4M
/etc/my.cnfへの変更後、mysqlを再起動することを忘れないでください。このページは役に立ちました...
http://dev.mysql.com/doc/refman/5.5/en/packet-too-large.html
私の解決策はどこにあるのかと同じ問題に直面していました。私はDjangoの新人であり、すでにここで遅れていますが、自分の解決策を投稿しています。それが誰かを助けるかもしれません。
DATABASES = {
'default': {
'ENGINE': 'Django.db.backends.mysql',
'NAME': 'your_dabase',
'USER': 'your_user',
'PASSWORD': 'your_password',
'Host': 'your_Host',
'PORT': 'your_port',
'CONN_MAX_AGE': 290,
},
'OPTIONS': {
'timeout':20,
}
}
CONN_MAX_AGEと[〜#〜] options [〜#〜]を追加しましたが、完全に機能しています。
_pip install mysql_server_has_gone_away
_
settings.py:
_DATABASES = {
'default': {
'ENGINE': 'mysql_server_has_gone_away'
}
}
_
私の場合、私はDjango ORM with long living request(webocket)を使用していました。_CONN_MAX_AGE
_は私の場合は機能しません。
最初にエラーをキャッチしようとするラッパーを作成しましたが、Django lazy-loadingのため、何をラップするかが明確ではありません。そのため、このコードをプロジェクト全体でこれはお尻の一種の痛みでした。たとえば、User.objects.get(id=3)
と書く代わりに、do_db(User.objects.get, id=3)
を実行し、dbはtry: return callback(*args, **kwargs); catch e: conn.close(); callback(*args, **kwargs)
でした。
Djangoバックエンドを掘り下げるとき、接続レベルでこのソリューションを移行できます。したがって、dbに行くすべてのクエリはそれでラップされます:
settings.py:
_DATABASES = {
'default': {
'ENGINE': 'lol'
}
}
_
lol/base.py:
_"""
https://stackoverflow.com/a/60894948/3872976
"""
from Django.db.backends.mysql import base
def check_mysql_gone_away(db_wrapper):
def decorate(f):
def wrapper(self, query, args=None):
try:
return f(self, query, args)
except (base.Database.OperationalError, base.Database.InterfaceError) as e:
if 'MySQL server has gone away' in str(e):
db_wrapper.connection.close()
db_wrapper.connect()
self.cursor = db_wrapper.connection.cursor()
return f(self, query, args)
# Map some error codes to IntegrityError, since they seem to be
# misclassified and Django would prefer the more logical place.
if e.args[0] in self.codes_for_integrityerror:
raise base.utils.IntegrityError(*Tuple(e.args))
raise
return wrapper
return decorate
class DatabaseWrapper(base.DatabaseWrapper):
def create_cursor(self, name=None):
class CursorWrapper(base.CursorWrapper):
@check_mysql_gone_away(self)
def execute(self, query, args=None):
return self.cursor.execute(query, args)
@check_mysql_gone_away(self)
def executemany(self, query, args):
return self.cursor.executemany(query, args)
cursor = self.connection.cursor()
return CursorWrapper(cursor)
_
Mysqlがアトミック操作中に切断すると、トランザクションにの問題が発生することに注意してください。しかし、残念ながら他に方法はありません。