记一次Hbase查询速度优化经历
项目背景:
在这次影像系统中,我们利用大数据平台做的是文件(图片、视频等)批次的增删改查,每个批次都包含多个文件,上传完成以后要添加文件索引(文件信息及批次信息),由于在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查询速度优化经历的更多相关文章
- mysql索引原理及查询速度优化
一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句 ...
- HBase查询速度慢原因排查
问题:通过HBase访问服务在HBase中查询 ASSET_NORMAL 表速度很慢 如下,查询一条数据需要2.970s时间: 如下,统计总条数需要14.675s时间: HBase访问服务部署了3个节 ...
- SAP内表查询速度优化实例-OPEN SQL
一.FOR ALL ENTRIES IN 案例 今天碰到工单报工统计分析表查询速度特别慢 经查看源代码: SELECT afpo~dwerk afko~aufnr afpo~matnr AS plnb ...
- 记一次真实的webpack优化经历
前言 公司目前现有的一款产品是使用vue v2.0框架实现的,配套的打包工具为webpack v3.0.整个项目大概有80多个vue文件,也算不上什么大型项目. 只不过每次头疼的就是打包所耗费的时间平 ...
- 记一次有惊无险的 JVM 优化经历
转载:https://my.oschina.net/u/3627055/blog/2995973 背景 生产环境有二台阿里云服务器,均为同一时期购买的,CPU.内存.硬盘等配置相同.具体配置如下: 节 ...
- 提高查询速度:SQL Server数据库优化方案
查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 ...
- 优化SQLServer数据库加快查询速度
查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 ...
- SQL Server数据库 优化查询速度
查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 ...
- Sql server2005 优化查询速度50个方法小结
Sql server2005 优化查询速度50个方法小结 Sql server2005优化查询速度51法查询速度慢的原因很多,常见如下几种,大家可以参考下. I/O吞吐量小,形成了瓶颈效应. ...
随机推荐
- day10、nfs+rsync全网备份及实时同步
题目要求 注意:博主使用的系统为: [root@web01 ~]# uname -a Linux web01 2.6.32-696.el6.x86_64 #1 SMP Tue Mar 21 19:29 ...
- Python Django CMDB项目实战之-1如何开启一个Django-并设置base页、index页、文章页面
1.环境 win10 python 2.7.14 django 1.8.2 需要用到的依赖包:MySQLdb(数据库的接口包).PIL/pillow(处理图片的包) 安装命令: pip install ...
- Jenkins+tomcat+jdk setup
Jenkins download: http://jenkins-ci.org/ jdk version:jdk-7u45-linux-x64.tar.gz tomcat version:apache ...
- KMP算法具体解释
这几天学习kmp算法,解决字符串的匹配问题.開始的时候都是用到BF算法,(BF(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配 ...
- 【Sqlserver系列】CAST和CONVERT
1 概述 本篇文章主要讲解SqlServer中类型转换涉及的两个函数:CAST和CONVERT. 2 具体内容 2.1 CAST (1)作用:将一种数据类型的表达式转换为另一种数据类型的表达 ...
- 前端笔记----类型转换display
display属性用来在行内元素,块元素,行内块元素之间进行转化. 常用的属性有: 1.none :元素隐藏且不占位置,相当于不存在,一般用在动态展示效果:2.block :元素以块元素显示,有些行内 ...
- cookie和session(一)
先来谈谈我对session和cookie的理解,事实上,只要你去面试web开发,面试官十有八九会问这个问题. cookie和session经常被放在一起问,其实在我看来这两个东西完全是两个不一样的. ...
- 关于flask登录视图报错AttributeError: '_AppCtxGlobals' object has no attribute 'user'
在一个小程序中写了一个登录视图函数,代码如下: @app.route('/login',methods = ['GET','POST']) @oid.loginhandler def login(): ...
- 深入理解计算机系统_3e 第五章家庭作业 CS:APP3e chapter 5 homework
5.13 A. B. 由浮点数加法的延迟,CPE的下界应该是3. C. 由整数加法的延迟,CPE的下界应该是1. D. 由A中的数据流图,虽然浮点数乘法需要5个周期,但是它没有"数据依赖&q ...
- 安装Redis后RedisDesktopManager无法连接
1.查看端口,发现端口不通 2.修改安装redis的目录的redis.conf文件,把bind改为虚拟机的本机ip 3.关闭虚拟机的防火墙 #1.查看防火墙状态[root@localhost src] ...