数据库乐观锁的实现:使用版本控制并发

本文深入探讨了乐观锁和悲观锁两种并发控制策略,详细解释了它们在数据库中的应用,尤其是在MyBatis框架下的实现方式。通过版本号字段和时间戳字段的使用,展示了乐观锁的具体实践,并介绍了MyBatis Plus插件如何简化乐观锁的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis

说到“版本号”,不仅用于表示软件的版本,还可以用作数据库的并发控制,这就是本文所要讲述的主要问题。欢迎各位读者驻足鉴赏。

1、并发的控制策略:

控制并发采用的策略通常分为乐观锁和悲观锁。

乐观锁的定义:顾名思义,对加锁持有一种乐观的态度,即先进行业务操作,不到最后一步不进行加锁,乐观地认为加锁一定会成功的,在最后一步更新数据的时候再进行加锁。

悲观锁的定义:正如其名字一样,悲观锁对数据加锁持有一种悲观的态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

简言之,

乐观锁:不是在数据库端锁住的,而是程序端控制的。此时可以在mybatis中实现乐观锁机制。

悲观锁:在数据库里面锁住,类似for update查询。

2、乐观锁的实现:

乐观锁的实现通常有两种方式:版本号字段和时间戳字段。

(1)版本(version)字段:

更新的时候给版本号字段加上1,然后UPDATE会返回一个更新结果的行数,通过这个行数去判断,如下所示:

UPDATE T_USER u

SET u.userName = #userName#, u.version = u.version + 1

WHERE u.userId = #userId# AND u.version = #version#

 程序实现逻辑为:

if(rowsUpdated= =0)
{
    throws new OptimisticLockingFailureException();
}

 

如果更新执行返回的数量是 0 表示产生并发问题了,则抛出乐观锁并发修改异常,需要重新获得最新的数据后再进行更新操作。

(2)时间戳(timestamps):

第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp),和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

此方案有缺点,就是当并发事务时间间隔小于当前系统平台的最小时间单位时,会发生覆盖前一个事务结果的问题。

 

<insert id="isExistsUser" >
    select count(1) from t_users where userId = #{id}
</insert>

<select id="selectVersion" parameterType="user" resultType="long" >
select version from  t_users   where userName = #{userName} 
</select>

<update id="updateByVersion" parameterType="user">
update t_users set  version=version+1, password= #{password} where  userName = #{userName} and version=#{version}
</update>

<select id="selectUsers" resultType="int">
  select count(1)
  from t_users
  where id = #{id}
</select>

 程序逻辑实现:

int userCount = UserMapper.selectUsers(id);
if (userCount==0) 
{
    Long version = UserMapper.selectVersion(user);
    int call = 0;
    user.setVersion(version);
    while (UserMapper.updateByVersion(user)==0) 
    {
        if (call++==3) 
        {
            break;
        }
    }
}

mybatis-plus

1.插件配置

spring xml:

<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>

spring boot:

@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
}

2.注解实体字段 @Version 必须要!

@Version
private Integer version;

特别说明:

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity 中
  • 仅支持 updateById(id) 与 update(entity, wrapper) 方法
  • 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!

示例Java代码 

int id = 100;
int version = 2;

User u = new User();
u.setId(id);
u.setVersion(version);
u.setXXX(xxx);

if(userService.updateById(u)){
    System.out.println("Update successfully");
}else{
    System.out.println("Update failed due to modified by others");
}

示例SQL原理

update tbl_user set name = 'update',version = 3 where id = 100 and version = 2

 参考:

https://mp.baomidou.com/guide/optimistic-locker-plugin.html#%E7%A4%BA%E4%BE%8B

http://www.mybatis.cn/archives/899.html

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值