zoukankan      html  css  js  c++  java
  • 解决 django 中 mysql gone away 的问题

    最近在项目中,我使用 Django Command 模块写了一个脚本,处理从 MQ 发来的消息,并入库。在测试过程中,程序运行良好,但是在程序上线并运行一段时间后,出现了以下错误:

    OperationalError: (2006, 'MySQL server has gone away')
    

    发现问题

    经过一段时间的排查后,我发现了问题的原因:因为我要入库的消息并不频繁,所以我的程序的入库操作之间可能会间隔一段时间,而当这段时间大于 MySQL 配置的超时时间后,MySQL 便会主动断开与该程序的连接;此时,程序做数据库相关操作,则会发现数据库连接已经失效,因而报 MySQL server has gone away的异常。

    查看 MySQL 配置的超时时间方法为:

    show variables like 'wait_timeout';
    

    分析问题

    在网上搜索相关问题后,我发现有很多人问过相关问题,而 Django 官网的这个讨论,给了我很大帮助。

    处理方法有两个:

    1) 每次调用完 Model 后,手动关闭 connection

    from django.db import connection
    
    connection.close()
    

    2) 调整数据库的超时时间(不推荐!)

    但是,这两个都不适合我的程序:

    • 方法1是针对 Model 操作间隔一定很长的情况,如果某个时间段内需要很频繁的操作数据库,那么频繁关闭-新建数据库连接无疑是低效的。而且,connection 是与默认的数据库的连接,即 settings 中定义的 default 数据库。如果项目配置了多个数据库(列如主从数据库),那么 connection.close()则不能与关闭其他数据库的连接,问题仍未解决。
    • 方法2直接修改数据库超时时间,很容易影响别的服务,会带来很多潜在的问题。

    针对我的情况,我参考了 Django 源码涉及数据库连接维护的部分。

    在 django.db.__init__.py 中,有以下代码片段:

    # Register an event to reset transaction state and close connections past
    # their lifetime.
    def close_old_connections(**kwargs):
        for conn in connections.all():
            conn.close_if_unusable_or_obsolete()
    signals.request_started.connect(close_old_connections)
    signals.request_finished.connect(close_old_connections)
    

    可见,Django 将请求开始和请求结束信号绑定给了 close_old_connections函数,每当有请求开始和结束以后,Django 都会检查目前有没有失效的连接,如果有的话就将其关闭。通过这种办法,Django 保证处理请求时,数据库连接都是可用的,不会出现我遇到的问题;而我的程序在涉及 Model 操作时,没有检查连接的有效性,因而出现了题目中的错误。

    解决问题

    在定位到问题且知道处理方法后,接下来的工作就非常简单了。 仿照上述代码,定义函数:

    from django.db import connections
    
    
    def close_old_connections():
        for conn in connections.all():
            conn.close_if_unusable_or_obsolete()
    

    然后在每次 Model 操作前调用close_old_connections()就解决问题了。

    James Zhao

    Henry Zhu • 2 months ago
    还有一种解决办法, 在Django中修改pool_recycle的值, 让它小于mysql的wait_timeout, 保证conn的实效性.
    但我还在尝试中, 要是成功了, 回来留言.


    •Reply•Share ›
    Avatar
    zhaojames0707 Mod Henry Zhu • 2 months ago
    等你的好消息:)


    •Reply•Share ›
    Avatar
    Henry Zhu zhaojames0707 • 2 months ago
    本来想直接设置CONN_MAX_AGE, 让它比wait_timeout小就行了. 后来发现, 像你文档说的, 只有request_started和request_finished的时候, 才会用到CONN_MAX_AGE..
    看来只能在任务开始和结束的时候, 手动调用close_old_connections了.
    ---
    但是你为什么重新写了close_old_connections呢(和`django.db.__init__.py`中的好像一模一样)?


    •Reply•Share ›
    Avatar
    zhaojames0707 Mod Henry Zhu • 2 months ago
    哈哈,因为这个方法在文档里没有提到,担心Django后续会去掉或者改地方;可能担心多余了,直接用Django里的就好。

  • 相关阅读:
    Linux防火墙配置(iptables, firewalld)
    利用RMAN恢复整个数据库
    RMAN常用命令汇总!
    Oracle RMAN 恢复数据库到不同主机(二)
    Oracle RMAN 恢复数据库到不同主机(一)
    linux sar命令详解
    Linux定时任务Crontab命令详解
    Win7 U盘安装Ubuntu16.04 双系统详细教程
    linux定时任务crontab
    linux服务器端口netstat
  • 原文地址:https://www.cnblogs.com/ExMan/p/9895191.html
Copyright © 2011-2022 走看看