Mysql事务处理问题
Mysql事务处理问题
今天和同学讨论起数据库事务处理的问题,感觉讨论中明白了一些,有些知识看过了,但是没有实际使用还是不理解。事务处理就是将一系列操作当做一个原子操作,要么全部执行成功,如果执行失败则保留执行期的状态。通过提交和回滚机制来实现操作,如果全部执行成功通过提交执行commit结果就会记录到数据库中,如果执行失败通过回滚操作rollback将发生错误之前的所有错误消除,回退到原来状态。
事务都应该具备ACID特征。所谓ACID是Atomic(原子性),Consistent(一致性),Isolated(隔离性),Durable(持续性)四个词的首字母所写,下面以“银行转帐”为例来分别说明一下它们的含义:
原子性:组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。换句话说,事务是不可分割的最小单元。比如:银行转帐过程中,必须同时从一个帐户减去转帐金额,并加到另一个帐户中,只改变一个帐户是不合理的。
一致性:在事务处理执行前后,数据库是一致的。也就是说,事务应该正确的转换系统状态。比如:银行转帐过程中,要么转帐金额从一个帐户转入另一个帐户,要么两个帐户都不变,没有其他的情况。
隔离性:一个事务处理对另一个事务处理没有影响。就是说任何事务都不可能看到一个处在不完整状态下的事务。比如说,银行转帐过程中,在转帐事务没有提交之前,另一个转帐事务只能处于等待状态。
持续性:事务处理的效果能够被永久保存下来。反过来说,事务应当能够承受所有的失败,包括服务器、进程、通信以及媒体失败等等。比如:银行转帐过程中,转帐后帐户的状态要能被保存下来。
注意Mysql支持的存储引擎中,默认为MyISAM,是不支持事务处理的,一般都有InnoDB,是支持事务型的。
(1)如果对一个表进行操作的时候需要事务支持,需要配置存储引擎为InnoDB等支持事务型的。
create table XX() engine=InnoDB;
(2)默认情况下,mysql是自动提交模式(autocommit=1),此时会在每一条语句执行完毕后将所做修改立即提交,此时的commit相当于没用的,rollback只对前一句语句起作用,其实也没用,一条mysql语句默认也是原子操作,没必要。
如果设置默认事务处理,需要将自动提交模式关闭即将autocommit设置为0.
set autocommit=0; 设置模式为关闭
select @@autocommit; 查看值是否已经改变
注意,如果在客户端设置的话,设置完,之后断掉连接后再重连又恢复默认设置。每个客户端只能设置客户自己的。
(3)如果自动提交模式是打开的,则需使用语句:
start transaction; 开始事务处理
XX1;
XX2;
commit; / rollback;
来开始事务处理;而如果设置为关闭,则无需使用start transaction,连续语句就为事务指导rollback或者commit。
(4)注意创建、改变、删除数据库或者其中的数据定义语言以及锁有关的都不能成为事务的一部分,如下面:
?
1 2 3 4 5 | start transaction; insert into test1 values("8"); create table test2(i int); insert into test1 values("8"); rollback; |
html" name="code">执行一个事务处理,当执行到要创建表时,mysql会自动提交,然后再执行创建语句。如果test1的i为主键,则第三条语句出错,回滚时test1还是插入成功,且创建了表test2.
(5)python中使用数据库,最好采用这种形式,
?
1 2 3 4 5 6 7 8 9 | import MySQLdb try: conn = MySQLdb.connect(host="localhost",user="root",passwd="your passwd",db="dbName") except MySQLdb.Error,e: print "Mysql Error %d: %s" % (e.args[0], e.args[1]) else: pass #conn.close() |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | try: cur=conn.cursor() cur.execute('set autocommit=0') #cur.execute('start transaction') cur.execute('insert into test1 values("8")') cur.execute('insert into test1 values("8")') except MySQLdb.Error,e: conn.rollback() print "Mysql Error %d: %s" % (e.args[0], e.args[1]) else: conn.commit() cur.close() conn.close() |
(6)并行处理问题
Mysql是一个多用户的系统,有多用户在同一时间访问统一数据表,MySIAM采用的是数据表级的锁定标记,来保证同一时间只有一个用户访问此表;Innodb采用了数据行级的访问机制,即两个用户可以对同一个表中不同行的数据同时进行修改,而如果是同一行,则先来的用户先锁住此行,操作结束释放锁后,下一个用户才能操作。
(7)事务处理的隔离性问题
InnoDB默认的隔离级别是repeatable read,如果某个用户两次执行同一个select语句,其结果是可重复的,如果在事务期间有用户对所要读取的数据进行了操作,那么也不会有显示,比如一个存储引擎为innodb的表,如果有一个客户用事务来select读取表数据,另一个用户此时对表做了一个插入之类的操作,第一个用户再进行同样的select读取时,显示数据是没有变化的。
(8)多语句操作非原子操作
如上面(6)中会出现一个问题,如果是一个事务操作,读取数据后,想对数据进行操作,但是可能有另外一个人对此做了操作,那再对此数据进行操作就不对了。
此时需要明确加锁来锁住表,防止别人更改数据,执行结束后释放锁。
lock tables XX write;
XXXXXX;
unlock tables;
也可以使用相对更新代替绝对更新,相对于当前值进行更新,不根据上次的值算出一个绝对值进行更新。这样避免了多条语句的非原子操作。
set a = a - 3 XXXXXXXXXXX;