如何使用mysql实现分布式锁

这篇具有很好参考价值的文章主要介绍了如何使用mysql实现分布式锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

如何使用mysql实现可重入的分布式锁


目录

  • 什么是分布式锁?
  • 如何实现分布式锁?
  • 定义分布式表结构
  • 定义锁统一接口
  • 使用mysql来实现分布式锁
    生成线程标记ID
    加锁
    解锁
    重置锁
  • 写在最后

1. 什么是分布式锁?

百度百科:分布式锁是控制分布式系统之间同步访问共享资源的一种方式。

ㅤ如引用所述,分布式锁是一种用于在分布式系统中对资源进行同步访问的机制。在分布式系统中,多个节点同时访问某个共享资源时,需要确保资源的一致性和正确性。分布式锁可以通过协调多个节点之间的操作,保证在同一时间内只有一个节点能够访问该资源,从而避免竞态条件和数据不一致的问题。

2. 如何实现分布式锁?

基于数据库的分布式锁:使用数据库的事务机制来实现分布式锁,通过在数据库中创建一个唯一约束或者加锁表来保证同一时间只有一个节点能够获得锁。
基于共享存储的分布式锁:使用共享存储如Redis、ZooKeeper等来实现分布式锁,通过在共享存储中创建占位节点或者临时节点来表示锁的状态。
基于乐观锁的分布式锁:通过使用版本号或者时间戳等方式,在修改资源时判断是否被其他节点修改,从而实现资源的同步访问。
ㅤ分布式锁的实现需要考虑各种复杂条件,如锁的可重入性、死锁的处理、锁的过期时间等。因此,使用分布式锁时需要谨慎设计和合理选择实现方式,以保证系统的性能和可靠性。

3. 定义分布式表结构

create table if not exists t_distributed_lock
(
    id              int auto_increment primary key,
    lock_key        varchar(255) default '' not null comment '锁key',
    thread_ident    varchar(255) default '' not null comment '线程标识',
    timeout         mediumtext              not null comment '超时时间',
    reentrant_count int          default 0  not null comment '可重入次数',
    version         int          default 1  not null comment '版本号(用于乐观锁)'
)
    comment '分布式锁';

4. 定义锁统一接口

public interface ILock<T> {

    /**
     * 加锁(支持可重入)
     *
     * @param lockKey                   锁key
     * @param lockTimeoutMillisecond    锁超时时间(1.锁不是无限存在的 2.为可能存在的死锁做兜底)
     * @param getLockTimeoutMillisecond 获取锁的超时时间(获取锁的时间应该也是有限的)
     */
    boolean lock(String lockKey, long lockTimeoutMillisecond, long getLockTimeoutMillisecond);

    /**
     * 解锁
     *
     * @param lockKey 锁key
     */
    boolean unLock(String lockKey);

    /**
     * 重置锁
     *
     * @param t 锁对象
     */
    boolean resetLock(T t);
}

ㅤ定义关于锁的统一接口,对参数类型进行泛化。

5. 使用mysql来实现分布式锁

5.1 生成线程标记ID
import cn.hutool.core.lang.UUID;
import cn.wxroot.learn.distributedLock.mysql.entity.DistributedLock;
import cn.wxroot.learn.distributedLock.mysql.mapper.DistributedLockMapper;
import cn.wxroot.learn.distributedLock.ILock;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
@Slf4j
public class MysqlLock extends ServiceImpl<DistributedLockMapper, DistributedLock> implements ILock<DistributedLock> {

    ThreadLocal<String> threadIdent = new ThreadLocal<>();

    /**
     * 获取线程标记ID
     *
     * @return 线程标记ID
     */
    private String getThreadIdentId() {

        if (Objects.isNull(threadIdent.get())) {

            threadIdent.set(UUID.randomUUID().toString());
        }

        return threadIdent.get();
    }
    // 加锁、解锁、重置锁
}

ㅤ创建mysql方式的锁实现类。方法getThreadIdentId()来生成线程的唯一标识。

