ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

数据库事务及锁相关思考

2019-09-24 14:01:12  阅读:274  来源: 互联网

标签:封锁 协议 事务 数据库 更新 思考 共享 数据 及锁


  我了解数据库不同的锁,我了解不同的事务隔离级别,但是再一次生产中,服务器依然报出了死锁,这引起我的反思,先不讨论SPring transaction给我们造的轮子的问题,先探讨一下为什么会引起这次死锁?

这次死锁主要原因是 我们再测试条件下启动了一条线程去给数据库更新一条数据,而由于当时时间段比较特殊,我们后台的Timetask也在此时启动,到底很凑巧的两条线程的两个事物同时并行的去更改数据,后来看了一下日志,是两个事物同时获得共享锁,而其中一条线程企图更新数据,尝试将共享锁上升到排它锁,而另一条线程一直没有释放锁,至此导致死锁。

  错误类型。举例来说。

更新丢失:a事务中更新一条记录,却被b事务的更新操作给覆盖了。也就是说,a事务的操作没有生效。

脏读:b事务中执行了修改操作:将name从lee改成了tom,但是还没提交,a事务中读到到的数据是tom,当事务b回滚,name变回了lee。这样就导致了a事务中读到的tom是不存在的。

不可重复读:事务a中读取了name字段,其值为lee。在第二次读取name字段的过程中,事务b将name字段的值改成了jack,事务a第二次读到的是jack,发现与第一次读到的不一样,即两次读到的数据不一样

幻读:假设表(字段有id主键和value)中有三条记录,分别为(1,a),(2,b),(3,c)。a事务读取全部发现这三条,b事务新增一条记录(4,d),然后提交事务。然后a事务再查询,发现还是这三条(没能读取到最新那条),于是新增一条(4,d)。结果报错了,说主键重复了。可是事务a中查询到的记录明明没有id为4的记录。这就是幻读。

  首先介绍锁的类型:  

更新锁:读取表时使用更新锁,不使用共享锁,并将锁一直保持到语句或事务结束。Updlock的优点是可以允许其他线程读取数据,并在以后更新数据,同时保证自动从上次读取数据后的数据没有被修改。

共享锁:若事务T对数据加上了共享锁,则只允许对数据进行查询,但是不能修改,其他事务Q只能再对该数据加共享锁,不能加上排它锁,直到释放T共享锁才能加上排它锁。

 

  下面介绍为什么不同的隔离级别无法解决那些问题。

  理解背后的原因:

  一级封锁协议:一级封锁协议是指,事务T在修改数据R之前必须先对其进行加X锁,直到事务T结束才释放(事务结束包括正常结束的commit 和 非正常结束rollback)

一级封锁协议可以方式丢失更新的问题,事务t1想要对数据进行修改,那么先得加锁。这时事务t2也想对数据r进行修改,那得先给R上X锁。但是由于数据R已经被上了X锁,此时事务T2无法对其进行上X锁,因此只能一直等待,直到数据R上的X锁被释放。才能给它上X锁,才能进行修改操作,如此解决了丢失更新的问题。

但是为什么没能解决不可重复读和脏读的问题呢?

思考:如果选择了封锁协议,那么任何其他线程企图访问已经被X锁锁定的资源时,只能进行读取而不能进行修改,这时就有一个特别的“问题”,如果资源被X锁锁定时被更新,然后又回滚,这时其他线程一直在读,发现这个数据在事务中是一直在变的。这时就造成了不可重复读了和脏读了。

   二级封锁协议:在一级封锁协议的基础上增加事务T在读取R之前必须对其加S锁,读完才可释放S锁。

二级封锁协议是在一级封锁协议基础上的,因此它必然能解决丢失更新的问题,除此之外呢,还能解决脏读的问题,为什么呢?因为如果我们按照二级封锁协议规定来读数R,如果一级封锁没有放开锁,二级锁永远没办法加上。所以一直无法读,因此避开了脏读的问题,但是还是没办法解决不可重复读的问题,但在我看来,不可重复读本身不能算是问题。因为你不能限值一个数据在你读一次和读两次的数据完全一致,这不公平,也不科学。所以引申到read-repeat这个事务隔离级别,这个事务隔离级别是要干什么事呢?

  三级封锁协议:原则上,理论上使用锁实现

三级封锁协议是指,在以及封锁协议的基础上增加事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。

三级封锁协议是在一级封锁协议的基础上的,因此必然能解决丢失更新问题,另外,三级封锁协议规定,在增加事务T在读取数据R之前必须先对其加S锁,这和二级封锁协议一样,因此三级封锁协议也能解决脏读问题。除此之外,它还规定加上的S锁直到结束才释放。这就解决了不可重复读问题。继续以上面的例子来说。如果事务a想要执行查询操作,那么得先给该数据加S锁,没问题,加锁后执行了查询操作,读到的是(1,‘a’),读完后没有立马释放S锁。这时事务b想对该记录进行修改,那么先加R锁吧,发现,该记录已经被上了S锁,因此,没办法加R锁,因此只能一直等待,直到S锁被释放。如此就保证了事务a的两次查询之间不会插入另一个事务对同一记录的更新操作。这样一来,事务a两次读取的记录就一定会是一样的。如此,就避免了不可重复读问题。

  

  所谓锁:分两种大类

 

  1. 乐观锁
  2. 悲观锁

 

1.数据库乐观锁的实现是通过自定义version实现,适用于并发更改同一行数据很少的情况

 

2.而我们传统中的对数据库锁的认知都是悲观锁,就是操作之前加锁。

 

1、共享锁,共享锁使得不同session可以共享同一批次的数据,读不影响。但写操作时需要将共享锁上升至排他锁。

 

2、排它锁,排它锁保证当前锁定的数据只能由当前session进行修改,其他session可以读。不能加任何锁,包括共享锁

 

3、更新锁,同一批数据只允许加一个更新锁,加更新锁的数据允许其他session读取数据,也就是说,其他线程始终无法将共享锁上升至排他锁或者更新锁,而更新锁可以自然的上升到排他锁。可以避免共享锁互相等待的问题

 

 

 

另外,由于索引的关系,不同的数据库在查询时如果指定某一行数据,可能查询会导致整张表加锁。

 

标签:封锁,协议,事务,数据库,更新,思考,共享,数据,及锁
来源: https://www.cnblogs.com/yxs98/p/11577938.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有