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->alter table table_name type=InnoDB;
-
- 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事务回滚,创建一个测试的数据库,代码如下:
- mysql> CREATE DATABASE `shop_test` DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
- Query OK, 1 row affected (0.00 sec)
-
- mysql> use shop_test;
- Database changed
-
- mysql> CREATE TABLE IF NOT EXISTS `user_account`(
- -> `user` varchar(20) NOT NULL,
- -> `money` INT(10) NOT NULL,
- -> PRIMARY KEY (`user`)
- -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
- Query OK, 0 rows affected (0.51 sec)
-
- mysql> CREATE TABLE IF NOT EXISTS `user_order`(
- -> `id` INT(10) NOT NULL,
- -> `user` VARCHAR(20) NOT NULL,
- -> `price` INT(10) NOT NULL,
- -> `count` INT(10) NOT NULL,
- -> PRIMARY KEY (`id`)
- -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1;
- Query OK, 0 rows affected (0.33 sec)
-
- mysql> INSERT INTO `user_account` VALUES ('luchanghong', '100');
- Query OK, 1 row affected (0.00 sec)
PHP测试代码:
- $conn = mysql_connect('127.0.0.1', 'root', 'root');
- mysql_select_db('shop_test');
- mysql_query('SET NAMES UTF8');
-
- # start transaction
- mysql_query("START TRANSACTION");
- $sql = "INSERT INTO `user_order` VALUES ('1', 'luchanghong', '10', '2')";
- mysql_query($sql);
- $sql_2 = "UPDATE `user_account` SET `money` = `money` - 10*2 WHERE `user` = 'luchanghong'";
- mysql_query($sql_2);
-
- if (mysql_errno()){
- echo "error";
- mysql_query("ROLLBACK");
- }else{
- echo "OK";
- mysql_query("COMMIT");
- }
执行一次后查看数据库,代码如下:
- mysql> SELECT * FROM `user_account`;
- +
- | user | money |
- +
- | luchanghong | 80 |
- +
- 1 row in set (0.00 sec)
-
- mysql> SELECT * FROM `user_order`;
- +
- | id | user | price | count |
- +
- | 1 | luchanghong | 10 | 2 |
- +
- 1 row in set (0.00 sec)
那么,我添加一个条件,就是每次更新完 user_account 表后检查用户的 money 是否为负值,如果为负值那么就要撤销之前的操作,执行事务回滚,代码如下:
- $conn = mysql_connect('127.0.0.1', 'root', 'root');
- mysql_select_db('shop_test');
- mysql_query('SET NAMES UTF8');
-
-
- mysql_query("START TRANSACTION");
- $sql = "INSERT INTO `user_order`(`user`, `price`, `count`) VALUES ('luchanghong', '10', '2')";
- mysql_query($sql);
- $sql_2 = "UPDATE `user_account` SET `money` = `money` - 10*2 WHERE `user` = 'luchanghong'";
- mysql_query($sql_2);
-
- if (mysql_errno()){
- echo "error n";
- mysql_query("ROLLBACK");
- }else{
- $money = check_remain_money('luchanghong');
- echo $money." ";
- if ($money < 0){
- echo "No enough money n";
- mysql_query("ROLLBACK");
- }else{
- echo "OK n";
- mysql_query("COMMIT");
- }
- }
-
- function check_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 $ for x in `seq 6`; do php transaction.php ; done
- 60 OK
- 40 OK
- 20 OK
- 0 OK
- -20 No enough money
- -20 No enough money
再看数据库数据,代码如下:
- mysql> SELECT * FROM `user_account`;
- +
- | user | money |
- +
- | luchanghong | 0 |
- +
- 1 row in set (0.00 sec)
-
- 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 |
- +
- 5 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进程来进行回收. |