Mysql的事务

MySQL的事务(Transaction)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务能把数据库从一个一致性状态转变为另一个一致性状态。

1 事务得特性

  • A/Atomicity:原子性
  • C/Consistency:一致性
  • I/Isolation:独立性/隔离性
  • D/Durability:持久性

1.1 原子性

开启事务中得命令要不全部执行成功,要不全部执行失败,事务中得操作可以看作是一个原子操作不能分割。例如A给B转账,A-100 B+100,要不全部成功了要不就全部失败了,不存在但单独A成功或者B单独成功。

1.2 一致性

一致性表明事务发生得前后,数据是一致得。比如A给B转账,转账前后得A+B得总金额都是保持不变得,虽然转账导致了A和B得金额发生了变化,但是总得金额在前后保持一致性。

1.3 隔离性

两个事务之间的是相互隔离的,相互不干扰。MySQL中存在四种隔离等级

  • 读未提交:一个事务能读到另外事务未提交的数据

  • 读已提交:一个事务可以读到另一个事务已经提交的数据

  • 可重复读:在该事务中每次读取的数据都是一致的

  • 序列化/串行化:串行执行MySQL的命令。

1.4 持久性

持久性表示MySQL的数据会写入到磁盘做持久化处理。

2 Mysql中的事务操作

MySQL默认自动开启事务的,默认状态下一条语句就是一个事务,更改的Sql语句执行完成之后事务默认是自动提交的。

-- 查询是否开启自动提交
show variables like 'autocommit';

在这里插入图片描述

而且事务是基于连接的,创建了一个MySQL的连接会从MySQL连接池中分配一个线程,而开启的事务是基于本次的连接,操作不同的表数据可以加入到同一个事务中。

  • start transaction | begin | begin work:开启一个事务
  • commit:提交一个事务
  • rollback:回滚一个事务
-- 创建学生表
CREATE TABLE students (
	id INT auto_increment NOT NULL,
	stu_no varchar(100) NULL COMMENT '学号',
	name varchar(100) NULL COMMENT '学生名字',
	age INT NULL COMMENT '学生年龄',
	class_id INT NULL COMMENT '班级id',
	CONSTRAINT students_PK PRIMARY KEY (id)
);

--插入学生数据
INSERT INTO students (stu_no, name, age, class_id) VALUES
('1', '小红', 10, 1),
('2', '小蓝', 11, 2),
('3', '小黑', 13, 3),
('4', '小黑', 14, 1);
--示例1 rollback回滚事务
--窗口开启事务1
start transaction;
update students set age=100 where name="小红";
--窗口开启事务2
start transaction;
select * from students;
--事务1回滚
rollback;

在这里插入图片描述
在这里插入图片描述

--commit提交事务
start transaction;
update students set age=100 where name="小红";
commit;

在这里插入图片描述

--设置事务的回滚点
start transaction;
update students set age=100 where name="小黑";
savepoint update1;
update students set age=100 where name="小红";
savepoint update2;
update students set age=100 where name="不存在数据";
rollback to update1;
commit;

在这里插入图片描述

3 事务中发生的问题

3.1 脏写

AB同时开启事务,在同时刻同时对一个数据进行写操作。可能存在

  1. A更改成功了,但是B失败进行回退的时候,把A修改的数据还原了
  2. A更改成功,B也更改成功,但是B提交,A在提交的时候把B的数据覆盖了。

上面的两种情况都会出现MySQL数据的脏写问题。

3.2 脏读

在这里插入图片描述
一个事务2在执行过程读取到事务1未提交的数据,就是脏读,如果事务1出现异常回退,数据1会还原,而事务2获取到的数据就时脏数据。

3.3 不可重复读

在这里插入图片描述
如果事务2在第一次读取数据时事务还未提交,第二次读取时事务1提交了,会产生两个读取的结果不同,也就是说事务2读取事务1的已提交的内容,导致不可重复读的问题。

3.4 幻读

在这里插入图片描述
事务2在操作一堆数据的时候,事务1插入了一条数据,提交,明明没有数据为说存在数据,像是幻觉,这就是幻读。

4 MySQL的隔离级别

4.1 数据准备

-- 创建学生表
CREATE TABLE users (
	id INT auto_increment NOT NULL,
	user_no varchar(100) NULL COMMENT '用户编号',
	name varchar(100) NULL COMMENT '用户名字',
	age INT NULL COMMENT '用户年龄',
	CONSTRAINT students_PK PRIMARY KEY (id)
);

