我的编程空间,编程开发者的网络收藏夹
学习永远不晚

深入详解java高并发热点数据更新

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

深入详解java高并发热点数据更新

mysql update的时候到底是锁行还是锁表?

InnoDb 锁简单分类

按照数据操作的粒度

1)行级锁:锁住记录行

2)表级锁:锁住整张表

按照对数据操作的类型

1)读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。  

 2) 写锁(排它锁):当前操作没有完成之前,它会阻断其他写锁和读锁。

mysql 在update时会根据where 条件的类型决定锁行还是锁表 where的过滤条件列,如果用索引,锁行,无法用索引,锁表。按照索引规则,如果能使用索引,锁行,不能使用索引,锁表。 行锁是排他锁,当一条记录已经被一条update语句锁住时会阻断其他的update操作,在高并发场景下,对于热点数据来说会进行频繁的更新操作造成其他update操作锁等待超时请求失败

背景

以旅游支付场景为例,伴随着业务量的增加,系统的并发量会逐渐上升,例如“北京长城度假区”的账户流水会变得十分频繁,每次支付或者退款操作都需要去更新一下账户余额,并发较低时并不会有什么问题,但当旅游高峰期到来时并发量上升,数据库更新的时候需要获得数据行锁,在未释放这个行锁之前,其他事务只能是等待。

解决方案

1.支付时异步入账,退款增加一个欠款垫资户

用户支付入款需要给账户加钱时此时商户对于资金的实时性要求不高,追求准确性,因此可以将账户加款放到异步线程池,达到错峰的目的 然而当用户发起退款时,我们必须及时并且准确的从账户扣款,因此退款采取同步进行,退款的订单相对于支付来说量就会少很多,满足要求。但是存在一个问题高并发状态下某一个热点账户余额时刻在变很有可能退款发起时账户余额充足但是实际扣除时由于上一笔支付未入账,造成金额不足

1.加分布式锁

对特定账户加锁,保证某一刻只有一笔退款请求获得该账户的操作权 弊端:多个用时同时退款时只有一笔成功,对用户不友好,pass掉

2.新增垫资商户

热点账户增加一个指定透支额度的垫资户,实际账户余额不足时从垫资户借款,然后定期核对垫资户透支额度从实际账户一次性扣款, 推荐

2.合并请求

合并多条需要更新余额的请求

将一段时间内的请求,先进行阻塞,合并各个账户需要更新的金额,一次性处理,然后将结果拆分,唤醒被阻塞的请求

demo实现

 * @Author: xiaokunkun
 * @CreateTime: 2023-04-23  14:37
 * @Description: 合并更新,可以不捕捉异常报错后外层调用方直接捕获异常事务回滚
 */
@Service
public class CommodityAmountService {
    class Request {
        AcctUpdateDto acctUpdateDto;
        //预留字段 可不使用
        String atomCode;
        //暂定返回结果为true或者false
        CompletableFuture<Boolean> future; // 接受结果
    }
 
    // 积攒请求(每隔N毫秒批量处理一次)
    LinkedBlockingQueue<Request> queue = new LinkedBlockingQueue<>();
 
    // 定时任务的实现,N秒钟处理一次数据
    @PostConstruct
    public void init() {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            // 1、取出queue的请求,生成一次合并更新
            int size = queue.size();
            if (size == 0) {
                return;
            }
            ArrayList<Request> requests = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                //队列出栈
                Request request = queue.poll();
                requests.add(request);
            }
            System.out.println("批量处理数据量:" + size);
            // 2、组装一个合并更新 key为账户value为sum(amount)
            Map<String, Long> amountMap = new HashMap<>();
            ArrayList<String> commodityCodes = new ArrayList<>();
            for (Request request : requests) {
                //todo 根据accountNo分组
            }
            for (String key : amountMap.keySet()) {
                Long amount = amountMap.get(key);
                //update mysql
            }
            // 3、将结果响应 分发给每一个单独的用户请求。由定时任务处理线程 --> n个用户的请求线程
            for (Request request : requests) {
                // 将结果返回到对应的请求线程,只要不报错此批次全部返回true,否则false
                request.future.complete(true);
            }}, 0, 1000, TimeUnit.MILLISECONDS);
    }
 
    @Autowired
    CommodityRemoteService commodityRemoteService;
 
    // 合并金额并更新,多个用户请求
    public Boolean updateMergeAmount(String movieCode)
            throws ExecutionException, InterruptedException {
        // 并非立刻发起接口调用,请求收集起来,再进行
        Request request = new Request();
        request.atomCode = movieCode;
        // 异步编程:获取异步处理的结果
        CompletableFuture<Boolean> future = new CompletableFuture<>();
        request.future = future;
        queue.add(request);
        return future.get(); // 此处get方法,会阻塞线程运行,直到future有返回
    }
}

测试类:

//模拟500的并发量
public void updateMerge() {
    AcctCmdDriver acctCmdDriver = new AcctCmdDriver();
    TradeAccntOrderDetail detail = new TradeAccntOrderDetail();
    AcctNoInfo acctNoInfo = new AcctNoInfo();
    OrderConsist consistForOrder = OrderConsist.newInstance("0200_202304", "trade_accnt_merchant_order");
    detail.setConsistForOrder(consistForOrder);
    acctCmdDriver.setDetail(detail);
    acctCmdDriver.setAcctNoInfo(acctNoInfo);
    detail.setAccountCategory(AccountCategoryEnum.MERCHANT);
    detail.setAccountNo("02020001010000262977202304");
    detail.setAmount(6l);
    System.out.println("start build thread" + acctCmdDriver);
    Random rand = new Random();
    for (int i = 1; i <= 500; i++) {
        final String index = "code_" + i;
        Thread thread = new Thread(() -> {
            try {
                System.out.println("amount is:" + detail.getAmount());
                countDownLatch.await();
                Thread.sleep(rand.nextInt(150));
                Boolean res = updateMergeAmountService.mergeUpdate(acctCmdDriver);
                System.out.println("current i" + index + "res:" + res);
            } catch (InterruptedException e) {
                System.out.println("thread error is:" + e);
            }
        });
        thread.start();
        // 启动后,倒计时器倒计数减一,代表又有一个线程就绪了
        countDownLatch.countDown();
    }
}

以上就是深入详解java高并发热点数据更新的详细内容,更多关于java高并发热点数据更新的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

深入详解java高并发热点数据更新

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

深入详解java高并发热点数据更新

这篇文章主要为大家深入介绍了java高并发热点数据更新详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-18

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录