数据库应用:死锁、悲观锁、乐观锁

目录

一、理论

1.死锁

2.悲观锁

3.乐观锁

二、总结


一、理论

1.死锁

(1)概念

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。 一种情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源。例如,如果线程A锁住了记录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发生了死锁现象。计算机系统中,如果系统的资源分配策略不当,更常见的可能是程序员写的程序有错误等,则会导致进程因竞争资源不当而产生死锁的现象。锁有多种实现方式,比如意向锁,共享-排他锁,锁表,树形协议,时间戳协议等等。锁还有多种粒度,比如可以在表上加锁,也可以在记录上加锁。
 

(2)数据库死锁

当两个请求分别访问/读取2行记录,同时又需要读取对方的记录数据,因为(行锁的限制)而造成了阻塞的现象。

表1 死锁

数据库类别锁定类型特点
MyISAM表级锁定开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
innodb行级锁定开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高; 

(3)产生死锁的原因

① 系统资源不足。

进程运行推进的顺序不合适。

③ 资源分配不当等。

(4)产生死锁的四个必要条件

如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。

  ① 互斥条件:一个资源每次只能被一个进程使用。

  ② 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

  ③ 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

  ④ 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

2.悲观锁

(1)概念

当我们使用悲观锁的时候我们首先必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

关闭命令为:set autocommit=0;
悲观锁可以使用select…for update实现,在执行的时候会锁定数据,虽然会锁定数据,但是不影响其他事务的普通查询使用。此处说普通查询就是平时我们用的:select * from table 语句。

使用悲观锁的时候事务中的语句例如:

开始事务
begin;/begin work;/start transaction; (三选一)

查询信息
select * from order where id=1 for update;

修改信息
update order set name=’names’;

提交事务
commit;/commit work;(二选一)

此处的查询语句for update关键字,在事务中只有SELECT … FOR UPDATE 或LOCK IN SHARE MODE 同一条数据时会等待其它事务结束后才执行,一般的SELECT查询则不受影响。

执行事务时关键字select…for update会锁定数据,防止其他事务更改数据。但是锁定数据也是有规则的。

(2)查询条件与锁定范围

① 具体的主键值为查询条件
比如查询条件为主键ID=1等等,如果此条数据存在,则锁定当前行数据,如果不存在,则不锁定。

不具体的主键值为查询条件
比如查询条件为主键ID>1等等,此时会锁定整张数据表。

查询条件中无主键
会锁定整张数据表。

④ 如果查询条件中使用了索引为查询条件

明确指定索引并且查到,则锁定整条数据。如果找不到指定索引数据,则不加锁。

悲观锁的确保了数据的安全性,在数据被操作的时候锁定数据不被访问,但是这样会带来很大的性能问题。因此悲观锁在实际开发中使用是相对比较少的。

(3)原理图

3.乐观锁

(1)概念

相对悲观锁而言,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会对数据的冲突与否进行检测,如果发现冲突,则让返回用户错误的信息,让用户决定如何去做。

一般来说,实现乐观锁的方法是在数据表中增加一个version字段,每当数据更新的时候这个字段执行加1操作。这样当数据更改的时候,另外一个事务访问此条数据进行更改的话就会操作失败,从而避免了并发操作错误。当然,还可以将version字段改为时间戳,不过原理都是一样的。
 

例如有表student

字段:
id,name,version
1 a 1

当事务一进行更新操作:update student set name=’ygz’ where id = #{id} and version = #{version};

此时操作完后数据会变为id = 1,name = ygz,version = 2

当另外一个事务二同样执行更新操作的时候,却发现version != 1

此时事务二就会操作失败,从而保证了数据的正确性。

(2)原理图

 

 

二、总结

悲观锁会锁定数据,其他操作不会影响到被锁的数据,但是普通的查询没有影响,需要用到 for update语句

实现乐观锁的方法是在数据表中增加一个version字段,每当数据更新的时候这个字段执行加1操作。这样当数据更改的时候,另外一个事务访问此条数据进行更改的话就会操作失败,从而避免了并发操作错误。
 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值