代码编织梦想

使用MySQL的行级锁机制来实现分布式锁。 设计过程如下:
1.创建一张用于存储锁信息的表,包含以下字段:锁的名称、锁的持有者、锁的过期时间等。
2.当需要获取锁时,向该表插入一条记录,如果插入成功,则表示获取到了锁。
3.当需要释放锁时,根据锁的名称和持有者信息,删除该表中的相应记录,释放锁。

1 创建数据库表

首先,在MySQL数据库中创建一个表用于存储锁的状态。假设有一个表 distributed_lock,包含以下字段:

  • id:锁的唯一标识,可以是自增主键或全局唯一标识符(GUID)。
  • resource_key:锁的资源标识符,用于唯一标识需要加锁的资源。
  • owner:锁的拥有者,表示当前持有锁的客户端。
  • created_time:锁的创建时间,表示锁的获取时间。
  • expiration_time:锁的过期时间,表示锁的有效期。
CREATE TABLE lock (
  id INT PRIMARY KEY AUTO_INCREMENT, -- 锁的唯一标识
  resource_key VARCHAR(255) NOT NULL, -- 锁的资源标识符
  owner VARCHAR(255) NOT NULL, -- 锁的拥有者
  created_time DATETIME NOT NULL, -- 锁的创建时间
  expiration_time DATETIME NOT NULL -- 锁的过期时间
);

2 获取锁

在Java代码中,获取锁的过程可以通过执行MySQL的插入操作来实现。考虑到并发情况下可能会有多个客户端同时尝试获取锁,可以通过以下代码来实现:

import java.sql.*;

public class DistributedLock {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/my_db";
    private static final String DB_USER = "root";
    private static final String DB_PASSWORD = "password";

    public boolean acquireLock(String resourceKey, int timeout) {
        boolean lockAcquired = false;
        Connection conn = null;
        try {
            // 创建数据库连接
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);

            // 开始事务
            conn.setAutoCommit(false);

            // 查询是否已经有其他客户端持有锁
            String selectSql = "SELECT * FROM distributed_lock WHERE resource_key = ? FOR UPDATE";
            PreparedStatement selectStmt = conn.prepareStatement(selectSql);
            selectStmt.setString(1, resourceKey);
            ResultSet rs = selectStmt.executeQuery();

            if (!rs.next()) {
                // 如果没有其他客户端持有锁,则插入新的锁记录
                String insertSql = "INSERT INTO distributed_lock (resource_key, owner, created_time, expiration_time) VALUES (?, ?, NOW(), DATE_ADD(NOW(), INTERVAL ? SECOND))";
                PreparedStatement insertStmt = conn.prepareStatement(insertSql);
                insertStmt.setString(1, resourceKey);
                insertStmt.setString(2, Thread.currentThread().getName());
                insertStmt.setInt(3, timeout);
                insertStmt.executeUpdate();

                // 提交事务
                conn.commit();

                lockAcquired = true;
            }

            rs.close();
            selectStmt.close();
        } catch (SQLException e) {
            // 处理异常
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 关闭数据库连接
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        return lockAcquired;
    }
}

在上述代码中,acquireLock 方法接收两个参数:resourceKey 代表需要加锁的资源标识符,timeout 代表获取锁的超时时间(以秒为单位)。

首先,代码创建数据库连接,然后开始事务。接下来,通过执行 SELECT 语句查询是否已经有其他客户端持有锁。如果没有其他客户端持有锁,则执行 INSERT 语句插入新的锁记录,并提交事务。最后,关闭数据库连接,并返回获取锁的结果。

3 释放锁

获取锁成功后,需要在使用完资源后释放锁。释放锁的过程可以通过执行 MySQL 的删除操作来实现。以下是释放锁的代码:

public class DistributedLock {
    // ...

