匿名聊天网站开发,一个营业执照可以做几个网站,wordpress贴内幻灯片,北京网站建站推广前言
乐观锁是一种并发控制机制#xff0c;它假设在大多数情况下不会发生冲突#xff0c;因此在事务执行过程中不加锁。只有在提交时才会检查数据是否被其他事务修改过。如果数据在此期间被修改了#xff0c;则当前事务会被回滚或者需要重新执行。乐观锁的主要用途和优势包…前言
乐观锁是一种并发控制机制它假设在大多数情况下不会发生冲突因此在事务执行过程中不加锁。只有在提交时才会检查数据是否被其他事务修改过。如果数据在此期间被修改了则当前事务会被回滚或者需要重新执行。乐观锁的主要用途和优势包括 提高读取性能由于乐观锁不会锁定资源因此在读取数据时没有阻塞可以极大地提高读取操作的性能特别适合读多写少的应用场景。 减少死锁的可能性因为乐观锁不使用实际的数据库锁所以避免了传统悲观锁可能导致的死锁问题。 简化代码实现乐观锁的实现通常比悲观锁简单特别是在分布式系统中因为它不需要复杂的锁管理逻辑。 支持高并发场景在许多用户同时访问相同数据的情况下乐观锁能够更好地处理并发请求减少了等待时间提高了系统的吞吐量。 用户体验改善在Web应用等环境中乐观锁可以减少用户界面的等待时间提供更流畅的用户体验。 适用于长事务对于那些涉及长时间运行的业务逻辑采用乐观锁可以避免长时间持有锁导致的资源浪费。 支持离线或异步更新乐观锁允许客户端在离线状态下进行数据修改并在上线后合并这些更改这在移动应用开发中尤其有用。
适用场景
读多写少当系统中读取操作远多于写入操作时。低冲突率当数据项被多个事务同时修改的概率较低时。分布式系统在分布式系统中乐观锁可以简化跨节点的数据一致性问题。对最终一致性要求不高如果应用程序可以接受短时间内的数据不一致直到下一个成功的更新操作完成。
注意事项
高冲突率下的效率降低如果数据项频繁地被多个事务修改乐观锁会导致大量重试影响性能。版本号或时间戳的维护需要正确设计和维护用于检测冲突的版本号或时间戳字段。业务逻辑复杂性增加虽然乐观锁简化了某些方面但处理冲突和重试逻辑可能会增加业务逻辑的复杂度。
1. 什么是乐观锁
乐观锁通常通过版本号Version Number或时间戳Timestamp来实现。当一个记录被读取时会同时获取它的版本号或时间戳。在更新这条记录之前会再次检查这个版本号或时间戳确保自上次读取以来没有被修改。如果有修改那么更新操作将失败应用程序可以根据具体需求选择重试或者其他处理方式。
1.1 使用版本号实现乐观锁
在表中添加一个版本字段例如 version。读取记录时也读取版本信息。更新记录时根据版本号进行条件判断;如果 UPDATE 语句影响的行数为0说明在尝试更新的过程中已经有其他事务修改了该记录这时可以采取相应的措施如提示用户数据已更改、重试等。 比如这个SQL语句更新会将版本号1同时只有版本号未有其他操作时才能执行成功。
UPDATE table_name
SET column1 value1, column2 value2, version version 1
WHERE id some_id AND version current_version; 这种方式的优点是避免了锁定资源提高了并发性能但缺点是在高并发场景下可能会出现较多的重试情况。
1.2 使用时间戳实现乐观锁
第二种实现方式和第一种差不多同样是在需要乐观锁控制的table中增加一个字段名称无所谓字段类型使用时间戳timestamp和上面的version类似也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比如果一致则OK否则就是版本冲突。
此方案有缺点就是当并发事务时间间隔小于当前系统平台的最小时间单位时会发生覆盖前一个事务结果的问题。
2. Mybatis实现乐观锁
通常的做法是通过版本号version字段来实现
insert idisExistsUser select count(1) from t_users where userId #{id}
/insertselect idselectVersion parameterTypeuser resultTypelong
select version from t_users where userName #{userName}
/selectupdate idupdateByVersion parameterTypeuser
update t_users set versionversion1, password #{password} where userName #{userName} and version#{version}
/updateselect idselectUsers resultTypeintselect count(1)from t_userswhere id #{id}
/select
程序逻辑实现
int userCount UserMapper.selectUsers(id);
if (userCount0)
{Long version UserMapper.selectVersion(user);int call 0;user.setVersion(version);while (UserMapper.updateByVersion(user)0) {if (call3) {break;}}
}
3. MyBatis-Plus实现乐观锁
以下时基于SpringBoot的实现与非此方法大相径庭
3.1 添加依赖
首先确保你的项目中已经添加了 MyBatis-Plus 的依赖。如果你使用的是 Maven可以在 pom.xml 文件中加入以下依赖
dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion版本号/version
/dependency
3.2. 配置乐观锁插件
在 Spring Boot 应用程序的配置类或主类中你需要配置 MyBatis-Plus 的乐观锁插件。通常这一步是通过 MybatisPlusConfig 类来完成的。
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();// 添加乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}
3.3. 实体类设置
在实体类中需要添加一个字段来作为版本号。MyBatis-Plus 默认会识别名为 version 或带有 Version 注解的字段作为乐观锁的版本号。
import com.baomidou.mybatisplus.annotation.Version;public class ExampleEntity {private Long id;private String data;Versionprivate Integer version;// getters and setters
}
或者如果你的版本字段名不是 version你可以直接使用 Version 注解指定它
import com.baomidou.mybatisplus.annotation.Version;public class ExampleEntity {private Long id;private String data;Versionprivate Integer optimisticLockVersion; // 自定义名称// getters and setters
}
注意事项
支持的数据类型包括int, Integer, long, Long, Date, Timestamp, LocalDateTime。对于整数类型newVersion 是 oldVersion 1。newVersion 会自动回写到实体对象中。支持内置的 updateById(entity) 和 update(entity, wrapper), saveOrUpdate(entity), insertOrUpdate(entity) (version 3.5.7) 方法。自定义方法更新时如果满足内置参数的参数条件方式也会执行乐观锁逻辑例如自定义myUpate(entity) 这个和 updateById(entity) 是等价的会提取参数进行乐观锁填充但更新实现需要自行处理。在 update(entity, wrapper) 方法中wrapper 不能复用。 3.4. 使用乐观锁
当你更新数据时MyBatis-Plus 会自动处理乐观锁逻辑。例如
Service
public class ExampleService {Autowiredprivate ExampleMapper exampleMapper;Transactionalpublic void updateData(Long id, String newData) {ExampleEntity entity exampleMapper.selectById(id);if (entity null) {throw new RuntimeException(Entity not found);}entity.setData(newData);int result exampleMapper.updateById(entity);if (result 0) {throw new OptimisticLockingFailureException(失败了请重试);}}
} 这里当调用 updateById 方法时MyBatis-Plus 会自动检查并更新版本号。如果在尝试更新时发现版本号不匹配即数据已被其他事务修改则更新操作不会成功返回值将是 0这时可以抛出异常或进行重试。