基于mysql数据库做分布式锁的基本实现

基于redis作为工具做分布式锁

推荐文章:Java分布式锁看这篇就够了

【原理】

思路:利用主键唯一的特性,如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,当方法执行完毕之后,想要释放锁的话,删除这条数据库记录即可。

【实现】

创建task_lock表,注意key作为唯一主键
基于mysql数据库做分布式锁的基本实现_第1张图片
基于数据库 乐观锁的 分布式锁工具类 实现如下



/**
 * 基于数据库 乐观锁的 分布式锁
 *
 * 利用主键唯一的特性,如果有多个请求同时提交到数据库的话,
 * 数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,
 * 当方法执行完毕之后,想要释放锁的话,删除这条数据库记录即可。
 * Created by xjb on 2019/6/24
 */
@Component
public class SynTaskLockUtils {
     
    private static Logger logger = LoggerFactory.getLogger(SynTaskLockUtils.class);

    @Autowired
    TaskLockMapper taskLockMapper;

    //每个小时第52分钟执行一次
    @Scheduled(cron = "0 52 * * * ?")
    private void checkSynTaskKeyIsExpire() {
     
        logger.info("=========开始检查数据库分布式锁的过期时间=======");
        List<TaskLock> taskLocks = taskLockMapper.findAll();
        //筛选出过期的key
        List<TaskLock> taskLockList = taskLocks.stream().
                filter(taskLock -> new Date().getTime() - taskLock.getUtime().getTime()
                        - taskLock.getTimeout() * 1000 > 0).collect(Collectors.toList());
        taskLockList.forEach(taskLock -> {
     
            taskLockMapper.deleteByPrimaryKey(taskLock.getKey());
        });
        logger.info("=========trs_task_lock 删除{}条 过期的key=======",taskLockList.size());
    }

    /**
     * 获取分布式锁
     * 根据插入语句中的主键冲突,相同主键的多次插入操作,只会有一次成功,成功的就获得分布式锁执行任务的权利
     * @param key
     * @param ip
     * @param desc
     * @param timeout 单位秒
     * @return
     */
    public boolean tryLock(String key, String ip, String desc, int timeout) {
     
        TaskLock taskLock = new TaskLock();
        taskLock.setKey(key);
        taskLock.setTimeout(timeout);
        taskLock.setIp(ip);
        taskLock.setDescription(desc);
        int sum = 0;
        try {
     
            sum = taskLockMapper.insertSelective(taskLock);
        } catch (Exception e) {
     
            logger.info("{} 其他机器已经在执行", key);

        }
        return sum == 1;

    }

    /**
     * 删除分布式锁
     *
     * @param redisKey
     * @return
     */
    public boolean del(String redisKey) {
     
        int sum = taskLockMapper.deleteByPrimaryKey(redisKey);
        return sum == 1;
    }

    /**
     * 获得key中对象
     * @param redisKey
     * @return
     */
    public TaskLock get(String redisKey) {
     
        return taskLockMapper.selectByPrimaryKey(redisKey);
    }

    /**
     *
     * @param key
     * @param value
     * @param ip
     */
    public void set(String key, String value,String ip) {
     
        TaskLock taskLock = new TaskLock();
        taskLock.setKey(key);
        taskLock.setTimeout(-1);
        taskLock.setIp(ip);
        taskLock.setDescription(value);
        taskLockMapper.insertOrUpdate(taskLock);
    }

    public boolean hasKey(String queryEndTimeRedisKey) {
     
        return taskLockMapper.queryCount(queryEndTimeRedisKey)==1;
    }
}

TaskLock 实体类



/**
 * 分布式锁
 * 基于mysql数据库做的分布式锁 类redis的  key-value
 *
 */
public class TaskLock {
     
    /**
     * 键名  key-value的 key
     */
    private String key;
    /**
     * 过期时间 避免死锁
     */
    private Integer timeout;
    /**
     * 运行机器ip
     */
    private String ip;
    /**
     * 描述 key-value的 value
     */
    private String description;


    /**
     * 修改时间
     */
    private Date utime;

    public String getKey() {
     
        return key;
    }

    public void setKey(String key) {
     
        this.key = key == null ? null : key.trim();
    }

    public Integer getTimeout() {
     
        return timeout;
    }

    public void setTimeout(Integer timeout) {
     
        this.timeout = timeout;
    }

    public String getIp() {
     
        return ip;
    }

    public void setIp(String ip) {
     
        this.ip = ip == null ? null : ip.trim();
    }

    public String getDesc() {
     
        return description;
    }

    public void setDescription(String description) {
     
        this.description = description == null ? null : description.trim();
    }



    public Date getUtime() {
     
        return utime;
    }

    public void setUtime(Date utime) {
     
        this.utime = utime;
    }
}

