记一次使用 MySQLdb 事务时,select 出现的 bug
场景还原
为了能够清楚的描述这个问题,我们写一个简单的服务,语言使用 python,框架使用 tornado,数据库使用 mysql,数据库连接使用 MySQLdb。
在数据库里有这样一张表:
1 2 3 4 5
drop table if exists singers;CREATE TABLE singers( id int PRIMARY KEY AUTO_INCREMENT, name char (20 ) NOT NULL );
而我们的服务器代码也是很简单的, 根本没什么服务可言,就是收到访问,就读取 id 为 1 的歌手姓名,并打印:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
import MySQLdbimport tornado.ioloopimport tornado.webhost = '127.0.0.1' user = 'root' passwd = '627' port = 3306 db = 'test' conn = MySQLdb.connect(host=host, user=user, passwd=passwd, port=port, db=db) class MainHandler (tornado.web.RequestHandler) : def get (self) : cur = conn.cursor() cur.EⅩEcute("select name from singers Where `id` = 1" ) res = cur.fetchone() print res self.write("ok" ) application = tornado.web.Application([ (r"/" , MainHandler), ]) if __name__ == "__main__" : application.listen(8888 ) tornado.ioloop.IOLoop.instance().start()
并且我们的数据库中会有这样一条记录:
---- ------------
在 shell 请求此服务和打印的结果:
curl http:
现在我们保持服务器开启,在 mysql 中输入如下命令:
update singers set name = 'leehom' where id = 1 ;
再在 shell 请求此服务和打印的结果:
curl http:
好的,bug 出现了!那么有两种方式解决它,第一种是重启服务器,这种方法可以忽略了。第二种就是将代码修改成这种方式:
1 2 3 4
cur = conn.cursor() cur.EⅩEcute("select name from singers Where `id` = 1" ) res = cur.fetchone() print res
1 2 3 4 5
cur = conn.cursor() cur.EⅩEcute("select name from singers Where `id` = 1" ) res = cur.fetchone() conn.commit() print res
注意那条 commit 语句,本文其余部分解读此问题原因。
什么是事务
事务(Transaction)是并发控制的基本单位。是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
比如一个表在更新插入语句中,会经历 a,b,c,d,e 五个状态,那么如果我将 b-