mysql事务回滚使用与常见问题介绍
MYSQL中只有INNODB和BDB类型的数据表才能支持事务处理,其他的类型是不支持的! ***:一般MYSQL数据库默认的引擎是MyISAM,这种引擎不支持事务,如果要让MYSQL支持事务,可以自己手动修改.
方法如下:
1.修改c:appservmysqlmy.ini文件,找到skip-InnoDB,在前面加上#,后保存文件。
2.在运行中输入:services.msc,重启mysql服务。
3.到phpmyadmin中,mysql->show engines;(或执行mysql->show variables like 'have_%'; ),查看InnoDB为YES,即表示数据库支持InnoDB了,也就说明支持事务transaction了.
4.在创建表时,就可以为Storage Engine选择InnoDB引擎了,如果是以前创建的表,可以使用如下代码:
- mysql->altertabletable_nametype=InnoDB;
- --或
- mysql->altertabletable_nameengine=InnoDB;
来改变数据表的引擎以支持事务.
事务回滚在事务中,每个正确的原子操作都会被顺序执行,直到遇到错误的原子操作,此时事务会将之前的操作进行回滚,回滚的意思是如果之前是插入操作,那么会执行删除插入的记录,如果之前是update操作,也会执行update操作将之前的记录还原,因此,正确的原子操作是真正被执行过的.
MYSQL的事务处理主要有两种方法.
1、用begin,rollback,commit来实现
begin 开始一个事务
rollback 事务回滚
commit 事务确认
2、直接用set来改变mysql的自动提交模式
MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行,我们可以通过:
set autocommit=0 禁止自动提交
set autocommit=1 开启自动提交
来实现事务的处理,当你用 set autocommit=0 的时候,你以后所有的SQL都将做为事务处理,直到你用commit确认或rollback结束.
注意当你结束这个事务的同时也开启了个新的事务,按第一种方法只将当前的作为一个事务!
PHP实现MySQL事务回滚,创建一个测试的数据库,代码如下:
- mysql>CREATEDATABASE`shop_test`DEFAULTCHARSET=utf8COLLATE=utf8_general_ci;
- QueryOK,1rowaffected(0.00sec)
- mysql>useshop_test;
- Databasechanged
- mysql>CREATETABLEIFNOTEXISTS`user_account`(
- ->`user`varchar(20)NOTNULL,
- ->`money`INT(10)NOTNULL,
- ->PRIMARYKEY(`user`)
- ->)ENGINE=InnoDBDEFAULTCHARSET=utf8COLLATE=utf8_general_ci;
- QueryOK,0rowsaffected(0.51sec)
- mysql>CREATETABLEIFNOTEXISTS`user_order`(
- ->`id`INT(10)NOTNULL,
- ->`user`VARCHAR(20)NOTNULL,
- ->`price`INT(10)NOTNULL,
- ->`count`INT(10)NOTNULL,
- ->PRIMARYKEY(`id`)
- ->)ENGINE=InnoDBDEFAULTCHARSET=utf8COLLATE=utf8_general_ciAUTO_INCREMENT=1;
- QueryOK,0rowsaffected(0.33sec)
- mysql>INSERTINTO`user_account`VALUES('luchanghong','100');
- QueryOK,1rowaffected(0.00sec)
PHP测试代码:
- $conn=mysql_connect('127.0.0.1','root','root');
- mysql_select_db('shop_test');
- mysql_query('SETNAMESUTF8');
- #starttransaction
- mysql_query("STARTTRANSACTION");
- $sql="INSERTINTO`user_order`VALUES('1','luchanghong','10','2')";
- mysql_query($sql);
- $sql_2="UPDATE`user_account`SET`money`=`money`-10*2WHERE`user`='luchanghong'";
- mysql_query($sql_2);
- //phpfensi.com
- if(mysql_errno()){
- echo"error";
- mysql_query("ROLLBACK");
- }else{
- echo"OK";
- mysql_query("COMMIT");
- }
执行一次后查看数据库,代码如下:
- mysql>SELECT*FROM`user_account`;
- +-------------+-------+
- |user|money|
- +-------------+-------+
- |luchanghong|80|
- +-------------+-------+
- 1rowinset(0.00sec)
- mysql>SELECT*FROM`user_order`;
- +----+-------------+-------+-------+
- |id|user|price|count|
- +----+-------------+-------+-------+
- |1|luchanghong|10|2|
- +----+-------------+-------+-------+
- 1rowinset(0.00sec)
那么,我添加一个条件,就是每次更新完 user_account 表后检查用户的 money 是否为负值,如果为负值那么就要撤销之前的操作,执行事务回滚,代码如下:
- $conn=mysql_connect('127.0.0.1','root','root');
- mysql_select_db('shop_test');
- mysql_query('SETNAMESUTF8');
- //starttransaction
- mysql_query("STARTTRANSACTION");
- $sql="INSERTINTO`user_order`(`user`,`price`,`count`)VALUES('luchanghong','10','2')";
- mysql_query($sql);
- $sql_2="UPDATE`user_account`SET`money`=`money`-10*2WHERE`user`='luchanghong'";
- mysql_query($sql_2);
- if(mysql_errno()){
- echo"errorn";
- mysql_query("ROLLBACK");
- }else{
- $money=check_remain_money('luchanghong');
- echo$money."";
- if($money<0){
- echo"Noenoughmoneyn";
- mysql_query("ROLLBACK");
- }else{
- echo"OKn";
- mysql_query("COMMIT");
- }
- }
- functioncheck_remain_money($user){
- $sql="SELECT`money`FROM`user_account`WHERE`user`='{$user}'";
- $result=mysql_fetch_assoc(mysql_query($sql));
- return!emptyempty($result)?$result['money']:0;
- }
接着,在shell下多次执行这php文件,WIN下就手动执行几次吧,代码如下:
- lch@LCH:~/Desktop$forxin`seq6`;dophptransaction.php;done
- 60OK
- 40OK
- 20OK
- 0OK
- -20Noenoughmoney
- -20Noenoughmoney
再看数据库数据,代码如下:
- mysql>SELECT*FROM`user_account`;
- +-------------+-------+
- |user|money|
- +-------------+-------+
- |luchanghong|0|
- +-------------+-------+
- 1rowinset(0.00sec)
- mysql>SELECT*FROM`user_order`;
- +----+-------------+-------+-------+
- |id|user|price|count|
- +----+-------------+-------+-------+
- |1|luchanghong|10|2|
- |2|luchanghong|10|2|
- |3|luchanghong|10|2|
- |4|luchanghong|10|2|
- |5|luchanghong|10|2|
- +----+-------------+-------+-------+
- 5rowsinset(0.00sec)
1、为什么auto_increament没有回滚?
因为innodb的auto_increament的计数器记录的当前值是保存在存内 存中的,并不是存在于磁盘上,当mysql server处于运行的时候,这个计数值只会随着insert改增长,不会随着delete而减少。而当mysql server启动时,当我们需要去查询auto_increment计数值时,mysql便会自动执行:SELECT MAX(id) FROM 表名 FOR UPDATE;语句来获得当前auto_increment列的最大值,然后将这个值放到auto_increment计数器中。所以就算 Rollback MySQL的auto_increament计数器也不会作负运算。
2、MySQL的事务对表操作的时候是否是物理操作?
MySQL的事务是有redo和undo的,redo操作的所有信息都是记录到 redo_log中,也就是说当一个事务做commit操作时,需要先把这个事务的操作写到redo_log中,然后再把这些操作flush到磁盘上,当 出现故障时,只需要读取redo_log,然后再重新flush到磁盘就行了.
而对于undo就比较麻烦,MySQL在处理事务时,会在数据共享 表空间里申请一个段叫做segment段,用保存undo信息,当在处理rollback,不是完完全全的物理undo,而是逻辑undo,就是说会对之 前的操作进行反操作,但是这些共享表空间是不进行回收的,这些表空间的回收需要由mysql的master thread进程来进行回收.
热门评论