1. 事务的特性ACID
- 原子性: Atomicity
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。
- 一致性:Consistency
数据库总是从一个一致性的状态转换到另一个一致性的状态。
- 隔离性:Isolation
通常来说,一个事务所做的修改在最终提交前,对其它事务是不可见的。
- 持久性:Durability
一旦事务提交,则所有修改就永久保存在数据库的磁盘中。
RELDOLOG https://www.yuque.com/imyiren/notes/gwhyb9#Nf0aF
2. 事务的隔离级别
- 读未提交(read uncommitted)
- 读已提交(read committed)
- 可重复读(repeatable read)
- 串行化(serializable )
- 隔离级别产生的三个问题:
脏读(dirty read): 不可重复读(non-repeatable read): 幻读(phantom read):
2.1 读未提交
- 一个事务还没提交时,它做的变更就能被别的事务看到。
- 实现:“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;
-
2.2 读已提交
一个事务提交之后,它做的变更才会被其他事务看到。
- 实现:“读已提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的
-
2.3 可重复读 (MySQL默认 )
一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
- 实现:使用视图实现,视图是在事务启动时创建的,整个事务存在期间都用这个视图。
-
2.4 串行化
顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
- 实现:“串行化”隔离级别下直接用加锁的方式来避免并行访问。 (锁是部分锁,不是表锁)
- 问题:它会强制事务串行执行,避免了脏读、不可重复读、幻读的问题,但是牺牲了并发性。
2.5 问题解读
- 脏读
- 事务A 读到了事务B修改了但是还未提交的数据
- 不可重复读
- 事务A 开始读了一次数据,事务B开始修改了一个数据提交后,然后事务A又读了一次,发现和上次读取的数据不一样。
- 幻读
事务A 按照一定条件进行数据读取, 期间事务B 插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B 新插入的数据 称为幻读
2.6 隔离级别的案例
若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。
- 若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。
- 若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。
- 若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。
2.7 事务相关设置
查询事务隔离级别:
# 当前会话:
select @@tx_isolation;
show variables like 'transaction_isolation';
# 系统级别:
select @@global.tx_isolation;
设置事务隔离级别
# 注意 这样设置当前会话不会生效,只有下一个会话才会生效。 可重复读
set transaction isolation level REPEATABLE READ;
# 设置当前会话的事务隔离级别 : 读已提交
set transaction isolation level read committed;
# 设置同的事务隔离级别: 读未提交
set transaction isolation level read uncommitted;
启动提交回滚事务 ```sql
开始事务
begin start transaction
提交事务
commit
回滚事务
rollback
4. 关闭默认自动提交
```sql
set autocommit = 0;
- 该命令会把这个线程的自动提交关掉。这样只要执行一个select语句,事务就启动,并不会自动提交,直到主动执行commit或rollback或断开连接。
在autocommit=1的情况下用begin显式启动事务,如果执行commit则提交事务。
3. MVCC 多版本并发控制
MVCC(Mutil-Version Concurrency Control),就是多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。
- 在MySQL的InnoDB引擎中就是指在读已提交(READ COMMITTD)和可重复读(REPEATABLE READ)这两种隔离级别下的事务对于SELECT操作会访问版本链中的记录的过程。
3.1 MVCC在行中的隐藏列
- DATA_TRX_ID
- 记录最近更新这条行记录的
事务 ID
,大小为6
个字节
- 记录最近更新这条行记录的
- DATA_ROLL_PTR
- 表示指向该行回滚段
(rollback segment)
的指针,大小为7
个字节,InnoDB
便是通过这个指针找到之前版本的数据。该行记录上所有旧版本,在undo
中都通过链表的形式组织。
- 表示指向该行回滚段
- DB_ROW_ID
- 行标识(隐藏单调自增
ID
),大小为6
字节,如果表没有主键,InnoDB
会自动生成一个隐藏主键,因此会出现这个列。另外,每条记录的头信息(record header
)里都有一个专门的bit
(deleted_flag
)来表示当前记录是否已经被删除。
- 行标识(隐藏单调自增
4. MySQL中的各种锁
4.1 全局锁:
全局锁就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。
4.2 行锁 (InnoDB引擎支持)
行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。
- 在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
注意