Mysql事务与锁机制

事务的四大特性

原子性:一个事务要么全部提交成功要么全部失败回滚,不能只执行其中的一部分操作

一致性:执行事务前后,数据状态必须保持一致,多个事务对同一数据读取结果是相同的

隔离性:事务的隔离性是指在并发环境下,多个并发的事务之间应该相互隔离,互不干扰

持久性:一旦事务提交,那么它对数据库中对应数据的状态变更就会永久保存到数据库中,及时发生系统崩溃,只要数据库可以重新启动,那么就一定能回复到事务成功结束的状态

Mysql实现四大特性的方式 

持久性:重写日志  redo log

原子性和一致性:回滚日志 undo log 

隔离性:隔离级别、锁机制、MVCC机制

事务并发会出现的三种问题

脏读:事务A在提交事务时由于某些原因触发回滚,事务B读取到了A未提交的数据

幻读:事务A对一个表查询多条数据,同时事务B对表进行了新增,事务A再次查询得到的数据量与之前的不一致

不可重复读:事务A 对某行数据进行查询,同时事务B对该行数据进行更新,事务A再次进行读取,读取到的数据与之前不一致。

事务的四种隔离级别

Read UnCommitted 读取未提交:隔离界别最低,允许读尚未提交的数据变更,会发生脏读、幻读、不可重复读 

Read Committed 读取已提交(Oracle 默认隔离级别):允许读取并发事务已经提交的数据,可以防止脏读,不可防止幻读、不可重复读

Repeatable Read 可重复读(Mysql 默认隔离级别):对同一字段的多次读取是相同的,可以防止脏读、不可重复读,不能防止幻读 

Serializable 可串行化:最高的隔离几倍,事务完全服从ACID的隔离级别,所有事务依次逐个执行,可以防止脏读。幻读。不可重复读


Mysql锁机制

Mysql锁级别:

  • 表级锁:开销小,加解锁速度快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 行级锁:开销大,加解锁速度慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

Mysql 索颗粒度:

  • 锁的级别
  • 数据库引擎具有多粒度锁定,允许一个事务锁定不同类型的资源。 为了尽量减少锁定的开销,数据库引擎自动将资源锁定在适合任务的级别。
  • 锁定在较小的粒度(例如行)可以提高并发度,但开销较高,因为如果锁定了许多行,则需要持有更多的锁。 锁定在较大的粒度(例如表)会降低了并发度,因为锁定整个表限制了其他事务对表中任意部分的访问。 但其开销较低,因为需要维护的锁较少。

锁的层次结构:

  • 数据库引擎通常必须获取多粒度级别上的锁才能完整地保护资源。这组多粒度级别上的锁称为锁层次结构。例如,为了完整地保护对索引的读取,数据库引擎实例可能必须获取行上的共享锁以及页和表上的意向共享锁。
  • MySQL有三种锁的级别:页级、表级、行级
  • MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking)
  • InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

InnoDB行级锁—共享锁、排它锁:

  1. 共享锁(s锁):读锁, 对当前行加共享锁,不会阻塞其他事物对同一行的读请求,阻塞对同一行的写请求
  2. 排它锁(x锁):写锁,阻塞其他事物对同一行的读和写操作
  3. InnoDB引擎在 RR隔离级别下,对DML操作会自动给涉及的数据加上x锁
  4. 普通的select语句,不会加锁 ,加s锁 : lock in share mode
  5. 语句在读取时,另一个事务需要对该记录进行更新时会出现死锁。加x锁:for update

Mysql加锁模式: 不能完全说行锁是基于索引实现的

记录锁:Udine表中的记录进行加锁(行锁),防止其他事务插入、更新、删除 该行记录

记录锁也是 x锁

查询语句必须为精准匹配,不能做范围查询和模糊查询,否则会变成临键锁

通过 主键索引 或者 唯一索引对数据进行Update 操作,会对该行记录加记录锁

如果要锁的列没有索引,会对全表记录加锁

记录锁 锁的是索引记录,不是真正的数据记录

间隙锁:是InnoDB引擎在 rr级别下为了解决 幻读问题 引入的锁机制,是行锁的一种
使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。

例: SELECT * FROM emp WHERE empid > 100 FOR UPDATE

我们用条件检索数据,并请求共享或排他锁时,InnoDB 不仅会对符合条件的 empid 值为 101 的记录加锁,也会对 empid 大于 101(这些记录并不存在)的 “间隙” 加锁。

这个时候如果你插入 empid 等于 102 的数据的,如果那边事物还没有提交,那你就会处于等待状态,无法插入数据。

