您的位置:首页 > 游戏 > 游戏 > 天津网站设计诺亚科技_网站微信开发_餐饮营销引流都有什么方法_趣丁号友情链接

天津网站设计诺亚科技_网站微信开发_餐饮营销引流都有什么方法_趣丁号友情链接

2025/4/25 5:59:42 来源:https://blog.csdn.net/guoqi_666/article/details/144931577  浏览:    关键词:天津网站设计诺亚科技_网站微信开发_餐饮营销引流都有什么方法_趣丁号友情链接
天津网站设计诺亚科技_网站微信开发_餐饮营销引流都有什么方法_趣丁号友情链接

目录

前言(背景-思路):

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 {@Autowiredprivate 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; // 删除成功表示释放成功}/*** 销毁时关闭线程池*/@PreDestroypublic 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 {@Autowiredprivate DistributedLockService distributedLockService;@Autowiredprivate 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 {@PostConstructpublic void init() {// 在应用启动时执行的逻辑Date now = new Date();Date nowDateTime = new Date(now.getTime());deleteExpirekdKey(nowDateTime )}
}

下一篇写基于mysql数据库实现缓存

完整demo代码已上传github:https://github.com/qi-only/daydayuo-go

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com