01、什么是事务
事务是数据库管理系统(DBMS)执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
【1】、事务的四大特性(ACID)
原子性,Atomicity:数据库的一系列的操作,要么都是成功,要么都是失败
一致性,consistent:事务执行的前后都是合法的数据状态
隔离性,Isolation:互相不干扰的
做持久性,Durable:保存数据到硬盘
【2】、事务并发会问题
1、脏读。读取为提交的数据
2、不可重复读:同一个事务读取到了其他事务已提交的数据导致前后两次读取数据不一致
3、 幻读:一个事务前后两次读取数据数据不一致,是由于其他事务插入数据造成的
【3】、MySQL InnoDB对事物隔离级别的支持
InnoDB默认使用RR作为事务隔离级别的原因,既保证了数据的一致性,又支持较高的并发度。
02、InnoDB实现事务隔离的方案
【1】、LBCC:Lock Based Concurrency Control
【2】、MVCC:Multi Version Concurrency Control(多版本并发控制)【重点】
MVCC的核心思想是:
我可以查到在我这个事务开始之前已经存在的数据,即使它在后面被修改或者删除了。在我这个事务之后新增的数据,我是查不到的。
【3】、InnoDB的MVCC
InnoDB为每行记录都实现了两个隐藏字段:
DB_TRX_ID【创建版本号】:插入或更新行的最后一个事务的事务ID,事务编号是自动递增。
DB_ROLL_PTR【删除版本号】:数据被删除或记录为旧数据的时候,记录当前事务ID
案例:
03、MySQL InnoDB锁的基本类型【重点】
【1】、锁的粒度
表锁和行锁
【2】、锁的模式(Lock Model)
共享锁、排他锁、意向锁
【3】、行级别的锁 共享锁
也叫做读锁。
【注意】不要在加上了读锁以后去写数据,不然的话可能会出现死锁的情况。
select......lock in share mode;的方式手工加上一把读锁。
读锁可以重复获取
【4】、行级别的锁 排它锁
又叫做写锁
排它锁的加锁方式有两种方式:自动加排他锁、手工加锁(FORUPDATE给一行数据加上一个排它锁)
【5】、表锁 意向锁排他锁/共享锁
给一行数据加上排他锁之前,数据库会自动在这张表上面加一个意向排他锁。
给一行数据加上共享锁之前,数据库会自动在这张表上面加一个意向共享锁。
一张表上面至少有一个意向共享锁/排他锁,说明有其他的事务给其中的某些数据行加上了共享锁/排他锁。
两个表级别的锁存在的意义是什么?(重点)
当没有意向锁的时候,我们准备给一张表加上表锁的时候,必须先要去判断有没其他的事务锁定了,就要去扫描整张表才能确定能不能成功加上一个表锁。加表锁的效率很低。
04、锁的原理
【1】、没有索引的表
t1的表结构,它有两个字段,int类型的id和varchar类型的name。里面有4条数据,1、2、3、4。
在没有索引或者没有用到索引的情况下,会锁住整张表。
【2】、有主键索引的表
id上创建了一个主键索引
相同的id值去加锁,冲突;使用不同的id加锁,可以加锁成功。
【3】、唯一索引
id上创建了一个主键索引,name上创建了一个唯一索引。里面的数据是1、4、7、10。
总结:
InnoDB的行锁,就是通过锁住索引来实现的。
05、锁的算法
【1】、记录锁
唯一索引和主键索引精准匹配到一条记录的时候,这个时候使用的就是记录锁。
比如where id= 1 4 7 10。
【2】、间隙锁
当我们查询的记录不存在,没有命中任何一个record。
【注意】间隙锁主要是阻塞插入insert。相同的间隙锁之间不冲突。
比如:where id>4 and id<7,where id=6。
【关闭间隙锁】
把事务隔离级别设置成RC,并且把innodb_locks_unsafe_for_binlog设置为ON。
这种情况下除了外键约束和唯一性检查会加间隙锁,其他情况都不会用间隙锁。
【3】、临建锁
我们使用了范围查询,不仅仅命中了Record记录,还包含了Gap间隙,在这种情况下我们使用的就是临键锁。
临建锁是MySQL里面默认的行锁算法,相当于记录锁加上间隙锁。
临键锁,锁住最后一个key的下一个左开右闭的区间
左开右闭的区间?——就是为了解决幻读的问题。
【4】、总结隔离级别的实现
1、Read Uncommited(为提交读)
RU隔离级别:不加锁。
2、Read Commited(已提交读)
普通的select都是快照读,使用MVCC实现。
加锁的select都使用记录锁,除外键约束检查和重复键检查会使用间隙锁封锁区。RC会出现幻读的问题。
3、Repeatable Read(可重复度)
普通的select使用快照读(snapshotread),底层使用MVCC来实现。
加锁的select(select...in share mode/select...for update)以及更新操作update,delete等语句使用当前读(currentread),底层使用记录锁、或者间隙锁、临键锁
4、Serializable(串行化)
所有的select语句都会被隐式的转化为select...in share mode,会和update、delete互斥。
05、死锁
【1】、控制获取锁的等待时间,默认是50秒。
show VARIABLES like 'innodb_lock_wait_timeout';
【2】、产生死锁的条件
1、同一时刻只能有一个事务持有这把锁
2、其他的事务需要在这个事务释放锁之后才能获取锁
3、当多个事务形成等待环路的时候,即发生死锁。
【3】、查看锁信息
show status like 'innodb_row_lock_%';
Innodb_row_lock_current_waits:当前正在等待锁定的数量;
Innodb_row_lock_time:从系统启动到现在锁定的总时间长度,单位ms;
Innodb_row_lock_time_avg:每次等待所花平均时间;
Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花的时间;
Innodb_row_lock_waits:从系统启动到现在总共等待的次数。
【4】、分析事务与锁的情况(重点)
select * from information_schema.INNODB_TRX;--当前运行的所有事务,还有具体的语句
select * from information_schema.INNODB_LOCKS;--当前出现的锁
select * from information_schema.INNODB_LOCK_WAITS;--锁等待的对应关系
总结:先看【锁等待的对应关系】、根据字段向上查询