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日 随着我们系统用户数量的日增,业务数据处于一个爆发前,增长的数据量已经给我们的系统造成了很大的不确定.在上个周末用户量较多,并发较大的情况下,读写频繁的验证码表,数据量 ...
随机推荐
- 09:jQuery(02)
内容概要 jQuery操作标签 jQuery绑定事件 jQuery补充知识点 jQuery动画效果(了解) 零散补充 内容详细 jQuery练习题 $('#i1') r.fn.init [div#i1 ...
- css基本内容笔记(学习整理)
一.css简介 1.什么是css 层叠样式表. 层叠:层层叠加,若果有冲突应用优先级高,不冲突的部分共同作用 样式表:就是css属性样式的集合: 2.作用 a.修饰html,使得html样式更好看 b ...
- 终于放弃了单调的swagger-ui了,选择了这款神器—knife4j
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案(在非Java项目中也提供了前端UI的增强解决方案),前身是swagger-bootstrap-ui,取名knife ...
- 十七、.net core(.NET 6)搭建基于Quartz组件的定时调度任务
搭建基于Quartz组件的定时调度任务 先在package包项目下,添加Quartz定时器组件: 新建类库项目Wsk.Core.QuartzNet,并且引用包类库项目.然后新建一个中间调度类,叫Qu ...
- Oracle数据库delete删除普通堆表千万条记录
Oracle数据库delete删除普通堆表千万条历史记录. 直接删除的影响: 1.可能由于undo表空间不足从而导致最终删除失败的问题: 2.可能导致undo表空间过度使用,影响到其他用户正常操作. ...
- ANDROID开发 Fatal signal 11(SIGSEGV) at 0x问题解决方案
最近做ANDROID开发,也遇到了很多程序员遇到的一个问题:FATAL SIGNAL 11(SIGSEGV) at 0xxxxx,自然是各种搜索是否有人已然解决,虽然搜索出来的已有案例不少,基本都是内 ...
- win10下简单截图
win10 下面可以 win+shift+s 拖动截图,个人感觉是最简单的
- 20204107 孙嘉临《Python程序设计》实验一报告
课程:<python程序设计> 班级:2041 姓名:孙嘉临 学号:20204107 实验教师:王志强 实验日期:2021年4月12日 必修/选修:公选课 ##一.实验内容 1.熟悉Pyt ...
- Pandas高级教程之:plot画图详解
目录 简介 基础画图 其他图像 bar stacked bar barh Histograms box Area Scatter Hexagonal bin Pie 在画图中处理NaN数据 其他作图工 ...
- hdu 1556 Color the ball 线段树 区间更新
水一下 #include <bits/stdc++.h> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 ...