模拟 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核数

Q.E.D.


春风亦有春风愁,不劳春风为我忧