丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。
脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6。
乐观锁:
适用于资源争用不激烈(数据更新不频繁)的时候使用,乐观锁定允许多个用户访问并编辑同一记录,并假设数据发生冲突的可能性最小。其原理是检查读取记录后是否有其他进程尝试更新记录,如果有就抛出ActiveRecord::StaleObjectError 异常,并忽略该更新。
使用:
每次更新记录时,Active Record 都会增加 lock_version 字段的值。如果更新请求中 lock_version 字段的值比当前数据库中 lock_version 字段的值小,更新请求就会失败,并抛出 ActiveRecord::StaleObjectError 异常;通过设置 ActiveRecord::Base.lock_optimistically = false 可以关闭乐观锁定。
user1 = User.find(1)
user2 = User.find(1)
user1.update(:age => 20)
user1.save
user2.update(:age => 30)
user2.save #会抛出ActiveRecord::StaleObjectError异常
悲观锁:
悲观锁适用于资源争用比较严重(数据更新频繁)的时候使用,悲观锁定使用底层数据库提供的锁定机制。在创建关联时使用 lock 方法,会在选定字段上生成互斥锁。使用 lock 方法的关联通常被包装在事务中,以避免发生死锁。
User.lock.find(1) #会导致行锁
User.where(:name => ‘guo’).lock(true).first #会导致表锁
mysql innodb 里面,对于 "select * from where xxx for update" 的情况,是会锁住整张表;Rails 也提供了一个很方便的方法 with_lock 来锁住单个记录,并且内嵌在事务之中:
user = User.find(1)
user.with_lock do
user.age = 30
user.save
end
悲观锁出错概率小,因为一旦获得锁,其他进程会堵塞,但是也导致速度会受影响,系统开销比较大,不利于并发。乐观锁适用于资源竞争不是那么多的地方,这样系统的开销较小,速度也比较快。都是为了解决高并发情况,类似于java上的同步、异步锁机制