hbase股票数据分析

本文是西南交大研究生课程《大数据智慧管理机制》的课程设计,做个笔记马克一下。

  1. 课程设计意义与目的

Hbase是非关系型大数据管理技术,基于hadoop的NoSQL,从而具有面向大数据5V特点的存储优势,具有强大的吞吐能力和扩展性。传统的关系型数据库涉及多表联结查询及Group by或Order by等操作,在分布式系统高并发环境下,当数据量达到几千万甚至至几亿级别时,一个SQL查询会达到分钟级别以上,效率低下;而HBase采用的是Key/Value的列式存储方式(数据即索引),即使数据海量,查询效率依然能满足通常的要求。此外,由于基于分布式文件系统,可以将数据分片放在不同的服务器上,进一步增加高并发能力,减少了负载压力。

由于HBase简单的存储方式,它不支持Group by, Order by等操作, 不擅长数据分析,只能借助MapReduce或协处理器( Coprocessor)来实现少量复杂查询。因此,HBase等NoSQL数据库适合千万以上数据量并且需要高并发的应用环境。因此此次的hbase实践有助于我们更深入的学习大数据的处理。

2. 课程设计内容

2.1需求分析及源数据集情况

本设计基于Hbase实现一个股票管理系统,应用NoSQL技术完成大数据的管理和查询任务。应用需求主要有股票记录的数据库维护管理、记录查询、数据统计分析。在记录维护管理模块,设计UI界面,添加、修改和删除数据;在查询和统计分析模块,实现简单的条件查询、组合条件查询、统计分析等任务。

实践项目使用了股票数据集作为源数据,该数据是关于20家上市公司的1991年到2016年的股票信息,数据量约为120000条记录。相关属性如:股票代码,股票简称,日期,前收盘价,开盘价,最高价,最低价,收盘价,成交量,成交金额,涨跌,涨跌幅,均价,换手率,总市值。其中股票代码和股票简称为股票的基本信息,其余为可计算的统计信息。

2.2 逻辑和物理设计,模型展示。

2.2.1系统架构

本设计的后端由数据库Hbase以及它所基于的分布式文件系统HDFS构成,客户端通过web和java服务器与后端通信,Hbase的java api实现数据的增删改查后,将数据返回给前端。

2.2.2 Hbasse数据库模型设计

Hbase与传统的关系型数据库不同,它是一个稀疏的、多维度的、排序的映射表。每张表都有一个列族集合, HBase通过列族的概念来组织数据 的物理存储。每行都有一个可排序的主键(主键之一的行键按字典顺序排列)和任意多个列。行和列所决定的单元中存储数据,数据类型是字节数组byte[]。由于HBase的无模式特性,同一张表里的每一行数据都可以有截然不向的列。HBase中所有数据库在更新时都有一个时间印标记,每一次更新都是一个新的版本,HBase会保留一定数量的版本,客户端可以选择获取距离某个时间点最近的版本单元的值,或者一次获取所有版本单元的值。根据HBase上述特点设计本实践项目中的股票记录表,如表1所示。该表分为两个列族,列族StockInfo包含股票基本信息,列族Statistic包含股票统计信息。

表1 股票记录表

Rowkey

列族(StackInfo)

列族(Statistic)

Code

Name

GMV

RANGE

03_000001.SZ_20071228

000001.SZ

平安银行

2376508379

50

03_000002.SZ_20071228

000002.SZ

万科A

601376594.4

73.3333

……

13_000004.SZ_20071228

000004.SZ

中国宝安

2832630790

11.1484

13_000005.SZ_20071228

000005.SZ

深物业A

2935635546

12.7659

Hbase中的数据按照rowkey进行排序,region的配置在建表的时候设定。本设计有两个列族,因此一个region有两个store,store以Hfile的形式存储在HDFS上。

2.2.3行键设计

Hbase行键的设计很重要,一条数据的唯一标识就是 rowkey,这条数据存储于哪个分区,取决于 rowkey 处于哪个一个预分区的区间内,设计 rowkey 的主要目的,就是让数据均匀的分布于所有的 region 中,在一定程度上防止数据倾斜。

本设计的rowkey设计方案:将 Rowkey 的高位作为散列字段,由股票的年月进行哈希取余,中位为股票代码,低位放时间字段,例如:

hashvalue (202004) %299 ” + “_” + stock code + “_” + stock date

这样将提高数据均衡分布在每个 Regionserver 实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息,将产生所有新数据都在一个 RegionServer 上堆积的热点现象,同时这样在做数据检索的时候负载将会集中在个别 RegionServer,降低查询效率。

最后是表的创建。HBase默认建表时有一个region,这个region的rowkey是没有边界的,即没有startkey和endkey,在数据写入时,所有数据都会写入默认的region,随着数据量的不断增加,此region已经不能承受不断增长的数据量,会进行split,分成2个region。在此过程中,会产生两个问题:

1.数据往一个region上写,会有写热点问题。

2.region split会消耗宝贵的集群I/O资源。

