项目背景

  在这次影像系统中,我们利用大数据平台做的是文件(图片、视频等)批次的增删改查,每个批次都包含多个文件,上传完成以后要添加文件索引(文件信息及批次信息),由于在Hbase存储的过程中,每个文件都对应一个文件rowKey,一个批次就会有很多个RoweKey,查询的下载的时候就必须根据每个文件的rowkey找到对应的文件,如果一个批次有很多个文件的话,就需要查找很多次,这样是很浪费时间的,一开始没注意这么多,开发并且完成功能测试后,觉得一切OK,但是作为大数据后台,对效率的要求非常高,在压力测试的时候出现了问题,并发量上来之后,查询下载的速度非常慢,TPS总上不去,仔细分析代码后,发现了问题。

改进之前的部分代码如下:

public List<FileInfo> batchGetFileMeta (String systemType, String batchNo,

                         String fileName,String versionNo,BufferedOutputStream bw) {

                 List<FileInfo> fileInfoList = new ArrayList<FileInfo>();
FileStoreInfo fileStoreInfo = batchGetFileStoreInfo(systemType,batchNo, versionNo,bw);
if(fileStoreInfo == null){
return null;
}
List<String> fileNameList=batchGetFileNameByBathNO(systemType,batchNo, fileName,
versionNo,bw);
if(fileNameList == null || fileNameList.size()==0 ){
return null;
}
if(fileNameList.size()==1 && ("".equals(fileNameList.get(0)))){
fileInfoList.add(null);
return fileInfoList;
}
int hash = batchNo.hashCode();
String rowKey = "";
String fileNName = fileStoreInfo.getFile_N_Name();
String[] fileNNameArray = fileNName.split(Constants.SPLIT);
for(int i=0;i<fileNNameArray.length;i++){
for(int j=0;j<fileNameList.size();j++){
String[] fileNInfo = fileNNameArray[i].split(Constants.SPLITF);
if(fileNInfo[0].equals(fileNameList.get(j))){
String version = fileNInfo[1];
String versionNow = version;
if(versionNow != null && !versionNow.equals("")){
int length2 = versionNow.length();
for (int k=0 ;k<3-length2 ;k++) {
versionNow = "0"+versionNow;
}
}
rowKey = hash + "1" +batchNo + versionNow + fileNInfo[0];
FileInfo fileInfo = batchGetFileMetaByIndex(systemType, rowKey, bw);
if(fileInfo == null){
return null;
}
fileInfo.setFileVersionNO(version);
fileInfoList.add(fileInfo);
}
}
}
return fileInfoList;
}
public FileInfo batchGetFileMetaByIndex(String systemType, String rowKey,
BufferedOutputStream bw) {
Map<String,String> fileInfoMaps = new HashMap<String,String>();
fileInfoMaps = HbaseUtil.queryBykey(Constants.HBASE_TAB+systemType, rowKey,
Constants.HBASE_FAMILYY_CF1, Constants.HBASE_COLUMN_L);
if(fileInfoMaps == null ){
return null;
}
String fileInfoStr = fileInfoMaps.get("value");
FileInfo fileInfo = new FileInfo();
fileInfo = (FileInfo) Utils.jsonToObj(fileInfoStr,fileInfo);
if(fileInfo == null){
return null;
}
String userdefinede = getUserDefinedE(systemType, rowKey);
fileInfo.setUserDefined(userdefinede);
return fileInfo;
}
public Map<String,String > queryBykey(String tableName, String rowKey,String fam, String col) {
Map<String, String> result = new HashMap<String, String>();
HTable table=null;
try {
if(isExistTable(tableName)){
table = new HTable(conf, tableName);
Get scan = new Get(rowKey.getBytes());
Result r = table.get(scan);
byte[] bs = r.getValue(Bytes.toBytes(fam), Bytes.toBytes(col));
String value = Bytes.toString(bs);
result.put("value", value);
table.close();
return result;
}else{
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}

测试结果如下:

  虽然时间比较少,但是远远不能满足效率要求。仔细分析上面代码不难发现:由于业务需要查询数据的时候要校验文件信息,所以代码中出现了循环套循环的情况,如果某批次的文件数量特别多的话那么循环查询的次数的增长不是一个数量级的,相当大的一个数字,问题的原因在于拼接rowkey,然后拿着rowkey去查询,循环多少次就查多少次,虽然Hbase查询速度快,但这样也是在浪费时间,经过思考和研究HbaseAPI的时候发现,Hbase支持rowkey批量查询,思路大概是这样的:

1)  循环文件信息,循环之中得到拼接rowkey的信息

2)  把得到的rowkey放入list中