5.1 加锁
    @Override
    public boolean lock(String lockKey, long lockTimeoutMillisecond, long getLockTimeoutMillisecond) {

        boolean result = false;

        long startTime = System.currentTimeMillis();

        String threadIdentId = this.getThreadIdentId();

        while (true) {

            if (startTime + getLockTimeoutMillisecond < System.currentTimeMillis()) {
                log.info("获取锁超时!");
                break;
            }

            QueryWrapper<DistributedLock> distributedLockQueryWrapper = new QueryWrapper<>();
            distributedLockQueryWrapper.lambda().eq(true, DistributedLock::getLockKey, lockKey);
            DistributedLock distributedLock = this.getOne(distributedLockQueryWrapper);

            if (Objects.nonNull(distributedLock)) {

                if (StringUtils.isNotEmpty(distributedLock.getThreadIdent())) {

                    if (threadIdentId.equals(distributedLock.getThreadIdent())) {

                        log.info("重入锁+1");
                        distributedLock.setReentrantCount(distributedLock.getReentrantCount() + 1);
                        distributedLock.setTimeout(System.currentTimeMillis() + lockTimeoutMillisecond);
                        this.updateById(distributedLock);
                        result = true;
                        break;
                    } else {

                        // 其他线程锁定了该lockKey,需要等待
                        if (distributedLock.getTimeout() < System.currentTimeMillis()) {

                            this.resetLock(distributedLock);
                        } else {

                            try {
                                log.info("休眠");
                                Thread.sleep(100);
                            } catch (InterruptedException ignored) {

                            }
                        }
                    }
                } else {

                    log.info("占用锁");
                    distributedLock.setThreadIdent(threadIdentId);
                    distributedLock.setReentrantCount(1);
                    distributedLock.setTimeout(System.currentTimeMillis() + lockTimeoutMillisecond);
                    this.updateById(distributedLock);
                    result = true;
                    break;
                }

            } else {

                log.info("创建新记录");
                DistributedLock addDistributedLock = DistributedLock.builder()
                        .lockKey(lockKey)
                        .threadIdent(threadIdentId)
                        .timeout(System.currentTimeMillis() + lockTimeoutMillisecond)
                        .reentrantCount(1)
                        .version(1)
                        .build();

                this.save(addDistributedLock);
                result = true;
                break;
            }
        }

        return result;
    }

ㅤ实现加锁的方法boolean lock(···)应考虑以下几点:
ㅤ1. 使用锁的时间应该有限的,即不能永久占有锁。
ㅤ2. 获取锁的过程应该是有限的,即指定时间内获取不到锁应该返回。

5.1 解锁
    @Override
    public boolean unLock(String lockKey) {

        boolean result = false;

        String threadIdentId = this.getThreadIdentId();

        log.info("解锁");

        QueryWrapper<DistributedLock> distributedLockQueryWrapper = new QueryWrapper<>();
        distributedLockQueryWrapper.lambda().eq(true, DistributedLock::getLockKey, lockKey);
        DistributedLock distributedLock = this.getOne(distributedLockQueryWrapper);

        if (Objects.nonNull(distributedLock)) {

            if (distributedLock.getThreadIdent().equals(threadIdentId)) {

                if (distributedLock.getReentrantCount() > 1) {

                    distributedLock.setReentrantCount(distributedLock.getReentrantCount() - 1);
                    this.updateById(distributedLock);
                    result = true;
                } else {
                    result = this.resetLock(distributedLock);
                }
            }
        }

        return result;
    }

ㅤ基于可重入锁的特性,在进行解锁操作时,应该有所判断。

5.1 重置锁
    @Override
    public boolean resetLock(DistributedLock distributedLock) {

        log.info("重置锁");
        distributedLock.setThreadIdent("");
        distributedLock.setReentrantCount(0);
        this.updateById(distributedLock);
        return true;
    }

转载请注明出处文章来源地址https://www.toymoban.com/news/detail-747854.html

  • 作者:残酷兄技术站
  • 原文链接:https://www.cnblogs.com/YangPeng/p/17842144.html