临键锁:记录锁和间隙锁的组合,它指的是加在 某条记录以及这条记录前面间隙上 的锁。

临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。

意向锁:为了让InnoDB中的行锁和表锁更高效的共存

意向锁分为:

意向共享锁(IS) 意向排它锁(IX)

事务要获取某些行的排它锁 ,就必须先获取表的意向排它锁。 共享锁同理

  • 意向共享锁(IS)和 意向排他锁(IX)都是 表锁。
  • 意向锁是一种 不与行级锁冲突的表级锁,这一点非常重要。
  • 意向锁是 InnoDB 自动加的, 不需用户干预。
  • 意向锁是在 InnoDB 下存在的内部锁,对于MyISAM 而言没有意向锁之说。

 MVCC 多版本并发控制

MVCC的目的是在不加锁的情况下解决并发事务读写冲突问题

首先要理解什么事当前读和快照读

当前读:读取的是记录的最新版本,且并发事务不会对当前记录进行修改,InnoDB引擎会给当前读加上锁,如 共享锁 select......where......lock in share mode,排它锁 select........where.......for update,此外所有的DML操作也属于当前读

快照读:读取的可能是该条数据的历史版本,不加锁的 select,快照读可以在并发环境下提高性能,是MVCC机制的运用。

MVCC实现机制:为每次修改保存版本undo log,版本与事务时间相关联,每一个版本叫做一次快照,读操作只读事务开始前的快照版本。

MVCC实现原理依赖于 隐藏字段、undolog 回滚日志、readView 读视图

在事务记录中有三个隐藏字段:

DB_TRX_ID:创建这条记录 或者最后一次修改该记录的事务id

DB_ROLL_PTR:回滚指针,指向该记录的上一个版本的地址

DB_ROW_ID:隐藏的自增主键, 如果数据库中没有主键自动生成6字节的rowid

undo log 日志 通过回滚指针指向上一个版本记录的地址形成了一条链表,链首是最新的旧记录,链尾是最旧的记录。

读视图 Read View:读视图,在事务进行快照读操作的时候产生,方便我们进行可见性算法的判断,只不过并不是存储的实际的数据,而是存储的事务状态。

readView中三个关键字段:

trx_list:生成读视图时候,当前系统正活跃的事务id集合

up_limit_id:事务活跃列表中id最小的值

low_limit_id:系统尚未分配的下一个事务的id

可见性算法分析: 

  1. 获取当前最新修改记录的 DB_TRX_ID
  2. 获取当前活跃最小事务id up_limit_id,判断DB_TRX_ID是否小于up_limit_id,小于说明当前快照读可以获取最新数据。大于进行下一步判断
  3. 获取下一个系统未分配事务id low_limit_id,判断 DB_TRX_ID是否大于 low_limit_id,小于,进行下一步判断;大于,查阅undo log链表中下一条旧记录的 DB_TRX_ID,重新与up_limit_id判断
  4. 小于前提下,查看DB_TRX_ID是否在当前事务活跃集合 trx_list中,不在,说明DB_TRX_ID事务已提交,可以读取到最新数据;在,查阅undo log链表中下一条旧记录的 DB_TRX_ID,重新与up_limit_id判断

例: 

 事务1 t2时刻快照读:DB_TRX_ID = 0;up_limit_id = 1;DB_RTX_ID < up_limit_id成立,此时可以读取到最新的数据

事务1 t4时刻快照读:DB_TRX_ID = 2;up_limit_id = 1;DB_RTX_ID < up_limit_id不成立;low_limit_id = 4,DB_RTX_ID > low_limit_id 不成立;trx_list={1,3} 不包含DB_RTX_ID,说明在t4时刻 修改事务已提交,可以读取到最新的数据。

RC , RR 级别下的 InnoDB 快照读的不同点?

RC级别下,每次快照读都会生成最新的 Read View,RR级别下,同一个事务只会在第一次快照读时才会创建Read View,后续快照读是使用同一个Read View。

回看上面的例子,假如此时数据库为默认隔离级别RR,设为例2:

t4时刻的快照读使用的依旧是t2时刻生成的 Read View,trx_list={1,2,,3};up_limit_id = 1;low_limit_id = 4;DB_TRX_ID = 2。第一次判断DB_TRX_ID < up_limit_id不成立;第二次判断DB_TRX_ID > low_limit_id不成立;第三次查看DB_TRX_ID在trx_list中,此时快照读获取不到最新数据。那么t4时刻读取到的数据应该与t2时刻保持一致。

:例2就是MVCCRR级别下如何保证可重复读,RR级别下如何避免快照读时的幻读问题,同理可以推得在RC级别下MVCC无法解决幻读和不可重复读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值