⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/m0_57647880/article/details/131064291 「薄荷蓝柠」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

业务需求

今天去面试时hr问了个关于大量数据查询的问题。

❝

面试官:“我们公司是做数据分析的,每次需要从数据库中查询100万条数据进行分析,不能用分页,请问怎么优化sql或者java代码呢??”

❞

如果用普通查询需要5分多分钟才查询完毕,所以我们用索引加多线程来实现。

那我们就开始吧!GO!!GO!!

数据库设计

编写数据库字段

然后要生成100万条数据

在数据库添加索引

索引这个方面我还是不太了解,大家懂的可以优化索引

代码实现

java编写

controller类编写

package com.neu.controller;

import com.neu.mapper.UserMapper;
import com.neu.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* 用户查询多线程用户Controller
* @author 薄荷蓝柠
* @since 2023/6/6
*/
@Controller
public class ExecutorUtils {

@Resource
private UserMapper userMapper;


// 一个线程最大处理数据量
private static final int THREAD_COUNT_SIZE = 5000;



@RequestMapping("Executor")
public List<User> executeThreadPool() {

//计算表总数
Integer integer = userMapper.UserSum();

//记录开始时间
long start = System.currentTimeMillis();


//new个和表总数一样长的ArrayList
List<User> threadList=new ArrayList<>(integer);

// 线程数,以5000条数据为一个线程,总数据大小除以5000,再加1
int round = integer / THREAD_COUNT_SIZE + 1;

//new一个临时储存List的Map,以线程名为k,用做list排序
Map<Integer,ArrayList> temporaryMap = new HashMap<>(round);

// 程序计数器
final CountDownLatch count = new CountDownLatch(round);

// 创建线程
ExecutorService executor = Executors.newFixedThreadPool(round);

// 分配数据
for (int i = 0; i < round; i++) {
//该线程的查询开始值
int startLen = i * THREAD_COUNT_SIZE;
int k = i + 1;
executor.execute(new Runnable() {
@Override
public void run() {
ArrayList<User> users = userMapper.subList(startLen);
//把查出来的List放进临时Map
temporaryMap.put(k,users);
System.out.println("正在处理线程【" + k + "】的数据,数据大小为:" + users.size());
// 计数器 -1(唤醒阻塞线程)
count.countDown();
}
});
}
try {
// 阻塞线程(主线程等待所有子线程 一起执行业务)
count.await();
//结束时间
long end = System.currentTimeMillis();
System.out.println("100万数据查询耗时:" + (end - start) + "ms");
//通过循环遍历临时map,把map的值有序的放进List里
temporaryMap.keySet().forEach(k->{
threadList.addAll(temporaryMap.get(k));
});
} catch (Exception e) {
e.printStackTrace();
} finally {
//清除临时map,释放内存
temporaryMap.clear();
// 终止线程池
// 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。若已经关闭,则调用没有其他作用。
executor.shutdown();
}
//输出list的长度
System.out.println("list长度为:"+threadList.size());
return threadList;
}
}

编写Mapper

package com.neu.mapper;

import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.annotations.*;

import com.neu.pojo.User;

/**
* 用户查询多线程用户Controller
* @author 薄荷蓝柠
* @since 2023/6/6
*/
@Mapper
public interface UserMapper {

/**
* 检索user表的长度
* @return 表长度
*/
@Select("SELECT count(*) as sum FROM sysuser")
Integer UserSum();

/**
* 检索user表的所有记录
* @return 所有记录信息
*/
@Select("select * from sysuser LIMIT #{startLen},5000")
ArrayList<User> subList(@Param("startLen") int startLen);
}

编写完成后我们测试一波–>

测试结果20秒内,比之前快了好多

模糊查询

模糊查询呢?

咱测试一下:

修改Mapper

package com.neu.mapper;

import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.annotations.*;

import com.neu.pojo.User;

/**
* 用户查询多线程用户Controller
* @author 薄荷蓝柠
* @since 2023/6/6
*/
@Mapper
public interface UserMapper {


/**
* 检索user表id包含有“0”的长度
* @return 表长度
*/
@Select("SELECT count(*) as sum FROM sysuser where id like concat('%',0,'%')")
Integer UserSum();

/**
* 检索user表id包含有“0”的所有记录
* @return 所有记录信息
*/
@Select("select * from sysuser where id like concat('%',0,'%') LIMIT #{startLen},5000")
ArrayList<User> subList(@Param("startLen") int startLen);
}

修改完成后我们再测试一波–>

耗时5秒左右,可以满足业务需求

结束

目前基本的查询已经写完

看到这个文章的还可以对以下方面进行优化:

  • 索引进行优化。
  • 每个线程查询多少条数据最为合适??
  • 如果配置有线程池可以使用:总条数/线程数==每个线程需要查询多少条数据。
  • 进行代码优化,优化一些耗时的代码。
文章目录
  1. 1. 业务需求
  2. 2. 数据库设计
    1. 2.0.1. 编写数据库字段
  • 3. 代码实现
    1. 3.0.1. java编写
    2. 3.0.2. 模糊查询
  • 4. 结束