之前说过了Spring事务的四大特性和隔离级别的问题, 不了解的可以看看这里 -> Spring事务的四大特性ACID 和 Spring事务的隔离级别
虽然数据库的隔离级别可以解决大多数问题,但是灵活度较差,为此又提出了悲观锁和乐观锁的概念。
转载自: 置顶 、唐城 https://www.jb51.net/article/226652.htm
悲观锁和乐观锁
悲观锁
悲观锁就是某事务在更新数据过程中将数据锁定,其他任何事务都不能读取或修改,必须修改完成后才能访问数据(类似于Java的线程同步机制)。悲观锁的特点是具有排他性,通常依赖于数据库的锁机制,一般适合短事务处理。
可能你会想,说了半天也没说为何叫悲观锁呀,到底悲观在哪里呀?这个问题问得很好。根据悲观锁的定义可知,当一个事务加了悲观锁,其他任何事务是不能读取或修改数据,也就是只能在外面等待,什么事也干不了,直到悲观锁被释放为止。那么,想象一下,如果有很多事务都要访问数据库(高并发的情况),加了悲观锁就意味所有事务需要排着长长的队,一个一个访问数据库,那么访问数据库的效率是不是非常低呀,你说悲观不悲观呀。
乐观锁
乐观锁相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以只会在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回用户错误的信息,让用户决定如何去做。乐观锁的特点是并发性较好,事务修改数据时,其他事务仍可以修改数据。
实现乐观锁一般来说有以下2种方式:
1.使用版本号
使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
2.使用时间戳
乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
说白了,乐观锁其实根本不是一种数据库锁机制,而是一种冲突检测机制,这种冲突检测机制是依赖软件或应用程序实现的。
那乐观锁为何乐观呀,乐观在它的并发性比悲观锁好,一个事务在修改数据时,其他事务仍然可以修改数据。
悲观锁与乐观锁的优缺点及使用场景
悲观锁的优点是可以保障数据库的数据是绝对安全的,它是依赖数据库的锁机制,能很好的解决数据库访问的并发性问题,但是缺点就是会导致数据库访问性能低下,所以适合短事务(也就是事务执行时间很短)的情况。你想一想,如果事务执行时间很长,那么后面的事务就得一直排队等待嘛。它的使用场景是对数据安全性要求非常高的场景,比如银行系统、金融系统等。
乐观锁的优点是可以保障并发性比较好,也就景数据库访问性能可以,它是依赖软件的冲突检测机制实现的,但是缺点就是并没彻底解决数据库访问的并发性问题,所以数据库的数据不是绝对安全的。它的使用场景是对数据安全性要求不高而对性能要求很高的场景,比如各种信息管理系统等。