事务acid原则
- 原子性
- 隔离性
- 一致性
- 持久性
事务的概念
把需要保证原子性、隔离性、一致性和持久性的一个或者多个数据库操作称为事务。
事务的使用
语法
begin;//开启一个事务
some sql;//需要执行的语句
savepoint bussiness1;//事务保存点
some sql;
roll back to bussiness1;//回滚
commit;//提交事务
rollback;//如果需要手动回滚的话在提交之前删除这句
事务的自动提交
通常情况下mysql默认自动提交事务。既每一个语句都都会直接提交事务,可以通过set autocommit=off
来实现手动提交。
隐式提交
在一些操作下,哪怕在事务中也可能会自动把事务进行提交。
- 定义或者修改数据库对象的数据定义语言,指对数据库、表、视图、存储过程等进行修改。
- 隐式使用或者修改mysql数据库中的表:当我们使用ALTER USER、CREATE USER、DROP USER、GRANT、RENAME USER、SET PASSWORD等语句时也会隐式的提交前边语句所属于的事务。
- 事务控制或关于锁定的语句:前一个事务尚未完成又开启事务时上一个会自动提交。使用lock tables、unlock tables等关于锁定的语句也会隐式提交之前的事务。
- 加载数据的语句:如果使用load data语句从文件中批量往数据库中插入数据时,也会隐式提交前边语句所属的事务。
- 其他: analyze table(统计表数据)、cache index()、check table、flush、load index into cache、optimize table、repair table。
事务的隔离级别
set session transaction isolation level [level];
level类型:
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读、提交读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
翻了一通网上的文章和培训视频对脏幻不可重复读的理解就NM离谱……
去查了一下高性能mysql这本书。
脏读:
定义:事务中的修改,即使没有提交,对其他事务也是可见的。
实例:事务A查询记录1,事务B更新记录1(尚未commit),事务A再次查询记录1,得到事务B更新结果。
在rc级别上已经可以克服脏读的问题。
不可重复读:
定义:一个事务开始时,只能看见已经提交的事务所做的修改。
实例:事务A查询记录1,事务B更新记录1(commit),事务A再次查询记录1,得到事务B更新结果。
幻读:
定义:指的是当某个事务A在读取某个范围的记录时,另一个事务B又插入了一条,A再次查询时会得到查询出这条记录。
幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。
MVCC
innoDB存储引擎记录数据的行有三个隐藏列:
- row_id 行id 唯一标识记录(非必须)
- transaction_id 事务id (必须)
- roll_pointer 回滚指针(必须)
ReadView
select 时生成
对于RU级别的事务,直接读取最新记录的版本,对于使用Serializable级别的,使用加锁方式访问,使RC和RR级别的事务则需要使用版本链,核心:判断版本链中的哪个版本是当前事务可见的。
ReadView包含四个比较重要的内容:
- m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
- min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
- max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
- creator_trx_id:表示生成该ReadView的事务的事务id
在有了这个readview之后,需要按照下边步骤判断记录是否可见:
- trx_id与creator_trx_id相同,可见。
- 被访问版本的trx_id<min_trx_id,表明生成该版本的事务在当前事务生成readview前已经提交,所以该版本可以被访问。
- 如果trx_id>max_trx_id,表明该版本的事务在当前事务生成readview后才开启,不可以被当前事务访问
- 如果trx_id在min和max之间,需要判断一下trx_id属性值是不是在m_ids的列表中,如果在,说明生成该版本时,这个事务还是活跃的。该版本不可被访问;如果不在,则代表生成该版本时事务已经被提交,该版本可以被访问。
rc实现方式
每次读取数据前都生成一个readview。
rr实现方式
在第一次读取数据时生成一个readview
实例:
在RC级别下 原先数据为1
有两个事务A,B,时间线如下
B将数据更新,B此时可以读到2
此时A应该读更新前还是更新后的数据?
假设A的事务ID是100,B的是200
如果
min_trx_id=99,
max_trx_id=200;
m_ids=[99,100,200]
此时此时A事务符合规则4,读到的数据应该为1
关于锁
锁的分类
- 读锁:共享锁、shared Locks、S锁
- 写锁:排他锁、Exclusive Locks、X锁
X锁 | S锁 | |
---|---|---|
X锁 | 冲突 | 冲突 |
S锁 | 冲突 | 不冲突 |
读操作
对于普通的select语句InnoDB不会加任何锁。
select …… lock in share mode
将查找到的数据加上一个S锁,其他事务能读,但是不能写。
读出数据后:其他事务不能修改,但自己也不一定能修改,因为有可能有别的事务也可以使用select lock in share mode继续加读锁。
select …… for update
加上一个X锁。
写操作
- delete:先加X锁,再执行删除。
- insert:插入一条记录时,会先加隐式锁来保护这条新加入的数据在本事务提交之前不被别的事务访问到。
todo:隐式锁实现是啥呢……
- update
- 如果被更新的列修改前后没有导致存储空间变化,加X锁,修改记录。
- 如果被更新的列修改前后导致存储空间的变化,加X锁,删除,insert。
行锁与表锁
行锁
- LOCK_REC_NOT_GAP:单个行记录
- LOCK_GAP:间隙锁,锁定一个范围,但不包括记录本身。
- LOCK_ORDINARY:锁定一个范围以及记录本身。
原文地址:https://www.cnblogs.com/callmechen1997/p/11567367.html