3-Mysql事务与锁详解

Mysql事务与锁详解

数据库事务的定义

事务是数据库管理系统(DBMS) 执行过程中的一个逻辑单位(整体不可分割,原子性),由一个有限的数据库操作序列构成。

事务的四大特性(ACID)

原子性(Atomicity)

最小逻辑单位,不可分割,要么都成功,要么都失败。

依赖事务日志 undo.log实现。

一致性(Consistency)

事务开始之前,事务结束之后,数据都要是合法的;数据库自身的完整性约束;用户自定义的完成性的约束

隔离性(Isolation)

多个事务间互不干扰。

持久性(Durability)

依赖 redo.log +双写缓冲实现。

事务并发的问题

脏读

在一个事务内读取到另一个事务未提交的数据。

image-20220302224922097

不可重复读

在一个事务内读取到另一个事务已提交的数据。

更新update或删除delete导致的数据变化叫做不可重复读。

幻读

在一个事务内读取到另一个事务已提交的插入数据。

只有当插入insert导致行数的增加才叫做幻读。

总结:事务并发的三大问题实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

事务隔离级别

Read Uncommitted(未提交读)一未解决任何并发问题

事务未提交的数据对其他事务也是可见的,会出现脏读。

Read Committed(已提交读)一解决脏读问题

一个事务开始之后,只能看到已提交的事务所做的修改,会出现不可重复读。

Repeatable Read(可重复读:Mysql5.7默认的事务隔离级别)一解决不可重复读问题

在同一个事务中多次读取同样的数据结果是一样的,这种隔离级别未定义解决幻读的问题。

但是在mysql InnoDB的存储引擎中是已经解决了幻读的问题了(因为使用了间隙锁,能够阻塞插入,防止了幻读问题),所以默认用的就是Repeatable Read(可重复读)。

Serializable(串行化)一解决所有问题

最高的隔离级别,通过强制事务的串行执行。导致并发度的效率大大降低。

Mysql InnoDB 对事务隔离级别的支持程度

快照读: 生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级 或事务级)的一致性读取 (MVCC) Multi Version Concurrency Control。

1
2
-- 普通的select语句
SELECT * FROM t;

当前读: 在读取数据前,对其加锁,阻止其他事务对数据进行 修改(LBCC)Lock Based Concurrency Control。基于最新的数据去操作的时候就是当前读

1
2
3
-- 加锁的select语句
SELECT * FROM t FOR UPDATE
-- DML语句

RC和RR的区别

RR的快照建立时问是第一次查询的时候,所以未提交的事务和其后的事务的事务是不能看见的。

RC的快照建立时问是当前select,所以能够看到其他事务已提交的数摆。

具体的机制就是两种隔离级别的ReadView的差异。

Mysql 锁基本类型

行锁与表锁区别

锁定粒度:表锁 > 行锁

加锁效率:表锁 > 行锁

冲突概率:表锁 > 行锁

并发性能:表锁 < 行锁

1、一张表没有索引,为什么会锁表?

一张表里如果有主键索引,会将它作为聚簇索引,其次如果没有主键索引,它会找表里不为空并且是唯一的索引作为聚簇索引,再其次如果既没有主键也没有不为空并且是唯一的索引,它就会用隐藏的字段rowid作为聚簇索引;这时它会进行全表扫描,锁住所有rowid的行,就导致整张表锁住了。

2、同一条数据唯一索引加锁,为什么阻塞主键索引加锁?

二级索引加锁时,因为二级索引是非聚簇索引,加锁时会通过非聚簇索引找到聚簇索引(也就是主键索引),锁住的其实是聚簇索引,所以就导致加锁冲突了。

InnoDB Locking

行锁:共享锁(Shared Locks)

共享锁又称为读锁,简称S锁

共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数 据,但是只能读不能修改。

加锁方式:

1
SELECT * FROM t WHERE id = 1 LOCK IN SHARE MODE;

释放锁方式:事务结束释放锁

行锁:排他锁(Exclusive Locks)

排他锁又称为写锁,简称X锁。

排他锁不能与其他锁并存,如一个事务获取了一个数据行的排他锁, 其他事务就不能再获取该行的锁(共享锁、排他锁),只有该获取了排他锁的事务是可以对数据行进行读取和修改。

加锁方式:

  • 自动:DELETE/UPDATE/INSERTDML语句默认动加上排他锁(X锁)
  • 手动:FOR UPDATE语句

释放锁方式:事务结束释放锁

意向共享锁(IS)/意向排他锁(IX)

意向锁是又存储引擎自己维护的,用户无法手动操作意向锁。

作用:为了提升加表锁的效率。

  • 意向共享锁(Intention Shared Locks,简称IS锁):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须取的该表(表级别)的意向共享锁(IS锁);类似JAVA中的偏向锁。
  • 意向排他锁(Intention Exclusive Locks,简称IX锁):表示事务准备给数据行加入排他锁,也就是说一个数据行加排他锁前必须取的该表(表级别)的意向排他锁(IX锁);类似JAVA中的偏向锁。

锁的算法(在什么样的情况下锁的范围)

记录锁(Record Locks):锁定记录

当你使用FOR UPDATE语句进行加锁时,精准命中一条记录,就会锁住该条记录;

间隙锁(Gap locks):锁定范围

当你使用FOR UPDATE语句进行范围查询或者等值查询时,如果没有命中任何数据记录的时候,就会锁住这个范围的区间;

注意:相同的间隙锁不排斥,排斥的是插入

比如我使用

1
SELECT * FROM t WHERE id = 6 FOR UPDATE;

这时锁住的是4-7之间的范围,这时我再使用该条语句,是不会排斥的,但是,当我进行插入时就会排斥

1
2
INSERT INTO `t`(`id`,`name`) VALUES (5,"张三");
INSERT INTO `t`(`id`,`name`) VALUES (6,"张三");

临键锁(Nest-key Locks):锁定范围加记录

InnoDB存储引擎行锁的默认算法;临键锁包含了间隙锁;它能够阻塞插入所以防止了幻读的问题;

条件是必须是范围查询,同时命中数据的记录(包含记录和区间);如果用等值查询是不满足条件的即包含记录和区间的;

会锁住最后一个记录(Record)的下一个key的左开右闭的区间。


3-Mysql事务与锁详解
https://happyloves.cn/20220707/f9f1e678c9b4.html
作者
赵小胖
发布于
2022年7月7日
许可协议