zoukankan      html  css  js  c++  java
  • 锁的阻塞,死锁,锁升级

    阻塞

    因为不同锁之间的兼容性关系,所以在有些时刻,一个事务中的锁需要等待另一个事务中的锁释放它所占用的资源。在InnoDB存储引擎的源代码中,用Mutex数据结构来实现锁。在访问资源前需要用mutex_enter函数进行申请在资源访问或修改完毕后立即执行mutex_exit函数当一个资源已被一个事务占有时,另一个事务执行mutex_enter函数会发生等待,这就是阻塞。阻塞并不是一件坏事,阻塞是为了保证事务可以并发并且正常运行。

    在InnoDB存储引擎中,参数innodb_lock_wait_timeout用来控制等待的时间(默认是50秒),innodb_rollback_on_timeout用来设定是否在等待超时时对进行中的事务进行回滚操作(默认是OFF,代表不回滚)。

    参数innodb_lock_wait_timeout是动态的,可以在MySQL数据库运行时进行调整,而innodb_rollback_on_timeout是静态的,不可在启动时进行修改,如:

    set @@innodb_lock_wait_timeout=60;

    set @@innodb_rollback_on_timeout=on;

    ERROR 1238(HY000):Variable'innodb_rollback_on_timeout'is a read only variable

    当发生超时时,数据库会抛出一个1205的错误,如:

    begin;

    select * from t where a=1 for update;

    ERROR 1205(HY000):Lock wait timeout exceeded;try restarting transaction

    需要牢记的是,默认情况下InnoDB存储引擎不会回滚超时引发的错误异常。其实InnoDB存储引擎在大部分情况下都不会对异常进行回滚。

    如在一个会话中执行了如下语句:

    #会话A

    select * from t;

    begin;

    select * from t where a<4 for update;

    会话A中开启了一个事务,Next-Key Lock算法下锁定了小于4的所有记录(其实也锁定了4这个记录)。在另一个会话中执行如下语句:

    #会话B

    begin;

    insert into t select 5;

    insert into t select 3;

    ERROR 1205(HY000):Lock wait timeout exceeded;try restarting transaction

    select * from t;

    可以看到,在会话B中插入记录5是可以的,但是插入记录3的话,因为Next-Key Lock算法的关系,需要等待会话A中事务释放这个资源,因此等待后产生了超时。但是在超时后,我们再进行SELECT会发现,5这个记录并没有并回滚。其实这时事务发生了错误,但是既没有commit,也没有rollback,这是十分危险的,用户必须判断是需要commit还是需要rollback,然后再进行下一步操作。

    死锁

    如果程序是串行的,那么不可能发生死锁。死锁只发生于并发的情况,数据库就是一个并发进行着的程序,因此可能会发生死锁。InnoDB存储引擎有一个后台的锁监控线程,该线程负责查看可能的死锁问题,并自动告知用户。

    下面的操作演示了死锁的一种经典的情况,即A等待B,B在等待A:

    (for update加的是排他锁)在上述操作中,会话中的事务抛出了1213这个出错提示,即发生了死锁。死锁的原因是会话A和B的资源互相在等待。大多数的死锁InnoDB存储自己可以侦测到,不需要人为进行干预。

    但是在上面的例子中,会话B中的事务抛出死锁异常后,会话A中马上得到了记录为2的这个资源,这其实是因为会话B中的事务发生了回滚,否则会话A中的事务是不可能得到该资源的。InnoDB存储引擎并不会回滚大部分的错误异常,但是死锁除外发现死锁后,InnoDB存储引擎会马上回滚一个事务,这点是需要注意的。如果在应用程序中捕获了1213这个错误,其实并不需要对其进行回滚。

    Oracle数据库中产生死锁的常见原因是没有对外键添加索引,而InnoDB存储引擎会自动对其进行添加,因此很好地避免了这种情况的发生。人为删除外键上的索引数据库会抛出一个异常:

    create table p(a int,primary key(a));

    create table c(b int,foreign key(b) references p(a))engine=innodb;

    show index from cG;

    drop index b on c;

    ERROR 1553(HY000):Cannot drop index 'b':needed in a foreign key constraint

    可以看到,虽然在建立子表时指定了外键,但是InnoDB存储引擎还是自动在外键列上建立了一个索引b,而人为删除这个列却是不允许的。

    锁升级

    锁升级(Lock Escalation)是指将当前锁的粒度降低。举例来说,数据库可以把一个表的1 000个行锁升级为一个页锁,或者将页锁升级为表锁。如果数据库的设计中认为锁是一种稀有资源,而且想避免锁的开销,那数据库中会频繁出现锁升级现象。

    Microsoft SQL Server数据库的设计认为锁是一种稀有的资源,在适合的时候会自动地将行、键或者分页级锁升级为更粗粒度的表级锁。这种升级保护了系统资源,防止系统使用太多的内存来维护锁,从一定程度上提高了效率。

    即使在Microsoft SQL Server 2005的版本之后,SQL Server数据库支持了行锁,但是其设计和InnoDB存储引擎完全不同,在以下情况下依然可能发生锁升级:

    1. 由一句单独的SQL语句在一个对象上持有的锁数量超过了阈值,默认的这个阈值为5000。值得注意的是,如果是不同对象的话,则不会发生锁升级。
    2. 锁资源占用的内存超过了激活内存的40%时,就会发生锁升级。

    在Microsoft SQL Server数据库中,因为锁是一种稀有的资源,因此锁升级会带来一定的效率提高。但是锁升级带来的一个问题却是,因为锁粒度的降低而导致并发性能的降低。

    InnoDB存储引擎不存在锁升级的问题。在InnoDB存储引擎中,1个锁的开销与1 000 000个锁是一样的,都没有开销。这一点和Oracle数据库比较类似。

  • 相关阅读:
    自然语言处理中CNN模型几种常见的Max Pooling操作
    知识图谱(收藏)
    知识图谱ppt&论文笔记
    《A Survey of Answer Extraction Techniques in Factoid Question Answering》Reading Notes
    Reading notes 《A survey on question answering technology from an information retrival perspective》
    《神经网络与深度学习》(七) 浅谈AlphaGo背后所涉及的深度学习技术
    安装opencv4.5中出现的一些问题
    ATK-esp8266加arduino nano
    ubuntu下修改usb设备为固定设备名
    Could not find a package configuration file provided by "manipulation_msgs"
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/6296172.html
Copyright © 2011-2022 走看看