记一次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吞吐量小,形成了瓶颈效应. ...
随机推荐
- 选择客栈noip2011
哈,没想到吧.今天居然有两篇(算什么,厕所读物吗 选择客栈 本题的更优解请跳转zt 这题11年,刚改2day. 对于30% 的数据,有 n ≤100: 对于50% 的数据,有 n ≤1,000: 对于 ...
- 从Windows迁移SQL Server到Linux
前一篇博客关于SQL Server on Linux的安装,地址:http://www.cnblogs.com/fishparadise/p/8057650.html,现在测试把Windows平台下的 ...
- 使用 dotnet core 和 Azure PaaS服务进行devOps开发(Web API 实例)
作者:陈希章 发表于 2017年12月19日 引子 这一篇文章将用一个完整的实例,给大家介绍如何基于dotnet core(微软.NET的最新版本,支持跨平台,跨设备的应用开发,详情请参考 https ...
- 运行循环 - RunLoop
1.RunLoop简介 1.1 什么是RunLoop 简单来说就是:运行循环,可以理解成一个死循环,一直在运行. RunLoop实际上就是一个对象,这个对象用来处理程序运行过程中出现的各种事件(触摸. ...
- hdu1754 I Hate It (线段树 更新点的值)
I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- OSChinaclient源代码学习(1)--Android与Server的交互
这里主要针对"综合"这个选项卡下的"资讯"模块为样例.对核心代码进行解读. 准备工作:參考我的另外一篇博客 http://blog.csdn.net/csp27 ...
- 2016年7月微软MVP申请開始了!
2016年7月微软MVP申请開始了! CSDN与微软合作,长期为用户提供申请"微软最有价值专家"的平台.希望有兴趣.资历的朋友以及正在朝这个方向努力的朋友能够积极參与. 2016年 ...
- [前端] jquery验证手机号、身份证号、中文名称
验证: 中文姓名.手机号.身份证和地址 HTML(表单): <form action=""> <div class="form-group"& ...
- bzoj1513【POI2006】Tet-Tetris 3D
1513: [POI2006]Tet-Tetris 3D Time Limit: 30 Sec Memory Limit: 162 MB Submit: 733 Solved: 245 [Subm ...
- 项目实战12.1—企业级监控工具应用实战-zabbix安装与基础操作
无监控,不运维.好了,废话不多说,下面都是干货. 警告:流量党勿入,图片太多!!! 项目实战系列,总架构图 http://www.cnblogs.com/along21/p/8000812.html ...