4、什么是隔离级别?有哪些隔离级别?
隔离级别是对事务并发控制的等级,描述了一个事务必须与由其他事务进行的资源或数据更改相隔离的程度。数据库的事务隔离级别有四种,分别是读未提交、读已提交、可重复读、序列化,不同的隔离级别下会产生脏读、幻读、不可重复读等相关问题,因此在选择隔离级别的时候要根据应用场景来决定,使用合适的隔离级别。
各种隔离级别和数据库异常情况对应情况如下:
隔离级别 | 脏读 | 不可重复 读 | 幻读 |
READ- UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE- READ | × | × | √ |
SERIALIZABLE | × | × | × |
SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED(读取未提交): 事务的修改,即使没有提交,对其他事务也都是可见的。事务能够读取未提交的数据,这种情况称为脏读。
READ-COMMITTED(读取已提交): 事务读取已提交的数据,大多数数据库的默认隔离级别。当一个事务在执行过程中,数据被另外一个事务修改,造成本次事务前后读取的信息不一样,这种情况称为不可重复读。
REPEATABLE-READ(可重复读): 这个级别是MySQL的默认隔离级别,它解决了脏读的问题,同时也保证了同一个事务多次读取同样的记录是一致的,但这个级别还是会出现幻读的情况。幻读是指当一个事务A读取某一个范围的数据时,另一个事务B在这个范围插入行,A事务再次读取这个范围的数据时,会产生幻读
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。
5、如何解决数据的读一致性问题
LBCC
第一种,既然要保证前后两次读取数据一致,那么读取数据的时候,锁定我要操作的数据,不允许其他的事务修改就行了。这种方案叫做基于锁的并发控制 Lock Based Concurrency Control(LBCC)。
如果仅仅是基于锁来实现事务隔离,一个事务读取的时候不允许其他时候修改,那就意味着不支持并发的读写操作,而我们的大多数应用都是读多写少的,这样会极大地影响操作数据的效率。
MVCC
后续专门的文档进行讲解
6、Innodb中包含哪些锁?
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
锁的基本模式——共享锁
第一个行级别的锁就是我们在官网看到的 Shared Locks (共享锁),我们获取了一行数据的读锁以后,可以用来读取数据,所以它也叫做读锁。而且多个事务可以共享一把读锁。那怎么给一行数据加上读锁呢?
我们可以用 select lock in share mode;的方式手工加上一把读锁。
释放锁有两种方式,只要事务结束,锁就会自动事务,包括提交事务和结束事务。
锁的基本模式——排它锁
第二个行级别的锁叫做 Exclusive Locks(排它锁),它是用来操作数据的,所以又叫做写锁。只要一个事务获取了一行数据的排它锁,其他的事务就不能再获取这一行数据的共享锁和排它锁。
排它锁的加锁方式有两种,第一种是自动加排他锁,可能是同学们没有注意到的:
我们在操作数据的时候,包括增删改,都会默认加上一个排它锁。
还有一种是手工加锁,我们用一个 FOR UPDATE 给一行数据加上一个排它锁,这个无论是在我们的代码里面还是操作数据的工具里面,都比较常用。
释放锁的方式跟前面是一样的。
锁的基本模式——意向锁
意向锁是由数据库自己维护的。
也就是说,当我们给一行数据加上共享锁之前,会自动在这张表上面加一个意向共享锁。
当我们给一行数据加上排他锁之前,会自动在这张表上面加一个意向排他锁。
反过来说:
如果一张表上面至少有一个意向共享锁,说明有其他的事务给其中的某些数据行加上了共享锁。
记录锁
记录锁表示的是给记录行所在的索引上添加的锁。例如select c1 from t where c1 = 10 for update;防止任何其他事务插入、更新或者删除c1=10的行
记录锁总是锁定索引记录,即使表没有定义索引,innodb也会创建一个隐藏的聚簇索引,并使用该索引进行记录锁定
间隙锁
间隙锁的锁定范围是索引记录之间的间隙,或者在第一个索引记录之前或最后一个索引记录之后的间隙,间隙锁是针对事务隔离级别为可重复读或以上级别。
临键锁
临键锁是记录锁和间隙锁的组合,也就是索引记录本身加上之前的间隙,间隙锁保证在RR级别不会出现幻读问题,防止在同一个事务内得到的结果不一致。假设索引包含10,11,13,20这几个值,那么临键锁的范围就是(-∞,10],(10,11],(11,13],(13,20],(20,+∞)。对于最后一个间隔,临键锁锁定索引中最大值以上的间隙,以及值高于索引中任何实际值的supremum(在查看加锁信息的时候可以看到这个标识)。
上面简单介绍了下各种锁的基础概念,实际情况下锁的情况会更加复杂,需要根据不同表的索引情况来判断,可以参考文档《mysql的加锁情况》。
7、什么是死锁?如何解决死锁的问题?
死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对应的资源,从而导致恶性循环的现象,如图所示:
解决死锁的思路一般就是切断环路,尽量避免并发形成环路:
1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁的概率
2、在同一个事务中,尽可能做到一次锁定所有的资源,减少死锁产生的概率
3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁粒度,通过表锁来减少死锁的概率
4、死锁和索引密不可分,合理优化索引
发表评论