1、 背景
网站日趋成熟,用户量趋向稳定。客户想从网站产生的用户行为信息中获取一些有用的信息,以便支持决策。便有了今天《运营分析平台》这个项目,项目的主要内容包括:1、日志入库(数据库使用DB2),2、根据客户提供的规则进行一些简单的统计汇总,然后报表展示结果,这些规则一般很简单。3、对资源和 业务进行阀值实时监控预警。4、日志明细的查询。
网站日趋成熟,用户量趋向稳定。客户想从网站产生的用户行为信息中获取一些有用的信息,以便支持决策。便有了今天《运营分析平台》这个项目,项目的主要内容包括:1、日志入库(数据库使用DB2),2、根据客户提供的规则进行一些简单的统计汇总,然后报表展示结果,这些规则一般很简单。3、对资源和 业务进行阀值实时监控预警。4、日志明细的查询。
如今网站一天产生1000多万条记录,面对暴涨的数据量。原先使用DB2的方案中遇到了一些瓶 颈,其中包括入库流程,统计过程与明细查询。经常收到运维同事的抱怨,今天存储不够了,明天统计速度慢了,数据查了老半天查不出来了,很是烦恼。正好之前 有过hadoop的相关工作经验,决定使用分布式的方案来缓解这些老调长谈的问题。现在把这个过程简单记录一下,以便日后学习。
网站的日志格式如下,并且一条记录占据一行,以文本的形式存放在存储上面。以前的方法是写定时任务,按时将日志存入DB2中。现在也是采用这种方式,而将数据迁移到hbase中。
SERIAL,APPID,STARTTIME,USETIME,CHANNEL,MOBILENO,BRANCH,BRAND,LOGINTYPE,IP,OPERARESULT
日志内容示例:
"sSOXmxAxWP3","ucs.server","2013-10-16-08.31.12.000000",192,0,"13724086484","GZ",3,1,"127.0.0.1",1
hbase使用三维有序存储,三维是指:rowkey(行主键),column key(columnFamily+qualifier),timestamp(时间戳)。
我们知道rowkey是行的主键,而且hbase只能指定rowkey,或者一个rowkey范围(即scan)来查找数据。所以rowkey的设计是至关重要的,关系到你应用层的查询效率。我们知道,rowkey是以字典顺序排序的。比如,有两个rowkey:aa、bb,因为按字典排序,那么rowkey1是排在 rowkey2前面的。这个理解了,我们在根据rowkey范围查询的时候,我们一般是知道startRowkey和endRowkey的,这样查询的范围就确定下来了,就可以快速查询到这个范围内的所有数据,而且不需要遍历,这也是hbase的优势之一。比如说rowkey设计为:用户ID-日期,那么查某个用户某天的数 据,startKEY为1234-20140729,endKey为:1234+20140730,那么你查到的就是用户为1234在20140729这一天的数据。
按照这种规则以及日志格式,hbase表与rowkey设计如下:
表名:T_LOGIN_DETAIL
rowKey:CHANNEL-BRANCH-BRAND-OPERARESULT-STARTTIME
如:00000000000000000000000000
簇族名称:cotenxt
qualifier:SERIAL,APPID,STARTTIME,USETIME,
CHANNEL,MOBILENO,BRANCH,BRAND,LOGINTYPE,IP,OPERARESULT
事先分割好regions,即确定每个regions的startKey和endKey。能够进行将region分好,而无需启用hbase的自split策略,这种方式能够提高集群稳定性,缺点是操作上较困难。代码如下:
private static org.apache.hadoop.conf.Configuration conf = HbaseHelper.getHbaseConfiguration(); /** * 创建hbase表 * @param tableName 表名 * @param familyNames 列族名 * @param regions */ public static boolean createTable(String tableName, String[] familyNames, byte[][] regions) { boolean result = false; try { HBaseAdmin admin = new HBaseAdmin(conf); // 初始化table,并设置名称 HTableDescriptor table = new HTableDescriptor(tableName); // 添加列族 for(String familyName : familyNames) { HColumnDescriptor col = new HColumnDescriptor(familyName); table.addFamily(col); } // 执行创建表命令 admin.createTable(table, regions); result = true; } catch (TableExistsException e) { System.out.println("the table already exists..."); } catch (MasterNotRunningException e) { e.printStackTrace(); } catch (ZooKeeperConnectionException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result; } public static void main(String[] args) { // 根据row分割好regions byte[][] regions = new byte[][] { Bytes.toBytes("00000000000000000000000000"), Bytes.toBytes("00019999999999999999999999"), Bytes.toBytes("00029999999999999999999999"), Bytes.toBytes("00039999999999999999999999"), Bytes.toBytes("00049999999999999999999999"), Bytes.toBytes("00059999999999999999999999"), Bytes.toBytes("00069999999999999999999999"), Bytes.toBytes("00079999999999999999999999"), Bytes.toBytes("00089999999999999999999999"), Bytes.toBytes("00099999999999999999999999"), Bytes.toBytes("00109999999999999999999999"), Bytes.toBytes("00119999999999999999999999"), Bytes.toBytes("00129999999999999999999999"), Bytes.toBytes("00139999999999999999999999"), Bytes.toBytes("00149999999999999999999999"), Bytes.toBytes("00159999999999999999999999"), Bytes.toBytes("00169999999999999999999999"), Bytes.toBytes("00179999999999999999999999"), Bytes.toBytes("00189999999999999999999999"), Bytes.toBytes("00199999999999999999999999"), Bytes.toBytes("00209999999999999999999999"), Bytes.toBytes("00219999999999999999999999"), Bytes.toBytes("00229999999999999999999999"), Bytes.toBytes("00239999999999999999999999"), Bytes.toBytes("00249999999999999999999999"), Bytes.toBytes("00259999999999999999999999"), Bytes.toBytes("99999999999999999999999999") }; createTable("T_LOGIN_DETAIL", new String[]{"cotenxt"}, regions); }
程序执行结束之后,打开控制台,可看到如下效果:
@Override public boolean doInLogSaveList(List<Object> dataList) throws SQLException { int listLen = dataList != null ? dataList.size() : 0; List<Put> puts = new ArrayList<Put>(); for (int i = 0; i < listLen; i++) { LoginDetail loginDetail = (LoginDetail) dataList.get(i); String channel = String.valueOf(loginDetail.getChannel()); String branch = loginDetail.getBranch(); String brand = String.valueOf(loginDetail.getBrand()); String operaresult = String.valueOf(loginDetail.getOperaResult()); // 拼凑rowkey,形式为:CHANNEL-BRANCH-BRAND-OPERARESULT-STARTTIME StringBuffer sb = new StringBuffer(); sb.append(LoginDetailHelper.trunChannel(channel)); // CHANNEL sb.append(LoginDetailHelper.trunBranch(branch)); // BRANCH sb.append(brand); // BRAND sb.append(LoginDetailHelper.trunValue(operaresult)); // OPERARESULT sb.append(loginDetail.getTime().replace(".", "").replace("-", "").replace("\"", "")); // STARTTIME String key = sb.toString(); // 拼凑列族中的列名与值 String[] items = new String[11]; String columnsString = "SERIAL,APPID,STARTTIME,USETIME,CHANNEL,MOBILENO,BRANCH,BRAND,LOGINTYPE,IP,OPERARESULT"; String[] columns = columnsString.split(","); items[0] = LoginDetailHelper.trunValue(loginDetail.getSerial()); // SERIAL items[1] = LoginDetailHelper.trunValue(loginDetail.getAppid()); // APPID items[2] = LoginDetailHelper.trunValue(loginDetail.getTime().toString()); // TIME items[3] = String.valueOf(loginDetail.getUseTime()); // USETIME items[4] = LoginDetailHelper.trunChannelChinese(channel); // CHANNEL items[5] = LoginDetailHelper.trunValue(loginDetail.getMobileNo()); // MOBILENO items[6] = LoginDetailHelper.trunBranchChinese(branch); // BRANCH items[7] = LoginDetailHelper.getBrandCode(brand); // BRAND items[8] = LoginDetailHelper.trunValue(""); // LOGINTYPE items[9] = LoginDetailHelper.trunValue(loginDetail.getIp()); // IP items[10] = LoginDetailHelper.getOperaResult(operaresult); // OPERARESULT for (int j = 0; j < items.length; j++) { Put put = new Put(key.getBytes()); String value = items[j] == null ? "" : items[j]; put.add("cotenxt".getBytes(), columns[j].getBytes(), value.getBytes()); // 放弃写WAL日志 put.setWriteToWAL(false); puts.add(put); } // 2000条数据提交一次 if (puts.size() >= 2000) { try { table.put(puts); table.flushCommits(); puts.clear(); } catch (IOException e) { e.printStackTrace(); } } } // 检查列表中是否还存在数据,如果有,则再提交一次 if (puts.size() > 0) { try { table.put(puts); table.flushCommits(); } catch (IOException e) { e.printStackTrace(); } } return true; }
hbase的存储结构是key-value形式。相当于一个大map,于是无法做到在不遍历的情况下,多条件查询。
在查询开始之前,预先设置row的startKey和endKey。
public List<Map<String, String>> getHbaseData(String sdate, String sdate2, String channel, String branch, String brand, String mobileno) { List<Map<String, String>> result = new ArrayList<Map<String, String>>(); try { HTable table = new HTable(conf, "T_LOGIN_DETAIL"); // 拼接key值 String prefix = channel + branch + brand + "0"; String startKey = prefix + sdate.replace("-", "") + "000000000000"; String stopKey = sdate2 == null || sdate2.trim().length() == 0 ? prefix + sdate.replace("-", "") + "999999999999": prefix + sdate2.replace("-", "") + "999999999999"; Scan scan = new Scan(); if(mobileno != null && mobileno.trim().length() != 0) { BinaryComparator comparable = new BinaryComparator(Bytes.toBytes(mobileno)); SingleColumnValueFilter filter1 = new SingleColumnValueFilter(Bytes.toBytes("cotenxt"), Bytes.toBytes("MOBILENO"),CompareOp.EQUAL, comparable); scan.setFilter(filter1); } else { // 使用分页过滤器 PageFilter filter = new PageFilter(100); scan.setFilter(filter); } // 设置起始key值 scan.setStartRow(Bytes.toBytes(startKey)); scan.setStopRow(Bytes.toBytes(stopKey)); System.out.println("startKey:" + startKey); System.out.println(" stopKey:" + stopKey); ResultScanner resultScanner = table.getScanner(scan); for (Result r : resultScanner) { Map<String, String> map = new HashMap<String, String>(); map.put("DATE", Bytes.toString(r.getRow())); for (KeyValue kv : r.raw()) { map.put(Bytes.toString(kv.getQualifier()), Bytes.toString(kv.getValue())); } result.add(map); } } catch (IOException e) { e.printStackTrace(); } return result; }
相关推荐
《HBase实战》是一本基于经验提炼而成的指南,它教给读者如何运用HBase设计、搭建及运行大数据应用系统。全书共分为4个部分。前两个部分分别介绍了分布式系统和大规模数据处理的发展历史,讲解HBase的基本原理模式...
**HBase实战** HBase,全称为Apache HBase,是一款高度可扩展的开源数据库,它构建于Hadoop分布式文件系统(HDFS)之上,专为处理海量数据而设计。HBase是NoSQL数据库的一种,主要面向列式存储,提供实时读写能力,...
HBase实战(中文扫描版)
HBase实战_Cn.pdf 完整版
HBase实战
hbase实战,hadoop的数据库管理软件
Hbase实战中文版,适合于想了解面向列数据库和海量数据存储的读者
HBase实战.pdf 个人收集电子书,仅用学习使用,不可用于商业用途,如有版权问题,请联系删除!
hbase实战中文版pdf,清晰度较高, 带目录方便阅读~~~~~~
Hbase实战中文版 《HBase实战》是一本基于经验提炼而成的指南,它教给读者如何运用HBase设计、搭建及运行大数据应用系统。全书共分为4个部分。前两个部分分别介绍了分布式系统和大规模数据处理的发展历史,讲解...
《HBase实战》是一本基于经验提炼而成的指南,它教给读者如何运用HBase设计、搭建及运行大数据应用系统。全书共分为4个部分。前两个部分分别介绍了分布式系统和大规模数据处理的发展历史,讲解HBase的基本原理模式...
《HBase资源合集》包含了四本重量级的书籍,分别是《HBase企业应用开发实战》、《HBase权威指南》、《HBase实战》以及《HBase应用架构》。这些书籍深入浅出地探讨了HBase在大数据环境中的应用与开发,是学习和掌握...
hbase实战,这是一本具体操作hbase很不错的书
本文将深入解析HBase的基础概念、需求调研、RowKey设计以及实战案例,帮助读者更好地理解和运用HBase。 首先,我们来看HBase的基础概念。HBase是一种基于Google Bigtable理念设计的开源分布式数据库,它构建于...