(六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题

本文主要是介绍(六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

本节内容是关于使用分布式锁解决并发访问“超卖”问题的最终篇,在前面的章节中我们介绍了使用mysql的行锁、乐观锁、悲观锁解决并发访问导致的超卖问题,存在的问题是行锁、乐观锁、悲观锁不太灵活,需要和具体的业务耦合到一起,不利于使用,本节内容我们通过mysql的唯一索引特性,数据不能重复插入,达到独占排它的基本要求,实现通用的分布式锁功能。

正文

  • 创建表app_lock,用于实现分布式锁

- app_lock表

CREATE TABLE `app_lock` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',`lock_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '锁名称',`class_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类名',`method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '方法名称',`server_ip` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '服务器IP',`thread_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '线程名称',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`expire_time` datetime DEFAULT NULL COMMENT '过期时间',`remark` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',PRIMARY KEY (`id`),UNIQUE KEY `idx_lock_name` (`lock_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='应用锁';

PS:lock_name为锁名称,建立唯一索引,保证锁的唯一性,server_ip主机信息和thread_name线程号,可以将分布式锁扩展为可重入锁,expire_time过期时间,可通过定时任务或者消息中间键,自动释放锁。

  • 表app_lock实体类创建 
package com.ht.atp.plat.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 java.io.Serializable;
import java.time.LocalDateTime;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;@Data
@TableName("app_lock")
@ApiModel(value = "AppLock对象", description = "应用锁")
public class AppLock implements Serializable {private static final long serialVersionUID = 1L;@ApiModelProperty("ID")@TableId(value = "id", type = IdType.AUTO)private Long id;@ApiModelProperty("锁名称")@TableField("lock_name")private String lockName;@ApiModelProperty("类名")@TableField("class_name")private String className;@ApiModelProperty("方法名称")@TableField("method_name")private String methodName;@ApiModelProperty("服务器IP")@TableField("server_ip")private String serverIp;@ApiModelProperty("线程名称")@TableField("thread_name")private String threadName;@ApiModelProperty("创建时间")@TableField("create_time")private LocalDateTime createTime;@ApiModelProperty("过期时间")@TableField("expire_time")private LocalDateTime expireTime;@ApiModelProperty("备注")@TableField("remark")private String remark;}
  • 创建应用锁的加锁和解锁方法 

- 实现代码

package com.ht.atp.plat.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ht.atp.plat.entity.AppLock;
import com.ht.atp.plat.mapper.AppLockMapper;
import com.ht.atp.plat.service.AppLockService;
import org.springframework.stereotype.Service;@Service
public class AppLockServiceImpl extends ServiceImpl<AppLockMapper, AppLock> implements AppLockService {@Overridepublic void lock(AppLock appLock) {try {this.baseMapper.insert(appLock);} catch (Exception ex) {// 获取锁失败,则重试try {Thread.sleep(50);this.lock(appLock);} catch (InterruptedException e) {e.printStackTrace();}}}@Overridepublic void unlock(AppLock appLock) {this.baseMapper.deleteById(appLock.getId());}
}

- 通过自旋的方式加锁

- 解锁直接将锁删除

  • 实现扣减库存的业务代码 

- 实现代码

    @Autowiredprivate AppLockService appLockService;@Overridepublic void checkAndReduceStock() {AppLock appLock = new AppLock();appLock.setLockName("lock");appLock.setClassName(this.getClass().getName());appLock.setMethodName("checkAndReduceStock");appLock.setServerIp("127.0.0.1");appLock.setThreadName(Thread.currentThread().getName());appLock.setCreateTime(LocalDateTime.now());appLock.setExpireTime( LocalDateTime.now().plusSeconds(30));appLock.setRemark("这是一个测试用锁!");try {// 加锁appLockService.lock(appLock);// 查询库存WmsStock wmsStock = baseMapper.selectById(1L);// 验证库存大于0再扣减库存if (wmsStock != null && wmsStock.getStockQuantity() > 0) {wmsStock.setStockQuantity(wmsStock.getStockQuantity() - 1);baseMapper.updateById(wmsStock);}} catch (Exception e) {e.printStackTrace();} finally {// 解锁appLockService.unlock(appLock);}}

  • 启动7000、7001、7002服务器,并将库存设置为10000

 

  •  启动jmeter压测扣减库存接口

  • 最终压测结果

 - 库存扣减为:0

-  压测结果:平均响应时间3461ms,每秒钟的吞吐量为27,性能相比redis和zookeeper锁,性能相差不少。

  • 关于mysql分布式锁的几点总结 

- mysql数据库存在单点故障,一旦数据库宕机,将导致应用锁不可用,可通过搭建主备数据库保证高可用

- 没有实现自动释放锁:可通过定时任务扫描应用锁数据,如果过期时间小于当前时间,将锁删除掉;或者通过消息中间件去删除锁

- 锁是非重入的:可以通过使用主机信息、端口号、线程号等信息判断是否是同一个线程,如果是同一个线程,则锁可重入

- mysql数据库分布式锁性能一般,不及redis和zookeeper分布式锁,使用简单

结语

关于使用mysql分布式锁解决“超卖”问题的内容到这里就结束了,后会有期。。。。。。

这篇关于(六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/375377

相关文章

Java实战之利用POI生成Excel图表

《Java实战之利用POI生成Excel图表》ApachePOI是Java生态中处理Office文档的核心工具,这篇文章主要为大家详细介绍了如何在Excel中创建折线图,柱状图,饼图等常见图表,需要的... 目录一、环境配置与依赖管理二、数据源准备与工作表构建三、图表生成核心步骤1. 折线图(Line Ch

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

如何使用CSS3实现波浪式图片墙

《如何使用CSS3实现波浪式图片墙》:本文主要介绍了如何使用CSS3的transform属性和动画技巧实现波浪式图片墙,通过设置图片的垂直偏移量,并使用动画使其周期性地改变位置,可以创建出动态且具有波浪效果的图片墙,同时,还强调了响应式设计的重要性,以确保图片墙在不同设备上都能良好显示,详细内容请阅读本文,希望能对你有所帮助...

关于Nginx跨域问题及解决方案(CORS)

《关于Nginx跨域问题及解决方案(CORS)》文章主要介绍了跨域资源共享(CORS)机制及其在现代Web开发中的重要性,通过Nginx,可以简单地解决跨域问题,适合新手学习和应用,文章详细讲解了CO... 目录一、概述二、什么是 CORS?三、常见的跨域场景四、Nginx 如何解决 CORS 问题?五、基

MySQL zip安装包配置教程

《MySQLzip安装包配置教程》这篇文章详细介绍了如何使用zip安装包在Windows11上安装MySQL8.0,包括下载、解压、配置环境变量、初始化数据库、安装服务以及更改密码等步骤,感兴趣的朋... 目录mysql zip安装包配置教程1、下载zip安装包:2、安装2.1 解压zip包到安装目录2.2

Rust中的注释使用解读

《Rust中的注释使用解读》本文介绍了Rust中的行注释、块注释和文档注释的使用方法,通过示例展示了如何在实际代码中应用这些注释,以提高代码的可读性和可维护性... 目录Rust 中的注释使用指南1. 行注释示例:行注释2. 块注释示例:块注释3. 文档注释示例:文档注释4. 综合示例总结Rust 中的注释

python安装whl包并解决依赖关系的实现

《python安装whl包并解决依赖关系的实现》本文主要介绍了python安装whl包并解决依赖关系的实现,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录一、什么是whl文件?二、我们为什么需要使用whl文件来安装python库?三、我们应该去哪儿下

MySQL安装时initializing database失败的问题解决

《MySQL安装时initializingdatabase失败的问题解决》本文主要介绍了MySQL安装时initializingdatabase失败的问题解决,文中通过图文介绍的非常详细,对大家的学... 目录问题页面:解决方法:问题页面:解决方法:1.勾选红框中的选项:2.将下图红框中全部改为英

Linux使用cut进行文本提取的操作方法

《Linux使用cut进行文本提取的操作方法》Linux中的cut命令是一个命令行实用程序,用于从文件或标准输入中提取文本行的部分,本文给大家介绍了Linux使用cut进行文本提取的操作方法,文中有详... 目录简介基础语法常用选项范围选择示例用法-f:字段选择-d:分隔符-c:字符选择-b:字节选择--c

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图