Java数据库分表与多线程查询结果汇总
今天接到一个需求:要对一个物理分表的逻辑表进行查询统计。而数据库用的是公司自己研发的产品,考虑的到公司产品的特点以及业务的需求,该逻辑表是按年月进行分表的,而非分区。我们来看一下,在按时间段进行查询统计的时候,会有哪些问题:
- 需要对多个表查询,且表个数不确定
- 时间跨度越大,查询等等表个数越多,对应查询时间也会越长
如何解决?一起来看一下
分表与分区
目的
既然谈到数据的分表与分区,那我们来简单了解一下。先说一下分表与分区的目的。我们日常开发中都会经常遇到百万或千万级的数据大表,这些表数据量大,数据增速快,不用太久就会造成在查询或修改数据库数据的时候造成性能低下的问题,联合查询的时候,情况可能更糟。一次有必要对原来的表进行改造设计。这时候数据库分区和分表技术就应运而生了
区别
分表
分表是将一个大表按照一定的规则分解成多张子表,而各个子表存储空间彼此独立。
分区
分区也是按照一定的规则进行数据划分,对各部分数据各自存储,但在处理逻辑上,散列存放的数据还是属于同一张大表。
依赖于数据库实现,对程序屏蔽,减轻程序员编程压力
分表逻辑下的多线程查询与数据汇总
回到文首提到的情况,当前的情况是分表,分表的划分依据是根据年月划分,一个月一张表。意味着当我们要统计跨多个隔离单位的数据进行统计时,要自己去实现的对分散在多个表中数据的查询汇总处理。
通常表名会带有划分依据的信息,比如按年月划分,表名格式一般为
TABLE_NAME_YYYYMM
确定数据表
当前的需求是对一段时间内的数据进行统计,时间单位精确到月份。一次当我们根据服务入参拿到开始月份和结束月份后,要先得到所有涉及的月份。我们可以计算出将所有月份并保存在一个List中,方便我们查询各个表时进行表名的拼接。代码实现如下
/**
* 获取时间段内所有月份集合
* @param beginMonth 开始年月
* @param endMonth 结束年月
**/
private List<String> getMonths(String beginMonth,String endMonth){
List<String> result = new ArrayList<>();
Date beginDate = DateUtils.getDate("yyyyMM",beginMonth);
Date endDate = DateUtils.getDate("yyyyMM",endMonth);
if (beginDate.after(endDate)) {
throw new BusiException("时间入参非法");
}
result.add(beginMonth);
Calendar cal = Calendar.getInstance();
Date originalDate = beginDate;
while (endDate.after(originalDate)) {
cal.setTime(originalDate);
cal.add(Calendar.MONTH, 1);
originalDate = cal.getTime();
result.add(DateUtils.getFormatDate(originalDate).substring(0,6));
}
return result;
}
确认线程个数
拿到所有月份后,进行分多线程处理的操作,增加单位时间内查询表的个数,以此缩短查询时间,通常我们都利用线程池来进行多线程操作。这里会涉及线程池大小的考虑问题,可以参考以下博文:计算线程池最佳线程数。我们姑且用CPU复杂型公式进行计算
int cpuNums = Runtime.getRuntime().availableProcessors() + 1;
均匀分配数据
确定好线程的大小,我们还要考虑一个问题,那就是我们如何为一个线程均匀地分配数据的处理量,在当前的需求下,就是如何均匀地为每个线程分配对应处理的月份,可以参考以下代码:
/**
* 平衡分组算法 - 已知分配份数
* @param sourceList 数据源
* @param groupNum 被非配份数
**/
public static <T> List<T>[] spiltDataList(List<T> sourceList,int groupNum){
List<T> [] group = new List[groupNum];
/* 初始化数组 */
for (int i = 0 ; i < groupNum ; i++) {
group[i] = new ArrayList<>();
}
int sourceSize = sourceList.size();
int batchNum = sourceSize % groupNum == 0 ? sourceSize / groupNum : sourceSize / groupNum + 1;
for (int i = 1; i <= batchNum ; i++){
if (i == batchNum){
int finalBatchNum = sourceSize - (i - 1) * groupNum;
for (int j = 0 ; j < finalBatchNum ; j++){
group[j].add(sourceList.get((i - 1) * groupNum + j));
}
}else {
for (int j = 0 ; j < groupNum ; j++){
group[j].add(sourceList.get((i - 1) * groupNum + j));
}
}
}
return group;
}
多线程实现
要对所有子线程进行汇总,就必须使用Callable和Future的方式来实现多线程,我们就可以拿到每个子线程的查询返回,进而汇总分析处理。关于多线程实现方式,可以参考Java多线程事务管理中对多线程实现方式的介绍
以下为核心代码实现
/**
* 银行扣费查询 - 多线程方案
* @param qryType 查询类型
* @param qryValue 查询值
* @param payType 扣费类型
* @param beginMonth 开始年月
* @param endMonth 结束年月
**/
public List<CollInfoQueryBo> collInfoQueryByThread(
String qryType,Long qryValue,String payType,String beginMonth,String endMonth)
throws InterruptedException,ExecutionException{
List<CollInfoQueryBo> collInfoQueryBos = new ArrayList<>();
List<String> months = getMonths(beginMonth,endMonth);
int cpuNums = Runtime.getRuntime().availableProcessors() + 1;
int totalNum = months.size();
int threadNum;
if (totalNum < cpuNums){
threadNum = totalNum;
}else {
threadNum = cpuNums;
}
/* 分线程处理 */
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(threadNum);
CountDownLatch endLock = new CountDownLatch(threadNum);
BlockingQueue<Future<List<CollInfoQueryBo>>> queue = new LinkedBlockingQueue<>();
List<String>[] stringList = spiltDataList(months,threadNum);
for (List<String> monthList : stringList) {
Future<List<CollInfoQueryBo>> future = fixedThreadPool.submit(new Callable<List<CollInfoQueryBo>>() {
@Override
public List<CollInfoQueryBo> call() throws Exception {
List<CollInfoQueryBo> collInfoQueryBoList = getAllMonthResult(monthList,qryType,qryValue);
endLock.countDown();
return collInfoQueryBoList;
}
});
queue.add(future);
}
endLock.await();
/* 汇总结果 */
for(Future<List<CollInfoQueryBo>> future : queue) {
List<CollInfoQueryBo> currentThreadList = future.get();
collInfoQueryBos.addAll(currentThreadList);
}
fixedThreadPool.shutdown(); //关闭线程池
return collInfoQueryBos;
}
Java数据库分表与多线程查询结果汇总的更多相关文章
- MySQL订单分库分表多维度查询
转自:http://blog.itpub.net/29254281/viewspace-2086198/ MySQL订单分库分表多维度查询 MySQL分库分表,一般只能按照一个维度进行查询. 以订单 ...
- MySQL数据库分表的3种方法
原文地址:MySQL数据库分表的3种方法作者:dreamboycx 一,先说一下为什么要分表 当一张的数据达到几百万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目 ...
- zabbix 数据库分表操作
近期zabbix数据库占用的io高,在页面查看图形很慢,而且数据表已经很大,将采用把数据库的数据目录移到新的磁盘,将几个大表进行分表操作 一.数据迁移: 1.数据同步到新的磁盘上,先停止mysql(不 ...
- mysql 数据库 分表后 怎么进行分页查询?Mysql分库分表方案?
Mysql分库分表方案 1.为什么要分表: 当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了.分表的目的就在于此,减小数据库的负担,缩短查询时间. m ...
- Oracle亿级数据查询处理(数据库分表、分区实战)
大数据量的查询,不仅查询速度非常慢,而且还会导致数据库经常宕机(刚接到这个项目时候,数据库经常宕机o(╯□╰)o). 那么,如何处理上亿级的数据量呢?如何从数据库经常宕机到上亿数据秒查?仅以此篇文章作 ...
- 数据库分表分区后的ID生成之雪花生成
转自https://www.cnblogs.com/jajian/p/11101213.html 传统的单体架构的时候,我们基本是单库然后业务单表的结构.每个业务表的ID一般我们都是从1增,通过AUT ...
- Hibernate与数据库分表
数据库分片(shard)是一种在数据库的某些表变得特别大的时候采用的一种技术. 通过按照一定的维度将表切分,可以使该表在常用的检索中保持较高的效率,而那些不常用的记录则保存在低访问表中.比如:销售记录 ...
- 一致性Hash算法在数据库分表中的实践
最近有一个项目,其中某个功能单表数据在可预估的未来达到了亿级,初步估算在90亿左右.与同事详细讨论后,决定采用一致性Hash算法来完成数据库的自动扩容和数据迁移.整个程序细节由我同事完成,我只是将其理 ...
- 数据库分表之Mybatis+Mysql实践(含部分关键代码)
2018年01月31日 随着我们系统用户数量的日增,业务数据处于一个爆发前,增长的数据量已经给我们的系统造成了很大的不确定.在上个周末用户量较多,并发较大的情况下,读写频繁的验证码表,数据量 ...
随机推荐
- 2021年Wordpress博客搭建
2021年WordPress博客搭建教程 这是一篇关于2021最新版的WP个人博客搭建教程.整篇文章会事无巨细的一步步讲述搭建博客的每一步. 0.前言 随着互联网和移动互联网的飞速发展,博客这一功能恍 ...
- 实验6、Flask API使用示例和拓展
实验介绍 1. 实验内容 Flask 提供了多种API拓展,本节我们主要学习基于RESTful的Flask应用程序设计 2. 实验要点 学习和掌握多种RESTful的设计模式 3.实验环境 Cento ...
- MySQL分页查询limit踩坑记
1 问题背景 线上有一个批处理任务,会批量读取昨日的数据,经过一系列加工后,插入到今日的表中.表结构如下: 1 CREATE TABLE `detail_yyyyMMdd` ( 2 `id` bigi ...
- beego搭建api服务
beego介绍 beego是一个Golang实现的开源Go应用开发框架,他可以用来快速开发 API.Web 及后端服务等各种应用,是一个 RESTful的框架,主要设计灵感来源于tornado.sin ...
- 熬夜总结vue3中setUp函数的2个参数详解
1.setUp函数的第1个参数props setup(props,context){} 第一个参数props: props是一个对象,包含父组件传递给子组件的所有数据. 在子组件中使用props进行接 ...
- Nexus3搭建Docker等私服
0.目的 docker私有仓库的搭建,方便后期的CI/CD dotnetcore项目sdk本地缓存,解决微软官方下载缓慢的问题 nuget私有仓库 等 1.环境情况 windows10 Docker ...
- 1、linux集群服务器规划
- vue cli3 使用elemet-plus
原文章: https://blog.csdn.net/qq_44827865/article/details/115457445 element-plus官方网站:https://element-pl ...
- Series 1 java秒组合数
Series 1 举几个例子发现, 系数中间对称,很容易想到组合数 c(n,m)==c[n,n-m).此题就是高精度求组合数,java秒之. time:1825ms ,接近时限,如果n还稍微大一 ...
- 重新整理 .net core 实践篇————缓存相关[四十二]
前言 简单整理一下缓存. 正文 缓存是什么? 缓存是计算结果的"临时"存储和重复使用 缓存本质是用空间换取时间 缓存的场景: 计算结果,如:反射对象缓存 请求结果,如:DNS 缓存 ...