基于Redis的分布式锁
- Redis复习
- 分布式锁的实现流程和原理
- 基于Redis实战实现分布式锁
- 数据库设计
除了之前我们数据层面的锁,业界上还包括基于Redis的原子操作实现分布式锁。以及Zoopkeeper的临时节点,和Watcher机制实现分布式锁。
本章将讲述Redis的原子性操作实现分布式锁
Redis复习
在之前抢红包的模块中,其实setnx就是原子性操作,并且它是以key-value存储的数据结构,具有丰富的数据类型,并且应用场景非常适合高并发
分布式锁的实现流程和原理
上述得出结论3个核心
1.第一个核心是静心设计一个跟业务相关的key,用于充当setnx的命令
2.第二个核心是采用setnx 和 expire命令,用于获取对共享资源的锁
3.操作完成后释放该锁
基于Redis实战实现分布式锁
在前面章节中,我们用的是抢红包,本章节我们采用用户注册,流程如下
1.后端首先判断用户名是否被注册,没有注册就插入到用户注册表,并返回注册,如果注册了就返回注册失败
高并发产生的问题
如果用户疯狂点击注册,那么就有可能生成两个一样用户名的用户,下面实战演示这个场景
数据库设计
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("提现异常");
}
}
发现高并发下出现问题
@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;
}