在使用mysql.connector.connect 创建数据库连接时,有一个非常重要的参数autocommit,它的默认值是False。如果设置为True,进行DML操作则不需要commit
import mysql.connector
mydb = mysql.connector.connect(
host="your ip", # 数据库主机地址
user="flink_user", # 数据库用户名
passwd="123456", # 数据库密码
port=6606,
database='flink_db',
autocommit=True
)
mycursor = mydb.cursor()
mycursor.execute("insert into city(name)values('北京')")
mycursor.execute("insert into city(name)values('上海')")
不执行mydb.commit(),这两条数据也能写进数据库,这样看起来方便和安全很多,不需要担心因为忘记commit而导致DML语句执行失效,可这样做性能会差很多。对于mysql而言,insert一条数据时其内部会创建一个事务,真正的insert操作是在事务内进行的,如果我们在执行大量insert操作时主动创建事务,那么这一组insert就是在一个事务内执行,减少了mysql创建事务的消耗。
autocommit 默认为False,这意味着你必须执行commit方法来提交事务。第一条sql被执行时就会隐性的打开事务,commit方法被执行时,事务结束,对于数据库的DML操作都将生效,你可以使用in_transaction属性来查看数据库连接是否处于事务状态中
import mysql.connector
mydb = mysql.connector.connect(
host="your ip", # 数据库主机地址
user="flink_user", # 数据库用户名
passwd="123456", # 数据库密码
port=6606,
database='flink_db'
)
mycursor = mydb.cursor()
print(mydb.in_transaction) # 没有开启事务 0
mycursor.execute("insert into city(name)values('北京')")
print(mydb.in_transaction) # 已经开启事务 1
mycursor.execute("insert into city(name)values('上海')")
mydb.commit()
print(mydb.in_transaction) # commit 之后,事务已经结束 0
使用start_transaction可以主动开启事务,该方法只有在autocommit设置为True时才能使用,事务一旦开启,只能使用commit或者rollback才能结束,在一个事务结束之前,不能开启新的事务。
事务里的操作,要么都成功,要么都失败,不能一部分成功,一部分失败,如果存在问题,需要使用rollback进行回滚,回滚到事务开启前的状态。
为了更好的演示事务的作用,我设计一个简单的实验
关于第3步,抛出异常的前提是业务层面上出现了错误,这里我简单的把业务错误设置成数据量为1,是为了简化实验,工作实践中要根据业务逻辑来决定是否抛出异常。比如银行转账,小明和小红转了1000元钱,那么需要有一个步骤检查小明的账号是不是减少了1000,小红的账号是不是增加了1000,如果这两个状态有一个不成立,都必须立即回滚。
下面是实验的代码
import mysql.connector
mydb = mysql.connector.connect(
host="10.110.30.3", # 数据库主机地址
user="flink_user", # 数据库用户名
passwd="123456", # 数据库密码
port=6606,
database='flink_db',
autocommit=True
)
def add(city):
mycursor = mydb.cursor()
mycursor.execute(f"insert into city(name)values('{city}')")
mycursor.close()
def get_count():
mycursor = mydb.cursor()
mycursor.execute("select count(1) as count from city")
data = mycursor.fetchone()
mycursor.close()
if data[0] == 1:
print(data, '抛出异常')
raise Exception('抛出异常')
mydb.start_transaction()
try:
add('北京')
get_count()
mydb.commit()
except:
mydb.rollback()
我设置autocommit为True,为的是向你展示如何主动的开启一个事务,autocommit默认是0,不需要使用start_transaction主动开启事物,而是隐性的在执行第一条sql时开启事物。理解这段代码,有一个关键点,执行完add函数后,执行get_count函数前,如果你此时去数据库里查看city表会发现这张表是没有数据的,但是在执行get_count函数时,却可以查到数据。出现这个现象的原因是commit还没有被执行,事务没有结束,这意味着这些sql的执行结果对于你是不可见的,而对于处于事务状态中的sql语句,前面sql的执行结果却是可见的,即在select语句看来,前面的insert语句已经执行成功了,因此才能查到city表里的数据量为1。
第3步抛出异常后,立即进行回滚操作,这时你去数据库里看,city表里果真没有数据。
有人可能有些疑惑,为什么一定要执行rollback呢,get_count函数抛出异常,mydb.commit()语句不会被执行,事务不会被提交,city表里同样不会有数据写入,一切都还是事务开启前的状态。如果你是这样考虑的,那么显然,你忽略了一个关键,只有commit成功执行或者执行rollback,事务才会结束,如果不执行rollback,而后面又不小心的执行了commit,那一条数据就会写入到city表里。即便你后面没有执行commit,也不行,当前这个事务没有结束,你就不能开启新的事物。下面的代码就会犯这样的错误。
mydb.start_transaction()
try:
add('北京')
get_count()
mydb.commit()
except:
pass
mydb.commit()
QQ交流群: 211426309