MySQL基础6——事务,ACID,并发异常

MySQL事务原理

事务

目的

事务将数据库从一种一致性状态转换为另一种一致性状态;

组成

事务可由一条非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成;

特征

在数据库提交事务时,可以确保要么所有修改都已经保存,要么所有修改都不保存;

事务是访问并更新数据库各种数据项的一个程序执行单元。

在 MySQL innodb 下,每一条语句都是事务;可以通过 set autocommit = 0; 设置当前会话手

动提交;

事务控制语句

-- 显示开启事务 
START TRANSACTION | BEGIN 
-- 提交事务,并使得已对数据库做的所有修改持久化 
COMMIT 
-- 回滚事务,结束用户的事务,并撤销正在进行的所有未提交的修改 
ROLLBACK 
-- 创建一个保存点,一个事务可以有多个保存点 
SAVEPOINT identifier 
-- 删除一个保存点 
RELEASE SAVEPOINT identifier
-- 事务回滚到保存点 
ROLLBACK TO [SAVEPOINT] identifier

ACID 特性

原子性 A

事务操作要么都做(提交),要么都不做(回滚);事务是访问并更新数据库各种数据项的一个程

序执行单元,是不可分割的工作单位;通过undolog来实现回滚操作。undolog记录的是事务每步

具体操作,当回滚时,回放事务具体操作的逆运算;

操作语句都会记录到undolog里,回滚则按照undolog回滚

隔离性I

事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互分离,也就是事务提交前对其

他事务都不可见;通过 MVCC 和 锁来实现;MVCC 时多版本并发控制,主要解决一致性非锁定

,通过记录和获取行版本,而不是使用锁来限制读操作,从而实现高效并发读性能。锁用来处理

并发 DML 操作;数据库中提供粒度锁的策略,针对表(聚集索引B+树)、页(聚集索引B+树叶

子节点)、行(叶子节点当中某一段记录行)三种粒度加锁;

MVCC避免给读操作加锁,给予一个版本控制,根据读操作选择对应的版本

持久性 D

事务提交后,事务DML操作将会持久化(写入redolog磁盘文件 哪一个页 页偏移值 具体数据);

即使发生宕机等故障,数据库也能将数据恢复。redolog记录的是物理日志;

一致性 C

一致性指事务将数据库从一种一致性状态转变为下一种一致性的状态,在事务执行前后,数据库完

整性约束没有被破坏。例如:一个表的姓名是唯一键,如果一个事务对姓名进行修改,但是在事务

提交或事务回滚后,表中的姓名变得不唯一了,这样就破坏了一致性;一致性由原子性、隔离性以

及持久性共同来维护的。

事务并发异常

脏读

事务(A)可以读到另外一个事务(B)中未提交的数据;也就是事务A读到脏数据;在读写分离的

场景下,可以将slave节点设置为 READ UNCOMMITTED;此时脏读不影响,在slave上查询并不

需要特别精准的返回值。

seq session A session B
1 SET @@tx_isolation='READ UNCOMMITTED';
2 BEGIN;
3 SELECT * FROM dirty_read_t WHRER id > 3;
4 SET @@tx_isolation = 'READ UNCOMMITTED';
5 BEGIN;
6 INSERT INTO dirty_read_t (id, name, sex, age)VALUES(5, 'miro', 2, 20 )
7 SELECT * FROM dirty_read_t WHRER id > 3;

不可重复读

事务(A) 可以读到另外一个事务(B)中提交的数据;通常发生在一个事务中两次读到的数据是不

一样的情况;不可重复读在隔离级别 READ COMMITTED 存在。一般而言,不可重复读的问题是

可以接受的,因为读到已经提交的数据,一般不会带来很大的问题,所以很多厂商(如Oracle、

SQL Server)默认隔离级别就是READ COMMITTED;

BEGIN
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
INSERT INTO table('id', 'name', 'sex', 'age')
VALUES
    (5, 'milo', 2, 20)
COMMIT;
ROLLBACK;
END;

# 没有提交前是搜索不到milo那一行数据的

幻读

事务中一次读操作不能支撑接下来的业务逻辑;通常发生在一个事务中一次读判断接下来写操作失

败的情况;例如:以name为唯一键的表,一个事务中查询select * from t where name = 'mark';

不存在,接下来insert into t(name) values ('mark');出现错误,此时另外一个

事务也执行了 insert 操作;幻读在隔离级别 REPEATABLE READ 及以下存在;但是可以在

REPEATABLE READ 级别下通过读加锁(使用next-key locking)解决;

BEGIN
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM table WHERE id = 5 lock in share mode;#(加共享锁)   或者事务隔离级别指定串行
INSERT INTO table ('id', 'name,','sex','age')
VALUES
(5, 'AA', 2 , 20)
COMMIT; ROLLBACK; END;

#相当于读锁,加锁后另一个session做INSERT INTO table ('id', 'name,','sex','age')VALUES(5, 'AA', 2 , 20)的插入会插入不进去
上一篇:分布式系统中ACID和CAP有什么区别?太厉害了!


下一篇:VulnHub渗透测试实战靶场 - ACID: SERVER