IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 并发数据问题 -> 正文阅读

[大数据]并发数据问题

并发数据问题

技术是解决问题慢慢出现的,不是凭空设计的。

幂等

定义:

接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的 ;

幂等的实现:

  • 数据库UK天然实现,插入时考虑同一个uk时的告警处理,更新时可以使用数据库乐观锁,加version;
  • redis实现,指令setnx;

模型示例:

请添加图片描述

正常创单模型

  • 用户创单生成订单落库,此时订单状态位待支付
  • 用户支付成功,更改订单状态已支付,履约接单
  • 物理域操作拣货-代发货,出库-已发货,货到达快递点-待收货
  • 用户收获,交易完成

涉及三方模型

上述创单成功,现在和三方和作,比如现在比较多的店长团长端需要看到用户信息,则订单支付成功后,需要同步调用店长端应用,进行部分可见订单信息的处理,示意如下;

店长域展示

请添加图片描述

本次设计只是做幂等方面的展示,模型仅仅示例,主要是店长域的订单插入和更新做幂等校验。我们要考虑的核心点是:分布式情况下,接口调用多次,怎么保证插入到店长订单DB的数据不会重复

表设计

表名:leader_order

uk:order_id

名称 类型 可空 注释
id bigint N
gmt_creat datetime N 创建时间
gmt_modify datetime N 修改时间
order_id varchar(32) N 订单号,年月日+redis生成的数字,从1开始,不够了补位,24位
order_status int N 订单状态,0-已支付,10-代发货,20-已发货,30-待取货,40-交易完后,80-交易关闭
order_amount varchar(32) N 支付金额
version int N 版本号,默认0

表详细字段示例:

请添加图片描述

链路示意

请添加图片描述

更改和插入链路一样,不做赘述

代码展示

数据库层面实现

插入

无uk插入

数据库设计不做订单号唯一uk情况;是会插入相同的数据的;

service

public void insertOrder(OrderStatusRequest request) {
        // 模拟并发线程切换
        System.out.println(Thread.currentThread().getName() + "开始休眠," + "时间:" + new Date());
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LeaderOrderVO leaderOrderVO = LeaderOrderConvert.convert2LeaderOrderVO(request);
        leaderOrderDAO.insertOrder(leaderOrderVO);
    }

sql

<insert id="insertOrder" parameterType="com.blabla.dao.vo.LeaderOrderVO">
        insert into leader_order
        values (null,now(),now(),#{orderId},#{orderStatus},#{orderAmount},0);
    </insert>

代码日志示例
请添加图片描述

数据库插入数据

请添加图片描述

增加团长订单表订单id为唯一uk,其实唯一uk已经作了天然幂等;

uk插入

uk示例:
请添加图片描述

调用示例:

请添加图片描述

这里两种方案:

  • try-catch掉主键冲突,sql不变,java代码![请添加图片描述](https://img-blog.csdnimg.cn/6b9557faffe9411aa16aae0771fffecf.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-W5ZCN6L-Y6KaB5oOz5Y2K5aSp,size_20,color_FFFFFF,t_70,g_se,x_16加try-catch
    public void insertOrder(OrderStatusRequest request) {
    // 模拟并发线程切换
    System.out.println(Thread.currentThread().getName() + “开始休眠,” + “时间:” + new Date());
    try {
    Thread.sleep(1000 * 10);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    LeaderOrderVO leaderOrderVO = LeaderOrderConvert.convert2LeaderOrderVO(request);
    try {
    leaderOrderDAO.insertOrder(leaderOrderVO);
    } catch (DuplicateKeyException e) {
    System.out.println(“主键冲突:” + e.getMessage());
    }
    }
    效果:
    请添加图片描述

  • 插入sql加ignore,进行去重;运行不报错。

    insert ignore into leader_order
    values (null,now(),now(),#{orderId},#{orderStatus},#{orderAmount},0);

    • 插入sql,存在就更新,但是对于订单来说,我们希望保存第一手的数据,这里只做sql示意
      insert into leader_order value(“xx”,“xx”) ON DUPLICATE KEY UPDATE

更新

数据库乐观锁实现,主要是version关键子

public void updateOrderStatusByOrderId(OrderStatusRequest request) {
        Integer version = leaderOrderDAO.getVersionByOrderId(request.getOrderId());
        LeaderOrderVO leaderOrderVO = LeaderOrderConvert.convert2LeaderOrderVO(request);
        leaderOrderVO.setVersion(version);
        System.out.println("获取数据库version:" + version);
        // 模拟并发线程切换
        System.out.println(Thread.currentThread().getName() + "开始休眠," + "时间:" + new Date());
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        leaderOrderDAO.updateOrderStatus(leaderOrderVO);
    }

<update id="updateOrderStatus" parameterType="com.blabla.dao.vo.LeaderOrderVO">
        update leader_order
        set
        gmt_modify = now()
        <!--<if test ="LeaderOrderVO.getOrderStatus != null">
            ,order_status = #{orderStatus}
        </if>
        <if test ="LeaderOrderVO.getOrderAmount != null">
            ,order_amount = #{orderAmount}
        </if>-->
        ,version = version+1
        where
        order_id = #{orderId} and
        version = #{version}
    </update>

redis实现

setnx 以插入为例

public void insertOrder(OrderStatusRequest request) {
        // 模拟并发线程切换
        System.out.println(Thread.currentThread().getName() + "开始休眠," + "时间:" + new Date());
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LeaderOrderVO leaderOrderVO = LeaderOrderConvert.convert2LeaderOrderVO(request);
        if (redisUtil.setnxExpire(request.getOrderId(), request.getOrderStatus(), 60L)) {
            System.out.println(Thread.currentThread().getName() + "准备执行插入数据库");
            leaderOrderDAO.insertOrder(leaderOrderVO);
        } else {
            System.out.println(Thread.currentThread().getName() + "未执行插入数据库");
        }
    }

package com.blabla.utils;

import com.sun.org.apache.regexp.internal.RE;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author yzw
 * @date 2022/1/23 16:25
 * @desc redis工具类
 */
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate redisTemplate;

    public boolean set(final Object key, Object value){
        if(null == key){
            return true;
        }
        redisTemplate.opsForValue().set(key, value);
        return true;
    }

    public synchronized boolean setnx(final Object key, Object value){
        if(null == key){
            return true;
        }
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    public boolean setExpire(final Object key, Object value, Long seconds){
        if(null == key){
            return true;
        }
        redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
        return true;
    }

    public synchronized boolean setnxExpire(final Object key, Object value, Long seconds){
        if(null == key){
            return true;
        }
        return redisTemplate.opsForValue().setIfAbsent(key, value, seconds, TimeUnit.SECONDS);
    }
}


<insert id="insertOrder" parameterType="com.blabla.dao.vo.LeaderOrderVO">
        insert into leader_order
        values (null,now(),now(),#{orderId},#{orderStatus},#{orderAmount},0);
    </insert>

运行结果:
请添加图片描述

更新其实原理一样,主要是setnx的原子操作,考虑好缓存的key 和value 以及过期时间的设置;

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-13 21:52:47  更:2022-03-13 21:56:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:00:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码