锁分两种:乐观锁,悲观锁
之前一直以为乐观锁的性能比悲观锁好是因为不用加for update锁,后来发现其实不然,查阅了资料后发现乐观锁和悲观锁的区别是:
悲观锁:在读取数据时锁住那几行,其他对这几行的更新需要等到悲观锁结束时才能继续
乐观所:读取数据时不锁,更新时检查是否数据已经被更新过,如果是则取消当前更新
一般在悲观锁的等待时间过长而不能接受时我们才会选择乐观锁
从数据库sql语句角度看,悲观锁一般使用select * from table where condition for update,这个是进行一个强制锁记录的操作。而乐观锁一般是使用update table set xxx=xxx where condition and version=${version},一般通过版本号来限制需要更新的记录版本。
之前一直以为select ... for update的效率不高,其实这个和update是一样的,都会对符合where条件的记录加锁。mysql的锁分为三种,行级锁、区间锁、next-key锁,具体可以参考:http://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
所以可以知道,乐观锁的性能之所以比悲观锁好,原因不是因为sql语句的不同,而是因为加锁时间的不同。悲观锁认为记录被修改的冲突概率较大,所以在对记录操作前就将记录锁定,然后更新记录,最后提交,所以提交的成功率很高,但是造成的并发能力较低。而乐观锁认为修改记录的冲突概率很小,所以在更新记录之前不会锁记录,而是在最后提交的时候提交锁,这样锁定记录的时间较短,并发能力较高,但是冲突概率就提高了。乐观锁一般会使用一个版本号概念,目的是记录当前记录的版本,如果当前记录没有被修改,那么相应的版本就不会改变。在更新记录的时候通过版本号来限定更新记录的版本,起到防止并发更新的问题。
数据库中有记录,可以通过乐观锁或者悲观锁进行锁定记录。
乐观锁:update tablename set version=version+1 where key=${key} and version=${version},判断update结果影响的行数。将乐观锁放在一个事务中,通过行级锁进行控制。一个事务占用了行级锁,另一个事务无法获得锁,会产生两种情况,一种是等待锁超时,一种是版本变更,update失败。
如果没有记录,可以通过insert + 唯一索引的机制进行锁定,比较暴力,通过锁表
事务隔离级别对于读取是有影响的,对于数据可见性效果不同。默认级别repeatable read可以支持mvcc,做到事务隔离。但是read commited是无法做到mvcc的。使用时候需要注意。事务隔离只是对于读取有影响。