TaskLockMapper.java


public interface TaskLockMapper {
     
    int deleteByPrimaryKey(String key);

    int insertSelective(TaskLock record);

    TaskLock selectByPrimaryKey(String key);

    List<TaskLock> findAll();

    int queryCount(String key);

    void insertOrUpdate(TaskLock taskLock);
}

TaskLockMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xjb.data.mapper.cluster.TaskLockMapper">
    <resultMap id="BaseResultMap" type="com.xjb.data.lock.TaskLock">
        <id column="key" property="key" jdbcType="VARCHAR"/>
        <result column="timeout" property="timeout" jdbcType="INTEGER"/>
        <result column="ip" property="ip" jdbcType="VARCHAR"/>
        <result column="description" property="description" jdbcType="VARCHAR"/>

        <result column="utime" property="utime" jdbcType="TIMESTAMP"/>
    </resultMap>
    <sql id="Base_Column_List">
    key, timeout, ip, description,  utime
  </sql>
    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String">
        select
        <include refid="Base_Column_List"/>
        from trs_task_lock
        where key = #{
     key,jdbcType=VARCHAR}
    </select>
    <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
    delete from trs_task_lock
    where key = #{
     key,jdbcType=VARCHAR}
  </delete>

    <insert id="insertSelective" parameterType="com.xjb.data.lock.TaskLock">
        insert into trs_task_lock
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="key != null">
                key,
            </if>
            <if test="timeout != null">
                timeout,
            </if>
            <if test="ip != null">
                ip,
            </if>
            <if test="description != null">
                description,
            </if>

            <if test="utime != null">
                utime,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="key != null">
                #{
     key,jdbcType=VARCHAR},
            </if>
            <if test="timeout != null">
                #{
     timeout,jdbcType=INTEGER},
            </if>
            <if test="ip != null">
                #{
     ip,jdbcType=VARCHAR},
            </if>
            <if test="description != null">
                #{
     description,jdbcType=VARCHAR},
            </if>

            <if test="utime != null">
                #{
     utime,jdbcType=TIMESTAMP},
            </if>
        </trim>
    </insert>

    <select id="findAll" resultMap="BaseResultMap">
        select * from trs_task_lock where timeout  <![CDATA[ > ]]>   0
    </select>

    <select id="queryCount" resultType="java.lang.Integer" parameterType="java.lang.String">
        select count(*) from trs_task_lock where key=#{
     key}
    </select>
    <insert id="insertOrUpdate">
        insert into trs_task_lock (
        key,
        timeout,
        ip,
        description
        )
        values
            (
            #{
     key,jdbcType=VARCHAR},
            #{
     timeout,jdbcType=VARCHAR},
            #{
     ip,jdbcType=VARCHAR},
            #{
     description,jdbcType=VARCHAR}
            )
        ON CONFLICT(key)
        do UPDATE
        set
        key=excluded.key,
        timeout=excluded.timeout,
        ip=excluded.ip,
        description=excluded.description
    </insert>
</mapper>

测试代码:


@Component
public class Task2 {
     
    private static Logger logger = LoggerFactory.getLogger(Task2.class);
    @Autowired
    SynTaskLockUtils synTaskLockUtils;

    String redisKey = "java-Task-isRun";

    //单位为秒  默认十分钟
    private int redis_default_expire_time = 60 * 3;


    @Scheduled(cron = "0 */1 * * * ?")
    public void run() throws InterruptedException {
     


        //-------------上分布式锁开始-----------------

        InetAddress addr = null;
        try {
     
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
     
            e.printStackTrace();
        }
        //获取本机ip
        String ip = addr.getHostAddress();
        //默认上锁时间为五小时
        //此key存放的值为任务执行的ip,
        // redis_default_expire_time 不能设置为永久,避免死锁
        boolean lock = synTaskLockUtils.tryLock(redisKey, ip, "1", redis_default_expire_time);
        logger.info("============本次聚类定时任务开始==============");
        if (lock) {
     
            logger.info("============获得分布式锁成功=======================");
            //TODO 开始执行任务 执行结束后需要释放锁
            ccc();
            synTaskLockUtils.del(redisKey);
            logger.info("============释放分布式锁成功=======================");

        } else {
     
            logger.info("============获得分布式锁失败=======================");
            ip =  synTaskLockUtils.get(redisKey).getIp();
            logger.info("============{}机器上占用分布式锁,聚类任务正在执行=======================", ip);
            logger.info("============本次聚类定时任务结束==============");
            return;
        }
    }

    public void ccc() throws InterruptedException {
     

        System.out.println("执行中");
        Thread.sleep(1000 * 60 * 2);
        System.out.println("执行结束");

    }
}

你可能感兴趣的:(mysql,分布式,java,分布式锁,乐观锁,数据库)