2013-01-04 37 views
23

原文:我最近已經開始從我的一些舊的代碼,使MySQL OperationalErrors,似乎無法追溯的問題。既然它以前工作,我認爲它可能是一個軟件更新,打破了一些東西。我使用python 2.7與django runfcgi與nginx。這是我原來的代碼:Python和Django的OperationalError(2006年,「MySQL服務器已經走了」)

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) 
+1

你有一個名爲'list'的本地函數?這是一個壞主意。 – hughdbrown

+0

在某些數據庫適配器/庫中,有一個設置爲自動重新連接,類似於mysql客戶端如何處理超時。看看你是否可以設置它。 –

回答

31

按照該MySQL documentation,當客戶不能發送一個問題給服務器的錯誤消息引發,最有可能的因爲服務器本身已經關閉了連接。在最常見的情況下,服務器將在8小時(默認)後關閉空閒連接。這在服務器端是可配置的。

MySQL documentation給出了許多其他可能的原因,這可能是值得探討,看看他們是否適合你的情況。

在每個函數中調用connect()(可能最終不必要地創建新連接)的替代方法是調查在連接對象上使用ping()方法;這測試連接與嘗試自動重新連接的選項。我在網上努力尋找ping()方法的decent documentation,但對this question的回答可能有幫助。

請注意,自動重新連接在處理事務時會很危險,因爲重新連接會導致隱式回滾(並且似乎是autoreconnect不是MySQLdb實現的一個功能)。

+0

建議在運行查詢前檢查連接是否處於活動狀態,這被認爲是一種浪費資源並且不可靠的反模式:https://www.percona.com/blog/2010/05/05/checking-for -a-live-database-connection-considered-harmful/ –

+0

謝謝你的鏈接,Jeff。我同意他們在那裏給出的方法(自動重試)更好。 –

0

幾歲這段代碼的代碼?自從至少有.96以來,Django已經在設置中定義了數據庫。我能想到的其他事情是多分貝支持,它改變了一些事情,但即使是1.1或1.2。

即使您需要對某些意見特殊的數據庫,我想你也許會更好,在設置中定義它。

+0

聖誕節前一週去休假前工作正常 –

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(...) 
+0

遊標在線程本身(在dispatch.execute中)和在views.py中定義。如果線程無法訪問它,我會得到一個不同的錯誤。 –

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:[email protected]/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() 
35

有時候,如果你ee「OperationalError:(2006,'MySQL server has gone away')」,這是因爲您發出的查詢太大。例如,如果您將會話存儲在MySQL中,並且您試圖在會話中放置一些非常大的內容,就會發生這種情況。要解決這個問題,你需要增加MySQL中max_allowed_pa​​cket設置的值。

的默認值是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

爲我工作!謝謝 –

+0

「如果你不知道如何重新啓動你的機器。」......「你好,IT ......你有試過關掉它嗎?」 –

0

我一直在努力解決這個問題。我不喜歡增加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) 

方法,你可以看到我寧可選擇捕捉異常不是查詢它之前每一次查驗數據庫。因爲捕捉異常是一種罕見的情況。我希望django自動重新連接,但他們似乎refused這個問題。

0

我有這個問題,沒有選擇更改我的配置。我終於發現問題是在我的50000記錄循環中發生了49500條記錄,因爲那是我再次嘗試(經過很久以前嘗試)後纔打到我的第二個數據庫的時間。

因此,我改變了我的代碼,使每隔幾千條記錄,我再次觸及第二個數據庫(用一個非常小的表的count()),並修復它。毫無疑問,「ping」或其他一些觸摸數據庫的方式也是可行的。

0

在我看來,關於這種警告的常見問題是,您的應用程序已達到MySQL的wait_timeout值。

我與我開發的PythonFLASK應用程序有同樣的問題,我解決了很容易。

P.S:我正在使用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 

一個重要的觀察:如果你搜索通過MySQL的批處理模式下的變量,這些值將出現,因爲它是。但是如果你執行「SHOW VARIABLES LIKE'wait%';」或「SHOW VARIABLES LIKE'interactive%';」,爲'interactive_timeout'配置的值將出現在兩個變量上,我不知道爲什麼,但實際上是爲每個變量的'/ etc /mysql/mysql.conf.d/mysqld.cnf',將被MySQL進程所推崇。

問候!

0

這個錯誤是神祕的,因爲MySQL不報告它爲什麼斷開連接,它只是消失。

看來有這種斷開的原因很多。我剛剛發現的是,如果查詢字符串太大,服務器將斷開連接。這可能與max_allowed_packets設置有關。

0

首先,您應該確保MySQL會話&全局環境wait_timeoutinteractive_timeout的值。其次,您的客戶應該嘗試重新連接到服務器以下的環境值。

相關問題