博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
锁的阻塞,死锁,锁升级
阅读量:5332 次
发布时间:2019-06-14

本文共 2745 字,大约阅读时间需要 9 分钟。

阻塞

因为不同锁之间的兼容性关系,所以在有些时刻,一个事务中的锁需要等待另一个事务中的锁释放它所占用的资源。在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 c\G;

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数据库比较类似。

 

转载于:https://www.cnblogs.com/wade-luffy/p/6296172.html

你可能感兴趣的文章
UC浏览器插件开发
查看>>
结构体数组
查看>>
如何查看maven plugin所包含的goal
查看>>
Python绘图matplotlib
查看>>
寻找子串位置 codevs 1204
查看>>
Flex 学习笔记 XML对象(转)
查看>>
个人简历
查看>>
Django学习笔记1
查看>>
异常处理
查看>>
Hibernate_继承映射的方法详解
查看>>
Asp.net中web.config配置文件详解
查看>>
shell脚本-5
查看>>
Temporary Post Used For Style Detection (3c95477f-54ce-474a-9e30-42586362b375)
查看>>
C 语言实例 - 连接字符串
查看>>
视觉slam十四讲学习笔记——Cmake入门
查看>>
selenium常用API实例
查看>>
【CSS】关于content
查看>>
30道面试题有答案
查看>>
IDEA的dto、mapper、mapping文件自动生成
查看>>
HTTP协议详解
查看>>