目录
一、事务的隔离级别
什么是事务、事务有哪些特性
持久性:事务一旦提交,它对数据库中的数据的改变就是永久性的 事务并发情况下可能会产生的问题有哪些
事务的隔离级别有哪些?默认的隔离级别是什么?
在设置可重复读的情况下,如何保证修改的是最新的数据?
mysql中设置事务隔离级别以及事务的操作
二、锁
说说mysql中有那些锁?
innodb有哪些行锁算法
innodb如何选择行锁与表锁
什么是死锁?怎么解决死锁问题?
事务与锁状态信息查看
事务:一个或一组sql语句组成的一个执行单元,这个执行单元里面的sql要么都执行,要么都不执行
四大特性:
原子性:
原子性指事务时一个不可分割的工作单位,事务中的操作要么都执行要么都不执行,原子性由undo log 日志实现
一致性:
从一个正确的状态,迁移到另一个正确的状态.什么叫正确的状态呢?就是当前的状态满足预定的约束就叫做正确的状态.,一致性由其他三个特性和业务代码正确性来实现(例如在业务代码中,一行代码修改了A的金额减少500,然后再修改B的金额中写入try,catch中,出现了异常,然而没有再catch语句中抛出异常,spring无法检测到,于是不对事务进行回滚,导致了最终结果不一致性)
隔离性:
事务的隔离性是指一个事务的执行不能被其他事务干,即一个事务内部的操作以及使用的数据对并发的其他事务时隔离的,并发执行的各个事务之前互不干扰,隔离性由mysql各种锁以及mvcc机制实现
持久性:
事务一旦提交,它对数据库中的数据的改变就是永久性的,持久性有redo log 日志实现
事务并发情况下可能会产生的问题有哪些
脏读:
对于两个事务T1、T2,T1读取了已经被T2更新但还没提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的
不可重复读:
对于两个事务T1、T2,T1读取了一个字段,然后T2更新了该字段之后,T1再读取一次该字段,值不相同
幻读:
对于两个事务T1、T2,T1从一个表中读取了一个字段,然后T2再在该表中插入一些新数据,之后T1再次读取同一个表,就会多出几行(对与可重复读隔离级别下,假设在A事务进行了一次查询select * ,有三条记录,然后在B事务新增一条数据id为4,然后再在A事务select * 仍然是三条事务,但是如果这时候再A事务中修改了id为4的记录,也就是修改刚新增的数据,那么再查询一次select * 就会出现四条数据,这就导致了幻读的情况)
READ UNCOMMITTED 读未提交,该模式下会出现并发事务的所有可能出现的问题
READ COMMITTED 读已提交,能够成功解决脏读现象(不支持间隙锁)
REPEATABLE READ(默认的隔离级别) 可重复读,能够成功解决不可重复读现象,可能会解决幻读现象(通过间隙锁),性能几乎不受影响(select语句通过mvcc维护,不加锁)
SERIALIZABLE 串行化,能够解决并发事务下的所有问题,通过禁止另一个事务增删改的方式,效率非常低下,在分布式事务下使用(对于所有的操作都加锁处理,对于所有的读操作都会加上读锁,写操作所有隔离级别都会默认加写锁)
根据可重复读的特性,在一个事务中通过MVCC机制多次读取到的值一定是相同的,而可能此时真实的数据以及被修改了,那么怎么保证修改的数据是最新的呢?
改变以往通过java代码获取当前值然后作为参数修改数据的形式,将数据的变更和获取融合在一起
update student set score = score+ 5 ;
因为在mysql的mvcc机制的控制下,select读取的实际是历史版本,而增删改会读取当前版本
按操作分类:
共享锁:也叫读锁。针对同一数据,多个事务读取操作可以同时加锁互不影响,但是不能修改数据。
innodb 共享锁:sql+ lock in share mode;例 select * from student lock in share mode;
当开启共享锁的时候,当前事务可以查询数据、修改数据,并且可以更换锁的类型;其他事务可以对该行进行共享锁查询,但是不能加排他锁查询,也不能修改数据。
myisam 读锁:加锁:lock table 表名 read;解锁:unlock tables;
可以多个会话同时对一个表添加读锁,但是不能修改数据以及添加写锁。同一个会话添加读锁后也不能修改当前表的数据,
排他锁:也叫做写锁。当前操作没完成时,会阻断其他操作的加锁读取和修改数据。
innodb 排他锁:sql+for update 例 select * from student for update;
当开启排他锁时,当前事务可以查找数据和修改数据,也可以修改锁的类型,但是即便从排他锁变成共享锁,其他事务也不能加锁查询,本质上还是排他锁,其他事务只能查询数据,不能加锁也不能修改数据。
myisam 写锁:lock table 表名 write ;解锁:unlock tables;
当前会话可以查询和修改数据,其他会话不能查询和修改数据
按粒度分类:
表级锁:锁住整个表,开销小,加锁快,锁粒度大,发生锁冲突概率高,并发力度低,不会出现死锁现象。
myisam存储引擎支持的就是表锁,innodb存储引擎也支持表锁,但是默认的是行锁
行级锁:锁住当前行,开销大,加锁慢,锁粒度小,发生锁冲突概率低,并发度高,会出现死锁现象。
innodb存储引擎默认支持行锁,但是需要用索引当作检索条件才能施加行锁(命中数据增加行锁,如果不命中数据则增加间隙锁(RR隔离级别)),否则会从行锁升级为表锁(RR级别会升级成表锁,RC级别不会,因为在RR级别上要保证可重复读,所以在遍历扫描数据的时候,为了防止扫描过的数据被其他数据修改或间隙被插入数据,从而导致数据不一致,索引mysql就把所有扫描过的数据和间隙都加上锁)
意向锁:意向锁主要是针对表锁的,主要是为了提高加表锁的效率,是mysql自己加的锁,当事务给表的数据行加了共享锁或者排他锁的时候,同时会给表设置一个表示,代表这张表已经加了行锁,其他事务想要加表锁的时候,就不必要逐行判断有没有行锁可能对表锁造成冲突,直接读取这个标记就知道该不该枷锁,而这个标记就是意向锁
按照使用方式:
悲观锁:对数据被外界修改保持保守状态,认为数据随时会被修改,整个数据处理过程中需要将数据加锁,悲观锁一般都是依靠关系型数据库提供的锁机制,我们之前学过的锁(共享锁,排他锁,读锁,写锁)都是悲观锁;用于写多读少
乐观锁:每次自己操作数据的时候认为没有人会修改他,所以不去加锁,但是在更新的时候去判断再次期间数据有没有被修改,需要用户自己去实现,不会发生抢占资源,只有在提交操作的时候检查是否违反数据完整性;用于读多写少
方式:给数据表添加一个version列,每次 更新后都将这个列的值加一,读取数据时,将版本号取出来,在执行更新的时候,比较版本号,如果相同则执行,如果不相同说明这条数据已经发生变化了,用户自行根据这个通知来决定怎么处理,比如重新更新一次或者放弃更新。
Record lock:单个行记录上的锁
Gap lock:
间隙锁:锁定一个范围,不包括记录本身(在RR隔离级别下才会出现)
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;也会对范围所包含的所有行记录(包括间隙行记录)以及行记录所在的间隙加锁
“间隙”细节点:
例如:
这里有一个h表
执行sql语句 UPDATE h set `name` = '111' where hm>5 and hm<8
间隙锁中的间隙指定并不是5-8之间,而是根据表数据中针对hm这一列数据来看,表中有三条数据,对应的间隙应该是两两之间的间隔,例如1-4之间有个2和3,间隙就是(1,4),4-9之间有5、6、7、8,间隙就是(4,9),而9之后还有还有可扩展的无穷多个数,间隙就是(9,+∞)
所以这里所锁住的内容是hm>5 和hm <8 之间的数所在的间隙,也就是6,7所在的间隙,就是在(4,9)中。 再来看一条sql UPDATE h set `name` = '111' where hm>7 and hm<11.
这条sql会直接锁住两个间隙,8处于间隙(4,9)中,所以这个间隙会锁住,而10处于间隙(9,+∞)中,所以这个间隙也会被锁住
关闭间隙锁的方法
Next-key lock:
Next-Key Locks是行锁与间隙锁的组合。例如上面的例子(4,9)在临键锁上是(4,9],又例如sql
UPDATE h set `name` = '111' where hm>4 and hm<=9,临键锁上是(4,9]
显示指定:
行锁: sql语句+ lock in share mode
表锁: sql语句+ for update
由innodb自动选择:
行锁:当sql语句where条件中的列是索引字段,使用的就是行锁
表锁:当sql语句where条件中的列不是索引字段,加表锁
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。
常见的解决死锁的方法
读操作是否需要加事务需要根据具体的业务场景,如果不需要查询同一时间维度的数据,那么可以不用加事务,但是如果需要查询同一时间维度的数据,那么需要加可重复读隔离界别的事务(RR)
1、并发情况下,容易造成数据库连接池被撑爆
2、锁定的数据过多,容易造成大量阻塞和锁超时
3、执行时间长,容易造成主从延迟
4、回滚所需要的时间比较长
5、undo log 膨胀
6、容易导致死锁
1、对于RC事务级别,可以将查询等数据准备操作放到事务外进行
2、事务中避免进行远程调用,远程调用要设置超时时间,防止事务等待太久
3、一次事务避免处理太多数据,可以拆分成多个事务分次处理
4、更新等涉及加锁的操作尽可能放在事务靠后的位置(因为更新操作是已经存在的数据,有可能其他事务还会使用这条记录,那么会造成等待)
5、能异步处理的尽可能异步处理
6、对于简单的业务,应用业务代码来保证数据准确性,不通过事务