记一次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吞吐量小,形成了瓶颈效应. ...
随机推荐
- BFS求最短路 Abbottt's Revenge UVa 816
本题的题意是输入起点,朝向和终点,求一条最短路径(多解时任意输出一个即可) 本题的主要代码是bfs求解,就是以下代码中的slove的主要部分,通过起点按照路径的长度来寻找最短路径,输出最先到终点的一系 ...
- django序列化时使用外键的真实值
展示: 普通情况下序列化得到的外键的内容仅仅是id: ... { fields: { uat_date: "2015-07-25", statu: "CG", ...
- J2EE十三个规范小结
J2ee是我们步入java学习的一个開始.它将开启这趟奇幻之旅,Java是一种简单的,跨平台的,面向对象的,分布式的.解释的.健壮的安全的.结构的中立的,可移植的.性能非常优异的多线程的,动态的语言. ...
- Android ListView Adapter的getItemViewType和getViewTypeCount多种布局
<Android ListView Adapter的getItemViewType和getViewTypeCount多种布局> 在Android的ListView中.假设在一个Lis ...
- linux命令的排列、替换与别名
命令的排列; 1.使用";" 命令语法: 命令1;命令2 当运行该命令时,无论命令1是否出错.接下来就运行命令2 2.使用"&&" 命令语法:命 ...
- HDU 3569 Imaginary Date 简单期望
推一下公式.就会发现是这个.. 由于设结果有x种方案.则每一个数字出现的概率都均等,然后和就是x*m 每种方案的概率是1/x 每一个数出现的概率都是1/n 所以每一个方案的和就是 sum/n *m # ...
- 理解vuex的状态管理模式架构
理解vuex的状态管理模式架构 一: 什么是vuex?官方解释如下:vuex是一个专为vue.js应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证以一种可预测的 ...
- SpringMVC实现JSON与前台交互
这几天忙着做学校的项目,感觉好久没有更新博客了,来整理一下. 由于要实现的功能是表单联动,只能自己去写ajax来实现提交给后台接口了,好久没有写前端,好多东西都忘记了,只能可怜巴巴的用原生的js去实现 ...
- QQ 相册后台存储架构重构与跨 IDC 容灾实践
欢迎大家前往云加社区,获取更多腾讯海量技术实践干货哦~ 作者简介:xianmau,2015 年加入腾讯 TEG 架构平台部,一直负责 QQ 相册平台的维护和建设,主导相册上传架构重构和容灾优化等工作. ...
- 自学Python5.2-类、模块、包
类.模块.包 一.类 类的概念在许多语言中出现,很容易理解.它将数据和操作进行封装,以便将来的复用. 二.模块module 通常模块为一个文件,直接使用import来导入就好了.可以作为module ...