由乐观锁延伸出的知识
锁是网络数据库中的一个非常重要的概念,当多个用户同时对数据库并发操作时,会带来数据不一致的问题,所以,锁主要用于多用户环境下保证数据库完整性和一致性以商场的试衣间为例,每个试衣间都可供多个消费者使用,因此,可能出现多个消费者同时需要使用试衣间试衣服。为了避免冲突,试衣间装了锁,某一个试衣服的人在试衣间里把锁锁住了,其他顾客就不能再从外面打开了,只能等待里面的顾客试完衣服,从里面把锁打开外面的人才能进去。
各种大型数据库所采用的锁的基本原理是一致的,但在具体实现上各有差别。在数据库中加锁时,除了可以对不同的资源加锁,还可以使用不同程度的加锁方式,即锁有多种模式共享锁、修改锁、独占锁、结构锁、意向锁及批量修改锁等。
以下将分别对这些种类的锁模式进行解释与分析。
(1)共享锁
共享锁也称为S(Share Lock)锁,用于所有的只读数据操作。共享锁是非独占的,允许多个并发事务读取其锁定的资源。它具有以下性质:多个事务可封锁一个共享页;任何事务都不能修改该页;通常是该页被读取完毕,S锁立即被释放。在SQL Server中,默认情况下数据被读取后,立即释放共享锁。例如,执行查询语句“SELECTFROM my_table”时,首先锁定第一页,读取之后,释放对第一页的锁定,然后锁定第二页。这样,就允许在读操作过程中,修改未被锁定的第一页。但是,事务隔离级别连接选项设置和SELECT语句中的锁定设置都可以改变SQL Server的这种默认设置。例如,语句“SELECTFROM my_table HOLDLOCK”就要求在整个查询过程中,保持对表的锁定,直到查询完成才释放锁定。
(2)排他锁
排他锁(Exclusive Lock)也叫写锁(X锁),表示对数据进行写操作。如果一个事务对对象加了排他锁,其他事务就不能再给它加任何锁了(某个顾客把试衣间从里面反锁了,其他顾客想要使用这个试衣间,就只有等待锁从里面给打开了)。排他锁具有以下几点性质:仅允许一个事务封锁此页;其他任何事务必须等到X锁被释放才能对该页进行访问;X锁一直到事务结束才能被释放。
产生排他锁的SQL语句如下所示:select*from ad_plan for update:
(3)更新锁
更新锁(也叫U锁)在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。因为当使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排他锁,再执行修改操作。这样如果有两个或多个事务同时对一个事务申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排他锁。这时,这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排他锁,就可以避免死锁。
更新锁具有以下性质:用来预定要对此页施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的页将要被更新时,则升级为X锁;U锁一直到事务结束时才能被释放。
从程序员的角度看,分为乐观锁和悲观锁。悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去取数据的时候都认为别人会修改,所以,每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block(阻塞),直到它拿到锁。传统的关系型数据库里就用到了很多这种锁机制,例如行锁、表锁、读锁及写锁等,它们都是在操作之前先上锁。乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的,其实都是提供的乐观锁。
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新