关于数据库中对于锁的理解相关整理

事务和锁的存在都是为了更好的解决并发访问造成的数据不一致性的的问题 

一、数据库中的事务:

1.数据库事务(Database Transaction):

是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务处理可以去报除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合成为一个要么全部成功、要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足事务的4个属性。(转自百度百科)

2.事务的 4 个属性(ACID):

(1)原子性(Atomicity):指事务必须是原子工作单元,即对于其数据修改,要么全部执行(提交成功),要么全部不执行(失败回滚)。对于一个事务来说,不可以只执行其中的一部分操作。

(2)一致性(Consistency):事务执行前后,数据库总数从一个一致性的状态转换为另一个一致性的状态。(例如转账操作前后,A B 两账户总和不变)

(3)隔离性(Isolation):一个事务所做的修改在最终提交之前,对其他事务是不可见的。即一个事务查看数据时数据所处的状态,只能是其他并发事务修改该数据前的状态或修改之后的状态,事务不会查看中间状态的数据。

(4)持久性(Durability):指一旦事务提交,事务对数据所做的修改就会永久地保存到数据库中,此时即使系统崩溃,修改的数据也不会丢失。

3.事务并发过程中可能出现的情况:

(1)脏读:(针对未提交数据)指一个事务A正在访问并修改数据,在事务提交成功之前另一个事务B也对该数据进行访问并使用,此时事务A对该操作进行回滚,事务B所读取到的数据即为脏数据。(简单来说,脏读指当前事务读取到了别的事务想要修改成为的但没有修改成功的数据)

(2)幻读:(针对其他事务提交前后,读取数据条数的对比)指一个事务A根据条件索引得到N条数据,如何数据B改变了N条数据以外的M条数据,使得这M条数据符合事务A的搜索条件,事务A再次搜索发现得到N+M条数据,产生了幻读。

(3)不可重复读:(针对其他事务提交前后,读取数据本身的对比)指事务A读取了一条数据,进行逻辑操作的时候,事务B对该数据进行了修改并提交成功,当事务A再次读取的时候,发现两条数据不匹配,也就是不可重复读。

Tips:幻读和不可重复读的区别在于:不可重复读的重点在于修改,同样的搜索条件,再次读取发现两次数据不一样;幻读的中点在于新增或删除数据(符合条件的数据条数的变化),同样的搜索条件,第一次和第二次读取的记录数不一样。

4.事务库事务的隔离级别:

(1)Read uncommitted(读未提交):最低的隔离级别,最直接的效果就是一个事务可以读取另一个事务并未提交的更新结果。可能出现脏读。

扫描二维码关注公众号,回复: 5018334 查看本文章

(2)Read committed(读提交):大部分数据库默认的隔离级别,一个事务的更新操作只有在该事务提交之后,另一个事务才可能读取到同一笔数据更新之后的结果,有效的避免了Read uncommitted级别下存在的脏读问题,但无法解决幻读和不可重复读的问题。

(3)Repeatable read(重复读):保证在同一个事务过程中,对同一笔数据的读取结果是相同的(mysql默认隔离级别),一旦事务开始,其他事务就不能对该数据进行修改,避免了不可重复读的出现,但无法避免幻读。

(4)Serializable(序列化):最严格的隔离级别,所有事务都必须按顺序依次进行,该级别可以避免其他级别出现的所有问题,是最安全的隔离级别,但同时也是性能最差的隔离级别,很少场景会使用该隔离级别,通常情况下,会使用其他隔离级别加上相应的并发锁来控制对数据的访问,既保证了系统性能不会有太大损失,也在一定程度上保证了数据的一致性。

二、并发锁

悲观锁和乐观锁都是为了解决并发控制中数据保持一致性的问题,乐观锁可以认为是一种在最后提交的时候检测冲突的手段,而悲观锁则是一种避免冲突的手段。

(1)悲观锁:一种实现在数据库机制上的锁,总是假设最坏的情况,即每次操作数据的时候都假设别人会进行修改,因此每次在拿数据的时候都会上锁,这样别人想拿这个数据就会开始阻塞直到它释放锁,(共享资源每次只给一个线程使用,其他线程阻塞,用完再将资源转让给其他线程)。比如表锁、行锁、读锁(共享锁)、写锁(排他锁)等等,都是在操作之前先上锁。

①行锁、表锁和页锁:指在数据库操作中,一旦上锁,其他事务对表中相应位置的数据有相应的操作都会被阻塞,直至锁的释放,分别对应数据库中的一行,一个表,页锁是对行锁和表锁的折中,介于行锁和表锁中间,一次锁定相邻的一组数据,这三种锁都有可能引发死锁。

②共享锁:(读锁)在同一个时间段内,多个用户可以读取同一个资源,读取的过程中数据不会发生任何变化,但是不允许对该资源进行修改,任何事务都不允许在此期间获取排他锁(写锁),共享锁(读锁)互相之间时不阻塞的。只有当数据上所有的共享锁释放之后,事务才能获取排他锁。

③排他锁:(写锁)在任何时候只能有一个用户来读写资源,当进行排他锁的时候,其他请求访问该资源的线程都会阻塞,既不能读也不能写,直到该排他锁(写锁)被释放。

(2)乐观锁:在应用系统层面和数据的业务逻辑层次上的锁(实际上并没有加锁,为了与悲观锁区分),利用程序来处理并发,总是假设最好的情况,即每次去拿数据都认为别人不会进行修改,所以不会上锁。但是在最后进行提交更新的时候会进行判断,判断别人是否在事务执行期间对该数据进行操作。一般使用版本号机制和CAS(Compare and Swap)算法实现。

乐观锁常用的两种实现机制:

①版本号机制:一般是在数据表中加上一个数据版本号(version)字段,表示数据被修改的次数,当数据被修改时,version值会加 1 ,当线程A要提交对数据的更新时,读取数据的同时读取version值,若之前读取到的version值与当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

(eg. A 和 B 同时读取了version = 1 的数据,然后A操作完成后发现version仍等于1,提交更新成功,并将version+1 = 2,随后B操作完成准备提交,发现version= 2 ,与之前读取时的version不符,因此提交失败,避免了B使用旧数据更新后覆盖了A的更新的可能)

②CAS算法(Compare and Swap):它包含三个参数 CAS(V,E,N)。V表示要更新的变量,E表示预期的值,N表示新值。仅当V值等于E值时,才会将V的值设置成N,否则什么都不做。最后CAS返回当前V的值。CAS算法需要你额外给出一个期望值,也就是你认为现在变量应该是什么样子,如果变量不是你想象的那样,那说明已经被别人修改过。你就重新读取,再次尝试修改即可。(摘自并发策略-CAS算法

(3)乐观锁的缺点:可能出现ABA问题、循环时间开销大、只能处理一个共享变量的原子操作(详情:面试必备之乐观锁与悲观锁

(4)乐观锁使用于多读的应用场景,可以提高吞吐量,而多写的场景则适用悲观锁。

本篇文章整理自其他相关的博客,加上自身理解整理而成,若有侵权,可联系本人删除

摘自:1.面试必备之乐观锁与悲观锁

2. 数据库中的事务和锁(乐观、悲观锁,共享、排他锁,死锁)

3.对于脏读,不可重复读,幻读的一点理解,看懂红字很关键

4.mysql的事务四个特性以及事务的四个隔离级别

5.数据库事务隔离级别-- 脏读、幻读、不可重复读(清晰解释)

6.并发策略-CAS算法

猜你喜欢

转载自blog.csdn.net/noob_Cliam/article/details/83001675