事务经常出现在大家的业务逻辑中
事务
- 什么是事务?
- 先和大家举个例子。在12306买票的时候,是否遇到过没票的情况,而官方会推荐中转车程,那在购票的过程中,前半程和后半程是不是只能同时购买成功或失败,只买到其中半程的票也没有意义。所以事务的定义,表示这同时成功或失败
- 什么是良好的事务?
- 一个良好的事务必须满足事务的ACID特性
- 什么是ACID?
- A 即atomiciy代表着原子性——原子是参加化学反应的最小单位,意义为同时成功或失败,中间不能存在第三种状态
- C 即consistency代表着一致性——当一个金融系统崩溃的时候要保证各个账户的金额保持总和不变,即回滚的时候要回到之前的某同一时刻。
- I 即isolation代表着隔离性——在并发事务中,当其中一个事务没有结束时,其他事务应该对该事务的操作不可见
- D 即durability代表着持久性——一个事务在正常结束后,数据应该被持久化存储即便系统崩溃也有恢复的机制
事务的隔离级别
事务的隔离级别是由SQL标准组织定义的
- 事务的四大隔离级别
-
READ UNCOMMITTED 与 SERIALIZZBLE平常都不会怎么用,代表着两个极端。前一个对事务的并发问题做不了任何处理,极容易导致系统的混乱。后一个保证事务的串行化处理,使得性能大幅下降
-
READ UNCOMMMITTED 读未提交。允许事务读取到其他事务未提交的数据
-
READ COMMITTED 读已提交。允许事务读取到其他事务已经提交的数据
-
REPEATABLE READ 可重复读。同一事务下对同一数据查询的结果保持一致
-
SERIALIZABLE 串行化事务。在该级别下,所有事务都是进行串行化顺序执行,这样就可以避免脏读、不可重复读与幻读所有问题了。但是这种事务隔离级别下事务执行的效率低下,比较耗数据库性能
-
查看系统隔离级别:
select @@global.tx_isolation;
查看当前会话隔离级别
select @@tx_isolation;
设置当前会话隔离级别
SET session TRANSACTION ISOLATION LEVEL serializable;
设置全局系统隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
锁
- 从锁的思想来看(这只是一种思想上的划分,可以理解这是一个接口)
- 乐观锁——对其他事务持乐观态度(阳光大男孩),总是认为不会对本事务的数据进行修改。CAS是常见的一种乐观锁实现方式
- CAS——在执行操作前对数据进行判断是否被修改,如数据一致则继续,不一致重新开启事务。
- 悲观锁——对其他事务持悲观态度(被害幻想症),总是认为本事务的数据会被修改
- 从锁的粒度来看
- 表锁——其他事务对整张表的操作被阻塞
- 行锁——其他事务对加锁行的操作被阻塞
- 间隙锁——对表中某个范围加锁,范围一定存在于表记录之间,如果查询的范围是4——6,但是表中的记录只有3——8,那么锁的范围就是(3,8)
- NEXT-KEY-LOCK——与间隙锁相似,范围是(x,y],即能锁住后边界的记录
- 从锁的特征来看
- 共享锁——通常用在读操作,多个事务共享读操作,但不允许修改,和加锁操作
- 排他锁——通常用在写操作,不允许其他事务进行操作(包括写和加锁操作)
MVCC 多版本并发控制
- 事务版本号
- 每次事务开启前都会从数据库获得一个自增长的事务ID,可以从事务ID判断事务的执行先后顺序
- 命令查看:select TRX_ID from INFORMATION_SCHEMA.INNODB_TRX;
- 隐藏字段(Innodb 为每行额外添加了3个字段)
- DB_TRX_ID:大小为6个字节。指插入或更新该行的最后一个事务的事务标识符,也就是事务ID。
此外,删除在内部被视为更新,在该更新中,该行中的特殊位被设置为将其标记为已删除。 - DB_ROLL_PTR:大小为7个字节。表示指向该行回滚段的指针。 回滚指针指向写入回滚段的撤消日志记录。
如果行已更新,则撤消日志记录将包含在更新行之前重建行内容所必需的信息。 - DB_ROW_ID:大小为6个字节。包含一个行ID,该行ID随着插入新行而单调增加。
如果InnoDB自动生成聚集索引,则该索引包含行ID值。 否则,DB_ROW_ID列不会出现在任何索引中
- DB_TRX_ID:大小为6个字节。指插入或更新该行的最后一个事务的事务标识符,也就是事务ID。
- undo.log
- Undo log是InnoDB MVCC事务特性的重要组成部分。Undo log 主要用于记录数据被修改之前的日志
- 当前读与快照读
- 当前读是指读取到的永远是最新的记录
- 需要上锁的操作都是当前读
select * from .... where ... for update、 select * from .... where ... lock in share mode
update .... set .. where ... delete from. . where ..
- 需要上锁的操作都是当前读
- 快照读是指读取数据的时候会根据一定规则读取事务可见版本的数据
- 普通的查询操作都是快照读 事务中都使用快照读,那么就不会产生幻读现象,但是快照读和当前读混用就会产生幻读。事务都使用当前读时,则会使用next-lock避免幻读现象
- 当前读是指读取到的永远是最新的记录