乐观锁和悲观锁(从策略上划分)
乐观锁:乐观锁就如同他的名字一样,非常了乐观,每次去读数据都认为其它事务没有在写数据,总是认为别人不会修改数据,所以就不上锁,只有在线程提交数据时会通过检查版本号的形式检测数据有没有被修改过。一般会在数据表中添加版本号(Version)字段来表示被修改的次数,当数据被修改,version+1,只有在version字段和当前数据库的version值相同时,才提交成功,其实乐观锁相当于一种检测冲突的手段,可通过为记录添加版本或添加时间戳来实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
悲观锁:顾名思义,就是很悲观,每次去拿数据的时候都认为别人会对数据进行修改,所以每次读数据的时候都会上锁,这样别人想拿这个数据就会block,直到取出数据。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性,但随之而来的是各种开销。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
悲观锁和乐观锁的区别:
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即如果并发量不大,或数据冲突的后果不严重,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,并发量大或数据冲突后果比较严重(对用户不友好),这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
悲观锁,从数据开始修改时就将数据锁住,直到更改完成才释放锁。乐观锁,直到数据修改完准备提交时才上锁,完成后释放。
注意:首先明确一点乐观锁和悲观锁是一种编程策略,并不是数据库具有悲观锁和乐观锁。
共享锁和排它锁(从读写角度划分)
共享锁(S锁,Shared Lock):也叫读锁(Read Lock),持有S锁的事务只读不可写。如 SELECT 语句。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
排它锁(X锁,Exclusive Lock):也叫写锁(Write Lock),持有X锁的事务可读可写。用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。如果事务T对数据A加上排他锁后,则其他事务不能再对A加任何类型的封锁。直到T对A的锁解除。获准排他锁的事务既能读数据,又能修改数据。
注意:共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。
表级锁和行级锁、页面锁(从粒度角度划分)
表级锁:表级锁将整个表加锁,性能开销最小。用户可以同时进行读操作。当一个用户对表进行写操作时,用户可以获得一个写锁,写锁禁止其他的用户读写操作。写锁比读锁的优先级更高,即使有读操作已排在队列中,一个被申请的写锁仍可以排在所队列的前列。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:行级锁仅对指定的记录进行加锁,这样其它进程可以对同一个表中的其它记录进行读写操作。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页级锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
特点:
三种锁各有各的特点,若仅从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如WEB应用;行级锁更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。
扩展信息:
死锁: 多个进程互相等待对方锁的释放。
锁冲突:一个进程等待另一个进程释放需要的锁。
如有错误,欢迎指正!