3)  循环完毕,用List去查Hbase,将得到的信息放入Map返回

4)  获取Map中的信息

下面是改进之后的代码:

改进后:

public List<FileInfo> batchGetFileMetaByBathNo(String systemType, String batchNo,
String fileName,String versionNo,BufferedOutputStream bw) {
List<FileInfo> fileInfoList = new ArrayList<FileInfo>();
FileStoreInfo fileStoreInfo =batchGetFileStoreInfo(systemType, batchNo, versionNo,bw);
if(fileStoreInfo == null){
return null;
}
List<String>fileNameList=batchGetFileNameByBathNO(systemType, batchNo, fileName,
versionNo,bw);
if(fileNameList == null || fileNameList.size()==0 ){
return null;
}
if(fileNameList.size()==1 && ("".equals(fileNameList.get(0)))){
fileInfoList.add(null);
return fileInfoList;
}
String rowKey = "";
List<String> rowkeylist=new ArrayList<>(); String fileNName = fileStoreInfo.getFile_N_Name();
String[] fileNNameArray = fileNName.split(Constants.SPLIT);
for(int i=0;i<fileNNameArray.length;i++){
for(int j=0;j<fileNameList.size();j++){
String[] fileNInfo = fileNNameArray[i].split(Constants.SPLITF);
if(fileNInfo[0].equals(fileNameList.get(j))){
String version = fileNInfo[1];
String versionNow = version;
if(versionNow != null && !versionNow.equals("")){
versionNow=PublicMethod.chengeVerNo(versionNow);
}
rowKey = batchNo.hashCode()+"1"+batchNo+ versionNow + fileNInfo[0];
rowkeylist.add(rowKey);
}
}
}
fileInfoList = batchGetFileMetaByIndex(systemType, rowkeylist, bw);
return fileInfoList;
} public List<FileInfo> batchGetFileMetaByIndex(String systemType, List<String> rowKey,
BufferedOutputStream bw) {
List<FileInfo> fileInfoList=new ArrayList<>();
List<Map<String,String>>list=HbaseUtil.queryByList(Constants.HBASE_TAB+systemType,
rowKey);
if(list.size()==0){
return null;
}
for(Map<String,String> resultMap:list){
FileInfo fileInfo = new FileInfo();
String LValue = resultMap.get(Constants.HBASE_COLUMN_L);
String EValue=resultMap.get(Constants.HBASE_COLUMN_E);
if(!"".equals(LValue)&&null!=LValue){
fileInfo = (FileInfo) Utils.jsonToObj(LValue,fileInfo);
}
if(!"".equals(EValue)&&null!=EValue&&fileInfo!=null){
fileInfo.setUserDefined(EValue);
}
fileInfoList.add(fileInfo);
}
return fileInfoList;
} public List<Map<String, String>> queryByList(String tableName,List<String> rowKeyList){ Connection connection=null;
List<Map<String, String>> list=new ArrayList<>();
List<Get> getList=new ArrayList<Get>();
try {
connection=ConnectionFactory.createConnection(conf);
Table table=connection.getTable(TableName.valueOf(tableName));
for(String rowKey:rowKeyList){
Get get=new Get(Bytes.toBytes(rowKey));
get.addFamily(Bytes.toBytes(Constants.HBASE_FAMILYY_CF1));
getList.add(get);
} Result[]results=table.get(getList);
for (Result result:results) {
Map<String, String> listMap=new HashMap<>();
for(Cell kv:result.rawCells()){
if(Bytes.toString(kv.getQualifier()).equals(Constants.HBASE_COLUMN_E)){
listMap.put(Constants.HBASE_COLUMN_E,
Bytes.toString(CellUtil.cloneValue(kv)));
}else{
listMap.put(Constants.HBASE_COLUMN_L,
Bytes.toString(CellUtil.cloneValue(kv)));
}
}
list.add(listMap);
}
} catch (IOException e) {
e.fillInStackTrace();
}finally{
try {
if(connection!=null&&!connection.isClosed()){
connection.close();
}
} catch (IOException e) {
e.fillInStackTrace();
}
}
return list;
}

  Hbase是支持批量查询的,经过改进之后,从代码中就可以看出,效率提升了很多,我们对10000条数据进行了测试,发现提升的效率非常明显,下面是测试图:

  进过优化后,从时间上,我们可以看到,提升的效率非常明显,这就告诉我们在做项目写代码的时候,不要只局限于功能的实现,还要考虑效率上的可行性,从一开始就要做好铺垫,否则到后期再改是非常麻烦的。

记一次Hbase查询速度优化经历的更多相关文章

  1. mysql索引原理及查询速度优化

    一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句 ...

  2. HBase查询速度慢原因排查

    问题:通过HBase访问服务在HBase中查询 ASSET_NORMAL 表速度很慢 如下,查询一条数据需要2.970s时间: 如下,统计总条数需要14.675s时间: HBase访问服务部署了3个节 ...

  3. SAP内表查询速度优化实例-OPEN SQL

    一.FOR ALL ENTRIES IN 案例 今天碰到工单报工统计分析表查询速度特别慢 经查看源代码: SELECT afpo~dwerk afko~aufnr afpo~matnr AS plnb ...

  4. 记一次真实的webpack优化经历

    前言 公司目前现有的一款产品是使用vue v2.0框架实现的,配套的打包工具为webpack v3.0.整个项目大概有80多个vue文件,也算不上什么大型项目. 只不过每次头疼的就是打包所耗费的时间平 ...

  5. 记一次有惊无险的 JVM 优化经历

    转载:https://my.oschina.net/u/3627055/blog/2995973 背景 生产环境有二台阿里云服务器,均为同一时期购买的,CPU.内存.硬盘等配置相同.具体配置如下: 节 ...

  6. 提高查询速度:SQL Server数据库优化方案

    查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 ...

  7. 优化SQLServer数据库加快查询速度

    查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 ...

  8. SQL Server数据库 优化查询速度

    查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 ...

  9. Sql server2005 优化查询速度50个方法小结

    Sql server2005 优化查询速度50个方法小结   Sql server2005优化查询速度51法查询速度慢的原因很多,常见如下几种,大家可以参考下.   I/O吞吐量小,形成了瓶颈效应.  ...

随机推荐

  1. Golang 探索对Goroutine的控制方法

    前言 在golang中,只需要在函数调用前加上关键字go即可创建一个并发任务单元,而这个新建的任务会被放入队列中,等待调度器安排.相比系统的MB级别线程栈,goroutine的自定义栈只有2KB,这使 ...

  2. Scala入门系列(十):函数式编程之集合操作

    1. Scala的集合体系结构 Scala中的集合体系主要包括(结构跟Java相似): Iterable(所有集合trait的根trait) Seq(Range.ArrayBuffer.List等) ...

  3. linux工作中遇到的问题总结---更新中

    今天这个,严格来说不算一篇文章,我只想把我工作中遇到的问题分享给大家,让大家少犯错误. 1.安装rmp包时出现错误 :header V3 DSA signature: nokey ,key id .. ...

  4. Python2/3中的urllib库

    urllib库对照速查表 Python2.X Python3.X urllib urllib.request, urllib.error, urllib.parse urllib2 urllib.re ...

  5. 使用js做创建图片及删除图片 若有什么不对或不完整的地方,请大家提出来,谢谢

    首先我们要在<body>中创建一个按钮<button>来用作点击创建图片,在<button>中写一个点击事件(随便命名), 在创建一个<div>存放图片 ...

  6. Java面试题集合(比较实用)

    1.Java集合框架是什么?说出一些集合框架的优点? 每种编程语言中都有集合,最初的Java版本包含几种集合类:Vector.Stack.HashTable和Array.随着集合的广泛使用...... ...

  7. archsummit2017见闻和思考

    前几天参加了archsummit的北京站.2天的日程安排的十分紧凑,大多数时间同时有多场专题分享,选择想要听的专题就成了首要的事情,按照感兴趣的,可能用到的,启发思考的原则选择了几场适合自己的专题,这 ...

  8. 来腾讯云开发者实验室 学习.NET Core 2.0

    腾讯云开发者实验室为开发者提供了一个零门槛的在线实验平台,开发者实验室提供的能力: 零门槛扫码即可免费领取实验机器,支持使用自有机器参与,实验完成后支持保留实验成果: 在线 WEB IDE 支持 sh ...

  9. HTTP请求中怎样选择Get和Post方式

    在应用中最经常使用的Http请求无非是get和post,get请求能够获取静态页面,也能够把參数放在URL字串后面,传递给servlet.post与get的不同之处在于post的參数不是放在URL字串 ...

  10. ThoughtWorks 2017技术雷达

    前言: ThoughtWorks人酷爱技术.我们对技术进行构建.研究. 测试.开源.记述,并始终致力于对其进行改进-以求造福 大众.我们的使命是支持卓越软件并掀起IT革命.我们创建 并分享Though ...