文章插图
【分布式锁的三种实现!】分布式锁是一种用于保证分布式系统中多个进程或线程同步访问共享资源的技术 。同时它又是面试中的常见问题,所以我们本文就重点来看分布式锁的具体实现(含实现代码) 。
在分布式系统中,由于各个节点之间的网络通信延迟、故障等原因,可能会导致数据不一致的问题 。分布式锁通过协调多个节点的行为,保证在任何时刻只有一个节点可以访问共享资源,以避免数据的不一致性和冲突 。
1、分布式锁要求分布式锁通常需要满足以下几个要求:
- 互斥性:在任意时刻只能有一个客户端持有锁 。
- 不会发生死锁:即使持有锁的客户端发生故障,也能保证锁最终会被释放 。
- 具有容错性:分布式锁需要能够容忍节点故障等异常情况,保证系统的稳定性 。
- 基于数据库实现的分布式锁:可以通过数据库的乐观锁或悲观锁实现分布式锁,但是由于数据库的 IO 操作比较慢,不适合高并发场景 。
- 基于 ZooKeeper 实现的分布式锁:ZooKeeper 是一个高可用性的分布式协调服务,可以通过它来实现分布式锁 。但是使用 ZooKeeper 需要部署额外的服务,增加了系统复杂度 。
- 基于 redis 实现的分布式锁:Redis 是一个高性能的内存数据库,支持分布式部署,可以通过Redis的原子操作实现分布式锁,而且具有高性能和高可用性 。
(1)悲观锁在数据库中使用 for update 关键字可以实现悲观锁,我们在 MApper 中添加 for update 即可对数据加锁,实现代码如下:
<!-- UserMapper.xml --><select id="selectByIdForUpdate" resultType="User">SELECT * FROM user WHERE id = #{id} FOR UPDATE</select>
在 Service 中调用 Mapper 方法,即可获取到加锁的数据:@Transactionalpublic void updateWithPessimisticLock(int id, String name) {User user = userMapper.selectByIdForUpdate(id);if (user != null) {user.setName(name);userMapper.update(user);} else {throw new RuntimeException("数据不存在");}}
(2)乐观锁在 MyBatis 中,可以通过给表添加一个版本号字段来实现乐观锁 。在 Mapper 中,使用标签定义更新语句,同时使用 set 标签设置版本号的增量 。<!-- UserMapper.xml --><update id="updateWithOptimisticLock">UPDATE user SETname = #{name},version = version + 1WHERE id = #{id} AND version = #{version}</update>
在 Service 中调用 Mapper 方法,需要传入更新数据的版本号 。如果更新失败,说明数据已经被其他事务修改,具体实现代码如下:@Transactionalpublic void updateWithOptimisticLock(int id, String name, int version) {User user = userMapper.selectById(id);if (user != null) {user.setName(name);user.setVersion(version);int rows = userMapper.updateWithOptimisticLock(user);if (rows == 0) {throw new RuntimeException("数据已被其他事务修改");}} else {throw new RuntimeException("数据不存在");}}
4、Zookeeper 分布式锁在 Spring Boot 中,可以使用 Curator 框架来实现 ZooKeeper 分布式锁,具体实现分为以下 3 步:- 引入 Curator 和 ZooKeeper 客户端依赖;
- 配置 ZooKeeper 连接信息;
- 编写分布式锁实现类 。
<dependency><groupId>org.Apache.curator</groupId><artifactId>curator-framework</artifactId><version>latest</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>latest</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>latest</version></dependency>
(2)配置 ZooKeeper 连接在 application.yml 中添加 ZooKeeper 连接配置:spring:zookeeper:connect-string: localhost:2181namespace: demo
(3)编写分布式锁实现类@Componentpublic class DistributedLock {@Autowiredprivate CuratorFramework curatorFramework;/*** 获取分布式锁** @param lockPath锁路径* @param wAItTime等待时间* @param leaseTime锁持有时间* @param timeUnit时间单位* @return 锁对象* @throws Exception 获取锁异常*/public InterProcessMutex acquire(String lockPath, long waitTime, long leaseTime, TimeUnit timeUnit) throws Exception {InterProcessMutex lock = new InterProcessMutex(curatorFramework, lockPath);if (!lock.acquire(waitTime, timeUnit)) {throw new RuntimeException("获取分布式锁失败");}if (leaseTime > 0) {lock.acquire(leaseTime, timeUnit);}return lock;}/*** 释放分布式锁** @param lock 锁对象* @throws Exception 释放锁异常*/public void release(InterProcessMutex lock) throws Exception {if (lock != null) {lock.release();}}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 企业最需要什么样的Python工程师?了解一下!
- llama2.mojo比llama2.c快20%,最年轻的语言Mojo惊艳开发者社区
- Java泛型:提升代码可读性和安全性的关键
- 8个核心回归算法总结!!
- 轻松掌握MLOps的操作指南
- JavaScript 循环:拥有最佳性能的最佳实践
- 云架构中加入生成式AI的几点建议
- 迟来的混元大模型,能为腾讯带来多大能量?
- 静态计算图与动态计算图的对比,我们应该如何选择
- IR Intermediate Representation 在编译器中的多层实现