到了这里,关于如何使用mysql实现分布式锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • # Spring Boot 中如何使用 Spring Cloud Sleuth 来实现分布式跟踪?

    在微服务架构中,通常会有多个服务相互协作,为了方便排查问题,我们需要对服务之间的调用进行跟踪。Spring Cloud Sleuth 是 Spring Cloud 生态中的分布式跟踪解决方案,它可以帮助我们追踪请求在微服务系统中的传递路径,以及记录每个服务的处理时间等信息。 本文将介绍如

    2024年02月08日
    浏览(59)
  • 分布式锁实现(mysql,以及redis)以及分布式的概念

    我旁边的一位老哥跟我说,你知道分布式是是用来干什么的嘛?一句话给我干懵了,我能隐含知道,大概是用来做分压处理的,并增加系统稳定性的。但是具体如何,我却道不出个1,2,3。现在就将这些做一个详细的总结。至少以后碰到面试官可以说上个123。 那么就正式进入

    2024年01月21日
    浏览(61)
  • 分布式锁之mysql实现

    5000个请求测试买票,查看是否出现超卖问题 出现了超卖问题 或者使用ReentrantLock jmeter压测结果显示,5000总票数,压测5000,都能够解决超卖的现象   查库操作,演示超卖现象 5000总票数,压测5000,压测结果,显示超卖  加锁,本地锁解决超卖现象 5000总票数,压测5000,压测

    2024年02月11日
    浏览(36)
  • 如何模拟实现分布式文件存储

    传统做法是是在宕机存储。但随着数据变多,会遇到存储瓶颈 单机纵向扩展:内存不够加内存,磁盘不够家磁盘。有上限限制,不能无限制加下去 多机横向扩展:采用 多台机器存储 ,一台不够就加机器。理论上可以无线 多台机器存储也就意味迈入了 分布式存储 当文件被分

    2024年02月16日
    浏览(48)
  • redis如何实现分布式锁?

    首先,“分布式锁”的概念,是相对“本地锁”而言。 本地锁比如java中的synchronized 这类 JDK 自带的 本地锁 ,来控制一个 JVM 进程内的多个线程对本地共享资源的访问。 同一时刻只有一个线程可以获取到本地锁访问共享资源。 分布式系统下,不同的服务/客户端通常运

    2024年02月06日
    浏览(60)
  • (快手一面)分布式系统是什么?为什么要分布式系统?分布式环境下会有哪些问题?分布式系统是如何实现事务的?

    《分布式系统原理与泛型》中这么定义分布式系统: “ 分布式系统是若干独立计算机的集合, 这些计算机对于用户来说就像单个相关系统 ”, 分布式系统(distributed system)是建立在网络之上的软件系统。 就比如:用户在使用京东这个分布式系统的时候,会感觉是在使用一

    2024年02月08日
    浏览(69)
  • ​【五一创作】基于mysql关系型实现分布式锁

    看完该文预计用时:15分钟 看之前应具体的技术栈:springboot mysql nginx(了解即可) 目录 0.写在前面 1. 从减库存聊起 1.1. 环境准备   1.2. 简单实现减库存  1.3. 演示超卖现象 1.4. jvm锁问题演示  1.4.2. 原理 1.5. 多服务问题  1.5.1. 安装配置nginx 1.5.2. 压力测试  1.6. mysql锁演示 1

    2024年02月05日
    浏览(44)
  • 分别使用Redis、MySQL、ZooKeeper构建分布式锁

    本文使用Java构建三种中间件的分布式锁,下面介绍下三种分布式锁的优缺点, 使用MySQL构建分布式锁 ,因为数据库数据存储在磁盘中,所以IO速率相对较慢,因此构建出来的分布式锁不适合用在高并发场景,对于一些对并发要求不高的系统中可以使用,进一步提高系统的安全

    2024年02月06日
    浏览(46)
  • ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊)?

    目录 一、面试官心理分析 二、面试题剖析         在搜索这块,lucene 是最流行的搜索库。几年前业内一般都问,你了解 lucene 吗?你知道倒排索引的原理吗?现在早已经 out 了,因为现在很多项目都是直接用基于 lucene 的分布式搜索引 擎—— ElasticSearch ,简称为 ES 。  

    2024年03月15日
    浏览(44)
  • 图解Redisson如何实现分布式锁、锁续约?

    使用当前(2022年12月初)最新的版本:3.18.1; 案例 案例采用redis-cluster集群的方式; redission支持4种连接redis方式,分别为单机、主从、Sentinel、Cluster 集群;在分布式锁的实现上区别在于hash槽的获取方式。 具体配置方式见Redisson的GitHub(https://github.com/redisson/redisson/wiki/2.-%E9

    2023年04月16日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包