1. 程式人生 > >"Mysql has gone away"的幾種可能

"Mysql has gone away"的幾種可能

AR -name cloud .sql lar creat insert contex example

現象:

在本地利用Flask自帶的WSGI服務進行調試沒有問題後,通過Gunicorn進行部署。

但是在一晚上沒有訪問之後,第二天再次訪問會出現500(Internal error)。

原因:

通過追蹤日誌文件,發現是Sqlalchemy連接Mysql的斷開問題

2006, "MySQL server has gone away (BrokenPipeError(32, ‘Broken pipe‘))"

此問題來自於Mysql的連接(Session)在一段時間(默認為8小時,所以在我這裏體現為第二天)沒有響應後會自動斷開,而SQLAlchemy並不會監測到Session的斷開所以在嘗試使用舊Session的時候會出現此錯誤。查看mysql的日誌,也可以看到它確實會主動放棄過期session:

Aborted connection 112225 to db。。。。。。。。。。。。。。。(Got an error reading communication packets)

解決方案:

既然是因為Sqlalchemy的Session過期問題,所以當然要解決過期,Stack Overflow上找了不同的解決方案,主要分為幾種:

1.增加mysql的wait_timeout讓mysql不要太快放棄連接。

假如你的服務訪問還算頻繁,不太會出現長時間無連接。那麽增加此變量從而避免短時間無連接造成的abort是可以的,但是假如超出時間,早晚還是會崩潰。具體到自己的Mysql的此設置為多少可以通過此命令查看(wait_timeout):

show variables like ‘%timeout%‘;
+-----------------------------+----------+
| Variable_name               | Value    |
+-----------------------------+----------+
| connect_timeout             | 10       |
| delayed_insert_timeout      | 300      |
| innodb_flush_log_at_timeout | 1        |
| innodb_lock_wait_timeout    | 50       |
| innodb_rollback_on_timeout  | OFF      |
| interactive_timeout         | 28800    |
| lock_wait_timeout           | 31536000 |
| net_read_timeout            | 30       |
| net_write_timeout           | 60       |
| rpl_stop_slave_timeout      | 31536000 |
| slave_net_timeout           | 3600     |
| wait_timeout                | 28800    |
+-----------------------------+----------+

2.在創建Engine的時候通過設定 pool_recycle讓Sqlalchemy的session pool定期回收過期Session

可能被mysql拋棄了的session,所以應該要比你的mysql wait_timeout小)來保證Session的有效性。

e = create_engine("mysql://scott:tiger@localhost/test", pool_recycle=3600)
‘‘‘
pool_recycle=-1: this setting causes the pool to recycle
connections after the given number of seconds has passed. It
defaults to -1, or no timeout. For example, setting to 3600
means connections will be recycled after one hour. Note that
MySQL in particular will disconnect automatically if no
activity is detected on a connection for eight hours (although
this is configurable with the MySQLDB connection itself and the
server configuration as well).
‘‘‘

!註意,如果是雲服務提供商,可能對這個斷開的時間限制有不同的設定,比如pythonanywhere的設置是5分鐘:

Using SQLAlchemy with MySQL

SQLAlchemy needs to some extra arguments to work on PythonAnywhere:

engine = create_engine(‘mysql+mysqldb://...‘, pool_recycle=280)

The RDS service disconnects clients after 5 minutes (300s), so we need to set the pool_recycle to something lower than that, or you‘ll occasionally see disconnection errors in your logs.

而新浪雲的設置則更加短,只有30秒:

MySQL gone away問題

MySQL連接超時時間為30s,不是默認的8小時,所以你需要在代碼中檢查是否超時,是否需要重連。

對於使用sqlalchemy的用戶,需要在請求處理結束時調用 db.session.close() ,關閉當前session,將mysql連接還給連接池,並且將連接池的連接recyle時間設的小一點(推薦為60s)。

3.重建Session??(來自StackOverflow)

在這個問題中的最高票答案說,在每一個你需要使用SqlAlchemy的地方實例化一個Session。

但是在我在實際操作中,通過查看Session id發現,兩次實例的ID一致,也就是說明Session()只是從session pool裏面取回來舊的Session,那麽這個Session早晚會過期.事實也證明,這個方法並沒有解決我的服務中mysql gone away的問題。所以這個答案我持保留態度。

4.SQlAlchemy官方:設置pool_pre_ping=True

官方文檔其實對於myslq斷鏈的問題,官方除了設置pool_recycle之外還建議在創建Engine的時候設置pool_pre_ping=True也就是在每一次使用Session之前都會進行簡單的查詢檢查,判斷是Session是否過期。

engine = create_engine("mysql+pymysql://user:pw@host/db", pool_pre_ping=True)

詳細可見參考網站。

但是以上幾個方法我都嘗試過後,依然會重現該問題,最後看到這篇文檔中說:

To use SQLAlchemy in a declarative way with your application, you just have to put the following code into your application module. Flask will automatically remove database sessions at the end of the request or when the application shuts down:

from yourapplication.database import db_session

@app.teardown_appcontext
def shutdown_session(exception=None):
    db_session.remove()

終於,mysql has gone away不見啦! 也就是每一次請求結束,Flask都會關閉Session.(TODO: 測試此方法是否低效率。)

Reference:

Avoiding “MySQL server has gone away” on infrequently used Python / Flask server with SQLAlchemy

Connection Pooling?

SQLAlchemy in Flask

http://docs.sqlalchemy.org/en/latest/core/pooling.html#dealing-with-disconnects

https://mofanim.wordpress.com/2013/01/02/sqlalchemy-mysql-has-gone-away/

"Mysql has gone away"的幾種可能