基于redis的普通分布式锁

1、业界有哪些主流的分布式锁实现方案?

目前主流的有三种,如下:

- 基于数据库实现

基于数据库来做分布式锁的话,通常是采用数据库的乐观锁或悲观锁来实现。

- 基于ZooKeeper实现

基于ZooKeeper,是采用它的临时有序节点来实现的分布式锁。

- 基于Redis实现

基于Redis实现的锁机制,主要是依赖redis自身的原子来实现

以上三种方式都可以实现分布式锁,如果并发量不大的话,直接采用数据库就可以,如果高并发的话,就要考虑zookeeper或redis,但从高并发高性能角度考虑,基于 Redis 实现性能会更好;所以如何选择,还是取决于业务需求。

2、下面介绍基于redis的普通分布式锁

SET key value [NX] [XX] [EX <seconds>] [PX [millseconds]] 

以上set 代替了 setnx +expire 需要分2次执行命令操作的方式,保证了原子性

127.0.0.1:6379> set lock true NX px 600000

OK

127.0.0.1:6379> set lock true NX px 600000

(nil)

 

如果setnx 返回ok 说明拿到了锁

如果setnx 返回 nil,说明拿锁失败,被其他线程占用。

3、案例实战:基于redis的分布式锁实现下订单防止重复提交

@PostMapping(value = "/createOrder", produces = APPLICATION_JSON_UTF8_VALUE, consumes = APPLICATION_JSON_UTF8_VALUE)
public String createOrder(@RequestBody  OrderDTO obj) {

    //步骤1:先转换为唯一MD5
    String json=JsonUtil.object2Json(obj);
    String md5 = DigestUtils.md5DigestAsHex(json.getBytes()).toUpperCase();

    //步骤2:把md5设置为分布式锁的key
    /**
     * setIfAbsent 的作用就相当于 SET key value [NX] [XX] [EX <seconds>] [PX [millseconds]]
     * 设置过期
     */
    Boolean bo=stringRedisTemplate.opsForValue().setIfAbsent(md5,"1",60, TimeUnit.SECONDS);
    if(bo){
        // 加锁成功
        log.debug("{}拿锁成功,开始处理业务",md5);
        try {
            //模拟10秒 业务处理
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //不要删除,万一业务处理时间很短,页面上点了两次,点第二次的时候,第一次已经处理完了,这样就同一张单子处理了两次,还是等他过期比较好
        //stringRedisTemplate.delete(md5);
        log.debug("{}拿锁成功,结束处理业务",md5);
        return "ok";
    }else{
        log.debug("{}拿锁失败",md5);
        //拿不锁,直接退出
        return  "请不要重复点击!";
    }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章