锁是网络数据库中的一个非常重要的概念,当多个用户同时对数据库并发操作时,会带来数据不一致的问题,所以,锁主要用于多用户环境下保证数据库完整性和一致性。
帮助理解:以商场的试衣间为例,每个试衣间都可供多个消费者使用,因此,可能出现多个消费者同时需要使用试衣间试衣服。为了避免冲突,试衣间装了锁,某一个试衣服的人在试衣间里把锁锁住了,其他顾客就不能从外面打开了,只能等待里面的顾客试完衣服,从里面把锁打开,外面的人才能进去。
数据库锁出现的目的,或者说是想要解决什么问题?
答案就是:高并发
在当今互联网发达的今天,每一个生活中我们使用的软件都会出现高并发问题,如果没有数据库锁,那么很有可能就会出现一些非常恐怖的问题。
比如:当我们在淘宝购买一件衣服的时候,我们同时五个人购买,如果货源只剩一件的情况,那么由谁来获得这件衣服的购买权呢?如果没有锁,有可能就会出现这种情况,显示还剩一件衣服,但是大家都一起下单,一起付款,就会出现脏数据,五个人都购买成功,这样是非常麻烦的事情。
并发控制的主要采用的技术手段:乐观锁、悲观锁和时间戳。
锁的分类:
从数据库系统角度分为三种:排他锁、共享锁、更新锁。
从程序员的角度分析: 乐观锁,悲观锁
悲观锁(Pessimistic Lock)
顾名思义,很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人拿这个数据就会block(阻塞),直到它拿锁。
悲观锁(Pessimistic Lock):正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
传统的关系数据库里用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在操作之前先上锁。
共享锁(Share Lock)
S锁,也叫读锁,用于所有的只读数据操作。共享锁是非独占的,允许多个并发事务读取其锁定的资源。
性质
- 多个事务可封锁同一个共享页;
- 任何事务都不能修改该页;
- 通常是该页被读取完毕,S锁立即被释放。
例如:在SQL Server中,默认情况下,数据被读取后,立即释放共享锁。 例如,执行查询语句“SELECT * FROM my_table”时,首先锁定第一页,读取之后,释放对第一页的锁定,然后锁定第二页。这样,就允许在读操作过程中,修改未被锁定的第一页。 例如,语句“SELECT * FROM my_table HOLDLOCK”就要求在整个查询过程中,保持对表的锁定,直到查询完成才释放锁定。
排他锁(Exclusive Lock)
X锁,也叫写锁,表示对数据进行写操作。如果一个事务对对象加了排他锁,其他事务就不能再给它加任何锁了。(某个顾客把试衣间从里面反锁了,其他顾客想要使用这个试衣间,就只有等待锁从里面打开了。)
性质
- 仅允许一个事务封锁此页;
- 其他任何事务必须等到X锁被释放才能对该页进行访问;
- X锁一直到事务结束才能被释放。
产生排他锁的sql语句 select * from ljd_user for update
更新锁(update lock)
U锁,在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。
1 | 因为当使用共享锁时,修改数据的操作分为两步: |
行锁
行锁的作用范围为行级别
表锁
表锁的作用范围为整张表
数据库能够确定那些行需要锁的情况下使用行锁,如果不知道会影响哪些行的时候就会使用表锁。
比如,当我们执行一个sql 明确指定唯一id,where id = ?时,这里只会修改某一行的数据,数据库就会上行锁,如果没有指定具体的id,那么数据库就会给给这张表上表锁。
乐观锁(Optimistic Lock)
顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以,不会上锁。但是在更新的时候会判断一下在此期间别人有没有更新这个数据,可以使用版本号等机制。
1 | 乐观锁( Optimistic Locking ): 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。 |
活锁
定义:指的是T1封锁了数据R,T2同时也请求封锁数据R,T3也请求封锁数据R,当T1释放了锁之后,T3会锁住R,T4也请求封锁R,则T2就会一直等待下去。
解决方法:采用“先来先服务”策略可以避免。
死锁
定义:就是我等你,你又等我,双方就会一直等待下去。比如:T1封锁了数据R1,正请求对R2封锁,而T2封住了R2,正请求封锁R1,这样就会导致死锁,死锁这种没有完全解决的方法,只能尽量预防。
预防方法:
- 一次封锁法,指的是一次性把所需要的数据全部封锁住,但是这样会扩大了封锁的范围,降低系统的并发度;
- 顺序封锁法,指的是事先对数据对象指定一个封锁顺序,要对数据进行封锁,只能按照规定的顺序来封锁,但是这个一般不大可能的。 系统判定死锁的方法:
超时法:如果某个事物的等待时间超过指定时限,则判定为出现死锁;
等待图法:如果事务等待图中出现了回路,则判断出现了死锁。
对于解决死锁的方法,只能是撤销一个处理死锁代价最小的事务,释放此事务持有的所有锁,同时对撤销的事务所执行的数据修改操作必须加以恢复。
- 本文作者: Cayden
- 本文链接: http://example.com/2019/07/12/数据库锁/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!