因此为负载均衡,建表的时候,创建多个空region进行预分区,使得不同年月份的股票存在不同regionserver中。由于存储在相同的列族具有相同的特性,因此对于股票数据集,分为两个列族,StackInfo和Statisitc,保存股票基本信息和统计信息。

图2 建表

2.3 系统功能模块和数据分析

2.3.1构建hbase并加载数据

(1)开发环境介绍

环境:Ubuntu21,jdk1.7,hadoop2.6.5,tomcat9.0,hbase1.2.6,开发工具:eclipse。搭建伪分布式环境,使用hbase自带的zookeeper。

(2)使用java api,调用put函数将股票信息导入HbaseStock表中,

插入数据主要代码如下:

Configuration cfg = HBaseConfiguration.create();

//HTable对象用于与HBase进行通信。

HTable table = new HTable(cfg,tableName);

//通过Put对象为已存在的表添加数据

Put put = new Put(row.getBytes());

if(column==null)//判断列限定符是否为空,如果为空,则直接添加列数据

put.add(columnFamily.getBytes(),null,data.getBytes());

else

put.add(columnFamily.getBytes(),column.getBytes(),data.getBytes());

//table对象的put输入参数是put对象,而put对象则表示每一单元格数据。

table.put(put);

(3)在命令行中输入hbase shell进入hbase界面,输入scan ‘HbaseStock’可查看加载进去的数据。

图3 查看所有数据

2.3.2Hbase数据库维护

(1)删除、添加和修改数据

图4 put命令添加数据

图5 delete命令删除数据

Hbase添加和修改都是采用put直接添加数据,修改不会覆盖源数据,只是在数据行上新加了一条时间戳不同的数据。因此在后端处理添加和修改均调用的put函数。关键代码:

public void insert(TableBean tBean) {
              try(Connectionconn = this.connect();
                     TabletableDes =conn.getTable(TableName.valueOf("HbaseStock")) ){
                     Putput = new Put(Bytes.toBytes(tBean.getRowKey()));
                     HTableDescriptorhtab = tableDes.getTableDescriptor();
                 HColumnDescriptor[]columnFamilies = htab.getColumnFamilies();
                 Stringstatistic = columnFamilies[0].getNameAsString();
                 StringstockInfo = columnFamilies[1].getNameAsString();
                 System.out.println(statistic);
                 System.out.println(stockInfo);
                 //Code,Name,date,PreClose,OpenPrice,MaxPrice,
               //LowPrice,ClosePrice,Vol,TurnVol,UaD,Range,Avg,TurnOver,GMV
                  put.addColumn(Bytes.toBytes(stockInfo),Bytes.toBytes("Name"),…);
                  //将修改更新到表
                 tableDes.put(put);
              }catch(Exceptione){
              }
       }

图6 前端修改删除记录

图7 前端修改添加页面

(2)查询

图8 get命令查询一条数据

查询某一rowkey下的股票信息的关键代码如下:

    publicList<TableBean> selectRowKey(String rowKey) {
                 List<TableBean> tabBeans =new ArrayList<TableBean>();
                 TableBean tabBean = newTableBean();
                 try(Connection conn =this.connect();
                 Table tableDes=conn.getTable(TableName.valueOf("HbaseStock"))){
                        Get get = newGet(Bytes.toBytes(rowKey));//调用get函数
                        Result result =tableDes.get(get);
                        ……
                    tabBean.set…(…)
                 }catch(Exception e){
                        System.out.println(e);
                        return tabBeans;
                 }
                 return tabBeans;
          }

图9 前端根据行键查询结果

(3)限定范围查询。

限制行键范围输出,使用STARTROW和STOPROW过滤输出,如图9查询同一股票万科A,在2000年11月的全部记录。

图10 命令行限定范围查询

关键代码:

public  ArrayList scanRangRow(String tableName,String Start,String End)  throws IOException{
        Connection connection = this.connect();
        Table table = connection.getTable(TableName.valueOf(tableName));
        byte[] startRow = Bytes.toBytes(Start);
        byte[] endRow = Bytes.toBytes(End);
        Scan s = new Scan(startRow,endRow);
        ResultScanner rs = table.getScanner(s);
        ArrayList L = new ArrayList();
        String colFamily = "StockInfo";
        String col = "Name";
        for(Result result:rs){
             System.out.println("--------" + Bytes.toString(result.getRow()));
             System.out.println(new String(result.getValue(colFamily.getBytes(),col==null?null:col.getBytes())));
        }
        table.close();
        return L;
    }

(4)条件查询

在hbase shell里使用过滤器查询之前先引入相应的包:

        import org.apache.hadoop.hbase.filter.CompareFilter
        import org.apache.hadoop.hbase.filter.SingleColumnValueFilter
        import org.apache.hadoop.hbase.filter.SubstringComparator

列值过滤器查询股票涨跌幅度为0的记录。

Scan 'HbaseStock',{FILTER => "SingleColumnValueFilter('Statistic','Range',=,'binary:0')"}

图11 命令行列值过滤器查询结果

行值过滤法查找股票日期为2007年9月27日的股票信息

scan 'HbaseStock',{FILTER =>"RowFilter(=,'substring:20070927')"}

图12 命令行行值过滤器查询结果

关键代码:

