马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
目录
前言(背景-思绪):
1.新增锁锁表
2.详细代码实现
2.1 工具方法
2.2 mapper实现
2.3 测试小列子
2.4 极度情况服务挂了,数据存锁一直得不到释放
前言(背景-思绪):
我们组redis翻车了,给客户带来问题。固然紧急出包得到相识决,但是大领导一句话,直接禁用了。下面是我基于mysql(oracle)数据库实现的分布式锁:
主题思绪就是,借助数据库的行锁,然后拿到锁则继续执行,拿到锁并启动ScheduledExecutorService (定时周期执行任务-每隔锁过期时间/3续期一次,雷同看门狗机制),拿不到锁则自旋等待。
废话不多说上代码-拿去即可使用:
实现背景:springboot、mybatis、mybatis-plus
1.新增锁锁表
- CREATE TABLE `cache_lock_info` (
- `lock_name` varchar(255) NOT NULL,
- `lock_value` varchar(255) NOT NULL,
- `expire_date_time` datetime NOT NULL,
- `update_date` date NOT NULL,
- `update_time` time NOT NULL,
- PRIMARY KEY (`lock_name`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
复制代码 2.详细代码实现
2.1 工具方法
- package onlyqi.dayday01lock.lock;
- import onlyqi.dayday01lock.lock.mapper.LockInfoMapper;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import javax.annotation.PreDestroy;
- import java.util.Date;
- import java.util.concurrent.Executors;
- import java.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.TimeUnit;
- @Service
- public class DistributedLockService {
- @Autowired
- private LockInfoMapper lockInfoMapper;
- private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
- /**
- * 尝试获取锁并启动续期任务
- *
- * @param lockName 锁名称
- * @param lockValue 锁值(唯一标识)
- * @param expireSec 锁过期时间(秒)
- * @return 是否获取成功
- */
- public boolean tryLockWithRenewal(String lockName, String lockValue, int expireSec) {
- Date now = new Date();
- Date expireDateTime = new Date(now.getTime() + expireSec * 1000L);
- try {
- lockInfoMapper.insertLock(lockName, lockValue, expireDateTime, now, now);
- // 检查线程池是否已关闭,如果已关闭则重新初始化
- if (scheduler.isShutdown()) {
- scheduler = Executors.newScheduledThreadPool(1);
- }
- // 启动续期任务
- scheduler.scheduleAtFixedRate(() -> {
- if (!renewLock(lockName, lockValue, expireSec)) {
- // 续期失败,停止任务
- scheduler.shutdown();
- }
- }, expireSec / 3, expireSec / 3, TimeUnit.SECONDS);
- return true;
- } catch (Exception e) {
- // ===========防止服务挂了,数据存在过期锁一直得不到释放====================//
- // 锁已过期,删除旧记录并重新获取锁=======然后返回false,下次自旋获取====此方式不太推荐,推荐服务挂了,再次部署的时候删除数据库所有过期的key。此方法仅做参考//
- lockInfoMapper.deleteLockExpire(lockName,expireDateTime);
- // 主键冲突,锁已被其他线程持有
- return false;
- }
- }
- /**
- * 阻塞自旋获取锁
- *
- * @param lockName 锁名称
- * @param lockValue 锁值(唯一标识)
- * @param expireSec 锁过期时间(秒)
- * @param timeoutSec 自旋超时时间(秒)
- * @return 是否获取成功
- */
- public boolean spinLock(String lockName, String lockValue, int expireSec, int timeoutSec) {
- long startTime = System.currentTimeMillis();
- while (System.currentTimeMillis() - startTime < timeoutSec * 1000L) {
- if (tryLockWithRenewal(lockName, lockValue, expireSec)) {
- // 获取锁成功
- return true;
- }
- try {
- Thread.sleep(100); // 自旋间隔
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return false;
- }
- }
- // 超时未获取锁
- return false;
- }
- /**
- * 续期锁
- *
- * @param lockName 锁名称
- * @param lockValue 锁值(唯一标识)
- * @param expireSec 续期时间(秒)
- * @return 是否续期成功
- */
- public boolean renewLock(String lockName, String lockValue, int expireSec) {
- Date now = new Date();
- Date expireDateTime = new Date(now.getTime() + expireSec * 1000L);
- // LockInfo lockInfo = lockInfoMapper.selectById(lockName);
- // lockInfo.setExpireDateTime(expireDateTime);
- // int rowsUpdated = lockInfoMapper.updateById(lockInfo);
- int rowsUpdated = lockInfoMapper.renewLock(lockName, lockValue, expireDateTime, now, now);
- return rowsUpdated > 0; // 更新成功表示续期成功
- }
- /**
- * 释放锁并停止续期任务
- *
- * @param lockName 锁名称
- * @param lockValue 锁值(唯一标识)
- * @return 是否释放成功
- */
- public boolean releaseLockWithRenewal(String lockName, String lockValue) {
- scheduler.shutdown(); // 停止续期任务
- return releaseLock(lockName, lockValue);
- }
- /**
- * 释放锁
- *
- * @param lockName 锁名称
- * @param lockValue 锁值(唯一标识)
- * @return 是否释放成功
- */
- public boolean releaseLock(String lockName, String lockValue) {
- int rowsDeleted = lockInfoMapper.deleteLock(lockName, lockValue);
- return rowsDeleted > 0; // 删除成功表示释放成功
- }
- /**
- * 销毁时关闭线程池
- */
- @PreDestroy
- public void destroy() {
- if (scheduler != null) {
- scheduler.shutdown();
- }
- }
- }
复制代码 2.2 mapper实现
- package onlyqi.dayday01lock.lock.mapper;
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import onlyqi.dayday01lock.lock.LockInfo;
- import org.apache.ibatis.annotations.Mapper;
- import org.apache.ibatis.annotations.Param;
- import java.util.Date;
- @Mapper
- public interface LockInfoMapper extends BaseMapper<LockInfo> {
- int insertLock(@Param("lockName") String lockName, @Param("lockValue") String lockValue,
- @Param("expireDateTime") Date expireDateTime, @Param("updateDate") Date updateDate,
- @Param("updateTime") Date updateTime);
- int renewLock(@Param("lockName") String lockName, @Param("lockValue") String lockValue,
- @Param("expireDateTime") Date expireDateTime, @Param("updateDate") Date updateDate,
- @Param("updateTime") Date updateTime);
- int deleteLock(@Param("lockName") String lockName, @Param("lockValue") String lockValue);
- int deleteLockExpire(@Param("lockName") String lockName, @Param("expireDateTime") Date expireDateTime);
- }
复制代码 2.3 测试小列子
- package onlyqi.dayday01lock.lock;
- import onlyqi.dayday01lock.lock.mapper.LockInfoMapper;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- @Component
- public class DistributedLockExample {
- @Autowired
- private DistributedLockService distributedLockService;
- @Autowired
- private LockInfoMapper lockInfoMapper;
- public void lockTest(String operatorNo) {
- String lockName = "my_lock";
- String lockValue = Thread.currentThread().getName();
- int expireSec = 30; // 锁过期时间为 30 秒
- try {
- // 尝试获取锁并启动续期任务
- if (distributedLockService.spinLock(lockName, lockValue, expireSec,60)) {
- System.out.println("Lock acquired successfully!======operatorNo:"+operatorNo+"========在执行");
- // 模拟业务逻辑-模拟业务逻辑执行3 秒
- Thread.sleep(3000);
- System.out.println("Business logic completed!==="+operatorNo+"===执行结束=====");
- } else {
- System.out.println("Failed to acquire lock!"+operatorNo);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- // 释放锁并停止续期任务
- if (distributedLockService.releaseLockWithRenewal(lockName, lockValue)) {
- System.out.println("Lock released successfully!");
- }
- }
- }
- }
复制代码 2.4 极度情况服务挂了,数据存锁一直得不到释放
实现一:每次获取不到锁的时候,增长删除数据库过期的锁的动作
实现二:启动项目的时候,扫除数据库所有过期的锁(推荐)
- @Component
- public class StartupTask {
- @PostConstruct
- public void init() {
- // 在应用启动时执行的逻辑
- Date now = new Date();
- Date nowDateTime = new Date(now.getTime());
- deleteExpirekdKey(nowDateTime )
- }
- }
复制代码 下一篇写基于mysql数据库实现缓存
完备demo代码已上传github:https://github.com/qi-only/daydayuo-go
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |