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日 随着我们系统用户数量的日增,业务数据处于一个爆发前,增长的数据量已经给我们的系统造成了很大的不确定.在上个周末用户量较多,并发较大的情况下,读写频繁的验证码表,数据量 ...
随机推荐
- 【NX二次开发】Block UI 组
设置组及组内成员不可见 this->group->GetProperties()->SetLogical("Show", false); 设置组及组内成员不可操作 ...
- 【NX二次开发】Block UI 面收集器
属性说明 属性 类型 描述 常规 BlockID String 控件ID Enable Logical 是否可操作 Group ...
- StackOverflow上面 7个最好的Java答案
StackOverflow发展到目前,已经成为了全球开发者的金矿.它能够帮助我们找到在各个领域遇到的问题的最有用的解决方案,同时我们也会从中学习到很多新的东西.这篇文章是在我们审阅了StackOver ...
- leetcode5697. 检查二进制字符串字段
5697. 检查二进制字符串字段给你一个二进制字符串 s ,该字符串 不含前导零 . 如果 s 最多包含 一个由连续的 '1' 组成的字段 ,返回 true .否则,返回 false . 示例 ...
- 3-Partition 问题
这是算法考试的最后一题,当时匆匆写了个基于 Subset Sum 的解法,也没有考虑是否可行. 问题描述如下: 给定 \(n\) 个正整数 \(a_1 \dots a_n\) ,设下标的整数集合 \( ...
- WIN10无法进行Android应用开发真机调试解决方案
在WIN10操作系统进行ANDROID开发真机调试时,遇到的问题主要归纳一下有以下几点: 一.没有打开"USB调试"项.这点不再赘述: 二.没有安装ADB Interface驱动: ...
- 注解式项目开发!详细解析Java中各个注解的作用和使用方式
@Target 作用: 指明了修饰的这个注解的使用范围, 即被描述的注解可以用在哪里 @Target(ElementType.Type) ElementType取值的类型: TYPE: 类,接口或者枚 ...
- javascript之一切都是对象
在学习的过程中,我们常常能听到这样一句话:一切皆是对象.那么这句话该如何理解呢?首先,我们要明确对象的概念.要明白除了基本数据类型都是对象. typeof操作符是大家经常使用的,我们常用它来检测给定变 ...
- excel函数提取内容中的汉字
RIGHT(A2,LENB(A2)-LEN(A2)) 函数LENB将每个汉字(双字节字符)的字符数按2计数,LEN函数则对所有的字符都按1计数.因此"LENB(A2)-LEN(A2)&quo ...
- [Django REST framework - 视图组件之视图基类、视图扩展类、视图子类、视图集]
[Django REST framework - 视图组件之视图基类.视图扩展类.视图子类.视图集] 视图继承关系 详图见文章末尾 视图组件可点我查看 两个视图基类:APIView.GenericAP ...