基于select for update 实现数据库分布式锁

1、select for update 的基本语法

SELECT * FROM table_name WHERE condition FOR UPDATE;

2、select for update 的定义及作用

2.1 、select for update的含义是在查询数据的同时对所选的数据行进行锁定,以保证数据的一致性和并发控制。在并发环境下,多个事务可能同时对同一数据进行读取和修改。如果不加任何锁机制,就会出现脏数据的情况,即一个事务读取了另一个事务尚未提交的数据,导致数据不一致。
2.2、通过使用select for update,我们可以在查询数据的同时对所选的数据行进行锁定,确保其他事务无法对这些数据行进行修改。这种加锁机制可以有效地避免脏数据的问题,保证数据的一致性。

3、基于select for update 实现数据库分布式锁

注意事项

  1. select for update 必须在事务中才会生效。
  2. 该语法适用于mysql的innodb 数据库引擎。

java实现

package com.monika.main.system.lock.impl;

import com.monika.main.system.lock.Lock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.beans.factory.InitializingBean;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author:whh
 * @date: 2024-05-11 18:47
 * <p></p>
 */

@Component
public class JdbcRowLock implements Lock,InitializingBean {

    private static final Logger LOG = LoggerFactory.getLogger(JdbcRowLock.class);

    private static final String LOCK_NAME = "JDBC_LOCK_NAME";

    private static final int LOCK = 1;

    private static final int UNLOCK = 0;

    private static final String SELECT_SQL = "select lock_name,lock_status from tb_lock where lock_name = ? for update";


    private static final String UPDATE_SQL = "update tb_lock set lock_status = ? where lock_name = ?";



    private JdbcTemplate jdbcTemplate;

    /**
     * 事务
     */
    private TransactionTemplate transactionTemplate;





    @Override
    public boolean lock() {
        Boolean lock = transactionTemplate.execute(action -> {
            Map<String, Object> result = jdbcTemplate.queryForMap(SELECT_SQL, LOCK_NAME);

            int lockStatus = (int) result.get("lock_status");
            if (LOCK == lockStatus) {
                LOG.info("线程{}获取锁[{}]失败,当前锁已被占用", Thread.currentThread().getName(), LOCK_NAME);
                return false;
            }
            jdbcTemplate.update(UPDATE_SQL, ps -> {
                ps.setInt(1, LOCK);
                ps.setString(2, LOCK_NAME);
            });
            LOG.info("线程{}获取锁[{}]成功", Thread.currentThread().getName(), LOCK_NAME);
            return true;
        });
        return lock;
    }

    @Override
    public void unlock() {
        jdbcTemplate.update(UPDATE_SQL, ps -> {
            ps.setInt(1, UNLOCK);
            ps.setString(2, LOCK_NAME);
        });
        LOG.info("线程{}释放锁[{}]成功", Thread.currentThread().getName(), LOCK_NAME);
    }

    @Override
    public void interrupt() {
  jdbcTemplate.update(UPDATE_SQL, ps -> {
            ps.setInt(1, UNLOCK);
            ps.setString(2, LOCK_NAME);
        });

    }



/**
*  初始化时重置状态锁
*/
  @Override
  public void afterPropertiesSet()throws Exception{
	  jdbcTemplate.update(UPDATE_SQL, ps -> {
            ps.setInt(1, UNLOCK);
            ps.setString(2, LOCK_NAME);
        });
        
}

    @Autowired
    public JdbcRowLock(JdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.transactionTemplate = transactionTemplate;
    }
}




  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要引入达梦数据库的Java驱动包,可以在Maven中添加以下依赖: ```xml <dependency> <groupId>dm</groupId> <artifactId>dmjdbc</artifactId> <version>8.0.0-jar-with-dependencies</version> </dependency> ``` 接下来,我们可以使用Java的ReentrantLock类来实现分布式锁,然后通过达梦数据库事务来确保锁的正确性。以下是一个示例代码: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class DmLock implements Lock { private static final String LOCK_TABLE = "t_dm_lock"; private static final String LOCK_NAME = "my_lock"; private static final String URL = "jdbc:dm://localhost:5236"; private static final String USERNAME = "username"; private static final String PASSWORD = "password"; private Connection connection; private Lock innerLock = new ReentrantLock(); public DmLock() throws SQLException { connection = DriverManager.getConnection(URL, USERNAME, PASSWORD); createLockTable(); } private void createLockTable() throws SQLException { String sql = "CREATE TABLE IF NOT EXISTS " + LOCK_TABLE + " (name VARCHAR(255) PRIMARY KEY, locked BOOLEAN)"; try (PreparedStatement statement = connection.prepareStatement(sql)) { statement.executeUpdate(); } } @Override public void lock() { innerLock.lock(); try { while (!tryLock()) { Thread.sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void lockInterruptibly() throws InterruptedException { innerLock.lockInterruptibly(); try { while (!tryLock()) { Thread.sleep(100); } } catch (InterruptedException e) { innerLock.unlock(); throw e; } } @Override public boolean tryLock() { try { connection.setAutoCommit(false); String sql = "SELECT locked FROM " + LOCK_TABLE + " WHERE name = ? FOR UPDATE"; try (PreparedStatement statement = connection.prepareStatement(sql)) { statement.setString(1, LOCK_NAME); if (!statement.execute()) { return false; } if (statement.getResultSet().next()) { boolean locked = statement.getResultSet().getBoolean(1); if (locked) { return false; } } sql = "INSERT INTO " + LOCK_TABLE + " (name, locked) VALUES (?, ?)"; try (PreparedStatement insertStatement = connection.prepareStatement(sql)) { insertStatement.setString(1, LOCK_NAME); insertStatement.setBoolean(2, true); insertStatement.executeUpdate(); } connection.commit(); return true; } } catch (SQLException e) { try { connection.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } return false; } } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { long timeout = unit.toMillis(time); long start = System.currentTimeMillis(); innerLock.lockInterruptibly(); try { while (!tryLock()) { if (System.currentTimeMillis() - start >= timeout) { return false; } Thread.sleep(100); } } catch (InterruptedException e) { innerLock.unlock(); throw e; } return true; } @Override public void unlock() { try { connection.setAutoCommit(false); String sql = "UPDATE " + LOCK_TABLE + " SET locked = ? WHERE name = ?"; try (PreparedStatement statement = connection.prepareStatement(sql)) { statement.setBoolean(1, false); statement.setString(2, LOCK_NAME); statement.executeUpdate(); } connection.commit(); } catch (SQLException e) { try { connection.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } finally { innerLock.unlock(); } } @Override public Condition newCondition() { throw new UnsupportedOperationException(); } } ``` 以上是一个基于达梦数据库8.0版本的分布式锁实现的示例代码。需要注意的是,该实现仅供参考,实际使用中需要根据具体的场景和需求进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值