首页 > 代码库 > 数据库乐观锁与悲观锁

数据库乐观锁与悲观锁

前面说到了数据库的隔离级别,隔离性是数据库中数据有意义的条件之一,而不同的隔离级别,归根到底其实是在读和写的操作中对表、事务后者是表进行对应的锁定操作,所以下面简单总结下数据库的两种类型锁:乐观和悲观锁,很多是概念性的东西和个人理解,不足之处也请指正。

一、锁的概念

  简单说说数据库锁的概念,和多线程中的锁类似,数据库中对数据的锁定其实也是保证数据同步的主要手段,它表现出来的是:并发下,线程之间对数据库数据的操作是单方的。而在数据库里面主要有两种锁手段保证这种并发下的线程单方面操作:悲观锁和乐观锁,两者对数据同步采取的机制不同,下面逐一进行简单总结。

 

二、悲观锁

  1、概念。悲观锁是这样一种概念:当线程访问数据库的某些数据时,我先给数据上锁,然后进行事务处理,当事务提交(or失败回滚)成功的时候,我才释放这些数据的锁,其他线程才能访问这些数据。悲观锁其实和多线程中的锁同步概念是很类似的。总的来说,悲观锁对高并发持一种悲观的态度:它总是认为系统数据的访问时高并发的,所以需要在操作数据(包括读和写)的第一时间就获取锁,这和下面的乐观锁形成鲜明的对比。

  2、实现原理。悲观锁的实现原理和编程中的多线程差不多,数据库中的数据都会有本身对应的锁(有数据库自己提供),当该数据的锁被对应线程获得时,其他访问该数据的线程处于阻塞状态,只有该线程释放该锁时,该数据才可再次被访问。总的来说,悲观锁原理和线程中的lock概念差不多。

  3、具体用法。乐观锁只是一种概念而已,在mysql中,一般的用法表现是利用sql语句的for update指令对数据进行锁定。例如,select * from user where user_id=1 for update这条语句,它是这样一种意思:我要拿user表中user_id为1的数据出来并且后面我会对该行数据进行更新操作,所以请数据库帮我锁定该条数据不 让其他线程访问(读写都不行),这时就会导致其他访问该数据的线程处于阻塞状态。

 

三、乐观锁

  1、概念。相比于上面的悲观锁,乐观锁的概念和其不同,它不会在业务持续开始对数据库进行事务操作时就开始给数据库上锁,而是将数据的同步时间后移到数据提交的时候再进行检查,检查完数据是否有冲突再进行提交。乐观锁对并发问题持有一种乐观的态度:它总是假设系统的数据在一段时间内不会有太多改变(即写跟新数据的并发线程远远小于读的线程)。

  2、实现原理。乐观锁其实并没有在数据库中对数据进行加锁的操作,它不会直接锁定数据,而是采用一种手段来标记数据的版本,在事务开始的时候先获取版本,然后事务结束的时候对比版本是否有改变,如果没改变,直接将事务提交成功,更新数据;否则告诉用户版本冲突,不可执行事务。

  而具体来说,实现乐观锁的原理大概有两种:一个是版本号的记录,另外一个就是时间节点的记录。前者数据库会记录每次数据修改的版本号,用于标记;后者则根据数据修改时间进行标记操作。具体的实现原理就不展开说,有兴趣的可以百度谷歌MVCC和CAS进行了解。

 

四、悲观锁VS乐观锁。

   所以,我们应该在悲观和乐观锁的两者之间如何抉择呢?个人理解有一下总结,具体用法还是看业务需要:

  1、数据更新(写操作)并发严重,采用悲观锁可以保证程序执行更新操作成功的次数,而采用乐观锁可以提高响应的速度但是执行更新操作成功次数会降低;

  2、复杂业务采用悲观锁会容易死锁,乐观锁不存在这方面的问题;

  3、业务回滚的代价:若回滚代价过大则采用悲观锁,否则再考虑是否采用乐观锁;

  4、对于写读读少的,采用悲观锁更好;读多与写的话考虑乐观锁。

  

数据库乐观锁与悲观锁