php网站建设系统,许昌市网站建设找汉狮,沈阳网站建设价格,辽宁工程信息参考#xff1a;Redis实现分布式锁的7种方案 - 知乎
1、 准备数据库表#xff0c;如下SQL表示库存表#xff0c;有主键ID和库存数量字段
CREATE TABLE t_stock (id bigint(20) NOT NULL AUTO_INCREMENT,quantity bigint(20) NOT NULL,PRIMARY KEY (id)
) ENGINEInnoDB DEF…参考Redis实现分布式锁的7种方案 - 知乎
1、 准备数据库表如下SQL表示库存表有主键ID和库存数量字段
CREATE TABLE t_stock (id bigint(20) NOT NULL AUTO_INCREMENT,quantity bigint(20) NOT NULL,PRIMARY KEY (id)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin;
初始数据id quantity 1111 9
2、pom.xml文件
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.6.4/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.hmblogs/groupIdartifactIdhmblogs/artifactIdversion0.0.1-SNAPSHOT/versionnamehmblogs/namedescriptionhmblogs/descriptionpropertiesjava.version8/java.versiondruid.version1.2.8/druid.versionlog4jdbc.version1.16/log4jdbc.version/propertiesdependencies!-- druid数据源驱动 --dependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion${druid.version}/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!-- mybatis --dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.3.1/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency!--Mysql依赖包--dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdscoperuntime/scope/dependency!--lombok插件--dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency!--监控sql日志--dependencygroupIdorg.bgee.log4jdbc-log4j2/groupIdartifactIdlog4jdbc-log4j2-jdbc4.1/artifactIdversion${log4jdbc.version}/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.9/version/dependencydependencygroupIdredis.clients/groupIdartifactIdjedis/artifactId/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project3、应用配置文件
server:port: 8081servlet.context-path: /#配置数据源
spring:datasource:druid:db-type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpyurl: jdbc:log4jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:eladmin}?serverTimezoneAsia/ShanghaicharacterEncodingutf8useSSLfalseusername: ${DB_USER:root}password: ${DB_PWD:123456}
4、StockMapper.xml如下
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.hmblogs.backend.dao.StockMapperresultMap idBaseResultMap typecom.hmblogs.backend.entity.Stockid columnid propertyid/result columnquantity propertyquantity//resultMapsql idBaseResultMapid, quantity/sqlselect idfindAll resultMapBaseResultMapselectinclude refidBaseResultMap/from t_stock/selectselect idfindById resultMapBaseResultMap parameterTypecom.hmblogs.backend.entity.Stockselectinclude refidBaseResultMap/from t_stockwhere id#{id}/selectupdate idupdateStockById parameterTypecom.hmblogs.backend.entity.Stockupdate t_stock set quantityquantity-1 where id#{id}/update
/mapper
5、BackendApplication
package com.hmblogs.backend;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class BackendApplication {public static void main(String[] args) {SpringApplication.run(BackendApplication.class, args);}}6、Stock.java如下
package com.hmblogs.backend.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;Data
TableName(t_stock)
public class Stock {TableId(valueid, type IdType.AUTO)private Integer id;private Integer quantity;}
7、StockMapper
package com.hmblogs.backend.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmblogs.backend.entity.Stock;
import org.apache.ibatis.annotations.Mapper;import java.util.List;Mapper
public interface StockMapper extends BaseMapperStock {ListStock findAll();Stock findById(Stock stock);Integer updateStockById(Stock stock);
}
8、OrderController
package com.hmblogs.backend.controller;import com.hmblogs.backend.dao.StockMapper;
import com.hmblogs.backend.entity.Stock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;RestController
Slf4j
public class OrderController {Autowiredprivate StockMapper stockMapper;//总库存private long nKuCuen 0;//商品key名字private String shangpingKey computer_key;//获取锁的超时时间 秒private int timeout 30 * 1000;GetMapping(/qiangdan)public ListString qiangdan() {//抢到商品的用户ListString shopUsers new ArrayList();//构造很多用户ListString users new ArrayList();IntStream.range(0, 10000).parallel().forEach(b - {users.add(神牛- b);});//初始化库存nKuCuen 10;//模拟开抢users.parallelStream().forEach(b - {String shopUser qiang(b);if (!StringUtils.isEmpty(shopUser)) {shopUsers.add(shopUser);}});return shopUsers;}/*** 模拟抢单动作** param b* return*/private String qiang(String b) {//用户开抢时间long startTime System.currentTimeMillis();//未抢到的情况下30秒内继续获取锁while ((startTime timeout) System.currentTimeMillis()) {//商品是否剩余if (nKuCuen 0) {break;}Jedis jedisCom new Jedis(localhost,6379);jedisCom.auth(heming);if (jedisCom.setnx(shangpingKey, b)1) {//用户b拿到锁log.info(用户{}拿到锁..., b);try {//商品是否剩余if (nKuCuen 0) {break;}//模拟生成订单耗时操作方便查看神牛-50 多次获取锁记录try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//抢购成功商品递减记录用户nKuCuen - 1;int id 1111;Stock stock new Stock();stock.setId(id);Stock stockDo stockMapper.findById(stock);Integer quantity stockDo.getQuantity();if(quantity!null quantity0){stockMapper.updateStockById(stock);log.info(update success.);}else{log.info(no update.);}//抢单成功跳出log.info(用户{}抢单成功跳出...所剩库存{}, b, nKuCuen);return b 抢单成功所剩库存 nKuCuen;} finally {log.info(用户{}释放锁..., b);//释放锁jedisCom.del(shangpingKey, b);}}}return ;}}9、验证
浏览器访问http://localhost:8081/qiangdan 查看Idea的console内容 将console内容拿到notepad里面搜索 但是搜索update success.内容预期是9次实际也是9次符合我的需要没有让库存变成负数 查看数据库表的库存id为1111的记录的quantity为0不是1也不是负数 10、继续第二种纬度的验证
StockController的代码如下
package com.hmblogs.backend.controller;import com.hmblogs.backend.dao.StockMapper;
import com.hmblogs.backend.entity.Stock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import java.util.Date;RestController
Slf4j
public class StockController {Autowiredprivate StockMapper stockMapper;/*** redis test* return*/GetMapping(value /reduceStock)public void redisTestLock(){log.info(reduceStock);int id 1111;String key reduceStockid;String time new Date().getTime();Jedis jedisCom new Jedis(localhost,6379);jedisCom.auth(heming);if (jedisCom.setnx(key, time)1) {log.info({}生成锁..., time);try{Stock stock new Stock();stock.setId(id);Stock stockDo stockMapper.findById(stock);Integer quantity stockDo.getQuantity();if(quantity!null quantity0){stockMapper.updateStockById(stock);log.info(update success.);}else{log.info(no update.);}} finally {log.info({}释放锁..., time);//释放锁jedisCom.del(key, time);}}}
}将库存改为10验证通过压测验证有没有保证锁应该有的作用控制查库存和当不小于0的时候减少库存这2个逻辑的原子性预期做到实际做到了。利用APIPost工具做压测如下图所示 查看server的控制台 然后把这些内容复制到notepad里面搜索
搜索reduceStock有100个结果 搜索生成锁...和释放锁...都有14次结果说明jedisCom.setnx(key, time)1成立的次数有14次。 搜索update success.有10次结果说明减少库存减少了10次 此时查看数据库表的库存发现为0 搜索no update.发现有4个结果说明有4次获得锁了但是库存已经是0了不能再减库存了库存不能为负的。