MySQL(零基础)详解之事务

目录

一、什么是事务

二、事务的属性(面试重点)

三、事务执行流程

3.1、开启事务

3.2、提交事务

3.3、回滚事务

3.4、回滚标记

四、事务隔离级别(面试重点)

4.1、什么是事务隔离级别

4.2、查看隔离级别

4.3、设置隔离级别

4.4、演示隔离级别

4.4.1、读未提交

4.4.2、读已提交

4.4.3、可重复读


一、什么是事务

事务( Transaction )由一次或者多次基本操作构成,或者说事务由一条或者多条 SQL 语句构成。事务是数据库操作最基本单元 ,是逻辑上的一组操作,要么都成功,要么都失败。
事务有一个最显著的特征,就是它包含的所有 SQL 语句作为一个整体向数据库提交,只有所有的 SQL 语句都执行完成,整个事务才算成功,一旦某个 SQL 语句执行失败,整个事务就失败了。事务失败后需要回滚所有的 SQL 语句。
事务中的所有 SQL 语句是一个整体,共同进退,不可分割,要么全部执行成功,要么全部执行失败。

二、事务的属性(面试重点)

事务的属性也就是事务的特点,经常会出现面试中。

一般来说,事务具有四个标准属性,分别是原子性( A tomicity )、一致性( C onsistency )、隔离性( I solation )、持久性( D urability ),简称 ACID 。具体说明如下:

(1)原子性(Atomicity):一个事务中的所有 SQL 语句构成最小单元,要么全部执行成功,要么全部执行失败,不会结束在中间的某个环节。

(2)一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。简单说就是操作前后它的总量是不变的。

(3)隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。简单的说就是多个事务是相互独立的不会产生影响。

(4)持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。简单的说就是当提交事务后,表中的数据不会再发生变化。

三、事务执行流程

各种数据库对事务的支持细节不尽相同,我们以 MySQL 数据库为例进行讲解, MySQL 使用标准 SQL 来支持事务。

与事务控制有关的 SQL 命令包括:

(1)BEGIN 或者 START TRANSACTION:开始事务;

(2)COMMIT:提交事务;

(3)ROLLBACK:回滚事务;

(4)SAVEPOINT:在事务内部设置回滚标记点;

(5)RELEASE SAVEPOINT:删除回滚标记点;

(6)ROLLBACK TO:将事务回滚到标记点(ROLLBACK 命令的一种变形写法)。

一个事务要么提交( Commit ),要么回滚( Rollback ),提交意味着成功,回滚意味着失败。 编写事务代码时,以 BEGIN 命令开头,后跟一条或者多条 SQL 语句,最后书写 COMMIT 或者 ROLLBACK 命令; COMMIT ROLLBACK 对应事务的两种状态,只能出现一个。

事务控制命令仅能与 DML 类别的 SQL 命令一起使用,包括 INSERTUPDATEDELETE 和SELECT,在创建或者删除表时不能使用事务,因为这些操作在数据库中是自动提交的。

3.1、开启事务

开始事务有以下两种命令,选择其一即可:

begin;

或者:

start transaction;

3.2、提交事务

提交事务使用如下命令:

commit;
提交事务意味着真正执行事务包含的 SQL 语句,并把对数据库的修改写入到磁盘上的物理数据库中。COMMIT 意味着事务结束,并且执行成功。
准备数据表:
CREATE TABLE customers ( 
id int primary key auto_increment, 
name varchar(20), 
age int(2), 
address varchar(20), 
salary float(6,2) 
);
INSERT INTO customers (NAME, AGE, ADDRESS, SALARY) 
VALUES ('Ramesh', 32, 'Ahmedabad', 2000.00 ); 
INSERT INTO customers (NAME, AGE, ADDRESS, SALARY) 
VALUES ('Khilan', 25, 'Delhi', 1500.00 ); 
INSERT INTO customers (NAME, AGE, ADDRESS, SALARY) 
VALUES ('kaushik', 23, 'Kota', 2000.00 );

执行后:

打开一个 MySQL 命令行窗口(我们称它为 A 窗口),使用事务向表中插入两条数据:

 

console 窗口中输入:
begin ; 
INSERT INTO customers (NAME, AGE, ADDRESS, SALARY) 
VALUES ('Chaitali', 25, 'Mumbai', 6500.00 ); 
INSERT INTO customers (NAME, AGE, ADDRESS, SALARY) 
VALUES ('Hardik', 27, 'Bhopal', 8500.00 );
然后在 console_2  窗口中执行 select 查询:
select * from customers;

 说明在 console 窗口中插入的数据还未成功。

要想看到 console 窗口中插入的数据,我们需要在这个窗口中执行 commit 命令。
commit;
在次在 console_2 窗口中执行查询:
select * from customers;

