版权归作者所有,任何形式转载请联系作者。
作者:petanne(来自豆瓣)
来源:https://www.douban.com/note/580618150/
缘由:有一个django守护进程Daemon一直在后台运行,轮询读数据库,导致被锁无法ALTER表结构。
涉及django==1.4 django==1.9 mysql
在1.4中(1.5之前版本均为这样),默认的transaction为autocommit,在读写数据库时,均会对数据表产生一个状态为RUNNING的事务,写操作在save的时候会触发autocommit,结束该事务,但是读操作不会触发autocommit,导致该事务一直RUNNING,并且不能对表结构进行操作。
注:1.6之前的autocommit只对写操作有效,对读操作无效!
注:1.6之前的autocommit只对写操作有效,对读操作无效!
注:1.6之前的autocommit只对写操作有效,对读操作无效!
先看看这个很有趣的例子,复现方式:
1.在django的python shell 环境下执行一条读语句
python manage.py shell
>>User.objects.get(id=1)
<User: xxxxx>
2.在mysql shell里执行
>>use information_schema;
>>select * from INNODB_TRX G;
此时,可以看到有一个RUNNING的事务
>>show processlist;
此时,可以看到process里有一个Id字段为上面事务的trx_mysql_thread_id的process,然后看该process的Host字段(通信的客户端IP和端口),可以确定此事务即为1步骤读操作占用。
3.退出1中的python shell,可发现2中RUNNING的事务被结束。
也就是说,守护进程Daemon一直运行不退出的话,只要里面有读操作,就会因为事务占用一直锁住这个表。同理,每次web请求,使用ORM读的时候,也是这样,但web请求结束django关掉数据库连接,就会结束该事务。
解决方法:
在django==1.5版本之前,在Daemon里每次执行完读操作后,都手动commit一下
方法1:
>>from django.db import transaction
>>User.objects.get(id=1)
>>transaction.commit()
方法2:将查询语句置于事务管理commit_on_success的block下,顾名思义,其作用为在block中的代码执行完毕且无Exception的时候自动commit
>>with transaction.commit_on_success():
User.objects.get(id=1)
django==1.6版本,在settings.py的数据库配置项DATABASES中,添加了"AUTOCOMMIT"参数,默认为True,详见文档https://docs.djangoproject.com/en/dev/ref/settings/#autocommit,即为所有操作自动提交。
另外注意在1.6 至 1.6.2版本中,该参数有BUG,会导致AUTOCOMMIT不生效,django在1.6.2版本fix了该BUG,详见https://docs.djangoproject.com/en/1.10/releases/1.6.2/