定位线上同步锁仍然重复扣费的Bug定位及Redis分布式锁解决方案

在实际生产环境中,处理订单的并发请求时,我们经常会遇到重复扣费的问题。本文将通过一个具体的代码示例,分析在使用同步锁时仍然导致重复扣费的原因,并提供一个基于Redis分布式锁的解决方案。

背景:这个案例出现在商家在小程序端接单重复扣费,PC端也能接单,并且PC端和小程序端不是一套代码,但是接单的代码几乎一致

一、问题描述

在以下代码中,OrderServiceImpl 类使用了 Java 的同步锁来保证对订单状态变更的操作是线程安全的:

public class OrderServiceImpl {
    public Operating orderStateChange(OrderStateReq orderStateReq) {
        synchronized (OrderServiceImpl.class) {
            //订单id
            Integer orderId = orderStateReq.getOrderId();
            //根据订单id查看订单是否满足扣费  不满足则抛异常  满足则扣费
        }
    }  
}

二、问题分析

尽管我们在 orderStateChange 方法中使用了同步锁,但仍然可能导致重复扣费的问题,原因有以下几点:

锁粒度过大:synchronized (OrderServiceImpl.class) 锁定的是整个类,这样虽然可以避免多个线程同时进入临界区,但在分布式环境下,这种锁机制无法跨JVM工作。

锁的范围有限:Java 的 synchronized 锁仅在单个 JVM 中有效,如果你的应用程序部署在多台服务器上,每个服务器上的 JVM 都会有自己的锁,这就无法避免分布式环境下的并发问题。

业务逻辑不完善:即使在单机环境中,锁住整个类也会导致性能瓶颈,因为所有订单处理请求都必须排队进入同步块,无法充分利用多线程的优势。

三、解决方案

为了解决上述问题,我们可以使用 Redis 分布式锁。Redis 分布式锁的特点是可以跨多个 JVM 保证唯一性,从而避免分布式环境下的并发问题。

1. 引入 Redis 依赖
首先,在你的项目中引入 Redis 相关依赖(以 Spring Boot 为例):

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.16.2</version>
        </dependency>

2. 实现 Redis 分布式锁
然后,我们实现一个简单的 Redis 分布式锁机制。可以使用 Redisson 库,这个库封装了 Redis 锁的实现,使用起来非常方便。

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;

public class RedisLockUtil {
    private static RedissonClient redissonClient;
	// 有指定库和密码也需要赋值
    static {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        config.useSingleServer().setPassword("redisPassword");
        config.useSingleServer().setDatabase("database");
        redissonClient = Redisson.create(config);
    }

    public static RLock getLock(String lockKey) {
        return redissonClient.getLock(lockKey);
    }
}

3. 使用 Redis 分布式锁
在 OrderServiceImpl 中使用 Redis 分布式锁来实现订单状态变更操作:

import org.redisson.api.RLock;

public class OrderServiceImpl {
    public Operating orderStateChange(OrderStateReq orderStateReq) {
        String lockKey = "orderLock:" + orderStateReq.getOrderId();
        RLock lock = RedisLockUtil.getLock(lockKey);
        try {
            // 尝试加锁,等待时间为10秒,锁超时时间为30秒
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                try {
                    //订单id
                    Integer orderId = orderStateReq.getOrderId();
                    //根据订单id查看订单是否满足扣费  不满足则抛异常  满足则扣费
                } finally {
                    lock.unlock();
                }
            } else {
                // 获取锁失败,处理逻辑
                throw new RuntimeException("获取锁失败,请稍后再试");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("线程中断", e);
        }
    }
}

在使用了分布式锁后上线一周内让DB再查看已经没有了重复扣费现象

四、总结

通过以上步骤,我们可以解决同步锁在分布式环境下无法避免重复扣费的问题。使用 Redis 分布式锁,不仅能在多台服务器上保证锁的唯一性,还能提高系统的并发处理能力,避免性能瓶颈。

