实现一百万条数据的状态秒级更新!

84 阅读3分钟

点击上方“程序员蜗牛g”,选择“设为星标”跟蜗牛哥一起,每天进步一点点程序员蜗牛g大厂程序员一枚 跟蜗牛一起 每天进步一点点31篇原创内容**公众号为了提高订单状态更新的效率和系统的响应能力,我们决定采用分布式任务调度实现高效的订单状态批量更新。

应用场景

数据批处理

  • 批量数据导入/导出:将大量数据从一个系统迁移到另一个系统时,可以将数据分成多个批次进行处理。
  • 日志处理:分析和处理大量的日志文件,每个分片处理一部分日志。

实时监控与报警

  • 监控指标收集:实时收集和处理监控指标,每个分片负责收集一部分系统的监控数据。
  • 报警规则评估:评估报警规则,每个分片处理一部分报警条件。

任务分片的目的

图片

如何进行任务分片?

图片

代码实操

    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- MySQL Connector -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- ElasticJob Lite Core -->
    <dependency>
        <groupId>com.dangdang</groupId>
        <artifactId>elastic-job-lite-core</artifactId>
        <version>2.1.5</version>
    </dependency>

    <!-- ElasticJob Lite Spring Boot Starter -->
    <dependency>
        <groupId>com.dangdang</groupId>
        <artifactId>elastic-job-lite-spring-boot-starter</artifactId>
        <version>2.1.5</version>
    </dependency>

    <!-- Zookeeper Client -->
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>4.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>4.2.0</version>
    </dependency>

application.yml

server:
  port:8080

spring:
datasource:
    url:jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
    username:root
    password:123456
    driver-class-name:com.mysql.cj.jdbc.Driver

jpa:
    hibernate:
      ddl-auto:update
    show-sql:true
    properties:
      hibernate:
        dialect:org.hibernate.dialect.MySQL5InnoDBDialect

elasticjob:
regCenter:
    serverLists:localhost:2181# ZooKeeper服务器地址
    namespace:elastic-job-demo   # 命名空间
jobs:
    orderStatusUpdateJob:
      cron: 0 0 * * * ?     # Cron表达式,每小时执行一次
      shardingTotalCount:5   # 分片总数
      jobClass:com.example.job.OrderStatusUpdateJob# 任务类全限定名
      description:"更新订单状态"# 任务描述

Application.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling  // 启用定时任务调度
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

订单状态更新任务

package com.example.job;

import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.simple.api.SimpleJob;
import com.example.service.OrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 订单状态更新任务类
 * 实现SimpleJob接口,用于定期更新订单状态
 */
@Component
publicclass OrderStatusUpdateJob implements SimpleJob {

    privatestaticfinal Logger log = LoggerFactory.getLogger(OrderStatusUpdateJob.class);

    @Autowired
    private OrderService orderService;  // 注入OrderService服务

    /**
     * 执行任务的方法
     * @param context 分片上下文
     */
    @Override
    public void execute(ShardingContext context) {
        int shardingItem = context.getShardingItem();  // 获取当前分片项
        int shardingTotalCount = context.getShardingTotalCount();  // 获取总分片数
        long maxOrderId = orderService.getMaxOrderId();  // 获取最大订单ID

        // 计算当前分片需要处理的订单范围
        long startId = (long) shardingItem * (maxOrderId / shardingTotalCount);
        long endId = Math.min(startId + (maxOrderId / shardingTotalCount), maxOrderId);

        log.info("Processing orders from {} to {}", startId, endId);  // 记录处理的订单范围
        int updatedCount = orderService.updateStatusInRange(startId, endId, "Processed");  // 更新订单状态

        log.info("Updated {} orders in range {} to {}", updatedCount, startId, endId);  // 记录更新结果
    }
}

订单实体类

package com.example.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 * 订单实体类
 * 映射到数据库中的order表
 */
@Entity
publicclass Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;  // 订单ID

    private String status;  // 订单状态

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
}

订单Repository

package com.example.repository;

import com.example.model.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 订单Repository接口
 * 提供基本的CRUD操作和自定义查询
 */
publicinterface OrderRepository extends JpaRepository<Order, Long> {

    /**
     * 根据ID范围查找订单
     * @param startId 开始ID
     * @param endId 结束ID
     * @return 订单列表
     */
    List<Order> findByIdBetween(Long startId, Long endId);

    /**
     * 根据ID范围更新订单状态
     * @param startId 开始ID
     * @param endId 结束ID
     * @param newStatus 新状态
     * @return 更新的订单数量
     */
    @Modifying
    @Transactional
    @Query("UPDATE Order o SET o.status = :newStatus WHERE o.id BETWEEN :startId AND :endId")
    int updateStatusInRange(@Param("startId") Long startId, @Param("endId") Long endId, @Param("newStatus") String newStatus);
}

订单服务类

package com.example.service;

import com.example.model.Order;
import com.example.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 订单服务类
 * 处理订单相关的业务逻辑
 */
@Service
publicclass OrderService {

    @Autowired
    private OrderRepository orderRepository;  // 注入OrderRepository

    /**
     * 根据ID范围获取订单
     * @param startId 开始ID
     * @param endId 结束ID
     * @return 订单列表
     */
    public List<Order> getOrdersByRange(Long startId, Long endId) {
        return orderRepository.findByIdBetween(startId, endId);
    }

    /**
     * 根据ID范围更新订单状态
     * @param startId 开始ID
     * @param endId 结束ID
     * @param newStatus 新状态
     * @return 更新的订单数量
     */
    public int updateStatusInRange(Long startId, Long endId, String newStatus) {
        return orderRepository.updateStatusInRange(startId, endId, newStatus);
    }

    /**
     * 获取最大订单ID
     * @return 最大订单ID
     */
    public long getMaxOrderId() {
        return orderRepository.findAll().stream()
                .mapToLong(Order::getId)
                .max()
                .orElse(0L);
    }
}

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!