首页 > 数据库 >深入探索分布式任务调度框架:MySQL实现高效锁机制

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

时间:2024-08-22 10:52:07浏览次数:13  
标签:COMMENT 抢锁 lock time namespace MySQL owner 任务调度 分布式

本文主要介绍项目中怎么使用 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> {
    @Override
    public 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
SET
    update_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 怎么去执行业务逻辑,那又是另一件事情了,后面再单独说。

标签:COMMENT,抢锁,lock,time,namespace,MySQL,owner,任务调度,分布式
From: https://blog.csdn.net/yqq962464/article/details/141400936

相关文章

  • Mysql锁查看
    查看InnoDB的锁情况SHOWENGINEINNODBSTATUS;命令会返回一个包含详细InnoDB引擎状态的报告,其中包含当前锁的详细信息。在输出的LATESTDETECTEDDEADLOCK部分找到死锁信息,并在TRANSACTIONS部分找到当前的锁等待信息。查看MyISAM锁情况--方法一:查看表锁SHOWOPEN......
  • Mac导出Mysql千万级表数据
    MysqlDumpmysqldump-h[服务器地址]-u[用户名]-p[密码]--single-transaction--quick[数据库名][表名]>[导出文件.sql]•--single-transaction保证了数据的一致性,并且不会在导出期间锁定表(只针对InnoDB有效)。•--quick选项进一步减少了内存使用,因为它逐行导出数......
  • Windows-安装MySQL数据库
    mysql安装:11.将zip包解压到指定目录,例如:D:\tools\mysql-8.0.18-winx64注:不放到C硬盘,win10系统权限不够会报错,麻烦2.复制改变my.ini文件放在目录:D:\tools\mysql-8.0.18-winx64\bin,并添加相关配置(给的文件中配置内容已经都有了)注1:my.ini配置文件里面非注释语句不要有“”双引......
  • Linux下的分布式锁
    一:什么是分布式锁1、定义        在分布式系统中,一个应用部署在多台机器当中,在某些场景下,为了保证数据一致性,要求在同一时刻,同一任务只在一个节点上运行,即保证某个行为在同一时刻只能被一个线程执行;在单机单进程多线程环境,通过锁很容易做到,比如mutex、spinlock、......
  • 京东面试:mysql分库分表,深度翻页太慢,如何解决
    京东面试:mysql分库分表,深度翻页太慢,如何解决?在40岁老架构师尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、shein希音、shopee、百度、网易的面试资格,遇到很多很重要的面试题:mysql分库分表,深度翻页太慢,如何解决?分库分表后,分页......
  • MySQL基本数据类型
    MySQL数据类型MySQL中定义数据字段的类型对你数据库的优化是非常重要的。MySQL支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。数值类型MySQL支持所有标准SQL数值数据类型。这些类型包括严格数值数据类型(INTEGER、SMALLINT、DECIMAL和NUMERIC),以......
  • 分布式事务解决方案
    背景分布式事务,后端开发中比较常见啦。因为在面试的时候,总是有interviewers让我给他普及一下分布式事务,虽然我会的也不多呀但是还是浅浅说一说;今天心血来潮,好好地总结一下分布式事务,希望每一位后端工程师都能彻底理解分布式事务。什么是分布式事务?答:既然是分布式,首先必然是分......
  • 银河麒麟系统V10(arm版)安装Mysql-5.7.29说明
    #银河麒麟系统适配#随着2024年微软全球蓝屏丑闻的出现,系统安全越来越重要。目前很多企业开始尝试国产化操作系统上,本文介绍如何在国产化银河麒麟系统V10(arm)版上安装mysql。本资源使用的是arm版本的Mysql-5.7.29离线安装包,能够在arm版国产化银河麒麟系统上进行安装,并配置防火......
  • MySQL 源码|51 - 语法解析:高级表达式
    目录文档:MySQL源码|源码剖析文档目录源码位置(版本=MySQL8.0.37):sql/sql_yacc.yy前置文档:MySQL源码|50-语法解析:基础表达式(simple_expr)在梳理了基础表达式simple_expr之后,我们就可以梳理更高级的表达式了。高级表达式的相关关系如下图所示:其中绿色节点为本章节梳......
  • 数据库MySQL之事务、索引
    目录1.概述2.事务3.索引3.1索引结构3.2操作语法1.概述场景:假如我们需要解散教学部,那么该部门下的所有员工都需要删除。如果教学部成功删除了,但员工出于某些原因(比如SQL语句写错了等)并没有删除,此时就会出现数据不一致的问题。这时我们可以通过数据库中的事务来解决。......