问题描述:使用pymysql操作数据库时,先使用游标将多条sql语句添加到缓存中,然后一次性提交事务,那么同一次提交的事务中,查询的sql语句是否会查询到本次事务中增加的sql语句插入的记录呢?
首先声明:这里使用的表是innoDB存储引擎,对该引擎,支持事务操作,以及行级锁,外键等等。mysql数据中的知识
开始测试:
先来测试,创建表,和在该表中插入记录,如果是事务,那么是否遵守其约定?
import pymysql
config = {
'host':'localhost'
,'user':'root'
,'password':'xxxxxx'
,'database':'dbx'
,'charset':'utf8'
,'port':3306 #注意端口为int 而不是str
}
db = pymysql.connect(**config)
cur = db.cursor()
try:
c_sql = '''create table test1(
id int(3) zerofill primary key auto_increment,
name varchar(15) not null
)engine=InnoDB,charset=utf8;
'''
cur.execute(c_sql)
#插入一条记录
i_sql ='insert into test1(name) values("chizer");'
cur.execute(i_sql)
#统一提交
db.commit()
except Exception as e:
db.rollback()
print('Failed:',e)
cur.close()
db.close()
这里是将创建表和在该表插入纪录统一提交,结果运行没有问题。那么交换一下顺序,先插入一个记录,前提是数据库中没有该表,再去创建,那么统一提交之后是否按照队列执行
import pymysql
config = {
'host':'localhost'
,'user':'root'
,'password':'xxxxxx'
,'database':'dbx'
,'charset':'utf8'
,'port':3306 #注意端口为int 而不是str
}
db = pymysql.connect(**config)
cur = db.cursor()
try:
c_sql = '''create table test1(
id int(3) zerofill primary key auto_increment,
name varchar(15) not null
)engine=InnoDB,charset=utf8;
'''
#插入一条记录
i_sql ='insert into test1(name) values("chizer");'
cur.execute(i_sql)
cur.execute(c_sql) #此处先提交插入,后提交创建表
#统一提交
db.commit()
except Exception as e:
db.rollback()
print('Failed:',e)
cur.close()
db.close()
结果是报错,这可以想的明白,比如mysql中,表还没有创建就要插入?当然找不到表。这是一个sql错误。
Failed: (1146, "Table 'db5.test1' doesn't exist")
接下来回到正题,在该表中先插入在查询 和 先查询在插入 得到的查询结果 是否和 添加到缓存顺序有关?
import pymysql
config = {
'host':'localhost'
,'user':'root'
,'password':'xxxxxx'
,'database':'dbx'
,'charset':'utf8'
,'port':3306 #注意端口为int 而不是str
}
db = pymysql.connect(**config)
cur = db.cursor()
try:
c_sql = '''create table test1(
id int(3) zerofill primary key auto_increment,
name varchar(15) not null
)engine=InnoDB,charset=utf8;
'''
i_sql ='insert into test1(name) values("chizer");'
s_sql = 'select * from test1;'
#先去查询,查询不需要提交到缓存,所以报错是没有表,因为创建表的事物还没有commit
cur.execute(s_sql)
cur.execute(c_sql) #加入创表
cur.execute(i_sql) #加入插入记录
db.commit()#统一提交
# cur.execute(s_sql) #加入查询记录
result = cur.fetchall() #得到查询结果
print(result)
except Exception as e:
db.rollback()
print('Failed:',e)
cur.close()
db.close()
Failed: (1146, "Table 'db5.test1' doesn't exist") 同样的错误类似上面,没有创建表。
接下来:
import pymysql
config = {
'host':'localhost'
,'user':'root'
,'password':'123456'
,'database':'db5'
,'charset':'utf8'
,'port':3306 #注意端口为int 而不是str
}
db = pymysql.connect(**config)
cur = db.cursor()
try:
c_sql = '''create table test1(
id int(3) zerofill primary key auto_increment,
name varchar(15) not null
)engine=InnoDB,charset=utf8;
'''
i_sql ='insert into test1(name) values("chizer");'
s_sql = 'select * from test1;'
#先去查询,查询不需要提交到缓存,所以报错是没有表,因为创建表的事物还没有commit
# cur.execute(s_sql)
cur.execute(c_sql) #加入创表
cur.execute(i_sql) #加入插入记录
db.commit()#统一提交
#提交之后,再去查询,注意注意!!!该游标对象执行查询时往下并没有commit,
#那么可以认为执行查询语句时,并不会添加到缓存,而是直接执行!
cur.execute(s_sql) #执行查询记录
result = cur.fetchall() #得到查询结果
print(result)
except Exception as e:
db.rollback()
print('Failed:',e)
cur.close()
db.close()
得到的结果:((1, 'chizer'),)
那么再去试着在后面插入一条数据看看得到的查询结果>>
import pymysql
config = {
'host':'localhost'
,'user':'root'
,'password':'123456'
,'database':'db5'
,'charset':'utf8'
,'port':3306 #注意端口为int 而不是str
}
db = pymysql.connect(**config)
cur = db.cursor()
try:
c_sql = '''create table test1(
id int(3) zerofill primary key auto_increment,
name varchar(15) not null
)engine=InnoDB,charset=utf8;
'''
i_sql ='insert into test1(name) values("chizer");'
s_sql = 'select * from test1;'
cur.execute(c_sql) #加入创表
cur.execute(i_sql) #加入插入记录
db.commit()#统一提交
cur.execute(s_sql) #执行查询记录
result = cur.fetchall() #得到查询结果
print(result)
i_sql ='insert into test1(name) values("chizer2");'
cur.execute(i_sql)
db.commit() #提交第二条插入sql
except Exception as e:
db.rollback()
print('Failed:',e)
cur.close()
db.close()
返回结果:((1, 'chizer'),),符合预期猜测,插入一条记录之后去查询,得到一条,再去提交插入第二条时并不会查到。说明和查询的顺序以及提交的顺序有关。那么稍微改变一下代码,看一看结果,加第二次添加的cursor对象放到查询前面,同时将第二次提交的语句放在后面试试看>>
import pymysql
config = {
'host':'localhost'
,'user':'root'
,'password':'123456'
,'database':'db5'
,'charset':'utf8'
,'port':3306 #注意端口为int 而不是str
}
db = pymysql.connect(**config)
cur = db.cursor()
try:
c_sql = '''create table test1(
id int(3) zerofill primary key auto_increment,
name varchar(15) not null
)engine=InnoDB,charset=utf8;
'''
i_sql ='insert into test1(name) values("chizer");'
s_sql = 'select * from test1;'
cur.execute(c_sql) #加入创表
cur.execute(i_sql) #加入插入记录
db.commit()#统一提交
i_sql ='insert into test1(name) values("chizer2");'
cur.execute(i_sql)
cur.execute(s_sql) #执行查询记录
result = cur.fetchall() #得到查询结果
print(result)
db.commit() #提交第二条插入sql
except Exception as e:
db.rollback()
print('Failed:',e)
cur.close()
db.close()
得到结果:((1, 'chizer'), (2, 'chizer2'))
这个结果很意外,不是由commit执行提交第二次插入记录吗?查询只管去查询啊,怎么会得到两条记录?在运行代码时我发现得到打印结果之前,稍微的停顿了一下,说明在插入语句和提交之间的查询cur去执行查询时,会发生阻塞!这个结果让我感到很意外。当然,把查询cur总是放在最后,保证所有的commit都在前面执行完毕,一定会得到最后更新出来的那张表,去执行查到结果。这是没有问题的。
到这里应该明白,查询,是不需要commit,它是直接去数据库中查对应的表,如果没有这个表,就报错。如果查询写在添加和提交中间,会发生阻塞,等所有执行完才会去查询。
总结:创建,增,删,改 都需要用数据库连接的对象去提交事务,而查询并不用去提交,代码执行到查询处,游标对象会直接去查询,将得到的结果保存在自己的内部,使用fetch*方法可以获取。所以前面所说的问题根本不存在什么时候添加到缓存,与顺序是否有关,因为查询是不需要commit,它会直接去数据库中对应的表去查询。
那么和什么有关呢,查询的到的结果和commit的先后顺序有关,如果先提交了插入的记录,再去查询,那么ok,select * 会包括该记录,如果先去select * 再去将添加的记录commit ,那么查询得到的结果一定不包含该记录。
所以也不存在一开始的顾虑,即是否和存储引擎有关。其实问题很简单,我想的有点复杂化,关键在于查询不需要commit。
思考:pymysql为什么没有将查询加入到事务中,或者可以理解为查询不需要修改数据库连接的虚拟对象?所以不要提交它?
这些问题还需要进一步去探索,以上是我的一些个人看法,有什么问题希望大家指出,多多交流!