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

发布于:2024-05-13 ⋅ 阅读:(166) ⋅ 点赞:(0)

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;
    }
}





网站公告

今日签到

点亮在社区的每一天
去签到