希望本文对你在解决分布式系统中的并发问题有所帮助,如果有任何问题或建议,欢迎交流讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/774986.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通信安全员考试精选练习题库,2024年备考必刷题!

16.设计单位必须在设计文件中&#xff08;&#xff09;计列安全生产费。 A.全额 B.部分 C.按建设单位要求 D.按工程建设需要 答案&#xff1a;A 17.日最高气温达到&#xff08;&#xff09;℃以上&#xff0c;应当停止当日室外露天作业。 A.38 B.36 C.35 D.40 答案&…

2024年智慧教育与社会科学国际会议 (ICSSS 2024)

2024年智慧教育与社会科学国际会议 (ICSSS 2024) 2024 International Conference on Smart Education and Social Sciences 【重要信息】 大会地点&#xff1a;北京 大会官网&#xff1a;http://www.icicsss.com 投稿邮箱&#xff1a;icicssssub-conf.com 【注意&#xff1a;稿…

达梦数据库的系统视图v$auditrecords

达梦数据库的系统视图v$auditrecords 在达梦数据库&#xff08;DM Database&#xff09;中&#xff0c;V$AUDITRECORDS 是专门用来存储和查询数据库审计记录的重要系统视图。这个视图提供了对所有审计事件的访问权限&#xff0c;包括操作类型、操作用户、时间戳、目标对象等信…

2024年07月03日 Redis部署方式和持久化

Redis持久化方式&#xff1a;RDB和AOF&#xff0c;和混合式 RDB&#xff1a;周期备份模式&#xff0c;每隔一段时间备份一份快照文件&#xff0c;从主线程Fork一个备份线程出来备份&#xff0c;缺点是会造成数据的丢失。 AOF&#xff1a;日志模式&#xff0c;每条命令都以操作…

【docker nvidia/cuda】ubuntu20.04安装docker踩坑记录

docker nvidia 1.遇到这个错误&#xff0c;直接上魔法(科学上网) OpenSSL SSL_connect: Could not connect to nvidia.github.io:443 这个error是运行 NVIDIA官方docker安装教程 第一个 curl 命令是遇到的 2. apt-get 更新 sudo apt update遇到 error https://download.do…

kylin arm xcb版本异常问题解决

源码编译qt 未生成xcb库&#xff0c;查看源码xcb readme.txt 提示 版本要求 下载 [ANNOUNCE] libxcb 1.14 [ANNOUNCE] xcb-proto 1.14 解压源码编译, 先编译xcb-proto sudo ./configure --prefix/usr/local/xcb-proto make make install 在编译xcb export PKG_CONFIG_PATH…

解决uni-app中全局设置页面背景颜色只有部分显示颜色的问题