3.3、回滚事务

回滚意味着撤销尚未保存到物理数据库中的操作,具体语法格式如下:
rollback;
事务执行过程中如果发生了某种故障,事务不能继续执行,就可以撤销事务,此时对数据库的修改并不会保存到物理数据库中。撤销意味着事务结束,并且执行失败。
示例:使用事务删除最后两个用户,并回滚
console  窗口中使用:
BEGIN; 

DELETE FROM customers WHERE ID=4; 
DELETE FROM customers WHERE ID=5;
查看console窗口中使用:
SELECT * FROM customers;

 发现console窗口中数据已被删除。

在console_2中查看:

 

SELECT * FROM customers;
发现 console_2中数据没有被删除。
这时后悔了,我们希望回滚这个事务。因此我们在 console 窗口中执行 rollback 命令
rollback;

执行完上面的命令后,就撤销了那两条删除语句。

 此时console中的数据又回来了。

3.4、回滚标记

ROLLBACK 命令默认回滚整个事务,也即事务中的所有修改操作都无效。但是 SQL 允许回滚事务的一部分,此时需要在事务中设置一个标记点,在该标记点之后的 SQL 语句将被回滚,之前的 SQL 语句将被成功执行。
设置标记点使用 SAVEPOINT 命令,具体语法如下:
SAVEPOINT point_name;
point_name 为标记点名字。
例如:我们在console中执行如下命令
BEGIN; 
DELETE FROM customers WHERE ID=4; 
savepoint mydelete; -- 添加回滚标记 
DELETE FROM customers WHERE ID=5; 
rollback to mydelete; -- 回滚到标记
在console再次查询
select * from customers;

 

会把回滚标记前的数据删除。

四、事务隔离级别(面试重点)

4.1、什么是事务隔离级别

