最近我们的Django项目供Java Sofa应用进行tr调用时, 经常会出现一个异常: django.db.utils.OperationalError: (2006, 'MySQL server has gone away'). 本文记录了分析, 本地重现与解决此问题的全过程.

原因分析:

Django在1.6引入长链接(Persistent connections)的概念, 可以在一个HTTP请求中一直用同一个连接对数据库进行读写操作.

但我们的应用对数据库的操作太不频繁了, 两次操作数据库的间隔大于MySQL配置的超时时间(默认为8个小时), 导致下一次操作数据库时的connection过期失效.

Our databases have a 300-second (5-minute) timeout on inactive connections. That means, if you open a connection to the database, and then you don’t do anything with it for 5 minutes, then the server will disconnect, and the next time you try to execute a query, it will fail.

重现问题:

设置mysql wait_timeout为10s

在macOS上的mysql配置文件路径: /usr/local/etc/my.cnf

1

2

3

4

5

6

# Default Homebrew MySQL server config

[mysqld]

# Only allow connections from localhost

bind-address = 127.0.0.1

wait_timeout = 10

interactive_timeout = 10

重启mysql:

1

2

3

4

➜ ~ brew services restart mysql

Stopping `mysql`... (might take a while)

==> Successfully stopped `mysql` (label: homebrew.mxcl.mysql)

==> Successfully started `mysql` (label: homebrew.mxcl.mysql)

检查wait_timeout的值是否已被更新.

1

2

3

4

5

6

7

8

9

mysql> show variables like '%wait_timeout%';

+--------------------------+----------+

| Variable_name | Value |

+--------------------------+----------+

| innodb_lock_wait_timeout | 50 |

| lock_wait_timeout | 31536000 |

| wait_timeout | 10 |

+--------------------------+----------+

3 rows in set (0.00 sec)

重现exception:

1

2

3

4

5

6

7

8

9

10

11

12

>>> XXX.objects.exists()

True

>>> import time

>>> time.sleep(15)

>>> XXX.objects.exists()

True

>>> XXX.objects.exists()

...

django.db.utils.OperationalError: (2013, 'Lost connection to MySQL server during query')

>>> XXX.objects.exists()

...

django.db.utils.OperationalError: (2006, 'MySQL server has gone away')

有意思的一个点是, sleep 10s 之后, 第一次操作数据库, 会出现(2013, 'Lost connection to MySQL server during query’)异常. 之后再操作数据库, 才会抛出(2006, 'MySQL server has gone away’)异常.

解决问题:

第一个最暴力的方法就是增加mysql的wait_timeout让mysql不要太快放弃连接. 感觉不太靠谱, 因为不能杜绝这种Exception的发生.

第二个办法就是手动把connection直接关闭:

1

2

3

4

5

6

7

8

>>> Alarm.objects.exists()

True

>>> from django.db import connection

>>> connection.close()

>>> time.sleep(10)

>>> Alarm.objects.exists()

True

>>>

发现不会出现(2006, 'MySQL server has gone away’)异常了, 但总感觉还是不够优雅.

最终决定在客户端(Django), 设置超时时间(CONN_MAX_AGE: 5)比mysql服务端(wait_timeout = 10)小:

1

2

3

4

5

6

7

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.mysql',

'CONN_MAX_AGE': 5,

}

}

但很奇怪没有生效??? 看了源代码, 发现只有在request_started(HTTP request)和request_finished的时候, 在close_if_unusable_or_obsolete才用到CONN_MAX_AGE并去验证时间关闭connection.

具体代码见: python3.6/site-packages/django/db/__init__.py#64

1

2

3

4

5

6

7

8

9

# 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)

而我的代码是处理一个任务而不是HTTP请求, 所以不会触发这个signal. 于是我写了一个装饰器, 在任务的开始和结束的时候, 关闭所有数据库连接.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

from django.db import connections

# ref: django.db.close_old_connections

def close_old_connections():

for conn in connections.all():

conn.close_if_unusable_or_obsolete()

def handle_db_connections(func):

def func_wrapper(request):

close_old_connections()

result = func(request)

close_old_connections()

return result

return func_wrapper

# ------割-------

@handle_db_connections

def process_trsbrain_request(request):

...

ps. CONN_MAX_AGE默认其实为0, 意味着默认在http请求和结束时会关闭所有数据库连接.

其他:

django.db中connection和connections的区别???

connection对应的是默认数据库的连接, 用代码表示就是connections[DEFAULT_DB_ALIAS]