//条件查询
    public List<TableBean> select(String name, String ……) throws IOException {
        List<Filter> filters = new ArrayList<Filter>();//过滤器列表
        List<TableBean> tabBeans = new ArrayList<TableBean>();
            nameFilter = new SingleColumnValueFilter(
                Bytes.toBytes("StockInfo"), Bytes.toBytes("Code"), CompareOp.EQUAL, new SubstringComparator(name));
            filters.add(nameFilter);//添加条件
        }
        ……
            filters.add(filter);
        }
        //创建FilterList
        FilterList filterList = new FilterList(Operator.MUST_PASS_ALL, filters);
        //FilterList filterList = new FilterList(filters);
        Scan scan = new Scan();
        scan.setFilter(filterList);    //设置过滤器
        try(Connection conn = this.connect();
            Table tableDes =conn.getTable(TableName.valueOf("HbaseStock"));//lhs_mymoney
            ResultScanner rs = tableDes.getScanner(scan);){……}

(5)组合条件查询

(5)组合条件查询

使用AND将多个过滤器连接,进行组合条件查询。例如分析万科A公司在1991年到1992年的股票信息。共10条记录。

scan 'HbaseStock' , {FILTER =>"(SingleColumnValueFilter ('StockInfo','Code',=,'binary:000002.SZ')) AND (RowFilter(=,'substring:1991'))"}

图13 命令行组合查询

关键代码:

//创建FilterList

FilterListfilterList = new FilterList(Operator.MUST_PASS_ALL, filters);//返回满足所有过滤条件的记录

图14 前端组合查询结果

2.3.3数据分析

在对股票市场进行基本了解后发现,股民分析股票的趋势时,通常根据股价和成交量的走势一起分析。比如低位量平价升,即股价从高处滑落时,往往成交量会减少,也就是所谓的缩量,但当一定的缩量之后,成交量如果与前日持平,而且股价已经开始上升,这说明底部已到,可以考虑入手了。同理还有低位量增价平,股价经过持续下跌的低位区,开始出现企稳的迹象,成交量也慢慢增加,可以适量买进股票等待上涨,等等。因此在统计分析模块主要分析成交量与股价的走势对比。由于股价在一天中是动态变化的,因此选择每天的收盘价作为股价。

数据分析部分基于组合条件查询的代码,图16展示了股票代码为000002.SZ公司万科A股1991到1992年股价走势及成交量走势,由两个图对比可知道,在大概1991年9月18号后,股票符合低位量平价升的模式。

图15 股票分析统计查询窗口

图16 股票分析统计

3.遇到的主要问题和解决方案、收获与感悟。

完成课程设计的过程中,因为使用的虚拟机搭建hadoop和hbase环境,虚拟机经常崩溃,以至于很多时间都在重装环境。因此总结以下几个问题:

  1. ssh的安装问题

安装ssh:

sudo apt-get install openssh-server

生成密钥对:

ssh-keygen -t rsa

(一路回车结束,进入到目录.ssh,可以发现生成以下文件:

id_rsa : 生成的私钥文件

id_rsa.pub :生成的公钥文件

如果没有.ssh目录,则执行ssh localhost,将会生成.ssh目录。)

执行以下命令生成授权文件:

cat .ssh/id_rsa.pub >> .ssh/authorized_keys

赋予authorized_keys 文件权限:

chmod 600 .ssh/authorized_keys

验证是否成功:

ssh localhost

  1. 启动之后没有Datanode的问题

由于多次格式化namenode,导致再启动会出现没有datanode

解决:首先vi core-site.xml 看有没有配置tmp,如果有,直接到此目录下删除data,如果没有配置,直接找根目录下的tmp目录,然后rm- rf data,再格式化一次hdfs namenode -format,再重启start-dfs.sh (前提是已经stop-dfs.sh)

  1. Eclipse无法启动的问题

Eclipse在安装javaee时出现HostNameError错误。

解决:在hosts文件里添加主机ip地址。实际我是重装了,因为我的ubuntu的apt没有网络的工具,需要apt update,但是在更新apt的时候会一直报错,为了节约时间选择了重装。

  1. Log4j报错

在使用java api连接hbase时,log4j会出现报错,每次执行完出现以下提示:

log4j:WARN No appenders could be found for logger(org.apache.ibatis.logging.LogFactory).

log4j:WARN Please initialize the log4j system properly.

log4j:WARNSeehttp://logging.apache.org/log4j/1.2/faq.html#noconfigfor more info.

解决:在配置文件log4j.properties(文件名必须这个,放在resources目录)全选粘贴如下代码

# Global logging configuration 开发时候建议使用 debug

log4j.rootLogger=DEBUG, stdout

# Console output...

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

经过多次重装,终于配置好了!这次课程设计,是我第一次在虚拟环境下编程,由于ubuntu不太稳定,所以经常要等虚拟机恢复响应,设计过程中也考虑过在物理机环境下远程连接hbase,无奈当时虚拟机的终端损坏了,很多配置查找无法进行。但是在多次重装后发现,只要备份好所有配置,搭建这些环境很快的。

图17 hbase配置成功

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值