数据库的锁

1. 简介

锁是网络数据库中的一个非常重要的概念,当多个用户同时对数据库并发操作时,会带来数据不一致的问题,所以,锁主要用于多用户环境下保证数据库完整性和一致性。

在数据库中加锁时,除了可以对不同的资源加锁,还可以使用不同程度的加锁方式,即锁有多种模式。

2. 乐观锁与悲观锁

从程序员的角度看,分为乐观锁和悲观锁。

悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以,每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block(阻塞),直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁、读锁和写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以,不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号、时间戳等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,如果数据库提供类似于write_condition机制,其实都是提供的乐观锁。

链接:https://www.nowcoder.com/questionTerminal/97c7972a55f6459a900a664ee687536f 来源:牛客网

3. 具体的锁

表级锁:共享、排他、意向锁

行级锁:共享、排他、更新锁

3.1 共享锁

共享锁也称为S(Share Lock)锁,用于所有的只读数据操作。

共享锁是非独占的,允许多个并发事务读取其锁定的资源。

它具有以下性质:多个事务可封锁一个共享页;任何事务都不能修改该页;通常是该页被读取完毕,S锁立即被释放。

在SQL Server中,默认情况下,数据被读取后,立即释放共享锁。例如,执行查询语句“SELECT * FROM my_table”时,首先锁定第一页,读取之后,释放对第一页的锁定,然后锁定第二页。这样,就允许在读操作过程中,修改未被锁定的第一页。

但是,事务隔离级别连接选项设置和SELECT语句中的锁定设置都可以改变SQL Server的这种默认设置。
例如,语句“SELECT * FROM my_table HOLDLOCK”就要求在整个查询过程中,保持对表的锁定,直到查询完成才释放锁定。

3.2 排他锁

排他锁(Exclusive Lock,也叫X锁)也叫写锁(X)。表示对数据进行写操作。如果一个事务对对象加了排他锁,其他事务就不能再给它加任何锁了。

排他锁具有以下几点性质:仅允许一个事务封锁此页;其他任何事务必须等到X锁被释放才能对该页进行访问;X锁一直到事务结束才能被释放。

产生排他锁的SQL语句如下所示:select * from ad_plan for update;

3.3 更新锁

更新锁(也叫U锁)在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。

因为,当使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排他锁,然后再执行修改操作。这样,如果有两个或多个事务同时对一个事务申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排他锁。这时,这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。

如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排他锁,就可以避免死锁。

更新锁具有以下性质:用来预定要对此页施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的页将要被更新时,则升级为X锁;U锁一直到事务结束时才能被释放。

3.4 意向锁

申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。

意向锁说明SQL Server有在资源的低层获得共享锁或独占锁的意向。

例如,表级的共享意向锁说明事务意图将独占锁释放到表中的页或者行。

意向锁又可以分为共享意向锁、 独占意向锁和共享式独占意向锁。

共享意向锁说明事务意图在共享意向锁所锁定的低层资源上放置共享锁来读取数据。

独占意向锁说明事务意图在共享意向锁所锁定 的低层资源上放置独占锁来修改数据。

共享式独占锁说明事务允许其他事务使用共享锁来读取顶层资源,并意图在该资源低层上放置独占锁。

参考来源:数据库的锁机制及原理