使用乐观锁替换悲观锁来更新记录
悲观锁
考虑这样一种数据库的应用场景:
任务单的接管:
多个操作人员从一个任务池中接管一个任务。只有第一个获取任务的操作人员才能成功接管该任务,后续操作人员的接管操作都必须失败。这时,就需要一种数据库记录的锁定机制。只有第一个事务才能更新记录。
数据库可以使用悲观锁和乐观锁来锁定数据库记录。悲观锁是如下sql语句实现的:
SELECT * FROM t FOR UPDATE
这条语句会在其他修改内容的事务提交后返回最新的数据。一旦执行这条语句,这些记录就被锁住了,不能被其他sql事务修改。直到本事务提交。
乐观锁
乐观锁,是应用程序实现的,不是数据库实现的机制。乐观锁,对于数据库来说,就是没有上锁。事务可以select其他事务已经提交的数据。更新数据时,数据库保证多个事务的更新是原子的。悲观锁,会导致事务等待其他事务完成。乐观锁,只会等待其他事务的更新语句的完成,不会等待整个事务完成,因此效率较高。
如何实现乐观锁
给数据库表添加一个version字段。
version是一个数字类型的字段,每次更新都加1。每次更新时都要检测version字段是否和当前事务的值相同。如果version字段不同,那么就表明在查询数据之后,有其他事务已经更新了该记录,就会导致此次更新失败。应用必须重新载入最新的数据,然后重新更新数据。如果使用乐观锁,那么如果数据库中version和应用中version相同,则用version+1的版本值更新version字段。
SQL语句如下:
update studentVersion set ver=?, name=? where id=? and ver=?
不使用锁更新独立状态的记录
考虑这样一种数据库的应用场景:
需要更新虚拟机的状态。
多个事务可能会同时更新虚拟机的状态为start或者stop。这种状态的更新和前一个阶段的状态是无关的,因此不需要锁定记录。直接更新即可。此时不需要使用悲观锁或者乐观锁。如果这个表添加了version字段,直接忽略对version字段的比较和更新即可。
SQL语句如下:
update studentVersion set name=? where id=?
总结
设计关系型数据库的表时,需要给表添加一个ID字段(自增字段,或者uuid字段)和一个version字段(数值类型)。ID字段作为物理主键,用于保证记录的不可重复性和用作外键关联。version字段用于实现乐观锁,提供比悲观锁更好的性能。特别是对于UI显示并可能出现并发更新的数据,更需要使用乐观锁来提升数据库访问性能。对于后台自动更新的任务,可以使用乐观锁实现。但需要在冲突发生时实现自动退让。也可以使用悲观锁在数据库上对事务进行排队来解决更新冲突问题。对于不关心记录的状态之间关系的场景,可以直接更新记录,忽略掉version字段的检测和更新。
喜欢的点个关注或赞赏支持一下!