前言

Mysql部分到此结束

正文

什么是事务

  1. 是数据库最小的工作单元,不可再拆分。
  2. 可能包含一个或者一系列的DML语句,包括insert,update,delete。(当然DDL,DCL也会包含事务)

事务特性

  1. 原子性:一次多个操作要么都成功,要么都失败。通过undo.log实现回滚,保证原子性。
  2. 隔离性:多个事务之间互不影响。通过MVCC + LBCC实现。
  3. 持久性:事务提交成功,结果应当写入磁盘。通过duuble write pool(双写缓冲)+redo.log实现。
  4. 一致性:事务开启前结束后,数据都是合法的状态。通过原子性+隔离性+持久性前三种特性来保证这一特性。

前提知识

  1. 事务都是有编号的,且会不断递增
  2. InnoDB为每行数据都设置了两个隐藏字段
    1. DB_TRX_ID:事务ID,此数据是在哪个事务插入或者修改为新数据的,就记录为当前事务id。
    2. DB_ROLL_PTR:回滚指针,删除版本号,正常为null,当数据被删除或者记录为旧数据(即被修改)时,记录当前事务ID。

MVCC (多版本并发控制)

只能查找创建时间小于等于当前事务ID的数据,和删除时间大于当前事务ID的行(或者未删除)。

undo.log链

当一个数据被标记为旧版本(即被修改)时,是被存在undo.log中的。
同一条数据被修改了多次,这些同一条数据的旧版本就会形成了一个链条,叫做undo.log链。
前面的DB_ROLL_PTR就是指向undo.log的指针。

具体是如何存储的这里就不详细说了,有点复杂。
简单来说,就是可以通过这个字段拿到属于自己可以拿到的旧版本,生成快照。 =.=

LBCC

锁的粒度

  1. 行锁
  2. 表锁

锁的类型

  1. 共享锁:只能读,不能写
  2. 排他锁:不能读写
  3. 意向锁:加锁前申请意向锁,提高加锁效率。

行锁原理

通过索引进行加锁,未走索引会造成表锁。

行锁的算法

以下内容都是在默认RR隔离级别下

Mysql总结之事务/锁_Mysql

Record Lock(记录锁)

主键值即record,如上图:数据库里有两条数据key为10和20,这就是对应两个记录锁

对唯一索引/主键索引进行等值加锁查询即产生记录锁(对于普通索引加锁范围不了解)

Gap Lock(间隙锁)

record之间不存在的区间(左开右开),即为间隙锁。

当我们的查询未命中一个record,无论是等值还是范围查询都会产生这个范围内的间隙锁。

Next-Key Lock(临建锁)

record lock+ 其左边的gap lock即为临建锁,范围左开右边闭。

当我们使用范围查询命中了一条数据时,会锁住下个key的左开右闭区间。
例:如上图所示内容:select * from table where Key >8 and Key<15;
结果:会产生Gap Lock1+Record Lock1+Gap Lock2+Record Lock2 即(0,10]+(10,20]
原因:主要为了解决幻读的问题,这也是为啥innoDB在RR级别下就解决了幻读。

事务总结

Mysql总结之事务/锁_事务_02

Read-Uncommited

RU不加锁

Serializable

所有select会被隐氏的转换成select ... in share mode ,会和update、delete互斥。

Repeatable-Read

默认隔离级别 普通select使用快照读,底层使用MVCC实现。
加锁的select,update,delete等语句使用当前读,底层使用记录锁,或者间隙锁,临建锁,。

Read-Commited

普通select使用快照读,底层使用MVCC实现。
加锁的select,update,delete等语句使用当前读,底层使用记录锁,其没有间隙锁。

避免死锁

  1. 操作多张表时,按照固定的顺序进行访问(避免环路等待)。
  2. 批量操作单表多个数据,先进行排序,即按照一定顺序访问(避免环路等待)。
  3. 申请足够级别的锁,如果要操作数据,就申请排他锁。
  4. 尽量使用索引去访问数据,避免锁表。
  5. 大事务转化成小事务(这里是大化小,还是小化大看情况而定)。
  6. 使用等职查询而不是范围查询,避免间隙锁的影响。