springboot一次性定时任务插入1000万条数据

模拟 1000 万个用户,再去查询

导入数据
  1. 用可视化界面:适合一次性导入、数据量可控

  2. 写程序:for 循环,建议分批,不要一把梭哈(可以用接口来控制)要保证可控、幂等,注意线上环境和测试环境是有区别的

    导入 1000 万条,for i 1000w

  3. 执行 SQL 语句:适用于小数据量

  4. 一次性任务main方法

  5. 定时任务

定时任务:

启动类上加注解

image-20220910155820598

插入数据

@Component
public class InsertUser {
    @Resource
    private UserMapper userMapper;

    public void doInsertUser() {
        //计时工具spring的
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        final int NUM = 1000;
        for (int i = 0; i < NUM; i++) {
            User user = new User();
            user.setUsername("假用户");
            user.setUserAccount("fake");
            user.setTags("");
            user.setAvatarUrl("https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3925/202209101442967.jpg");
            user.setGender(0);
            user.setUserPassword("123123");
            user.setEmail("213213@qq.com");
            user.setUserStatus(0);
            user.setPhone("123123");
            user.setUserRole(0);
            user.setPlanetCode("111");
            userMapper.insert(user);
        }
        stopWatch.stop();
        stopWatch.getTotalTimeMillis();
    }
}

计时工具spring的

StopWatch

怎么运行,不能main方法,会报错

因为用到了mapper,springboot没启动,注入不进来

image-20220910164851498

image-20220910164759074

指定最大值, 相当于只执行一次

在方法上加注解

 @Scheduled(initialDelay = 5000,fixedRate = Long.MAX_VALUE)

就可以只执行一次

或者写在单元测试里,但是在打包时候会执行一次单元测试,每次打包就会插入数据

package com.bo.partner.service.impl;

import com.bo.partner.mapper.UserMapper;
import com.bo.partner.model.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;

/**
* @author: bo
* @date: 2022/9/10
* @description:
*/
@SpringBootTest
public class InsertUserTest {
    @Resource
    private UserMapper userMapper;

    @Test
    public void doInsertUser() {
        //计时工具spring的
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        final int NUM = 1000;
        for (int i = 0; i < NUM; i++) {
            User user = new User();
            user.setUsername("假用户");
            user.setUserAccount("fake");
            user.setTags("");
            user.setAvatarUrl("https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3925/202209101442967.jpg");
            user.setGender(0);
            user.setUserPassword("123123");
            user.setEmail("213213@qq.com");
            user.setUserStatus(0);
            user.setPhone("123123");
            user.setUserRole(0);
            user.setPlanetCode("111");
            userMapper.insert(user);
        }
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }
}

时间主要是花费在==建立和释放数据库连接(使用批量查询可以解决)==for循环是绝对线性的(并发)

批量查询

mybatisplus的saveBatch();

        final int NUM = 1000;
        List<User> users = new ArrayList<>();

        for (int i = 0; i < NUM; i++) {
            User user = new User();
            user.setUsername("假用户");
            user.setUserAccount("fake");
            user.setTags("");
            user.setAvatarUrl("https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3925/202209101442967.jpg");
            user.setGender(0);
            user.setUserPassword("123123");
            user.setEmail("213213@qq.com");
            user.setUserStatus(0);
            user.setPhone("123123");
            user.setUserRole(0);
            user.setPlanetCode("111");
            users.add(user);
            /*userMapper.insert(user);*/

        }
        userService.saveBatch(users, 100);

再次优化

并发要注意执行的先后顺序没有影响不要用到非并发类的集合

image-20220910194630170

@Test
    public void doConcurrencyInsertUser() {
        //计时工具spring的
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        final int NUM = 100000;
        int j = 0;
        //10万分10组
        List<CompletableFuture<Void>> futureList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            List<User> users = Collections.synchronizedList(new ArrayList<>());
            while (true) {
                j++;
                User user = new User();
                user.setUsername("假用户");
                user.setUserAccount("fake");
                user.setTags("");
                user.setAvatarUrl("https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3925/202209101442967.jpg");
                user.setGender(0);
                user.setUserPassword("123123");
                user.setEmail("213213@qq.com");
                user.setUserStatus(0);
                user.setPhone("123123");
                user.setUserRole(0);
                user.setPlanetCode("111");
                users.add(user);
                if (j % 10000 == 0) {
                    break;
                }
            }
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                userService.saveBatch(users, 10000);
            });
            futureList.add(future);
        }
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[]{})).join();
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
        }

从自己的线程池取

private ExecutorService executorService = new ThreadPoolExecutor(60,1000,10000,TimeUnit.MINUTES,new ArrayBlockingQueue<>(10000));
  1. CPU密集型:分配的核心线程数=CPU - 1
  2. IO密集型:分配的核心线程数可以大于 CPU核数

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
首先,在pom.xml文件中添加Redis和MySQL的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> ``` 接着,在application.properties文件中添加Redis和MySQL的配置: ```properties # Redis配置 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= # 数据库配置 spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver ``` 然后,创建一个定时任务类,实现将Redis中的数据上传到MySQL的功能: ```java @Component public class RedisUploadTask { private static final String REDIS_KEY = "MY_KEY"; // Redis中存储数据的键名 private static final long PERIOD = 30 * 60 * 1000; // 定时任务执行的周期,这里设置为30分钟 @Autowired private StringRedisTemplate redisTemplate; @Autowired private JdbcTemplate jdbcTemplate; @Scheduled(fixedDelay = PERIOD) public void uploadRedisDataToMysql() { Set<String> dataSet = redisTemplate.opsForSet().members(REDIS_KEY); // 获取Redis中的数据集合 List<String> sqlList = new ArrayList<>(); for (String data : dataSet) { String sql = "INSERT INTO my_table (data) VALUES ('" + data + "')"; // 拼接插入语句 sqlList.add(sql); } jdbcTemplate.batchUpdate(sqlList.toArray(new String[sqlList.size()])); // 批量执行插入语句 } } ``` 在上面的代码中,我们使用了Spring Boot提供的StringRedisTemplate和JdbcTemplate来分别访问Redis和MySQL数据库。在定时任务方法中,我们首先通过redisTemplate获取Redis中存储的数据集合,然后遍历集合,将每个元素拼接成插入语句,并将这些语句添加到一个List中。最后,我们使用jdbcTemplate的batchUpdate方法批量执行这些插入语句,将数据上传到MySQL数据库中。 最后,我们需要在启动类上加上@EnableScheduling注解,启用定时任务功能: ```java @SpringBootApplication @EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 这样,当我们每隔30分钟就会执行一次RedisUploadTask中的uploadRedisDataToMysql方法,将Redis中的数据集合上传到MySQL中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值