--插入学生数据
INSERT INTO users (user_no, name, age) VALUES
('1', '小红', 10),
('2', '小蓝', 11),
('3', '小黑', 13),
('4', '小黑', 14);

4.2 读未提交

事务1 事务2
start transaction; start transaction;
update users set age=age+1; update users set age=age+1;
commit; commit;

在这里插入图片描述
在这里插入图片描述
如图所示,两个事务中修改同数据的时候会加一个互斥锁,当事务1获取锁时,事务2一致等待事务1的写互斥锁,当事务1完成时才释放锁,事务2获取了写互斥锁才能将数据写入。

事务1 事务2
SELECT @@transaction_isolation; SELECT @@transaction_isolation;
set global transaction isolation level read uncommitted; set global transaction isolation level read uncommitted;
exit exit
mysql -uroot -p 123456 mysql -uroot -p 123456
use test; use test;
SELECT @@transaction_isolation; SELECT @@transaction_isolation;
start transaction;
update users set user_no=100 where name=“小红”;
select * from users where name=“小红”;
rollback;

在这里插入图片描述
读未提交利用写互斥锁解决了数据库中的脏写的问题,但是依旧存在脏读、不可重复度和幻读问题

4.3 读已经提交

事务1 事务2
set global transaction isolation level read committed; set global transaction isolation level read committed;
exit exit
mysql -uroot -p 123456 mysql -uroot -p 123456
use test; use test;
SELECT @@transaction_isolation; SELECT @@transaction_isolation;
start transaction; start transaction;
select * from users where name=“小红”;
update users set age=200 where name=“小红”;
commit;
select * from users where name=“小红”;
commit;
在这里插入图片描述

读已经提交 通过MVCC多版本并发控制的技术解决了脏读的问题,但是依旧存在不可重复读幻读的问题。

读已提交中的MVCC机制相当于事务第一次读取数据创建一个ReadView(相当于快照),读取原本表中上一次提交的老数据。其他事务操作此数据的事务提交后此时MVCC机制又会创建一个新的ReadView,然后读取到最新的已提交的数据。

4.4 可重复读

-- 设置用户编号为唯一索引
alter table users add unique index un_no(user_no);
事务1 事务2
set global transaction isolation level repeatable read; set global transaction isolation level repeatable read;
exit exit
mysql -uroot -p123456 mysql -uroot -p123456
use test; use test;
SELECT @@transaction_isolation; SELECT @@transaction_isolation;
start transaction; start transaction;
select * from users where name=“小红”;
update users set age=300 where name=“小红”;
commit; select * from users where name=“小红”;
start transaction;
insert into users(user_no, name, age) values (“6”, “小青”, 10);
commit; select * from users;
insert into users(user_no, name, age) values (“5”, “小黄”, 12);

在这里插入图片描述
可重复读 解决了不可重复读的问题,但是依旧存在幻读的现象。

可重复读依旧是利用mvcc来解决不可重复读的问题,在读已提交中,一个事务中每次查询数据时,都会创建一个新的ReadView,然后读取最近已提交的事务数据,因此就会造成不可重复读的问题。而在可重复读级别中,则不会每次查询时都创建新的ReadView,而是在一个事务中,只有第一次执行查询会创建一个ReadView在这个事务的生命周期内,所有的查询都会从这一个ReadView中读取数据,从而确保了一个事务中多次读取相同数据是一致的,也就是解决了不可重复读问题。

innoDB的默认隔离级别是可重复读

4.5 串行化

事务1 事务2
set global transaction isolation level serializable; set global transaction isolation level serializable;
exit
mysql -uroot -p123456
use test;
SELECT @@transaction_isolation; SELECT @@transaction_isolation;
start transaction; start transaction;
select * from users;
insert into users(user_no, name, age) values (“6”, “小青”, 10);

在这里插入图片描述
在这里插入图片描述
这个隔离级别是最高的级别,处于该隔离级别的MySQL绝不会产生任何问题,因为从它的名字上就可以得知:序列化意思是将所有的事务按序排队后串行化处理,也就是操作同一张表的事务只能一个一个执行,事务在执行前需要先获取表级别的锁资源,拿到锁资源的事务才能执行,其余事务则陷入阻塞,等待当前事务释放锁。

上一篇:YUM仓库和编译安装


下一篇:ChatGPT指导下的学术写作:打造高质量论文