《面试经典系列》- 乐观锁和悲观锁及其应用场景
1、乐观锁
乐观锁(Optimistic Locking)是一种思想,相对悲观锁而言,乐观锁认为对同一个数据的并发操作,不会造成冲突,所以在数据提交更新的时候,才会正式对数据进行冲突校验,如果有冲突,则给用户返回错误的信息,让用户决定如何处理。乐观地认为,不加锁的并发操作是没有问题的。
具体是实现思路是,第一次读的时候,获取到某个字段值(版本/时间戳),处理完业务逻辑开始更新时,需要再次查看该字段的值是否和第一次的一样。如果一样就更新,反之拒绝。之所以叫乐观,是因为这个模式没有从数据库加锁。
1-1、乐观锁的实现方式
1、使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。
什么是数据版本?就是在数据中增加一个版本标识,一般是通过给数据库表增加一个数字类型的“version”字段来实现。
当读取数据的时候,将 version 字段的值一同读出,数据每更新一次,对此 version 值+1。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的 version 进行对比,如果数据库表当前版本号与第一次取出来的 version 值相等,则予以更新,否则认为是过期数据。
如上图一所示,如果更新操作顺序执行,则数据的版本(version)依次递增,不会产生冲突。但是,如果发生有不同的业务操作对同一版本的数据进行修改,如图二的用户2和用户3同时对 version2 进行修改,那么,先提交的操作(用户3)会把数据版本 version 更新为2,当用户2在用户3之后提交更新时,发现数据版本(version)已经被修改了,则用户2的更新操作会失败。代码示例:
update ccs_inv_current_inv t set t.stats = 5, t.cersion = t.version + 1 WHERE t.id = #{id} and t.version = #{version};
2、乐观锁的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,随意命名,字段类型使用时间戳(timestamp)[时间戳类型:行数据更新时,时间会自动更新],和上面的 version 类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前获取到的时间戳进行对比,如果一致则允许修改,否则就是版本冲突。
●乐观锁的实现 1、CAS(Compare And Swap) 2、MVCC(Multi-Version Concurrency Control) ●乐观锁的特点 1、并发度高; 2、程序实现,不会发生死锁;
2、悲观锁
悲观锁,正如其名,悲观的,具有强烈的独占和排他特性。它认为对于同一个数据的并发操作,一定会被修改,哪怕没有修改,也会认为被修改。因此,在它处理数据前,先对数据进行加锁,直到它完成修改操作(commit)才释放锁。悲观地认为,不加锁的并发操作一定会出问题。
悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
以MySQL的 InnoDB 为例,预设的 Transaction isolation level 为 REPEATABLE READ。【要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,set autocommit=0】
● Select 的读取锁有两种方式:
- Select ...... Lock In Share Mode;
- Select ...... For Update;
两种方式在事务(Transaction)进行中 Select 到同一个数据表时,都必须等待其他事务数据被(commit)之后才会执行。而两者主要的区别在于,Lock In Share Mode 在有一方事务要 Update 同一个表单时,很容易造成死锁。
简单地说,如果 Select 后面若要 Update 同一个表单,最好使用 Select ...... For Update。
● MySQL Select ... For Update 的 Row Lock(行锁) 和 Table Lock(表锁)。
由于 InnoDB 预设是 Row-Level Lock,所以只有【明确】的指定主键,MySQL才会执行 Row Lock(只锁住被选取的数据),否则 MySQL 将会执行 Table Lock(将整个表锁住)。
- 明确指定主键,并且有此数据,Row Lock;
- 明确指定主键,若查无此数据,无 Lock;
- 无主键/主键不明确,Table Lock;
● 除了主键外,使用索引也会影响数据库的锁定级别
- 明确指定索引,并且有此数据,Row Lock;
- 明确指定索引,如查无此数据,无 Lock;
● 悲观锁的特点 1、并发度小; 2、依靠数据实现;
3、各自适用场景
悲观锁和乐观锁是数据库用来保证数据并发安全,防止更新丢失的两种方法,两者大部分场景下差异不大,一些独特场景下有一些差别,一般我们可以从以下几方面来分析:
- 响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁;
- 冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率,如果冲突频率大,乐观锁会需要重试多次才能成功,代价比较大;
- 重试代价:如果重试代价大,建议采用悲观锁;
从各自的优缺点来看,我们可以认为,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,因为不加锁的话,性能会有大幅度的提升。
小结
两种锁机制区别不大,我们明白其中的原理,运用起来就不会有太大困难。
在实际应用中,我们也要充分考虑各自的优缺点,选择合理、可行的方案。
posted on 2020-03-13 11:40 Leung_柠檬先生 阅读(2312) 评论(0) 编辑 收藏 举报