    public void releaseLock(String resourceKey) {
        Connection conn = null;
        try {
            // 创建数据库连接
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);

            // 开始事务
            conn.setAutoCommit(false);

            // 删除锁记录
            String deleteSql = "DELETE FROM distributed_lock WHERE resource_key = ?";
            PreparedStatement deleteStmt = conn.prepareStatement(deleteSql);
            deleteStmt.setString(1, resourceKey);
            deleteStmt.executeUpdate();

            // 提交事务
            conn.commit();

            deleteStmt.close();
        } catch (SQLException e) {
            // 处理异常
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 关闭数据库连接
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在上述代码中,releaseLock 方法接收一个参数 resourceKey,代表需要释放的锁的资源标识符。

代码创建数据库连接,然后开始事务。接下来,通过执行 DELETE 语句删除锁记录,并提交事务。最后,关闭数据库连接。

4 应用场景:

一个典型的应用场景是分布式系统中的任务调度。假设有多个任务调度节点,每个节点同时只能执行一个任务,当有多个节点同时尝试执行任务时,需要使用分布式锁来保证同一时间只有一个节点执行任务。

示例代码:

public class TaskScheduler {
    private static final String TASK_RESOURCE_KEY = "task_resource";
    private static final int LOCK_TIMEOUT = 10; // 10秒

    public void scheduleTask() {
        DistributedLock lock = new DistributedLock();
        boolean lockAcquired = lock.acquireLock(TASK_RESOURCE_KEY, LOCK_TIMEOUT);
        if (lockAcquired) {
            try {
                // 执行任务
                System.out.println("Task scheduled by " + Thread.currentThread().getName());
                Thread.sleep(5000); // 模拟任务执行时间

                // 释放锁
                lock.releaseLock(TASK_RESOURCE_KEY);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        TaskScheduler scheduler = new TaskScheduler();

        // 创建多个线程模拟任务调度节点
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(scheduler::scheduleTask);
            thread.start();
        }
    }
}

在上述代码中,TaskScheduler 类负责任务调度。首先,创建一个 DistributedLock 对象,然后调用 acquireLock 方法尝试获取锁。如果获取锁成功,则执行任务,并在任务执行完毕后调用 releaseLock 方法释放锁。

main 方法中,创建多个线程模拟任务调度节点。每个线程调用 scheduleTask 方法来尝试执行任务。

5 MySQL实现分布式锁的优缺点

数据库实现分布式锁的优点是简单易用,可以实现互斥访问共享资源。但是在高并发、高性能的场景中,数据库实现分布式锁的效率不高,会增加数据库的开销。另外,数据库实现分布式锁还有以下缺点

  1. 单点故障:如果使用单个数据库实现分布式锁,那么数据库成为了单点故障的风险。如果数据库出现故障,那么所有使用该锁的应用都无法正常运行。
  2. 阻塞:当有多个请求同时争夺锁时,只有一个请求能够成功获取到锁,其他请求需要等待。这样会导致请求被阻塞,延长了响应时间。
  3. 容错性差:数据库实现分布式锁只能在一个数据库中实现,如果该数据库发生故障或者网络中断,那么整个分布式锁功能就无法正常工作。

为了解决数据库实现分布式锁的缺点,可以考虑使用其他工具或技术,如分布式缓存(如Redis)、ZooKeeper等来实现分布式锁。这些工具或技术可以提供更高效、更可靠的分布式锁实现方式,适用于高并发、高性能的场景。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/IT_WEH_coder/article/details/141331881

mysql 分布式锁_使用MySQL实现一个分布式锁-爱代码爱编程

介绍 在分布式系统中,分布锁是一个最基础的工具类。例如,部署了2个有付款功能的微服务中,用户有可能对一个订单发起2次付款操作,而这2次请求可能被发到2个服务中,所以必须得用分布式锁防止重复提交,获取到锁的服务正常进行付款操作,获取不到锁的服务提示重复操作。 我司封装了大量的基础工具类,当我们想使用分布式锁的时候只要做3件事情 1.在数据库中建gl

分布式前修课:mysql实现分布式锁_肥肥技术宅的博客-爱代码爱编程

前言 前面已经介绍了原理性的内容,如果原理看的不爽,想看点实际展示效果的话,那么它来了它来了,它带着代码走来啦:blush: 基于MySQL分布式锁实现原理及代码 工欲善其事必先利其器,在基于MySQL实现分布式锁之前,我们要先了解一点MySQL锁自身的相关内容 MySQL锁 我们知道: 锁是计算机协调多个进程或者线程并发访问同一资源的机制 ,而

mysql实现分布式锁_乐韵天城的博客-爱代码爱编程

前言 分布式锁目前的方案有很多比较流行的比如redis、zookeeper等,本文主要讲述mysql分布式锁的使用 服务集群部署的情况下,就会有多个服务连接同一个数据库,某些业务操作可能就会操作多次数据库,导致数据重复。

使用 mysql 实现分布式锁-爱代码爱编程

Mysql 实现分布式锁 背景 项目中经常需要分布式锁来处理一些并发或者避免重复的逻辑,如接口重复提交,消息重复消费,分布式任务调度等。实现分布式锁有很多解决方案,常见的有利用redis、zookeeper等中间价实现的

如何用mysql设计一个分布式锁?-爱代码爱编程

前言 分布式锁想必大家都不陌生,可以用来解决在分布式环境下,多个用户在同一时间读取/更新相同的资源带来的问题。比如秒杀场景下的库存问题、redis key失效情况下请求直接打到MySQL中造成MySQL负载过大的问题,这些问题都可以通过分布式锁来解决。 关于如何实现分布式锁,大家可能对基于Redis实现比较熟悉,但是往往很多情况是一些并发量不大的项

一种基于mysql实现分布式锁的方式-爱代码爱编程

介绍 分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。

mysql做分布式锁_使用mysql实现分布式锁-爱代码爱编程

分布式锁mysql实现方式 方式1:唯一索引 创建锁表,内部存在字段表示资源名及资源描述,同一资源名使用数据库唯一性限制。多个进程同时往数据库锁表中写入对某个资源的占有记录,当某个进程成功写入时则表示其获取锁成功其他进程由

​【五一创作】基于mysql关系型实现分布式锁_mysql分布式锁-爱代码爱编程

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