基于Redis的分布式锁

  • Redis复习
  • 分布式锁的实现流程和原理
  • 基于Redis实战实现分布式锁
  • 数据库设计



除了之前我们数据层面的锁,业界上还包括基于Redis的原子操作实现分布式锁。以及Zoopkeeper的临时节点,和Watcher机制实现分布式锁。


本章将讲述Redis的原子性操作实现分布式锁

Redis复习

在之前抢红包的模块中,其实setnx就是原子性操作,并且它是以key-value存储的数据结构,具有丰富的数据类型,并且应用场景非常适合高并发

基于redis分布式锁 基于redis的分布式锁_spring

分布式锁的实现流程和原理

基于redis分布式锁 基于redis的分布式锁_基于redis分布式锁_02


上述得出结论3个核心

1.第一个核心是静心设计一个跟业务相关的key,用于充当setnx的命令

2.第二个核心是采用setnx 和 expire命令,用于获取对共享资源的锁

3.操作完成后释放该锁

基于Redis实战实现分布式锁

在前面章节中,我们用的是抢红包,本章节我们采用用户注册,流程如下

1.后端首先判断用户名是否被注册,没有注册就插入到用户注册表,并返回注册,如果注册了就返回注册失败

基于redis分布式锁 基于redis的分布式锁_分布式锁_03


高并发产生的问题

如果用户疯狂点击注册,那么就有可能生成两个一样用户名的用户,下面实战演示这个场景

数据库设计

CREATE TABLE user_reg (
 id int(11) NOT NULL AUTO_INCREMENT,
 user_name varchar(255) NOT NULL COMMENT '用户名',
 password varchar(255) NOT NULL COMMENT '密码',
 create_time datetime DEFAULT NULL COMMENT '创建时间',
 PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户注册信息表';
package com.learn.boot.controller;

import com.learn.boot.dto.UserAccountDto;
import com.learn.boot.dto.UserLoginDto;
import com.learn.boot.mapper.redis.user.UserRegMapper;
import com.learn.boot.model.UserAccount;
import com.learn.boot.model.UserAccountRecord;
import com.learn.boot.model.UserReg;
import com.learn.boot.resultVo.ResultVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.Date;

/**
 * 用户注册
 */
@RestController
public class LoginController {

    //定义Redis的操作组件实例
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Autowired
    private UserRegMapper userRegMapper;
    @RequestMapping("/login")
    public ResultVo login(@RequestBody UserLoginDto model, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultVo.error("参数异常");
        }

        // 判断用户名是否被注册过
        UserReg userReg = userRegMapper.selectByUserName(model.getUserName());
        if (userReg == null) {
            UserReg insertUserReg = new UserReg(){
                {
                    setCreateTime(new Date());
                    setPassword(model.getPassword());
                    setUserName(model.getUserName());
                }
            };
            userRegMapper.insertSelective(insertUserReg);
        }
        return ResultVo.error("提现异常");
    }

}

发现高并发下出现问题

基于redis分布式锁 基于redis的分布式锁_Redis_04

@Autowired
    private UserRegMapper userRegMapper;
    @RequestMapping("/login")
    public ResultVo login(@RequestBody UserLoginDto model, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultVo.error("参数异常");
        }

        // 先加分布式锁
        StringBuffer sb = new StringBuffer(model.getUserName());
        sb.append("_lock");
        String value = "11";
        // 获取操作Key的ValueOperations实例
        ValueOperations valueOperations=stringRedisTemplate.opsForValue();
        Boolean res = (Boolean) valueOperations.setIfAbsent(sb.toString(),value);
        // 具体应根据实际情况而定
        stringRedisTemplate.expire(sb.toString(),2L, TimeUnit.SECONDS);
        // 判断用户名是否被注册过
        if (!res) {
            log.info("{}注册失败",model.getUserName());
            return ResultVo.error("用户信息已存在");
        }
        UserReg userReg = userRegMapper.selectByUserName(model.getUserName());
        if (userReg == null) {
            UserReg insertUserReg = new UserReg(){
                {
                    setCreateTime(new Date());
                    setPassword(model.getPassword());
                    setUserName(model.getUserName());
                }
            };
            userRegMapper.insertSelective(insertUserReg);
            log.info("{}注册成功",model.getUserName());
            // 执行完业务记得删除
            stringRedisTemplate.delete(sb.toString());
            return ResultVo.success("注册成功");
        }
        return null;
    }

基于redis分布式锁 基于redis的分布式锁_Redis_05


基于redis分布式锁 基于redis的分布式锁_基于redis分布式锁_06