事务隔离级别( Isolation )是反映多事务之间不会产生影响,是提交并发访问的处理态度。
如果不考虑事务的隔离级别,会有三个问题:脏读不可重复读幻读的情况。
(1) 脏读: 在多事务之间,一个未提交事务读取到另一个未提交事务修改的数据。例如 A B 同时对数 C 进行修改,这时 A 就可能读取到 B 还没有提交事务的数据。如果 B 把事务回滚了,那么 A 读取的 数据就会有问题。这就是脏读。(要避免脏读)
(2) 不可重复读: 一个未提交的事务读取到另一个提交事务修改的数据。例如 A B 同时对数据 C 进行 修改,都开启了数据,假设 B 对数据进行了修改,并且提交了事务,这时 A 就可以读取到 B 修改之 后的数据。而 A 的事务还没有提交,这就是不可重复读。通常针对数据 更新( UPDATE 操作。
(3) 幻读: 一个未提交事务读取到另一个提交事务添加的数据。针对数据 插入( INSERT 操作来说 的。假设事务 A 对某些行的内容作了更改,但是还未提交,此时事务 B 插入了与事务 A 更改前的记 录相同的记录行,并且在事务 A 提交之前先提交了,而这时,在事务 A 中查询,会发现好像刚刚的 更改对于某些数据未起作用,但其实是事务 B 刚插入进来的,让用户感觉很魔幻,感觉出现了幻
觉,这就叫幻读。
SQL 标准定义了四种隔离级别, MySQL 全都支持。这四种隔离级别分别是:
(1) READ UNCOMMITTED :读未提交,会产生脏读、不可重复读和幻读。
(2) READ COMMITTED :读已提交,会产生不可重复读和幻读。
(3) REPEATABLE READ :可重复读,会产生幻读。( MySQL 的默认级别)
(4)SERIALIZABLE :串行化,不会产生脏读、不可重复读和幻读。(单线程执行)

4.2、查看隔离级别

Oracle 支持的 2 种事务隔离级别: READ COMMITED, SERIALIZABLE Oracle 默认的事务隔离 级别为: READ COMMITED
Mysql 支持 4 种 事务隔离级别 . Mysql 默认的事务隔离级别为 : REPEATABLE-READ
每启动一个 mysql 程序 , 就会获得一个单独的数据库连接 . 每个数据库连接都有一个变量 @@tx_isolation , 表示当前的事务隔离级别。
(1) 查看当前的隔离级别 : SELECT @@tx_isolation;
(2) 查看全局的隔离级别: select @@global.tx_isolation;
(3) 设置当前 mySQL 连接的隔离级别 : set tx_isolation ='repeatable-read';
(4) 设置数据库系统的全局的隔离级别 : set global tx_isolation ='read-committed';
注意: MySQL 5.7.20 之后 SELECT @@transaction_isolation
查看事务隔离级别
select @@transaction_isolation;

 查看版本:

select VERSION();

4.3、设置隔离级别

在演示数据库的隔离级别之前,先了解如何修改隔离级别。修改隔离级别需要使用 set 关键字。
语法如下:
set [作用域] transaction isolation level [事务隔离级别];
完整的语法为:
SET [SESSION | GLOBAL] 
TRANSACTION ISOLATION LEVEL 
{READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
说明:
(1) SESSION 或者 GLOBAL 是隔离级别作用域, GLOBAL 是全局的而 SESSION 只针对当
前回话窗口。
(2) 隔离级别是 {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ |
SERIALIZABLE} 这四种,不区分大小写。
比如下面这个语句的意思是设置全局隔离级别为读提交级别。
set global transaction isolation level read committed;

设置后需要重启窗口才能看到。

4.4、演示隔离级别

下面用一张表来做一下验证,表结构简单如下:
CREATE TABLE `tb_user` ( 
`id` int(11) NOT NULL AUTO_INCREMENT, 
`name` varchar(30) DEFAULT NULL, 
`age` tinyint(4) DEFAULT NULL, 
PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO `tb_user`(name,age) VALUES('瀚海', 18);

4.4.1、读未提交

MySQL 事务隔离其实是依靠锁来实现的,加锁自然会带来性能的损失。而读未提交隔离级别是不加锁的,所以它的性能是最好的,没有加锁、解锁带来的性能开销。但有利就有弊,这基本上就相当于裸奔啊,所以它连脏读的问题都没办法解决。
下面来做个简单实验验证一下,首先设置全局隔离级别为读未提交。
set global transaction isolation level read uncommitted;
再查看:
select @@transaction_isolation;

注意:执行完上面语句后,需要重新开启新的会话窗口才能看到设置的隔离级别,在本窗口是没有效果的。

启动两个事务,分别为事务 A console )和事务 B console_7
console_6
begin ;
select * from tb_user;

 然后我们在 console_7 窗口中执行如下:

begin ;
update tb_user set age=20 where id=1;
然后在 console_6 窗口下执行查询:
select * from tb_user;

 然后我们在 console_7 窗口中执行如下:

rollback;
然后在 console_6 窗口下执行查询:
select * from tb_user;

此时,当我们把 console_7 事务回滚后,会发现我们查询的数据就不一致。这就是一个事务读取到了另一个未提交的更新事务。

总结:读未提交,其实就是可以读到其他事务未提交的数据,但没有办法保证你读到的数据最终一定是提交后的数据,如果中间发生回滚,那就会出现脏数据问题,读未提交没办法解决脏数据问题。

4.4.2、读已提交

读提交就是一个事务只能读到其他事务已经提交过的数据,也就是其他事务调用 commit 命令之后的数据,它可以解决脏读问题。
我们继续来做一下验证,首先把事务隔离级别改为读提交级别。
set global transaction isolation level read committed;
select @@transaction_isolation;
我们在 console_9 中执行如下:
use mydb;

begin;

select * from tb_user;

 我们在 console_8 中执行如下:

use mydb;
begin;
update tb_user set age=5 where id=1;
执行完上面的语句后,再执行 console_9 中查询语句,结果为:

 

从这个结果可以发现,没有读取到脏数据。
我们把更新数据的事务提交。
console_8 中执行如下:
commit;
执行完后,再次执行 console_9 中的查询语句:

 此时的查询结果就是提交事务后的执行结果。

从上面的操作过程可以发现,事务 console_9 中读取的 age 一直是 18 ,只有事务 console_8 中提交事务后,事务console_9 中才查询到 age 的值为 5
这就出现了一个问题,在同一事务中 (本例中的事务console_9),事务的不同时刻同样的查询条件,查询出来的记录内容是不一样的,事务console_8的提交影响了事务A的查询结果,这就是不可重复读,也就是读已提交隔离级别。
注意: 读提交解决了脏读的问题,但是无法做到可重复读,也没办法解决幻读。

4.4.3、可重复读

不可重复读是指同一事务不同时刻读到的数据值可能不一致。而可重复读是指事务不会读到其他事务对已有数据的修改,即使其他事务已提交。也就是说,事务开始时读到的已有数据是什么,在事务提交前的任意时刻,这些数据的值都是一样的。但是,对于其他事务新插入的数据是可以读到的,这也就引发了幻读问题。
同样的,需改全局隔离级别为可重复读级别。
set global transaction isolation level repeatable read;

 启动两个事务,分别为事务 console_8 和事务 console_10

事务 console_8
begin; 
select * from tb_user;
事务 console_10
use mydb; 
begin; 
update tb_user set age=1 where id=1;
当执行上面语句后,查询出来的数据不会有变量,因为事务 console_10 并没有提交。
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值