当前位置: 首页 > news >正文

深入探索分布式任务调度框架:MySQL实现高效锁机制

本文主要介绍项目中怎么使用 MySQL 实现分布式锁的

背景

假如我们现在要做一个高性能、可扩展的分布式任务调度框架,要怎么设计呢?下面是我之前自己设计的一个架构图。
分布式任务调度架构
为了方便后续的分布式锁的设计,我们大致描述下各个角色都做了哪些事情(这不是本篇文章的重点)

scheduler-client

做为一个 sdk,植入到各业务方系统,功能如下:

  • 定期注册心跳信息
  • 暴露 http 服务端口,监听 handler 请求

scheduler-web

与前端页面交互,保存任务调度信息

scheduler-trigger

该节点包含两个角色:masterslave

  • master:读任务表,分配任务
  • slave:按策略执行任务,调用 scheduler-client 的接口

分布式锁

从图中可以看到,scheduler-trigger分为了两个角色masterslave,那么角色是怎么确定的呢?就是通过去抢锁,谁抢到锁,谁就是 master

所以,重点就在于该怎么设计抢锁这个动作?

锁表设计

CREATE TABLE ` lock ` (` id ` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',` namespace ` varchar(128)  NOT NULL DEFAULT '' COMMENT '锁名',` owner ` varchar(128) DEFAULT NULL COMMENT '资源所有者(ip地址)',` version ` bigint(20) NOT NULL DEFAULT '0' COMMENT '版本号',` create_time ` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',` update_time ` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (` id `) USING BTREE,KEY ` idx_namespace ` (` namespace `) USING BTREE
) ENGINE = InnoDB  COMMENT = '锁信息表'

这就是基于 MySQL 的分布式锁表设计,其中namespace代表锁的名称,owner代表该锁被哪个 ip 节点所持有。

初始化锁

在系统启动的时候,我们需要去监听启动事件,然后去初始化锁信息。

@Component
public class LockerHandler implements ApplicationListener<ApplicationEvent> {@Overridepublic void onApplicationEvent(ApplicationEvent event) {try {if (event instanceof ApplicationStartedEvent) {//初始化锁}} catch (Exception e) {}}
}

初始化锁的逻辑就是insert 一条锁信息,只不过此时锁还未被任何 owner 所持有。

INSERT INTO lock(`namespace`,`version`,create_time, update_time) VALUE ('master',1,NOW(),NOW())

抢锁&锁续约

在系统启动几秒后,有一个定时任务,每秒钟执行一次。

@Scheduled(initialDelay = 5000,fixedDelay = 1000)
public void lock() {try {LockTypeEnum lt = lock("master", InetTool.LOCAL_IP, 3);/**这里面是抢锁之后的业务逻辑,暂且不表**/return;} catch (Exception e) {  }
}@Override
public LockTypeEnum lock(String namespace, String owner, int ttl) {//查询锁LockDO lock = this.lockDao.get(namespace);//如果锁过期或者无人持有锁,则触发DB抢锁if (lock.isExpired(ttl)) {return this.lockDao.lock(namespace, owner, lock.getVersion()) ? LockTypeEnum.PROMOTED : LockTypeEnum.UNKNOWN;}//如果锁未过期,且持有者是自己,则续约if (owner.equals(lock.getOwner())) {return this.lockDao.keepAlive(namespace, owner, ttl) ? LockTypeEnum.STAY : LockTypeEnum.LEAVE;}return LockTypeEnum.UNKNOWN;
}

下面来详细解剖下里面的代码逻辑

查询锁

通过namespace去查询锁信息

SELECT *,
(UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(update_time)) AS remaining
FROM lock
WHERE namespace = #{namespace}
LIMIT 1

remaining 代表当前时间与上一次keepAlive更新时间的差值,可用来判断锁是否过期。

判断触发抢锁的条件
//触发抢锁的条件 1.锁过期 2.无人持有锁
public boolean isExpired(int ttl) {return remaining - ttl > 0 || owner == null;
}

remaining > ttl 代表已经超过 3秒没有更新了,满足该条件或owner 为空,会触发抢锁。

抢锁
UPDATE lock
SETupdate_time = NOW(),owner = #{owner},`version` = `version` + 1
WHERE `namespace` = #{namespace} 
AND `version` = #{version}

抢锁涉及到并发操作,所以采用了版本号的方式来保证安全性

锁续约

如果锁未过期且持有者是自己,触发锁续约

UPDATE lock
SET
update_time = NOW()
WHERE namespace = #{namespace} 
AND owner = #{owner} 
AND update_time >= DATE_SUB(NOW(),INTERVAL 3 SECOND)

此处的where判断条件也再次校验了,update_time >= 当前时间 - 3秒,代表锁还没有过期。

总结

以上就是基于 MySQL 实现锁机制的流程,至于抢到锁后的 master 和 slaver 怎么去执行业务逻辑,那又是另一件事情了,后面再单独说。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 误删?损坏?SD卡数据恢复全攻略,让你的数据起死回生!
  • RK3568平台(PWM篇)PWM驱动
  • xss-靶场
  • 基于协同过滤算法的体育商品推荐系统_t81xg
  • 气膜游泳馆:夏日避暑的绝佳选择—轻空间
  • Perl(Practical Extraction and Reporting Language)脚本
  • 自主身份:Web3如何重新定义个人数据所有权
  • 基于 Spring Boot 的快速开发微信公众平台的框架-FastBootWeixin框架
  • RabbitMQ-消息队列之topic使用
  • Linux目录结构及基础查看命令和命令模式
  • EmguCV学习笔记 VB.Net 4.5 像素距离和连通区域
  • ECCV2024|商汤发布3D面部动画系统UniTalker:通过统一模型扩展音频驱动的 3D 面部动画
  • Verilog刷题笔记55
  • 第4章 汇编语言和汇编软件
  • MySQL索引的性能优化
  • @jsonView过滤属性
  • bearychat的java client
  • Django 博客开发教程 16 - 统计文章阅读量
  • echarts花样作死的坑
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • JavaScript 一些 DOM 的知识点
  • SpiderData 2019年2月25日 DApp数据排行榜
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • 工程优化暨babel升级小记
  • 记一次和乔布斯合作最难忘的经历
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 山寨一个 Promise
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 树莓派 - 使用须知
  • 赢得Docker挑战最佳实践
  • 仓管云——企业云erp功能有哪些?
  • ​queue --- 一个同步的队列类​
  • ​力扣解法汇总946-验证栈序列
  • #14vue3生成表单并跳转到外部地址的方式
  • #大学#套接字
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (1)(1.9) MSP (version 4.2)
  • (C++17) optional的使用
  • (C语言)fread与fwrite详解
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (PADS学习)第二章:原理图绘制 第一部分
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (算法)Game
  • (未解决)macOS matplotlib 中文是方框
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • .Net - 类的介绍
  • .net core 连接数据库,通过数据库生成Modell
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .NET NPOI导出Excel详解