美文网首页
2019-08-01 数据库部分核心面试(二)

2019-08-01 数据库部分核心面试(二)

作者: 惜小八 | 来源:发表于2019-08-01 15:39 被阅读0次

    一.数据库锁

    1.MyISAM和InnoDB在锁方面的区别是什么?


    MyISAM在对数据进行select的时候,他会自动为数据库加上一个表级别的读锁,而当我们在对数据进行增删改的时候,会对我们操作的表加上一个写锁,当读锁未被释放的时候另位一个session想对表加上写锁,就会被阻塞,直到读锁被释放,写锁才可以加上。

    //显示的给表加上读锁或者写锁
    lock tables 表名 read/writer
    
    //释放所有的锁
    unlock tables;
    
    读锁又称为共享锁:在进行范围查询的时候,依然可以对表里面的数据进行读操作,所以读锁又称为共享锁
    
    -- 添加一个读锁,如果在本次会话当中不释放,其他会话如果进行增删改(查找仍然可以,所以读锁又叫共享锁)就不能进行,因为会被锁住
    -- 显示的添加的锁,必须要显示的释放(在哪个会话当中添加的锁,就要在哪个会话当中释放,在其他会话当中释放是无效的)
    -- 之所以要显示的添加锁,这是因为我们表里面的数据太少了,如果数据很多,几千万条数据,当我们一个Session当中进行读操作,另一个Session进行读操作可以,但是增删改操作会被阻塞,直到读操作完毕才开始增删改操作。
    lock  tables t_user read;
    select * from t_user ;
    unlock tables;
    

    无论是表级锁还是行级锁都包括共享锁和排他锁

    写锁又称为排它锁,当一个Session加上写锁以后,另一个Session进行增删改查都不可以,因为写锁又称为排他锁,当有写锁时,其他操作都不能进行。

    -- 读操作添加排他锁,当该语句执行完毕后,其他会话当中的操作才可以执行
    -- MyISAM默认支持表级锁,不支持行级锁,表级锁会锁住整张表,比如我们在查询1--200w之间的数据的同时,更新201w条数据仍然会被锁住,如果是行级锁,我们就可以更新第201w条数据。
    select * from t_user for update;
    

    锁按照级别可以分为共享锁和排他锁
    上了共享锁以后,仍然可以上共享锁,不能上排他锁
    上了排他锁之后就不能上其他锁。

    InnoDB支持事物,MySQL默认是自动提交事物的,MyISAM不支持事物,”InnoDB使用的是二段锁(二段锁协议)“,即加锁和解锁是分成2个阶段进行的,即先对同一批事物里的一批操作分别进行加锁,然后在提交以后对添加的锁进行统一的解锁,而当前commit是自动提交的,所以看上去和MyISAM一样。未commit之前说明锁还未被释放.

    Set autocommit=0;
    -- 添加共享锁
    select * from t_user where id=21037 lock in share mode;
    Commit;
    

    即:开启事物的时候进行加锁,在commit/rollback之前都是加锁阶段,commit/rollback之后财货释放锁

    2.表级锁

    用到表级锁的时候,当我们只要操作到表里面的数据的时候都会触发表级锁,所以表级锁与索引无关,
    当不走索引的时候,整张表都会被锁住,InnoDB在SQL没用用到索引的时候走的是表级锁,在SQL用到索引的时候走的是行级锁以及GAP锁

    锁的粒度越细代价越高,相比表级锁在表的头部直接加锁来讲,行级锁还要在扫描到某行的时候对其上锁,代价是比较大的,InnoDB在支持事物的同时,相比MyISAM带来了更大的开销,InnoDB必须要有且仅有一个聚集索引,数据文件是和索引绑定在一块的

    MyISAM
    InnoDB
    锁的种类

    3.乐观锁和悲观锁

    乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。

    悲观锁:因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。他是全程使用排他锁来实悲观锁的。
    乐观锁:乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
    

    4.封锁

    所谓的封锁就是传统意义上的加锁,它是实现并发控制的一种重要手段,即读之前加读锁,写之前加写锁,

    5.死锁活锁

    活锁:如果事物T1对数据R加锁了,事物T2又请求对事物进行加锁,于是事物T2等待,此时事物T3也请求对数据R加锁,事物T1释放锁之后系统首先批准了事物T3的请求,于是T2继续等待,此时事物T4也请求对数据R加锁,事物T3释放锁之后系统首先批准了事物T4的请求,于是T2继续等待,T2可能永远的等下去,这就是活锁。

    避免活锁的简单方法是:先到先服务的原则,当多个事物请求同一事物对象时,系统按照先后次序对其进行排序,越早请求的越先获得请求锁

    死锁:事物T1对数据R1加锁,事物T2对数据R2加锁;同时事物T1请求数据R2,因为R2已经加锁,所以T1只能等待,同时事物T2请求请求数据R1,但由于R1已经加锁,所以T2只能等待。由于他们相互等待,所以T1,T2两个事物可能永远也不会结果,于是形成了死锁。

    6.死锁的解决办法

    在数据库当中产生死锁的原因是:2个或者多个事物已经对一些数据加锁,但是还想要访问被其他事物加锁的对象,从而出现死锁。
    预防死锁的方法通常有以下的2种:

    1.一次封锁法:

    它要求事物对要访问的数据必须要一次性全部加锁,否则就会执行不下去,一次加锁法虽然可以有效的防止死锁,但是增加了锁的粒度,从而降低了系统的并发性,而且数据库是不断变化的,事先很难准确的确定每个事物所需要的数据,所以只能扩大封锁范围,将事物在执行过程当中可能需要的数据全部进行加锁,这进一步降低了数据的并发度

    2.顺序封锁法

    顺序封锁法是预先对所存可加锁的数据对象规定一个封锁顺序,每个事务都必须按照这个顺序实行封锁,在释放时,按照相反的顺序进行。例如在B树结构的索引当中,可以规定加锁的顺序是从根节点开始到下一个子节点,依次内推,
    T1按照树形结构先对A加锁,在对B加锁,此时T2访问不了A(要访问B必须要先进过A),T1释放A之后,T2才可以访问,
    ****顺序封锁法可以有效的避免死锁,但是实现起来十分困难,因为很难事先确定哪一个事物要访问哪些数据,因此很难按照顺序去加锁**

    由此可见,数据库当中不适合预防死锁,只适合诊断和解除死锁

    7.死锁的诊断与解除

    数据库系统当中诊断死锁的方法和操作系统当中类似,一般使用超时法,事物等待图法。

    1.超时法:

    如果一个事物的等待时间超过了规定的时限,那么就认为其发送了死锁,超时法的劣势十分明显。
    1.有可能误判死锁,事物可能由于其他的原因等待时间过长,超过了时限
    2.若时限设置太长,则不能及时的发现死锁。

    2.事物等待图:

    事务等待图是一个有向图G=(T,U)。 T为结点的集合,每个结点表示正运行的事务;U为边的集合,每条边表示事务等待的情况。若T1等待T2,则T1、T2之间划一条有向边,从T1指向T2。事务等待图动态地反映了所有事务的等待情况。
    并发控制子系统周期性地(比如每隔1分钟)检测事务等待图,如果发现图中存在回路,则表示系统中出现了死锁。

    死锁的解决

    数据库管理系统当中的并发子系统一旦检测到数据库当中出现了死锁,就要设法解除死锁,通常的处理的方式是选择一个处理死锁代价最小的事物,将其撤销,释放次事物持有的所有锁,使其它事物可以继续运行下去,当然对撤销的事物的所有操作要进行恢复。

    二.事务隔离级别(恶果:脏读 幻读 不可重复读)

    1.事物的四大特性

    image.png

    2.事物隔离级别以及各级别下的并发访问问题

    Read uncommited(读未提交):该隔离级别下可以读出另一Session未提交的数据,即脏读
    
    Read commited(读已提交):可以避免脏读,Session1多次读取统一数据,二次读到的数据不一样(另一个session对数据进行了修改),即不可重复读(侧重于对同一数据的修改),
    
    Repeatable read(重复读):次隔离级别可以避免不可重复读,但是session1在对数据进行更新时,session2对表进行插入操作,session1发现更新的数据比原来的多,此为幻读(侧重于新增或者删除)
    Serializable:在此隔离级别下,所有的SQL执行都会加上锁,
    
    image.png

    3.快照读和当前读

    image.png
    image.png

    当前读:即是加了锁的增删改查语句,无论是排他锁还是共享锁都是当前读,它读取的是记录的最新版本并且读取之后还需要保证其他并发事物不能修改当前事物,故读取的事物加锁,其中除了select ...lock in share mode加了共享锁之外,其他的几个操作都会加排他锁。

    快照读:简单的读操作,不加锁,有可能会读取到数据的老版本,当前读是特殊的快照读。

    三.InnoDB在repeatable read隔离级别下如何避免幻读

    image.png

    表象:是基于伪MVCC机制实现的快照读(非阻塞读),避免我们看到幻行.

    在序列化状态下真防止使幻读的原因使因为加了next-key锁(行锁+gap锁)

    1.next-key锁

    next-key锁:它由行锁和gap锁组成
    行锁:加载单个行记录上的锁

    gap锁:间隙锁是锁定一个范围,但是不包括记录本身(比如:id=1,id=3,锁定(1,3]之间),gap锁是为了方式幻读而产生的,现在四大隔离级别当中,只有序列化和repeated read支持gap锁,其他的2种隔离级别是不支持gap锁的,gap锁主要是防止插入的

    2.repeated read 在什么情况下会使用gap锁?

    当我们使用范围检索而不是等值检索数据,并请求读锁和写锁的时候,InnoDB会给符合条件的数据加上锁,对于在键值范围内,但是并不存在的数据,InnoDB也会加上锁,这个锁就是gap锁,

    举例来说,假如 emp 表中只有 101 条记录,其 empid 的值分别是 1,2,...,100,101,下 面的 SQL:
    Select * from emp where empid > 100 for update;
    是一个范围条件的检索,InnoDB 不仅会对符合条件的 empid 值为 101 的记录加锁,也会对 empid 大于 101(这些记录并不存在)的“间隙”加锁。

    相关文章

      网友评论

          本文标题:2019-08-01 数据库部分核心面试(二)

          本文链接:https://www.haomeiwen.com/subject/dszfdctx.html