connections对应的是setting.DATABASES中所有数据库的connection

django mysql 2006_Django (2006, 'MySQL server has gone away') 本地重现与解决相关推荐

  1. MySQL启动出现The server quit without updating PID file错误解决办法

    解决办法其实很简单: 将 /etc/mysql 下的 my.cnf 文件删除,再次启动MySQL服务 删除前注意备份

  2. php5.5 mysql密码无法_MySQL密码正确却无法本地登录的解决方法

    MySQL root密码正确,却怎么也无法从本地登录MySQL,提示 ERROR 1045 (28000): Access denied for user 'root'@'localhost' (us ...

  3. pymysql.err.OperationalError: (2006, “MySQL server has gone away (BrokenPipe

    目录 第一种情况:真的是连接数据库超时导致,比较常见 第二种情况:MySQL插入内容超过4M 在使用python+Django写项目时,需要用到定时任务apscheduler,但服务在长时间运行时,定 ...

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

    问题描述 使用django+celery时, 运行长时间的任务, 一开始操作一次数据库, 20个小时后再次操作数据库. 过了20多个小时, 再次操作数据库时报错. raised unexpected: ...

  5. (2006, 'MySQL server has gone away') 错误解决 - dba007的空间 - 51CTO技术博客

    (2006, 'MySQL server has gone away') 错误解决 - dba007的空间 - 51CTO技术博客 (2006, 'MySQL server has gone away ...

  6. #2006 - MySQL server has gone away 问题解决方法 (全) (转)

    #2006 - MySQL server has gone away 问题解决方法 (全) (转) 参考文章: (1)#2006 - MySQL server has gone away 问题解决方法 ...

  7. Yii2 解决2006 MySQL server has gone away问题

    Yii2 解决2006 MySQL server has gone away问题 参考文章: (1)Yii2 解决2006 MySQL server has gone away问题 (2)https: ...

  8. Yii 数据库重连告别General error: 2006 MySQL server has gone away

    General error: 2006 MySQL server has gone away 错误原因 制造错误 解决办法 最新办法 错误原因 Mysql has gone away MySQL 服务 ...

  9. peewee mysql自动断开_flask+mako+peewee(下)(解决了Error 2006: MySQL server has gone away)

    这篇主要介绍在这次项目中使用的peewee 首先我们要初始化一个数据库连接对象.这里我使用了peewee提供的链接池.当然你也可以直接指定连接例如: db = SqliteDatabase('base ...

最新文章

  1. [python]关于字符串查找和re正则表达式的效率对比
  2. Python解密2021年最新富豪榜,马云居然连前三都没进
  3. 调查显示:中国医生乐于以新媒体为途径普及健康科普信息
  4. ebtables安装和使用
  5. string 与 c style 字符串的效率测试
  6. 图像处理之ROI区域裁剪
  7. Veeam Backup Replication试用(三):配置备份(Backup Job)与恢复(Restore)
  8. 【Verilog】数据流建模传输问题:赋值传输有方向
  9. 人是需要一个圈子互相激励的
  10. URLEncoder和URLDecoder;cookie中保存特殊字符以及URL中乱码问题解决方案
  11. 在RT-Thread上移植EtherCAT开源主站SOEM1.4.0
  12. 1110_win10专业版官方原版镜像64位
  13. 深入理解java8,深入理解Java8新特性与源码剖析
  14. 计算机指令执行过程详解
  15. PyTorch系列 | correct += (predicted == labels).sum().item()的理解
  16. 服务器如何查看隐藏文件,BlueHost主机如何查看隐藏文件夹
  17. String字符串操作--切割,截取,替换,查找,比较,去空格.....
  18. 搜狗输入法人机交互设计的用户体验
  19. 九步学会Git版本控制的基础使用(保姆级教程)
  20. quicktime 的历史

热门文章

  1. Java黑皮书课后题第5章:*5.14(计算最大公约数)下面是求两个整数n1和n2的最大公约数的程序清单5-9的另一种解法:...提示用户输入两个正整数,然后显示最大公约数
  2. c语言学习之用筛选法求100之内的素数。
  3. C++中的抽象类以及接口的区别联系
  4. mysql DATETIME和TIMESTAMP类型
  5. 在GitHub中创建目录
  6. mysql,mybatis常用小知识点
  7. log4j2动态修改日志级别及拓展性使用
  8. zbb20170606 oracle 查看空表
  9. WPF 自定义快捷键命令(COMMAND)(转)
  10. 设计模式-单例模式(1)