1 InnoDB 架构
2 数据库事务
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。(来自百度)
2.1 如何实现事务
实现事务采取了哪些技术以及思想?ACID: atomicity 、consistency 、isolation、durability
原子性使用 undo log ,从而达到回滚;持久性:用 redo log,从而达到故障后恢复;隔离性使用锁以及MVCC,运用的优化思想有读写分离,读读并行,读写并行;一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。
2.2 事务的隔离级别
默认是可重复读
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读(Read uncommitted) 不加锁 |
可能 | 可能 | 可能 |
已提交读(Read committed) 加锁时,底层使用Record锁 |
不可能 | 可能 | 可能 |
可重复读(Repeatable Read) innodb 默认(RR) 加锁时,底层使用Record锁、 间隙锁、临键锁 |
不可能 | 不可能 | innodb不可能 采用临键锁: 半开半闭 (1,10] (10,+∞) |
串行化(Serializable) 互斥锁 |
不可能 | 不可能 | 不可能 |
RR与RC在普通读情况下,都是读快照表,采用MVCC控制。
为甚大厂把RR改为RC?
提高并发s
在 RC 中,只会对索引增加Record Lock,不会添加Gap Lock和Next-Key Lock。
RC只支持row格式的binlog
在 RR 中,为了解决幻读的问题,在支持Record Lock的同时,还支持Gap Lock和Next-Key Lock;
RR 的隔离级别同时支持statement、row以及mixed三种。
MySQL 默认隔离级别是RR,为什么阿里等大厂会改成RC?
2.3 如何实现读一致性问题?
基于MVCC控制:多版本并发控制
-
脏读:事务A读取到了事务B更新后且没有提交的数据。===> 事务B有可能会回滚
-
不可重复读:事务A读取到了事务B更新/删除后且提交的数据。
-
幻读:事务A读取到了事务B插入后且提交的数据。
=》我们仍能查询事务之前的数据,当某一条数据修改或删除之后。
快照
2.4 锁
锁的颗粒度:行锁、表锁; mysql5.7 列出了8种类锁(2行2表,4算法)
2.4.1 行锁-共享锁(Shared Locks)
-- 给一行加读锁
select * from tableName lock in share mode;//行锁,事务完成后自动释放掉
2.4.2 行锁 - 排它锁(Exclusive Locks)
-- 1. mysql 增删改时自动添加一个排它锁
-- 2. 基于一行数据时,使用 for update
select * from user where id = 1 for update;//BLOCKED
2.4.3 意向锁(意向共享锁、意向排它锁)
数据库自己维护的;这两个表级别锁存在的意义是什么?
假如我们要为表加表锁时,我们是不是要去扫描全表,然后判断某些行中是否添加了行级锁。
如果加了行锁,需要等待行锁释放,然后再全表扫描,再判断,如此反复。
如果表数据量特别大,那性能就会非常低。所以才有了表级别的意向锁。
“
例如,SELECT ... LOCK IN SHARE MODE
设置一个IS
锁,然后SELECT ... FOR UPDATE
设置一个IX
锁。
意图锁定协议如下:
- 在事务可以获取表中行的共享锁之前,它必须首先获取
IS
表上的锁或更强的锁。 - 在事务获得表中行的排他锁之前,它必须首先获得
IX
表的锁。
” 表级锁类型兼容性总结在以下矩阵中:
X | IX | S | IS | |
---|---|---|---|---|
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 兼容 | 冲突 | 兼容 |
S | 冲突 | 冲突 | 兼容 | 兼容 |
IS | 冲突 | 兼容 | 兼容 | 兼容 |
====》来源
2.5 锁的算法
锁:锁住的是什么?
2.5.1 记录锁 (Record Lock)
锁住的是存在具体主键值;通过唯一索引或者主键值精确查找到一条记录时,使用记录锁,比如where id = 1 2 3 等。
使用不同的 key 去加锁,不会冲突,它只锁 住这个 record
2.5.2 间隙锁 (Gap Lock)
锁住的是主键值之间的间隙,比如where id>4 and id <7
当我们查询的记录不存在,没有命中任何一个record,无论是等值还是范围查询的时候,需要使用间隙锁间隙锁主要阻塞的是insert,相同的间隙锁不冲突。Gap Lock只存在于RR中,若关闭,则成为了RC。
2.5.3 临键锁 (Next Key Lock)
锁住的是间隙及左边的Record,即左闭右开的区间。
当我们使用了范围查询,不仅仅命中了 Record 记录,还包含了 Gap 间隙,在这种情况下我们使用的就是临键锁,它是 MySQL 里面默认的行锁算法,相当于记录锁加上间隙锁
2.6 死锁的解决方案
- 尽量使用等值查询(直接命中,采用Record Lock),避免使用范围查询,扩大锁范围(间隙锁对并发的影响)。
- 尽量使用索引来访问数据,避免没有where条件的操作,避免表锁。
- 如果可以,大事务化小事务。
- 批量操作单表数据时,先对数据排序处理(避免形成等待环路)。
- 操作多表时,尽量以相同的顺序来访问(避免形成等待环路)。
- 申请足够级别的锁,如果要操作数据,就使用排它锁。
参考
-- 一个人什么也不做,是不会平白无故成长的;想要持续成长,一定要进行事前的准备,以及养成习惯。 《终身成长行动指南》