文章目录
- 一、概念
- 1.查看事务支持版本-show engines
- 2.事务提交方式-show variables like 'autocommit'
- 3.事务常见操作方式
- 1.将mysql的默认隔离级别设置成==读未提交==,方便看到实验现象
- 2.需要重启终端,进行查看隔离级别
- 3.创建一个银行用户表
- 4.演示 - 证明事务的开始与回滚
- 5.演示原子性 - 证明未commit,客户端崩溃,MySQL自动会回滚
- 6.演示持久性-证明commit了,客户端崩溃,MySQL数据不会在受影响,已经持久化
- 7.演示begin会自动更改提交方式为手动,不管autocommit是不是自动提交
- 8.演示证明单条 SQL 与事务的关系
- 9.重要总结
- 二、事务的隔离级别
- 查看与设置隔离级别
- 读未提交-read uncommitted
- 读提交-read committed
- 可重复读-repeatable read
- 串行化-serializable
- 总结
- 三、一致性
- 四、多版本并发控制
- 记录中的3个隐藏字段
- undo日志
- 快照
- Read View
一、概念
事务由一条或多条SQL语句组成的一个整体,事务本质是能够简化我们的编程模型。
- 原子性: 一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中如果发生错误,则会自动回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
- 持久性: 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
- 隔离性: 隔离性可以保证多个事务在并发执行时,不会因为由于交叉执行而导致数据的不一致。
- 一致性: 在事务开始之前和事务结束以后,数据库的完整型没有被破坏。
原子性,持久性,隔离性是手段,一致性是目的
1.查看事务支持版本-show engines
show engines;
2.事务提交方式-show variables like ‘autocommit’
查看事务提交方式,ON表示打开自动提交,OFF表示关闭自动提交(开启手动提交)
show variables like 'autocommit';
设置事务的提交方式
set autocommit=0;//关闭自动提交.开启手动提交
set autocommit=1;//打开自动提交
3.事务常见操作方式
1.将mysql的默认隔离级别设置成读未提交,方便看到实验现象
set global transaction isolation level read uncommitted;
2.需要重启终端,进行查看隔离级别
quit退出重新登陆
select @@tx_isolation;
3.创建一个银行用户表
create table if not exists account(
id int primary key,
name varchar(50) not null default '',
blance decimal(10,2) not null default 0.0
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;
4.演示 - 证明事务的开始与回滚
-
使用begin 或 start transaction命令,可以启动一个事务。
-
使用savepoint 保存点,可以在事务中创建指定名称的保存点。
-
使用rollback to 保存点,可以让事务回滚到指定保存点。
-
使用rollback命令,可以直接让事务回滚到最开始。
-
使用commit命令,可以提交事务,提交事务后就不能回滚了。
start transaction;//启动一个事务或者begin
savepoint save1; //创建一个保存点save1
insert into account values (1, '张三', 100);//插入一条记录
savepoint save2;//创建一个保存点save2
insert into account values (2, '李四', 10000);//插入一条记录
rollback to save2; //回滚到保存点save2,此时表中只有张三的数据
rollback;//回滚在最开始,所有数据都没有了
5.演示原子性 - 证明未commit,客户端崩溃,MySQL自动会回滚
begin;//启动一个事务或者start transaction
insert into account values (1, '张三', 100);//插入一条记录
quit;//断开连接,mysql会自动让事务回滚到最开始,看不到任何记录
6.演示持久性-证明commit了,客户端崩溃,MySQL数据不会在受影响,已经持久化
begin;//启动一个事务或者start transaction
insert into account values (1, '张三', 100);//插入一条记录
commit;//提交事务
quit;//断开连接,仍然可以看到之前插入的记录
7.演示begin会自动更改提交方式为手动,不管autocommit是不是自动提交
//假如本来有张三的数据
set autocommit=1;//打开自动提交
begin;//启动一个事务或者start transaction
insert into account values (2, '李四', 10000);//插入一条记录
quit;//断开连接,mysql会自动让事务回滚到最开始,表中只有张三的数据
8.演示证明单条 SQL 与事务的关系
//假如本来有张三的数据
set autocommit=1;//打开自动提交
insert into account values (2, '李四', 10000);//插入一条记录
quit;//断开连接,mysql已经自动提交,表中有张三和李四两个人的数据
9.重要总结
- 输入begin或者start transaction,事务便必须要通过commit提交,才会持久化,否则会回滚,与是否设置autocommit无关
- 没用begin或者start transaction,只要设置autocommit=1(自动提交),退出会自动保存
- 没用begin或者start transaction,只要设置autocommit=0(手动提交),必须要通过commit提交,才会持久化
二、事务的隔离级别
数据库为了保证事务执行过程中尽量不受干扰,于是出现了隔离性的概念
-
读未提交(read uncommitted): 在该隔离级别下,所有的事务都可以看到其他事务没有提交的执行结果,实际生产中不可能使用这种隔离级别,因为这种隔离级别相当于没有任何隔离性,会存在很多并发问题,如脏读、幻读、不可重复读等。
-
读提交(read committed): 该隔离级别是大多数数据库的默认隔离级别,但它不是MySQL默认的隔离级别,它满足了隔离的简单定义:一个事务只能看到其他已经提交的事务所做的改变,但这种隔离级别存在不可重复读和幻读的问题。
-
可重复读(repeatable read): 这是MySQL默认的隔离级别,该隔离级别确保同一个事务在执行过程中,多次读取操作数据时会看到同样的数据,即解决了不可重复读的问题,但这种隔离级别下仍然存在幻读的问题。
-
串行化(serializable): 这是事务的最高隔离级别,该隔离级别通过强制事务排序,使之不可能相互冲突,从而解决了幻读问题。它在每个读的数据行上面加上共享锁,但是可能会导致超时和锁竞争问题,这种隔离级别太极端,实际生成中基本不使用。
查看与设置隔离级别
查看全局隔离级别
select @@global.tx_isolation;
查看会话隔离级别
select @@tx_isolation;
设置会话隔离级别
set session transaction isolation level 隔离级别;
设置全局隔离级别
set global transaction isolation level 隔离级别;
读未提交-read uncommitted
左终端 | 右终端 |
---|---|
设置隔离性,并查看 set session transaction isolation level read uncommitted; select @@tx_isolation; | 设置隔离性,并查看 set session transaction isolation level read uncommitted; select @@tx_isolation; |
begin;//启动一个事务或者start transaction | begin;//启动一个事务或者start transaction |
插入一条记录,此时没有提交,在右终端有数据 insert into account values (1, ‘张三’, 100); | select * from account;//表中没有数据 |
select * from account;//表中有数据 |
注意:一个事务在执行中,读到另一个执行中事务的更新但是未commit的数据,这种现象叫做脏读
读提交-read committed
左终端 | 右终端 |
---|---|
设置隔离性,并查看 set session transaction isolation level read committed; select @@tx_isolation; | 设置隔离性,并查看 set session transaction isolation level read committed; select @@tx_isolation; |
begin;//启动一个事务或者start transaction | begin;//启动一个事务或者start transaction |
插入一条记录,此时没有提交,在右终端没有数据 insert into account values (1, ‘张三’, 100); | select * from account;//表中没有数据,创建read view |
commit;//提交事务 | select * from account;//表中有数据,创建新read view |
注意:一个事务在执行过程中,两个相同的select查询得到了不同的数据,这种现象叫做不可重复读,中间没用commit
在可重复读隔离级别下,一个事务在执行过程中,相同的select查询得到的是相同的数据,这就是所谓的可重复读
事务每次进行快照读时都会创建一个Read View(使用select),所以RC级别是不可重复读的
可重复读-repeatable read
左终端 | 右终端 |
---|---|
设置隔离性,并查看 set session transaction isolation level repeatable read; select @@tx_isolation; | 设置隔离性,并查看 set session transaction isolation level repeatable read; select @@tx_isolation; |
begin;//启动一个事务或者start transaction | begin;//启动一个事务或者start transaction |
插入一条记录,此时没有提交,在右终端没有数据 insert into account values (1, ‘张三’, 100); | select * from account;//表中没有数据,创建read view |
commit;//提交事务 | select * from account;//表中没有数据,使用之前的read view |
commit;//提交事务 | |
select * from account;//表中有数据,创建新read view |
注意:一个事务在执行过程中,相同的select查询得到了新的数据,如同出现了幻觉,这种现象叫做幻读。中间用commit
事务在第一次快照会创建一个Read View(使用select)之后快照会使用最开始的Read View判断,所以之后的修改不可见,只会创建一次Read View,所以RR级别是可重复读的
串行化-serializable
左终端 | 右终端 |
---|---|
设置隔离性,并查看 set session transaction isolation level serializable; select @@tx_isolation; | 设置隔离性,并查看 set session transaction isolation level serializable; select @@tx_isolation; |
begin;//启动一个事务或者start transaction | begin;//启动一个事务或者start transaction |
插入一条记录,此时没有提交,左终端被阻塞 insert into account values (1, ‘张三’, 100); | 左终端被阻塞 |
当右终端commit后左终端insert才会提交 | commit;//提交事务 |
总结
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
读未提交(read uncommitted) | √ | √ | √ | 不加锁 |
读已提交(read committed) | X | √ | √ | 不加锁 |
可重复读(repeatable read) | X | X | X | 不加锁 |
可串行化(serializable) | X | X | X | 加锁 |
三、一致性
-
事务在执行中发生错误,则自动回滚到事务最开始的状态,即一致性需要原子性来保证。
-
事务处理结束后,对数据的修改必须是永久的,即一致性需要持久性来保证。
-
多个事务同时访问同一份数据时,必须保证这多个事务在并发执行时,不会因为由于交叉执行而导致数据的不一致,即一致性需要隔离性来保证。
四、多版本并发控制
多版本并发控制,要依赖记录中的3个隐藏字段、undo日志和Read View实现
记录中的3个隐藏字段
DB_TRX_ID
:6字节,记录最近一次修改事务的ID。DB_ROW_ID
:6字节,隐含的自增ID(隐藏主键)。DB_ROLL_PTR
:7字节,回滚指针,指向这条记录的上一个版本。
插入该记录的事务的事务ID为9,插入的第一条记录,DB_ROW_ID
字段填的就是1,没有历史版本,所以回滚指针DB_ROLL_PTR
的值设置为null
undo日志
undo log:回滚日志,是内存缓冲区。
快照
原本undo缓冲区中只有ID为9的张三,现在插入一个李四,所以ID自增1,回滚指针指向张三,现在修改李四所以ID自增1,回滚指针只想原来的李四,undo log中的一个个的历史版本就称为一个个的快照。
Read View
录并维护系统当前活跃的事务ID,select时会生成read view