在页面的style标签设置了背景色但是只显示一部分 <style lang="scss"> .content{background-color: #f7f7f7;height: 100vh; } </style>我们在app.vue里设置就行了 注意一定要是**page{}** <style>/*每个页面公共css */page{background-color:

劲爆!华为享界两款新车曝光,等等党有福了

文 | AUTO芯球 作者 | 雷慢 劲爆啊&#xff0c;北汽的一份环境影响分析报告&#xff0c; 不仅曝光了享界S9的生产进展&#xff0c; 还泄露了自家的另两款产品&#xff0c; 第一款是和享界S9同尺寸的旅行车&#xff0c; 我一看&#xff0c;这不是我最喜欢的“瓦罐”吗&…

【吊打面试官系列-MyBatis面试题】Xml 映射文件中,除了常见的 select|insert|updae|delete标签之外,还有哪些标签?

大家好&#xff0c;我是锋哥。今天分享关于 【Xml 映射文件中&#xff0c;除了常见的 select|insert|updae|delete标签之外&#xff0c;还有哪些标签&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Xml 映射文件中&#xff0c;除了常见的 select|insert|updae|…

江汉大学刘春萌同学整理的wifi模块 上传mqtt实验步骤

一.固件烧录 1.打开安信可官网 2.点击wifi模组系列的ESP8266 3.点击各类固件后选择固件号1471下载 4.打开烧录工具将下载的二进制文件导入并将后面的起始地址写为0x00000,下面勾选40mhz QIO 8Mbit点击start下载即可 二.本地部署mqtt服务器(windows) 1.下载mosquitto后有一个m…

从零开始学量化~Ptrade使用教程(三)——行情界面主要功能

技术分析 除复权 提供向前复权、向后复权&#xff0c;系统默认不复权。此外&#xff0c;全面支持月、周、日线复权&#xff0c;支持向前和向后复权、不同时段分段复权等功能。系统能够根据盘中即时行情&#xff0c;个股K线图可以根据该股除权日的送股、配股及红利情况圆滑地画出…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【生成密钥(C/C++)】

生成密钥(C/C) 以生成ECC密钥为例&#xff0c;生成随机密钥。具体的场景介绍及支持的算法规格。 注意&#xff1a; 密钥别名中禁止包含个人数据等敏感信息。 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复…

Stream的获取、中间方法、终结方法

1、获取Stream流 单列集合&#xff1a;foreach完整版 双列集合通过Ketset()、entryset() 数组的&#xff1a;通过Arrays Stream流的中间方法&#xff1a;链式编程&#xff0c;原stream流只能使用一次 filter&#xff1a; limit、skip&#xff1a; distinct(有自定义对象需要重写…

window.ai 开启你的内置AI之旅

❝ 成功是得你所想&#xff0c;幸福是享你所得 大家好&#xff0c;我是柒八九。一个专注于前端开发技术/Rust及AI应用知识分享的Coder ❝ 此篇文章所涉及到的技术有 AI( Gemini Nano) Chrome Ollama 因为&#xff0c;行文字数所限&#xff0c;有些概念可能会一带而过亦或者提供…

让你的 Rabbids Rockstar人物化身加入欢乐行列!

让你的 Rabbids Rockstar 人物化身加入欢乐行列&#xff01; https://www.youtube.com/watch?vwLBd20BxbS8 当这些调皮的小兔子以狂野的装扮、超棒的吉他弹奏和搞笑滑稽的动作登上舞台中央时&#xff0c;你将感受到它们异想天开的魅力。通过人物化身释放你内心的摇滚明星魅力&…

2024年7月3日 (周三) 叶子游戏新闻

老板键工具来唤去: 它可以为常用程序自定义快捷键&#xff0c;实现一键唤起、一键隐藏的 Windows 工具&#xff0c;并且支持窗口动态绑定快捷键&#xff08;无需设置自动实现&#xff09;。 卸载工具 HiBitUninstaller: Windows上的软件卸载工具 《魅魔》新DLC《Elysian Fields…

使用Spring Boot生成并下载包含下拉多选功能的Excel模板

步骤 1: 引入依赖 在pom.xml文件中添加EasyExcel <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.2.1</version></dependency> 步骤2: 准备xlsm模板 首先&#xff0c;需要在Exc…

案例精选 | 聚铭综合日志分析系统为江苏省电子口岸构建高效安全的贸易生态

江苏省电子口岸有限公司&#xff0c;成立于2009年&#xff0c;由江苏省贸促会携手南京海关、江苏检验检疫局及江苏海事局等部门共同出资组建。公司承载着推动江苏乃至长三角地区国际贸易便利化的重大使命&#xff0c;致力于打造一个集先进性、创新性、高效性于一体的电子口岸综…

关于 VuePress 的插件

插件就好比第三方功能&#xff0c;例如增加一个阅读进度条、增加光标效果等。VuePress 官网对插件的介绍&#xff1a;插件通常会为 VuePress 添加全局功能。 这里简单介绍几个本站用的插件吧&#xff01; ‍ ‍ 插件就好比第三方功能&#xff0c;例如增加一个阅读进度条、增…