首页 > 数据库 > MySQL > 正文

mysql事务回滚使用与常见问题介绍

2024-07-24 12:38:07
字体:
来源:转载
供稿:网友

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引擎了,如果是以前创建的表,可以使用如下代码:

  1. mysql->alter table table_name type=InnoDB; 
  2. --或  
  3. mysql->alter table table_name engine=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事务回滚,创建一个测试的数据库,代码如下:

  1. mysql> CREATE DATABASE `shop_test` DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; 
  2. Query OK, 1 row affected (0.00 sec) 
  3.  
  4. mysql> use shop_test; 
  5. Database changed 
  6.  
  7. mysql> CREATE TABLE IF NOT EXISTS `user_account`( 
  8.     -> `uservarchar(20) NOT NULL
  9.     -> `money` INT(10) NOT NULL
  10.     -> PRIMARY KEY (`user`) 
  11.     -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; 
  12. Query OK, 0 rows affected (0.51 sec) 
  13.  
  14. mysql> CREATE TABLE IF NOT EXISTS `user_order`( 
  15.     -> `id` INT(10) NOT NULL
  16.     -> `userVARCHAR(20) NOT NULL
  17.     -> `price` INT(10) NOT NULL
  18.     -> `countINT(10) NOT NULL
  19.     -> PRIMARY KEY (`id`) 
  20.     -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1; 
  21. Query OK, 0 rows affected (0.33 sec) 
  22.  
  23. mysql> INSERT INTO `user_account` VALUES ('luchanghong''100'); 
  24. Query OK, 1 row affected (0.00 sec)  

PHP测试代码:

  1. $conn = mysql_connect('127.0.0.1''root''root'); 
  2. mysql_select_db('shop_test'); 
  3. mysql_query('SET NAMES UTF8'); 
  4.  
  5. # start transaction 
  6. mysql_query("START TRANSACTION"); 
  7. $sql = "INSERT INTO `user_order` VALUES ('1', 'luchanghong', '10', '2')"
  8. mysql_query($sql); 
  9. $sql_2 = "UPDATE `user_account` SET `money` = `money` - 10*2 WHERE `user` = 'luchanghong'"
  10. mysql_query($sql_2); 
  11. //Vevb.com 
  12. if (mysql_errno()){ 
  13.     echo "error"
  14.     mysql_query("ROLLBACK"); 
  15. }else
  16.     echo "OK"
  17.     mysql_query("COMMIT"); 

执行一次后查看数据库,代码如下:

  1. mysql> SELECT * FROM `user_account`; 
  2. +-------------+-------+ 
  3. user        | money | 
  4. +-------------+-------+ 
  5. | luchanghong |    80 | 
  6. +-------------+-------+ 
  7. 1 row in set (0.00 sec) 
  8.  
  9. mysql> SELECT * FROM `user_order`; 
  10. +----+-------------+-------+-------+ 
  11. | id | user        | price | count | 
  12. +----+-------------+-------+-------+ 
  13. |  1 | luchanghong |    10 |     2 | 
  14. +----+-------------+-------+-------+ 
  15. 1 row in set (0.00 sec) 

那么,我添加一个条件,就是每次更新完 user_account 表后检查用户的 money 是否为负值,如果为负值那么就要撤销之前的操作,执行事务回滚,代码如下:

  1. $conn = mysql_connect('127.0.0.1''root''root'); 
  2. mysql_select_db('shop_test'); 
  3. mysql_query('SET NAMES UTF8'); 
  4.  
  5. // start transaction 
  6. mysql_query("START TRANSACTION"); 
  7. $sql = "INSERT INTO `user_order`(`user`, `price`, `count`) VALUES ('luchanghong', '10', '2')"
  8. mysql_query($sql); 
  9. $sql_2 = "UPDATE `user_account` SET `money` = `money` - 10*2 WHERE `user` = 'luchanghong'"
  10. mysql_query($sql_2); 
  11.  
  12. if (mysql_errno()){ 
  13.     echo "error n"
  14.     mysql_query("ROLLBACK"); 
  15. }else
  16.     $money = check_remain_money('luchanghong'); 
  17.     echo $money." "
  18.     if ($money < 0){  
  19.         echo "No enough money n"
  20.         mysql_query("ROLLBACK"); 
  21.     }else
  22.         echo "OK n"
  23.         mysql_query("COMMIT"); 
  24.     }    
  25.  
  26. function check_remain_money($user){ 
  27.     $sql = "SELECT `money` FROM `user_account` WHERE `user` = '{$user}'"
  28.     $result = mysql_fetch_assoc( mysql_query($sql) ); 
  29.     return !emptyempty($result) ? $result['money'] : 0; 

接着,在shell下多次执行这php文件,WIN下就手动执行几次吧,代码如下:

  1. lch@LCH:~/Desktop $ for x in `seq 6`; do php transaction.php ; done 
  2. 60 OK  
  3. 40 OK  
  4. 20 OK  
  5. 0 OK  
  6. -20 No enough money  
  7. -20 No enough money 

再看数据库数据,代码如下:

  1. mysql> SELECT * FROM `user_account`; 
  2. +-------------+-------+ 
  3. user        | money | 
  4. +-------------+-------+ 
  5. | luchanghong |     0 | 
  6. +-------------+-------+ 
  7. 1 row in set (0.00 sec) 
  8.  
  9. mysql> SELECT * FROM `user_order`; 
  10. +----+-------------+-------+-------+ 
  11. | id | user        | price | count | 
  12. +----+-------------+-------+-------+ 
  13. |  1 | luchanghong |    10 |     2 | 
  14. |  2 | luchanghong |    10 |     2 | 
  15. |  3 | luchanghong |    10 |     2 | 
  16. |  4 | luchanghong |    10 |     2 | 
  17. |  5 | luchanghong |    10 |     2 | 
  18. +----+-------------+-------+-------+ 
  19. rows in set (0.00 sec) 

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进程来进行回收.

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表