MySQL 事务
什么MySQL 事务?
MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等
事务(Transaction)是由一系列对系统中数据进⾏访问与更新的操作所组成的⼀个程序执行逻辑单元。
事务应当满足什么条件?
一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read
uncommitted)、读提交(read committed)、可重复读(repeatable
read)和串行化(Serializable)。持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
事务管理的知识点分布
什么是事务的ACID?
一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性:
事务的原⼦性是指事务必须是⼀个原子的操作序列单元。事务中包含的各项操作在1次执行过程中,只
允许出现两种状态之一。
(1)全部执行成功
(2)全部执行失败
事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执⾏过程中出错,
会回滚到事务开始前的状态,所有的操作就像没有发⽣一样。也就是说事务是⼀个不可分割的整体,就
像化学中学过的原子,是物质构成的基本单位。
一致性:
事务的一致性是指事务的执⾏不能破坏数据库数据的完整性和一致性,一个事务在执⾏之前和执行之
后,数据库都必须处以一致性状态。
比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,B账户没有加钱。
隔离性:
事务的隔离性是指在并发环境中,并发的事务是互相隔离的。也就是说,不同的事务并发操作相同的数
据时,每个事务都有各自完整的数据空间。
一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。
隔离性分4个级别,下面会介绍。
持久性:
事务的持久性是指事务⼀旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服
务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态
事务的语法:
1. start transaction; begin;
2. commit; 使得当前的修改确认
3. rollback; 使得当前的修改被放弃
MYSQL 事务处理主要有两种方法:
1、用 BEGIN, ROLLBACK, COMMIT来实现
BEGIN 开始一个事务
ROLLBACK 事务回滚
COMMIT 事务确认
2、直接用 SET 来改变 MySQL 的自动提交模式:
SET AUTOCOMMIT=0 禁止自动提交
SET AUTOCOMMIT=1 开启自动提交
mysql> use RUNOOB;
Database changed
mysql> CREATE TABLE runoob_transaction_test( id int(5)) engine=innodb; # 创建数据表
Query OK, 0 rows affected (0.04 sec)
mysql> select * from runoob_transaction_test;
Empty set (0.01 sec)
mysql> begin; # 开始事务
Query OK, 0 rows affected (0.00 sec)
mysql> insert into runoob_transaction_test value(5);
Query OK, 1 rows affected (0.01 sec)
mysql> insert into runoob_transaction_test value(6);
Query OK, 1 rows affected (0.00 sec)
mysql> commit; # 提交事务
Query OK, 0 rows affected (0.01 sec)
mysql> select * from runoob_transaction_test;
+------+
| id |
+------+
| 5 |
| 6 |
+------+
2 rows in set (0.01 sec)
mysql> begin; # 开始事务
Query OK, 0 rows affected (0.00 sec)
mysql> insert into runoob_transaction_test values(7);
Query OK, 1 rows affected (0.00 sec)
mysql> rollback; # 回滚
Query OK, 0 rows affected (0.00 sec)
mysql> select * from runoob_transaction_test; # 因为回滚所以数据没有插入
+------+
| id |
+------+
| 5 |
| 6 |
+------+
2 rows in set (0.01 sec)
mysql>
PHP中使用事务实例
<?php
$dbhost = 'localhost'; // mysql服务器主机地址
$dbuser = 'root'; // mysql用户名
$dbpass = '123456'; // mysql用户名密码
$conn = mysqli_connect($dbhost, $dbuser, $dbpass);
if(! $conn )
{
die('连接失败: ' . mysqli_error($conn));
}
// 设置编码,防止中文乱码
mysqli_query($conn, "set names utf8");
mysqli_select_db( $conn, 'RUNOOB' );
mysqli_query($conn, "SET AUTOCOMMIT=0"); // 设置为不自动提交,因为MYSQL默认立即执行
mysqli_begin_transaction($conn); // 开始事务定义
if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(8)"))
{
mysqli_query($conn, "ROLLBACK"); // 判断当执行失败时回滚
}
if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(9)"))
{
mysqli_query($conn, "ROLLBACK"); // 判断执行失败时回滚
}
mysqli_commit($conn); //执行事务
mysqli_close($conn);
?>
事务的并发问题
脏读:
读取到了没有提交的数据, 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的 数据是脏数
据。
不可重复读:
同一条命令返回不同的结果集(更新).事务 A 多次读取同一数据,事务 B 在事务A 多次读取的
过程中,对数据做了更新并提交,导致事务A多次读取同一数据时,结果不一致。
幻读:
重复查询的过程中,数据就发生了量的变化(insert, delete)。
实例:
create table users(
id int auto_increment primary key,
name varchar(10),
age int,
account int
)
insert into users values(null,'张三',25,10000),(null,'李李四',20,100),(null,'王五',23,0);
脏读:
事务 A 先写数据,把一行数据的值从 null 改成了 A,同样事务 A 并没有提交;
然后事务 B 过来读了,它读到的值自然是 A 喽;
接着事务 A 又回滚了!回滚之后值就要从 A 变回到 NULL;
事务 B 再去读的时候读到的就是 NULL 了
脏读就是事务 B 因为事务 A 回滚,读不到之前的值了。
不可重复读:
事务 A 先去读一行数据,读到值是 A;
事务 B 去修改数据,改成了 B。这里和前面不一样的地方就在,事务 B 它还提交了,不回滚了。
事务 A 第二次去读,读到的是 B,和第一次读到的 A 不一样。
那不可重复读是指什么?它是指在同一个事务里面查询同一行数据,每次查到的数据都不一样。是不是和脏读很像,区别在于脏读是由于别的事务回滚导致,而不可重复读读到的其实是已经提交的数据。
幻读:
事务 A 里有一个条件查询的语句 select name from t where id > 10,它进行了一次范围查询,查到了 10 行数据;
然后事务 B 网里面加入了一批数据
事务 A 再查的用条件查询语句查询的时候,发现查到了 15 条,其中 5 条是之前没见过的。这个事务 A 以为自己出现幻觉了,怎么会多出这么些个数据?这就是幻读了。
脏读、不可重复读,都是针对一行数据来说的,幻读不一样,幻读是指查到了之前没有的一批数据
事务隔离级别
4种事务隔离级别从上往下,级别越高,并发性越差,安全性就越来越高。 ⼀般数据默认级别是 读以提交或可重复 读
查看当前会话中事务的隔离级别
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.93 sec)
设置当前会话中的事务隔离级别
mysql> set session transaction isolation level read uncommitted;
Query OK, 0rows affected (0.00 sec)
隔离级别
1. 读未提交(READ_UNCOMMITTED)
读未提交,该隔离级别允许脏读取,其隔离级别是最低的。换句话说,如果一个事务正在处理理某一数
据,并对其进⾏了更新,但同时尚未完成事务,因此还没有提交事务;而以此同时,允许另一个事务也 能够访问该数据。
脏读示例:
在事务A和事务B同时执行时可能会出现如下场景:
余额应该为1500元才对。
请看T5时间点,事务A此时查询的余额为0,这个数据就是脏数据,他是事务B 造成的,很明显是事务没有进行隔离造成的。
2. 读已提交(READ_COMMITTED)
读已提交是不同的事务执行的时候只能获取到已经提交的数据。 这样就不会出现上面的脏读的情况了。 但是在同一个事务中执行同一个读取,结果不一致
不可重复读示例:
事务A其实除了了查询两次以外,其它什什么事情都没做,结果钱就从1000变成0了了,这就是不不可重复读的问题。
3. 可重复读(REPEATABLE_READ)
可重复读:
就是保证在事务处理理过程中,多次读取同一个数据时,该数据的值和事务开始时刻是一致的。
因此该事务级别限制了不可重复读和脏读,但是有可能出现幻读的数据。
幻读
幻读就是指同样的事务操作,在前后两个时间段内执行对同一个数据项的读取,可能出现不一致的结果。
诡异的更新事件
4. 顺序读(SERIALIZABLE)
顺序读是最严格的事务隔离级别。
它要求所有的事务排队顺序执行,即事务只能一个接一个地处理,不能并发。
不同的隔离级别的锁的情况
- 读未提交(RU):
有行级的锁,没有间隙锁。它与RC的区别是能够查询到未提交的数据。
- 读已提交(RC):
有行级的锁,没有间隙锁,读不到没有提交的数据。
- 可重复读(RR):
有行级的锁,也有间隙锁,每次读取的数据都是一样的,并且没有幻读的情况。
- 序列化(S):
有行级锁,也有间隙锁,读表的时候,就已经上锁了
隐式提交
DQL:
查询语句句 DML:写操作(添加,删除,修改)
DDL:
定义语句句(建库,建表,修改表,索引操作,存储过程,视图)
DCL:
控制语⾔言(给用户授权,或删除授权) DDL(Data Define Language):都是隐式提交。
隐式提交:
执⾏行行这 种语句句相当于执行commit;