MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,删除了一个人员,需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!
- 在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务
- 事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行
- 事务用来管理insert,update,delete语句
一般来说,事务是必须满足4个条件(ACID): Atomicity(原子性)、Consistency(稳定性)、Isolation(隔离性)、Durability(可靠性)
- 原子性:事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
- 一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。
- 隔离性:一个事务的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持续性:也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
MySQL中的InnoDB引擎的表才支持transaction。在一个事务里,如果出现一个数据库操作失败了,事务内的所有操作将被回滚,数据库将会回到事务前的初始状态。
在Mysql控制台使用事务来操作
1、开始一个事务
start transaction
2、做保存点
savepoint 保存点名称
3、操作
4、可以回滚,可以提交,没有问题,就提交,有问题就回滚。
PHP中使用事务实例
1、首先创建相关数据表
telephone
和
employee,
脚本和数据如下:
MariaDB[RUNOOB]> CREATE TABLE `employee`(
->`id`int NOT NULL AUTO_INCREMENT,
->`first_name` varchar(100) NOT NULL,
->`last_name` varchar(100) NOT NULL,
->`job_title` varchar(100) DEFAULT NULL,
->`salary`double DEFAULT NULL,
->`notes` text,
-> PRIMARY KEY (`id`)
->) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK,0 rows affected (0.11 sec)
MariaDB[RUNOOB]> INSERT INTO `employee`(`first_name`,`last_name`,`job_title`,`salary`) VALUES
->('Robin','Jackman','Software Engineer',5500),
->('Taylor','Edward','Software Architect',7200),
->('Vivian','Dickens','Database Administrator',6000),
->('Harry','Clifford','Database Administrator',6800),
->('Eliza','Clifford','Software Engineer',4750),
->('Nancy','Newman','Software Engineer',5100),
->('Melinda','Clifford','Project Manager',8500),
->('Harley','Gilbert','Software Architect',8000);
Query OK,8 rows affected (0.01 sec)
Records:8Duplicates:0Warnings:0
MariaDB[RUNOOB]> CREATE TABLE `telephone`(
->`id`int NOT NULL AUTO_INCREMENT,
->`employee_id`int DEFAULT NULL,
->`type` varchar(20) NOT NULL,
->`no` varchar(50) NOT NULL,
-> PRIMARY KEY (`id`)
->) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK,0 rows affected (0.04 sec)
MariaDB[RUNOOB]> INSERT INTO `telephone`(`employee_id`,`type`,`no`) VALUES
->(1,'mobile','245-249697'),
->(2,'mobile','270-235969'),
->(2,'land','325-888885'),
->(3,'mobile','270-684972'),
->(4,'mobile','245-782365'),
->(4,'land','325-888886'),
->(5,'mobile','245-537891'),
->(6,'mobile','270-359457'),
->(7,'mobile','245-436589'),
->(7,'land','325-888887'),
->(8,'mobile','245-279164'),
->(8,'land','325-888888');
Query OK,12 rows affected (0.02 sec)
Records:12Duplicates:0Warnings:0
MariaDB[RUNOOB]>select*from employee;
+----+------------+-----------+------------------------+--------+-------+
| id | first_name | last_name | job_title | salary | notes |
+----+------------+-----------+------------------------+--------+-------+
|1|Robin|Jackman|SoftwareEngineer|5500| NULL |
|2|Taylor|Edward|SoftwareArchitect|7200| NULL |
|3|Vivian|Dickens|DatabaseAdministrator|6000| NULL |
|4|Harry|Clifford|DatabaseAdministrator|6800| NULL |
|5|Eliza|Clifford|SoftwareEngineer|4750| NULL |
|6|Nancy|Newman|SoftwareEngineer|5100| NULL |
|7|Melinda|Clifford|ProjectManager|8500| NULL |
|8|Harley|Gilbert|SoftwareArchitect|8000| NULL |
+----+------------+-----------+------------------------+--------+-------+
8 rows inset(0.00 sec)
MariaDB[RUNOOB]>select*from telephone;
+----+-------------+--------+------------+
| id | employee_id | type |no|
+----+-------------+--------+------------+
|1|1| mobile |245-249697|
|2|2| mobile |270-235969|
|3|2| land |325-888885|
|4|3| mobile |270-684972|
|5|4| mobile |245-782365|
|6|4| land |325-888886|
|7|5| mobile |245-537891|
|8|6| mobile |270-359457|
|9|7| mobile |245-436589|
|10|7| land |325-888887|
|11|8| mobile |245-279164|
|12|8| land |325-888888|
+----+-------------+--------+------------+
12 rows inset(0.00 sec)
2、在下面第一个语句中,employee_id是在第一条语句中指定的,设想一下,当第一条语句失败,而第二条语句成功的状况。在这种状况下,telephone表中就会有一条employee_id为9的记录,而employee表中并没有id为9的记录,而如果将这两个语句放在MySQL事务中,如果第一条语句失败,那么第二条语句也将回滚,从而不会造成这种问题。
-
INSERT INTO `employee`(`id`,`first_name`,`last_name`,
`job_title`,`salary`) VALUES (9,'Grace','Williams','Softwaree Engineer',5000); - INSERT INTO `telephone`(`id`,`employee_id`,`type`,`no`) VALUES (13,9,'mobile','270-598712');
3、PHP脚本如下
我们试着通过PHP插入上面的两条数据,并故意将第1条语句写错。
php
- //$salary = 5000; //这句话才是正确的,下面这句是错误的。
$salary ='$5000';
/* Change database details according to your database */
$dbConnection = mysqli_connect('localhost','root','password','RUNOOB');
mysqli_autocommit($dbConnection,false);
$flag =true;
$query1 ="INSERT INTO `employee` (`id`, `first_name`, `last_name`, `job_title`, `salary`) VALUES (9, 'Grace', 'Williams', 'Softwaree Engineer', $salary)";
$query2 ="INSERT INTO `telephone` (`id`, `employee_id`, `type`, `no`) VALUES (13, 9, 'mobile', '270-598712')";
$result = mysqli_query($dbConnection, $query1);
if(!$result){
$flag =false;
echo "Error details: ". mysqli_error($dbConnection).". ";
}
$result = mysqli_query($dbConnection, $query2);
if(!$result){
$flag =false;
echo "Error details: ". mysqli_error($dbConnection).". ";
}
if($flag){
mysqli_commit($dbConnection);
echo "All queries were executed successfully";
}else{
mysqli_rollback($dbConnection);
echo "All queries were rolled back";
}
mysqli_close($dbConnection);
?>
4、运行结果如下:
5、运行后的数据表如下,数据表都回滚了:
MariaDB[RUNOOB]>select*from employee;
+----+------------+-----------+------------------------+--------+-------+
| id | first_name | last_name | job_title | salary | notes |
+----+------------+-----------+------------------------+--------+-------+
|1|Robin|Jackman|SoftwareEngineer|5500| NULL |
|2|Taylor|Edward|SoftwareArchitect|7200| NULL |
|3|Vivian|Dickens|DatabaseAdministrator|6000| NULL |
|4|Harry|Clifford|DatabaseAdministrator|6800| NULL |
|5|Eliza|Clifford|SoftwareEngineer|4750| NULL |
|6|Nancy|Newman|SoftwareEngineer|5100| NULL |
|7|Melinda|Clifford|ProjectManager|8500| NULL |
|8|Harley|Gilbert|SoftwareArchitect|8000| NULL |
+----+------------+-----------+------------------------+--------+-------+
8 rows inset(0.02 sec)
MariaDB[RUNOOB]>select*from telephone;
+----+-------------+--------+------------+
| id | employee_id | type |no|
+----+-------------+--------+------------+
|1|1| mobile |245-249697|
|2|2| mobile |270-235969|
|3|2| land |325-888885|
|4|3| mobile |270-684972|
|5|4| mobile |245-782365|
|6|4| land |325-888886|
|7|5| mobile |245-537891|
|8|6| mobile |270-359457|
|9|7| mobile |245-436589|
|10|7| land |325-888887|
|11|8| mobile |245-279164|
|12|8| land |325-888888|
+----+-------------+--------+------------+
12 rows inset(0.00 sec)
6、注意:
1、当执行mysqli_query函数的时候,结果被立即提交到了数据库。使用mysqli_autocommit函数,可以关闭自动提交,执行结果只有当你想提交的时候才提交。
2、如果任何语句执行失败我们都可以设置$flag变量为false。如果有很多语句要执行,可以考虑将他们放在for循环中。
3、最后,如果flag是true(也就是没有错误发生),我们使用mysqli_commit提交事务。否则我们使用mysqli_rollback回滚事务。所以事务可以在某种程度上帮助我们维护数据的完整和正确,另外,为了保证数据无误,我们还推荐使用外键。
4、并不是所有的语句都是支持事务的,例如,CREATE TABLE或者ALTER TABLE语句。