深入探索分布式任务调度框架: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 怎么去执行业务逻辑,那又是另一件事情了,后面再单独说。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/406859.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鹏哥C语言自定义笔记重点(29-)

29.函数指针数组 30.void指针是不能直接解引用&#xff0c;也不能-整数。 void*是无具体类型的指针&#xff0c;可以接受任何类型的地址。 31.qsort:使用快速排序的思想实现一个排序函数(升序) 32. 33.地址的字节是4/8 34.char arr[]{a,b} sizeof(arr[0]1)答案是4&#xff0…

Godot《躲避小兵》实战之游戏开始界面制作

我们的游戏还需要用户可操作的界面&#xff0c;比如开始游戏&#xff0c;退出以及显示分数等UI界面。 创建新场景&#xff0c;点击“其他节点”按钮&#xff0c;然后添加一个 CanvasLayer 节点并命名为 HUD。“HUD”是“heads-up display”&#xff08;游戏信息显示&#xff0…

windows所有功能都可使用就是电脑黑屏了

运行新任务然后输入explorer.exe勾选上创建任务确定就好了 下次尽量不手欠&#xff01;&#xff01;&#xff01;&#xff01;

Golang | Leetcode Golang题解之第367题有效的完全平方数

题目&#xff1a; 题解&#xff1a; func isPerfectSquare(num int) bool {x0 : float64(num)for {x1 : (x0 float64(num)/x0) / 2if x0-x1 < 1e-6 {x : int(x0)return x*x num}x0 x1} }

改编版猜数字小游戏,猜错了就黑屏(整蛊版本)

1. 前情提要 在前一篇博客中&#xff0c;我们了解到了如何获得随机数&#xff0c;并且通过运算可以规定所获得的这个随机数的范围在多少数值之间 那么接下来我们就需要去具体去实现猜数字游戏的各种布置 2. 布置主菜单 玩一个游戏&#xff0c;最开始的界面都会是一个主菜单…

hutool excel 导出,格式错误,打开乱码

现象 最近在使用hutool excel 实现网站数据导出excel功能。在swagger 中调试接口时候遇到下载文件不能打开或者乱码的问题。 1. xlsx 文件不能打开 2. xls 文件打开乱码 解决方案 直接使用postman 或者 get 请求使用浏览器地址栏 总之&#xff0c;excel 文件不能导出&#xf…

leetcode 49 字母异位分词

正文 基础解法 首先&#xff0c;我们创建一个字典对象&#xff0c;然后遍历整个字符串列表&#xff0c;并且使用 sorted() 函数对字符串列表进行排序&#xff0c;所有的异位分词经过排序后它们的组成和顺序会趋于一致。但是需要注意的是 sorted 对字符串进行排序后会变成一个由…

day06_算法训练

一. Stream流 1.1 Stream流概述 概念: jdk1.8以后提供的新的API, 主要用于批量操作数据(集合的另外一种操作方式),代码非常简洁 流式处理思想: 2.2 Stream对象获取 1.单列集合的Stream流对象获取 2.双列集合的Stream流对象获取 3.数组的Stream流对象获取 4.散装数据的St…

上线eleme项目

&#xff08;一&#xff09;搭建主从从数据库 主服务器master 首先下载mysql57安装包&#xff0c;然后解压 复制改目录到/usr/local底下并且改个名字 cp -r mysql-5.7.44-linux-glibc2.12-x86_64 /usr/local/mysql 删掉/etc/my.cnf 这个会影响mysql57的启动 rm -rf /etc…

科研绘图系列:Python语言时间趋势图

介绍 不同指标在时间上的变化,可以用时间序列线图表示趋势。 加载Python包 import sys import pandas as pd import numpy as np import scipy as sp from scipy import stats import randomimport seaborn as sns import matplotlib.pyplot as plt from matplotl

Open3D mesh 隐藏点移除

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2去除隐藏点后的点云 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&…

C++ | Leetcode C++题解之第363题矩形区域不超过K的最大数值和

题目&#xff1a; 题解&#xff1a; class Solution { public:int maxSumSubmatrix(vector<vector<int>> &matrix, int k) {int ans INT_MIN;int m matrix.size(), n matrix[0].size();for (int i 0; i < m; i) { // 枚举上边界vector<int> sum(…

开关电源测试系统方案:系统结构功能、特点、测试项目

为了应对开关电源测试中的复杂挑战&#xff0c;我们推出了NSAT-8000系列开关电源ate测试系统&#xff0c;它以其全面的功能和灵活性&#xff0c;适应了电源产品测试场景&#xff0c;解决了测试难题。 开关电源测试系统 NSAT-8000系列之开关电源测试系统适用于AC/DC和DC/DC开关电…

如何使用 Higress 快速构建 AI 应用?

随着 AI 时代到来&#xff0c;基于大模型的应用对网关提出了新的要求&#xff0c;例如在不同 LLM 提供商之间进行负载均衡、构建 AI 应用的可观测能力、基于 token 的限流保护与配额管理、AI 应用内容安全等等。Higress 基于企业内外的丰富场景沉淀了众多面向AI的功能&#xff…

【前端面试】看react源码,解读useState

点击:react git 链接 截止2024.8.22最新版本如下 React hooks 源码好深,hook封装位于packages/react-reconciler/src/ReactFiberHooks.js hook的数据类型: export type Hook = {memoizedState: any,baseState: any,baseQueue: Update<any, any> | null,queue: an…

数据结构(6.2_4)——图的基本操作

注&#xff1a;只探讨邻接矩阵和邻接表怎么实现图的基本操作 Adjacent(G,x,y):判断图G是否存在边<x,y>或(x,y) 领接矩阵 邻接表 有向图&#xff1a; Neighbors(G,x):列出图G中与结点x邻接的边 有向图 InsertVertex(G,x):在图G中插入顶点x DeleteVertex(G,x):在图G中删除…

软件测试-测试分类

测试分类 按照测试目标测试 界面测试 页面内展示的所有内容/元素都需要测试 参考UI图找不同 功能测试 ​ 如何设计功能测试用例&#xff1f; 参考产品规格说明书进行用例的编写&#xff0c;具体的测试用例需要使用黑盒设计测 试用例的方法&#xff0c;如等价类、边界值、…

深入浅出链表

目录 1.链表的基本概念及结构 1.1基本概念 1.2结构 2.链表的分类 3.链表的实现&#xff08;循环链表增删查改实现&#xff09; 1.动态申请节点&#xff08;结点&#xff09;​编辑 2.单链表打印 3.单链表尾插 4.单链表头插 5.单链表尾删 6.单链表头删 7.单链表查找 …

如何应对突发技术故障和危机:开发团队的应急策略

开发团队如何应对突发的技术故障和危机&#xff1f; 在数字化时代&#xff0c;软件服务的稳定性对于企业至关重要。然而&#xff0c;即使是大型平台&#xff0c;如网易云音乐&#xff0c;也可能遇到突发的技术故障。网页端出现502 Bad Gateway 报错&#xff0c;且App也无法正常…