一 锁和底层原理概念
1 锁
1 加读锁 sql语句 + lock in share mode
2 加写锁 sql语句 + for update
普通的delete 和update语句自动加写锁
insert 语句加隐式写锁,当在插入语句时有其他的事务对这一行数据进行加锁则会激活写锁进行阻塞
锁冲突
读锁 : 共享锁(读锁可以加无数把)
写锁 : 排它锁 (写锁只可以加一,读写锁也冲突)
普通的select语句没有锁,可以绕过锁读取数据
2 底层
逻辑行: 在表中放每一行
文件行: 保存在文件当中的每一行
局部性原理: 把某一行数据加载如内存时,会把相邻的数据同时加载入内存形成页 (innodb引擎底层单位 : 页)
文件行类型(行格式)
compact 类型
变长字段长度列表 | null标志位 | 记录头信息 | 数据 1 | 数据 2 |
---|
dynamic 类型与compact类型相似
行最大内存为65535 字符
页最大内存16k
2.1 如果发生了行溢出,行格式中会记录下一个页的地址
行溢出
MyiAsm : cpmpact 类型
第一页 第二页
数据+记录 数据+记录
InnoDB Dynamic 类型
第一页 第二页
记录 数据+记录(索引)
InnoDB : 聚集 返回记录的顺序是以主键排好的顺序(插入时,默认使用主键排序并用指针连接)
MyiAsm : 堆表 返回记录的顺序和输入记录的顺序一致
二 行锁
MySQL(innodb) 支持行锁·
行锁分类
1 行锁 :锁住单个行记录
2 间隙锁 : 锁定一个范围,包括本身,可以解决幻读
三 隔离级别
**1 Read Uncommited **
就是一个事务能够看到其他事务尚未提交的修改,允许脏读出现
2 Read Commited
事务能够看到的数据都是其他事务已经提交的修改,也就是保证不会看到任何中间性状态,当然脏读也不会出现
3 Repeatable Read
保证同一个事务中多次读取的数据是一致的,这是 MySQL InnoDB 引擎的默认隔离级别,
4 Serializable
并发事务之间是串行化的,通常意味着读取需要获取共享读锁,更新需要获取排他写锁,如果 SQL 使用 WHERE 语句,还会获取区间锁(MySQL 以 GAP 锁形式实现,可重复读级别中默认也会使用),这是最高的隔离级别
四 实现隔离级别的底层
4.1 select 语句 (MVCC : 多版本并发控制 ) 无锁
1 每一个事务都有一个唯一的transaction_id
2 同一个数据,一个事务对其修改一次就会产生一次版本数据,修改多次就会产生一条版本链
如果隔离级别为Read Commited(读已提交)
3 当select语句执行时会产生一个ReadView,里面包含了一个ids[ ] 属性,会把当前所有活跃的事务的 id 放入,如果有提交的事务,ids[ ] 属性会更新
4 遍历当前版本链,看哪一个版本的事务不在ids[ ] ,说明此版本的事务已提交,读取此版本数据
如果隔离级别为Repeatable Read(可重复读)
3 当select语句执行时会产生一个ReadView,里面包含了一个ids[ ] 属性,会把当前所有活跃的事务的 id 放入,如果有提交的事务,ids[ ] 属性不会更新
4 遍历当前版本链,看哪一个版本的事务不在ids[ ] ,说明此版本的事务已提交,读取此版本数据
4.2 insert 语句 间隙锁
如果隔离级别为Read Commited(读已提交)
无间隙锁 , 会产生幻读
如果隔离级别为Repeatable Read(可重复读)
有间隙锁 , 消除幻读
五 表锁
1 索引失效或者无索引,innoDB会使用表锁
索引失效时加表锁会产生锁冲突 ?
2 加行读锁或行写锁时会加一个IX/IS锁(意向表写锁 / 意向表读锁),以此来判断表上有没有锁