一、前言
在多用户的环境中,经常会出现多个用户同对一条记录进行修改,这时就不可避免的产生冲突。这也是并发场景中经常会遇到的问题。比较常见的冲突有:丢失更新与脏读。
一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失,如用户把A的值由6变成2,用户B把2变成6.此时对于用户A来说,他的修改是无效的;
当一个事务读取其它完成一半事务的记录时,就会发生脏读取。如用户A,B看到的值都是6,用户B把值改成2,此时A用户读到的仍是6。为了解决上述问题,需要引入并发控制,目前比较常见的并发控制就是加锁。
当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象。加锁对并发访问的影响体现在锁的粒度上。比如,放在一个表上的锁限制对整个表的并发访问;放在数据页上的锁限制了对整个数据页的访问;放在行上的锁只限制对该行的并发访问。可见行锁粒度最小,并发访问最好,页锁粒度最大,表锁介于2者之间。
二、悲观锁与乐观锁
1、悲观锁:
认为一定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。 悲观锁假定其他用户企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁。
悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好
。
2、乐观锁:
假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
乐观锁不能解决脏读的问题
。 乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。但是如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。
三、两种锁使用的选择
1、
从数据库厂商的角度看,使用乐观的页锁是比较好的,尤其在影响很多行的批量操作中可以放比较少的锁,从而降低对资源的需求提高数据库的性能。
2、
在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法。