没长正的技术专栏 勤动手、多思考

MySQL之事务

2014-01-05

阅读:

DB  MySQL
2021-09-09

1 InnoDB 架构

2021-09-09_innodb-architecture

图片来自官网

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有可能会回滚

    2021-11-13_MySQL脏读

  • 不可重复读:事务A读取到了事务B更新/删除后且提交的数据。

    2021-11-13_MySQL不可重复读

  • 幻读:事务A读取到了事务B插入后且提交的数据。

  • 2021-11-13_MySQL幻读

=》我们仍能查询事务之前的数据,当某一条数据修改或删除之后。

快照

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 意向锁(意向共享锁、意向排它锁)

数据库自己维护的;这两个表级别锁存在的意义是什么?

假如我们要为表加表锁时,我们是不是要去扫描全表,然后判断某些行中是否添加了行级锁

如果加了行锁,需要等待行锁释放,然后再全表扫描,再判断,如此反复。

如果表数据量特别大,那性能就会非常低。所以才有了表级别的意向锁。

  • 意图共享锁IS)指示一个事务打算设置一个共享 上各个行锁定在表中。
  • 意图独占锁IX)指示一个事务打算设定各行的排他锁在表中。

例如,SELECT ... LOCK IN SHARE MODE设置一个IS锁,然后SELECT ... FOR UPDATE设置一个IX锁。

意图锁定协议如下:

  • 在事务可以获取表中行的共享锁之前,它必须首先获取IS表上的锁或更强的锁。
  • 在事务获得表中行的排他锁之前,它必须首先获得IX 表的锁。

” 表级锁类型兼容性总结在以下矩阵中:

  X IX S IS
X 冲突 冲突 冲突 冲突
IX 冲突 兼容 冲突 兼容
S 冲突 冲突 兼容 兼容
IS 冲突 兼容 兼容 兼容
如果请求事务与现有锁兼容,则向请求事务授予锁,但如果与现有锁冲突,则不会。事务一直等到冲突的现有锁被释放。如果锁定请求与现有锁定发生冲突并且由于会导致[死锁](https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_deadlock)而无法授予 ,则会发生错误。

====》来源

2.5 锁的算法

锁:锁住的是什么?

2.5.1 记录锁 (Record Lock)

锁住的是存在具体主键值;通过唯一索引或者主键值精确查找到一条记录时,使用记录锁,比如where id = 1 2 3 等。

使用不同的 key 去加锁,不会冲突,它只锁 住这个 record

2021-11-14_MySQL记录锁

2.5.2 间隙锁 (Gap Lock)

锁住的是主键值之间的间隙,比如where id>4 and id <7

当我们查询的记录不存在,没有命中任何一个record,无论是等值还是范围查询的时候,需要使用间隙锁

间隙锁主要阻塞的是insert,相同的间隙锁不冲突。Gap Lock只存在于RR中,若关闭,则成为了RC。

2021-11-14_MySQL间隙锁

2.5.3 临键锁 (Next Key Lock)

锁住的是间隙及左边的Record,即左闭右开的区间。

当我们使用了范围查询,不仅仅命中了 Record 记录,还包含了 Gap 间隙,在这种情况下我们使用的就是临键锁,它是 MySQL 里面默认的行锁算法,相当于记录锁加上间隙锁

2021-11-14_MySQL临键锁

2.6 死锁的解决方案

  • 尽量使用等值查询(直接命中,采用Record Lock),避免使用范围查询,扩大锁范围(间隙锁对并发的影响)。
  • 尽量使用索引来访问数据,避免没有where条件的操作,避免表锁。
  • 如果可以,大事务化小事务。
  • 批量操作单表数据时,先对数据排序处理(避免形成等待环路)。
  • 操作多表时,尽量以相同的顺序来访问(避免形成等待环路)。
  • 申请足够级别的锁,如果要操作数据,就使用排它锁。

参考

深入浅出数据库索引原理

MySQL的索引原理(图解)

InnoDB参数设置

-- 一个人什么也不做,是不会平白无故成长的;想要持续成长,一定要进行事前的准备,以及养成习惯。   《终身成长行动指南》

欢迎拍砖,多多交流,转载请注明出处:[没长正的技术专栏](http://blog.meizhangzheng.com) 如涉及侵权问题,请发送邮件到xsj34567@163.com,如情况属实本人将会尽快删除。


下一篇 MySQL之索引

Comments

Content