HBase 简介(强烈推荐看)
本博文的主要内容有:
.HBase定义
.HBase 的特点
.HBase 访问接口
.HBase 存储结构
.HBase设计
.HBase安装
.HBase shell操作
.输入 help 可以看到命令分组
.部分命令清单
.下边分组举例 Shell 的各种操作
.general操作
.ddl操作
.dml操作
.HBase 客户端
.Scan 对象提供了默认构造函数,一般使用默认构造函数
.Put 常用的方法有以下几个
.MapReduce 操作 HBase
. org.apache.hadoop.hbase.mapreduce 包中的类和工具用来将 HBase 作为 MapReduce 作业的输出
.向 HBase 中写入数据
.读取 HBase 中的数据
.为什么要引入列族?
.COLUMN和COLUMNS的区别
.HBase的索引有哪些?
.表、列族、列标识、行键、单元格、版本号、逻辑视图、物理视图、客户端视图
.HBase架构中主要的组件
.HBase具有的特点
和传统数据库不同的是,HBase的表不用定义有哪些列(字段,Column),因为列是可以动态增加和删除的。但HBase表需要定义列族(Column Family)。每张表有一个或者多列族,每个列必须且仅属于一个列族。列族主要用来在存储上对相关的列分组,从而使得减少对无关列的访问来提高性能。一般来说,一个列族就足够使用了。
HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。HBase利用Hadoop MapReduce来处理HBase中的海量数据,同时利用Zookeeper作为其协同服务。
COLUMN的值实质上为“COLUMNS+:+列修饰符”。
HBase定义
HBase 是一个高可靠、高性能、面向列、可伸缩的分布式存储系统,利用Hbase技术可在廉价PC Server上搭建 大规模结构化存储集群。
HBase 是Google Bigtable 的开源实现,与Google Bigtable 利用GFS作为其文件存储系统类似, HBase 利用Hadoop HDFS 作为其文件存储系统;Google 运行MapReduce 来处理Bigtable中的海量数据, HBase 同样利用Hadoop MapReduce来处理HBase中的海量数据;Google Bigtable 利用Chubby作为协同服务, HBase 利用Zookeeper作为对应。
HBase 的特点
HBase 中的表一般有以下特点。
1)大:一个表可以有上亿行,上百万列。
2)面向列:面向列表(簇)的存储和权限控制,列(簇)独立检索。
3)稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。
HBase 访问接口
HBase 支持很多种访问,访问HBase的常见接口如下。
1、Native Java API,最常规和高效的访问方式,适合Hadoop MapReduce Job并行批处理HBase表数据。
2、HBase Shell,HBase的命令行工具,最简单的接口,适合HBase管理使用。
3、Thrift Gateway,利用Thrift序列化技术,支持C++,PHP,Python等多种语言,适合其他异构系统在线访问HBase表数据。
4、REST Gateway,支持REST 风格的Http API访问HBase, 解除了语言限制。
5、Pig,可以使用Pig Latin流式编程语言来操作HBase中的数据,和Hive类似,本质最终也是编译成MapReduce Job来处理HBase表数据,适合做数据统计。
6、Hive,当前Hive的Release版本尚没有加入对HBase的支持,但在下一个版本Hive 0.7.0中将会支持HBase,可以使用类似SQL语言来访问HBase。
HBase 存储结构
从HBase的架构图上可以看出,HBase中的存储包括HMaster、HRegionServer、HRegion、Store、MemStore、StoreFile、HFile、HLog等,本课程统一介绍他们的作用即存储结构。 以下是 HBase 存储架构图:
HBase中的每张表都通过行键按照一定的范围被分割成多个子表(HRegion),默认一个HRegion超过256M就要被分割成两个,这个过程由HRegionServer管理,而HRegion的分配由HMaster管理。
HMaster的作用:
1、 为Region server分配region。
2、 负责Region server的负载均衡。
3、发现失效的Region server并重新分配其上的region。
4、 HDFS上的垃圾文件回收。
5、 处理schema更新请求。
HRegionServer作用:
1、 维护master分配给他的region,处理对这些region的io请求。
2、 负责切分正在运行过程中变的过大的region。
可以看到,client访问hbase上的数据并不需要master参与(寻址访问zookeeper和region server,数据读写访问region server),master仅仅维护table和region的元数据信息(table的元数据信息保存在zookeeper上),负载很低。 HRegionServer存取一个子表时,会创建一个HRegion对象,然后对表的每个列族创建一个Store实例,每个Store都会有一个MemStore和0个或多个StoreFile与之对应,每个StoreFile都会对应一个HFile, HFile就是实际的存储文件。因此,一个HRegion有多少个列族就有多少个Store。 一个HRegionServer会有多个HRegion和一个HLog。
HRegion
table在行的方向上分隔为多个Region。Region是HBase中分布式存储和负载均衡的最小单元,即不同的region可以分别在不同的Region Server上,但同一个Region是不会拆分到多个server上。
Region按大小分隔,每个表一般是只有一个region。随着数据不断插入表,region不断增大,当region的某个列族达到一个阈值(默认256M)时就会分成两个新的region。
每个region由以下信息标识:
1、< 表名,startRowkey,创建时间>
2、由目录表(-ROOT-和.META.)记录该region的endRowkey
HRegion定位:Region被分配给哪个Region Server是完全动态的,所以需要机制来定位Region具体在哪个region server。
HBase使用三层结构来定位region:
1、 通过zk里的文件/hbase/rs得到-ROOT-表的位置。-ROOT-表只有一个region。
2、通过-ROOT-表查找.META.表的第一个表中相应的region的位置。其实-ROOT-表是.META.表的第一个region;.META.表中的每一个region在-ROOT-表中都是一行记录。
3、通过.META.表找到所要的用户表region的位置。用户表中的每个region在.META.表中都是一行记录。
-ROOT-表永远不会被分隔为多个region,保证了最多需要三次跳转,就能定位到任意的region。client会将查询的位置信息保存缓存起来,缓存不会主动失效,因此如果client上的缓存全部失效,则需要进行6次网络来回,才能定位到正确的region,其中三次用来发现缓存失效,另外三次用来获取位置信息。
Store
每一个region由一个或多个store组成,至少是一个store,hbase会把一起访问的数据放在一个store里面,即为每个ColumnFamily建一个store,如果有几个ColumnFamily,也就有几个Store。一个Store由一个memStore和0或者多个StoreFile组成。 HBase以store的大小来判断是否需要切分region。
MemStore
memStore 是放在内存里的。保存修改的数据即keyValues。当memStore的大小达到一个阀值(默认64MB)时,memStore会被flush到文件,即生成一个快照。目前hbase 会有一个线程来负责memStore的flush操作。
StoreFile
memStore内存中的数据写到文件后就是StoreFile,StoreFile底层是以HFile的格式保存。
HFile
HBase中KeyValue数据的存储格式,是hadoop的二进制格式文件。 首先HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。Trailer中有指针指向其他数据块的起始点,FileInfo记录了文件的一些meta信息。 Data Block是hbase io的基本单元,为了提高效率,HRegionServer中有基于LRU的block cache机制。每个Data块的大小可以在创建一个Table的时候通过参数指定(默认块大小64KB),大号的Block有利于顺序Scan,小号的Block利于随机查询。每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成,Magic内容就是一些随机数字,目的是防止数据损坏,结构如下。
HFile结构图如下:
Data Block段用来保存表中的数据,这部分可以被压缩。 Meta Block段(可选的)用来保存用户自定义的kv段,可以被压缩。 FileInfo段用来保存HFile的元信息,不能被压缩,用户也可以在这一部分添加自己的元信息。 Data Block Index段(可选的)用来保存Meta Blcok的索引。 Trailer这一段是定长的。保存了每一段的偏移量,读取一个HFile时,会首先读取Trailer,Trailer保存了每个段的起始位置(段的Magic Number用来做安全check),然后,DataBlock Index会被读取到内存中,这样,当检索某个key时,不需要扫描整个HFile,而只需从内存中找到key所在的block,通过一次磁盘io将整个 block读取到内存中,再找到需要的key。DataBlock Index采用LRU机制淘汰。 HFile的Data Block,Meta Block通常采用压缩方式存储,压缩之后可以大大减少网络IO和磁盘IO,随之而来的开销当然是需要花费cpu进行压缩和解压缩。目标HFile的压缩支持两种方式:gzip、lzo。
另外,针对目前针对现有HFile的两个主要缺陷:
a) 占用过多内存
b) 启动加载时间缓慢
基于此缺陷,提出了HFile Version2设计。
HLog
其实HLog文件就是一个普通的Hadoop Sequence File, Sequence File的value是key时HLogKey对象,其中记录了写入数据的归属信息,除了table和region名字外,还同时包括sequence number和timestamp,timestamp是写入时间,sequence number的起始值为0,或者是最近一次存入文件系统中的sequence number。 Sequence File的value是HBase的KeyValue对象,即对应HFile中的KeyValue。
HLog(WAL log):WAL意为write ahead log,用来做灾难恢复使用,HLog记录数据的所有变更,一旦region server 宕机,就可以从log中进行恢复。
LogFlusher
前面提到,数据以KeyValue形式到达HRegionServer,将写入WAL之后,写入一个SequenceFile。看过去没问题,但是因为数据流在写入文件系统时,经常会缓存以提高性能。这样,有些本以为在日志文件中的数据实际在内存中。这里,我们提供了一个LogFlusher的类。它调用HLog.optionalSync(),后者根据 hbase.regionserver.optionallogflushinterval (默认是10秒),定期调用Hlog.sync()。另外,HLog.doWrite()也会根据 hbase.regionserver.flushlogentries (默认100秒)定期调用Hlog.sync()。Sync() 本身调用HLog.Writer.sync(),它由SequenceFileLogWriter实现。
LogRoller
Log的大小通过$HBASE_HOME/conf/hbase-site.xml 的 hbase.regionserver.logroll.period 限制,默认是一个小时。所以每60分钟,会打开一个新的log文件。久而久之,会有一大堆的文件需要维护。首先,LogRoller调用HLog.rollWriter(),定时滚动日志,之后,利用HLog.cleanOldLogs()可以清除旧的日志。它首先取得存储文件中的最大的sequence number,之后检查是否存在一个log所有的条目的“sequence number”均低于这个值,如果存在,将删除这个log。 每个region server维护一个HLog,而不是每一个region一个,这样不同region(来自不同的table)的日志会混在一起,这样做的目的是不断追加单个文件相对于同时写多个文件而言,可以减少磁盘寻址次数,因此可以提高table的写性能。带来麻烦的时,如果一个region server下线,为了恢复其上的region,需要将region server上的log进行拆分,然后分发到其他region server上进行恢复。
HBase 设计
HBase 中的每一张表就是所谓的 BigTable。BigTable 会存储一系列的行记录,行记录有三个基本类型的定义:Row Key、Time Stamp、Column。
1、Row Key 是行在 BigTable 中的唯一标识。
2、Time Stamp 是每次数据操作对应关联的时间戳,可以看做 SVN 的版本。
3、Column 定义为< family>:< label>,通过这两部分可以指定唯一的数据的存储列,family 的定义和修改需要 对 HBase 进行类似于 DB 的 DDL 操作,而 label ,不需要定义直接可以使用,这也为动态定制列提供了一种手段 。family 另一个作用体现在物理存储优化读写操作上,同 family 的数据物理上保存的会比较临近,因此在业务设计的过程中可以利用这个特性。
数据模型(又名逻辑模型、逻辑视图)
HBase 以表的形式存储数据。表由行和列组成。列划分为若干个列族(row family),如下图所示。
HBase里的概念视图(又名为逻辑模型)
1、 Row Key
与 NoSQL 数据库一样,Row Key 是用来检索记录的主键。访问 HBase table 中的行,只有三种方式:
1)通过单个 Row Key 访问。
2)通过 Row Key 的 range 全表扫描。
3)Row Key 可以使任意字符串(最大长度是64KB,实际应用中长度一般为 10 ~ 100bytes),在HBase 内部,Row Key 保存为字节数组。
在存储时,数据按照 Row Key 的字典序(byte order)排序存储。设计 Key 时,要充分排序存储这个特性,将经常一起读取的行存储到一起(位置相关性)。
行的一次读写是原子操作(不论一次读写多少列)。这个设计决策能够使用户很容易理解程序在对同一个行进行并发更新操作时的行为。
2、 列族
HBase 表中的每个列都归属于某个列族。列族是表的 Schema 的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀,例如 courses:history、courses:math 都属于 courses 这个列族。
访问控制、磁盘和内存的使用统计都是在列族层面进行的。在实际应用中,列族上的控制权限能帮助我们管理不同类型的应用, 例如,允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)。
3、 时间戳
HBase 中通过 Row 和 Columns 确定的一个存储单元称为 Cell。每个 Cell 都保存着同一份数据的多个版本。 版本通过时间戳来索引,时间戳的类型是 64 位整型。时间戳可以由HBase(在数据写入时自动)赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也 可以由客户显示赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 Cell 中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。
为了避免数据存在过多版本造成的管理(包括存储和索引)负担,HBase 提供了两种数据版本回收方式。 一是保存数据的最后 n 个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。
4、 Cell
Cell 是由 {row key,column(=< family> + < label>),version} 唯一确定的单元。Cell 中的数据是没有类型的,全部是字节码形式存储。
物理存储(又名物理视图、物理模型)
Table 在行的方向上分割为多个HRegion,每个HRegion分散在不同的RegionServer中。
HBase里的物理视图(又名为物理模型)
每个HRegion由多个Store构成,每个Store由一个memStore和0或多个StoreFile组成,每个Store保存一个Columns Family
StoreFile以HFile格式存储在HDFS中。
HBase 安装
安装 HBase 有两种方式,单机安装和分布式安装。单机安装在一个节点上配置即可,分布式安装要用到 ZooKeeper 工具,hbase-0.98.11-hadoop2-bin.tar.gz 自带 ZooKeeper,默认是 被注释掉的。也可以自己下载 ZooKeeper 进行配置,本课程使用 HBase 默认的 ZooKeeper 来搭建 HBase 的分布式环境。
HBase 单机安装
HBase 需要再 Hadoop 环境运行,因此安装HBase 的前提是必须安装 Hadoop 环境。Hadoop 环境的安装可以参考前面课程的内容。下载与Hadoop2.2.0 相匹配的hbase-0.98.11-hadoop2-bin.tar.gz 软件包。
HBase 的安装步骤如下所示:
步骤一 :解压hbase-0.98.11-hadoop2-bin.tar.gz 到指定的目录下(这里是在/usr/java),并将权限分配给hadoop用户(运行hadoop的账户)。
这里下载的是hbase-0.98.11-hadoop2,Hadoop集群使用的是2.2.0,将其解压到/usr/java下并重命名为 hbase
[root@singlehadoop java]$ tar -zxvf hbase-0.98.11-hadoop2-bin.tar.gz [root@singlehadoop java]$ mv hbase-0.98.11-hadoop2 hbase [root@singlehadoop java]$ chown -R hadoop:hadoop hbase
步骤二: 配置 HBase 的环境变量到 /etc/profile 文件中。
[root@singlehadoop java]$ vi /etc/profile HBASE_HOME=/usr/java/hbase PATH=$JAVA_HOME/bin:$HIVE_HOME/bin:$HADOOP_HOME/bin:$HBASE_HOME/bin:$PATH
使配置文件立即生效。
[root@singlehadoop tmp]# source /etc/profile
步骤三 修改 conf/hbase-env.sh。
1) 去掉 JAVA_HOME 前的 “#”,并将其修改成自己安装的 Java 路径。
2) 去掉 HBASE_MANAGES_ZK 前的 “#”,并设置其值为 true(HBase 管理自己的 ZooKeeper,这样就不需要安装 ZooKeeper)。
步骤四 打开 conf/hbase-site.xml,添加如下内容。
hbase.rootdir 需要与之前安装的 Hadoop 目录下 conf/core-site.xml 文件中的 fs.default.name 属性值对应。
fs.default.name 设置为hdfs://singlehadoop:9000/ hbase.rootdir 设置为hdfs://singlehadoop:9000/hbase hbase.ZooKeeper.quorum 设置为 singlehadoop hbase.tmp.dir 设置为之前创建的 tmp 目录:/usr/java/hbase/tmp
步骤五 启动HBase(前提已经启动 Hadoop)。
[hadoop@singlehadoop conf]$ start-hbase.sh
步骤六 显示 HBase 运行成功。
通过 HBase 的 WebUI 查看 HBase 是否安装成功。打开浏览器,输入网址 192.168.1.128:60010。 192.168.1.128 是虚拟机的 IP,可以在 Windows 系统中访问,访问结果如下图所示。到这里 HBase 单机版已经安装成功。
HBase 的 Shell 操作
HBase 为用户提供了一个非常方便的使用方式, 我们称之为“HBase Shell”。
HBase Shell 提供了大多数的 HBase 命令, 通过 HBase Shell 用户可以方便地创建、删除及修改表, 还可以向表中添加数据、列出表中的相关信息等。
在启动 HBase 之后,用户可以通过下面的命令进入 HBase Shell 之中,命令如下所示:
输入 help 可以看到命令分组
Group Name | Commands |
---|---|
general | status,version |
ddl | alter,create,describe,disable,drop,enable,exists,is_disable,is_enable,list |
dml | count,delete,deleteall,get,get_counter,incr,put,scan,truncate |
tools | assign,balance_switch,balancer,close_region,compact,flush,major_compact,move,split,unassign,zk_dump |
replication |
add_peer,disable_peer,enable_peer,remove_peer,start_replication,stop_replication |
部分命令清单。
名称 | 命令表达式 |
---|---|
创建表 | create '表名称','行名称','列名称1','列名称2','列名称N' |
添加记录 | put '表名','行名','列名','值' |
查看记录 | get '表名','行名' |
查看表中的记录总数 | count '表名' |
删除记录 | delete '表名','行名称','列名称' |
删除一张表 | 第一步 disable'表名称'(先要屏蔽该表,才能对该表进行删除) 第二步 drop '表名称' |
查看所有记录 | scan '表名称' |
查看某个表某个列中的所有数据 | scan '表名称',['列名称:'] |
更新记录 | 就是重写一遍进行覆盖 |
下边分组举例 Shell 的各种操作
general操作
查询 HBase 服务器状态 status。
查询hbase版本 version
ddl操作
1、 创建一个表
create 'table1', 'tab1_id', 'tab1_add', 'tab1_info'
2、 列出所有的表
3、 获得表的描述
describe "table1"
4、 删除一个列族 disable alter enable
disable 'table1' alter 'table1', {NAME=>'tab1_add', METHOD=>'delete'} enable 'table1'
5、 查看表是否存在
exists 'table2'
6、 判断表是否为‘enable’
is_enabled 'table1'
7、 删除一个表
disable 'table1' drop 'table1'
dml操作
1、 插入几条记录
put 'member', 'scutshuxue', 'info:age', '24' put 'member', 'scutshuxue', 'info:birthday', '1987-06-17' put 'member', 'scutshuxue', 'info:company', 'alibaba' put 'member', 'scutshuxue', 'address:contry', 'china' put 'member', 'scutshuxue', 'address:province', 'zhejiang' put 'member', 'scutshuxue', 'address:city', 'hangzhou'
put命令比较简单,只有这一种用法:
hbase> put ‘t1′, ‘r1′, ‘c1′, ‘value', ts1
t1指表名,r1指行键名,c1指列名,value指单元格值。ts1指时间戳,一般都省略掉了。
2、 全表扫描 scan
3、 获得数据 get
1) 获得一行的所有数据
2) 获得某行,某列族的所有数据
3) 获得某行,某列族,某列的所有数据
4、 更新一条记录 put(把scutshuxue年龄改为99)
put 'member', 'scutshuxue', 'info:age', 99
5、 删除 delete、 deleteall
1) 删除行'scutshuxue', 列族为‘info' 中age的值
delete 'member', 'scutshuxue', 'info:age'
2) 删除整行
deleteall 'member', 'scutshuxue'
6、 查询表中有多少行
count 'member'
7、 给‘xiaoming’这个id增加'info:age'字段,并使用counter实现递增
incr 'member', 'xiaoming', 'info:age'
8、 将整个表清空
truncate 'member'
可以看出,HBase 是通过先对表执行 disable,然后再执行 drop 操作后重建表来实现 truncate 的功能的。
HBase 客户端
与 HBase 集群进行交互,有很多种不同的方式可以选择,如 Java API、REST、Thrift 等。下面我们以Java API为例介绍它们的用法。
Java API 交互
HBase 与 Hadoop 一样,都是用 java 编写的,所以 HBase 对 java 支持是必需的,下面看看怎么使用java 语言对 HBase 进行操作。Java API 核心类介绍如下。
1、 HBaseConfiguration 类
HBaseConfiguration 是每一个 HBase Client 都会使用到的对象,它代表 HBase 配置信息。有两种构造方式。
public HBaseConfiguration() public HBaseConfiguration(final Configuration c)
默认构造方式会尝试从 hbase-default.xml 和 HBase-site.xml 文件中读取配置。如果CLASSPATH 没有这两个文件,就需要自己配置。
Configuration HBASE_CONFIG = new Configuration(); HBASE_CONFIG.set("hbase.ZooKeeper.quorum","zkServer"); //hbase 服务地址 HBASE_CONFIG.set("hbase.ZooKeeper.property.clientPort","2181"); //端口号 HBaseConfiguration cfg = new HBaseConfiguration(HBASE_CONFIG); //读取配置文件
2、 创建表
创建表通过 HBaseAdmin 对象操作。HBaseAdmin 负责META 表信息的处理。HBaseAdmin 提供了 createTable 方法。
public void createTable(HTableDescriptor desc)
HTableDescriptor 表示表的 Schema,提供常用方法有以下两个。
1)setMaxFileSize:指定最大 Region 的大小。
2)setMemStoreFlushSize:指定 MemStore Flush 到 HDFS 上的文件大小。
3、 增加 Family
使用 addFamily 方法实现 Family 的添加。
public void addFamily(final HColumnDescriptor family)
HColumnDescriptor 代表 Column 的 Schema,提供的常用方法有以下几个。
1、setTimeToLive:指定最大的 TTL(单位是 ms),过期数据会被自动删除。
2、setInMemory:指定是否放在内存中,对小表有用,可用于提高效率。默认关闭。
3、setBloomFilter:指定是否使用 BloomFilter,可提高随机查询效率。默认关闭。
4、setCompressionType:设定数据压缩类型。默认无压缩。
5、setMaxVersions:指定数据最大保存的版本个数。默认为3。
举个简单的例子,创建 4 个 Family 表,命令如下。
HBaseAdmin hAdmin = new HBaseAdmin(hbaseConfig); HTableDescriptor table = new HTableDescriptor(tableName); table.addFamily(new HColumnDescriptor("f1")); table.addFamily(new HColumnDescriptor("f2")); table.addFamily(new HColumnDescriptor("f3")); table.addFamily(new HColumnDescriptor("f4")); hAdmin.createTable(table);
4、 删除表
删除表也是通过 HBaseAdmin 来操作,删除表之前首先要 disable 表。这是一个非常耗时的操作,所以不建议频繁删除表。
disableTable 和 deleteTable 分别用来执行 disable 和 delete 操作。使用方法如下。
HBaseAdmin hAdmin = new HBaseAdmin(hbaseConfig); if(hAdmin.tableExists(tableName)){ hAdmin.disableTable(tableName); hAdmin.deleteTable(tableName); }
5、查询数据
查询分为单条随机查询和批量查询。单条查询通过 Row Key 在Table 中查询某一行的数据,HTable 提供了get 方法完成单条查询。批量查询通过制定一段 Row Key 的范围来查询,HTable 提供了 getScanner 方法完成批量查询。
public Result get(final Get get) public ResultScanner getScanner(final Scan scan)
Get 对象包含一个 Get 查询需要的信息,它的构造方法有两种。
public Get(byte [] row) public Get(byte [] row,RowLock rowLock)
Row Lock 为了保证读写的原子性,可以传递一个已经存在 Row Lock,否则 HBase 会自动生成一个新的 Row Lock。
Scan 对象提供了默认构造函数,一般使用默认构造函数。
1) Get 和 Scan 的常用方法有以下几个。
addFamily/addColumn:指定需要的 Family 或者 Column,如果没有调用任何 Family 或者 Column,会返回所有的 Column。
setMaxVersions:指定最大的版本个数。如果不带任何参数调用 setMaxVersions,表示取所有的版本。如果不调用 setMaxVersions,只会取到最新的版本。
setTimeRange:指定最大的时间戳和最小的时间戳,只有在此范围内的 Cell 才能被获取。
setTimeStamp:指定时间戳。
setFilter:指定 Filter 过滤不需要的信息。
2) Scan 特有的方法如下。
setStartRow:指定开始的行。如果不调用,从表头开始。
setStopRow:指定结束的行(不含此行)。
setBatch:指定最多返回的 Cell 数目。防止一行中有过多的数据,导致 OOM 错误。
3) Result 代表是一行的数据。常用方法有以下几个。
getRow:返回 Row Key。
raw:返回所有的 KeyValue 数组。
getValue:按照 Column 来获取 Cell 的值。
ResultScanner 是 Result 的一个容器,每次调用ResultScanner 的next 方法会返回Result。
public Result next() throws IOException; public Result [] next(int nbRows) throws IOException;
示例代码如下。
Scan scan = new Scan(); scan.setMaxVersions(); ResultScanner ss = table.getScanner(scan); for(Result r:ss){ System.out.println(new String(r.getRow())); for(KeyValue kv:r.raw){ System.out.println(new String(kv.getColumn())); } }
6、 插入数据
HTable 通过 put 方法插入数据,可以传递单个 put 对象 或 List put 对象分别实现单条插入和批量插入。
public void put(final Put put) throws IOException public void put(final List< Put> puts) throws IOException
Put 提供3 种构造方式。
public Put(byte [] row) public Put(byte [] row) public Put(byte [] row,RowLock rowLock) public Put(Put putToCopy)
Put 常用的方法有以下几个
1)add:增加一个 Cell。
2)setTimeStamp:指定所有 Cell 默认的 timestamp,如果一个 Cell 没有指定 timestamp,就会用到这个值。如果没有调用,HBase 会将当前时间作为未指定 timestamp 的Cell 的 timestamp。
3)setWriteToWAL:WAL 是 Write Ahead Log 的缩写,指的是 HBase 在插入操作前是否写 Log。默认是打开,关掉会提高性能,但是如果系统出现故障(负责插入的Region Server 挂掉),数据可能会丢失。
另外 HTable 也有两个方法会影响插入的性能。
1)setAutoFlash:AutoFlush 指的是在每次调用 HBase 的 Put 操作,是否提交到 HBase Server。默认是 true,每次会提交。如果此时是单条插入,就会有更多的I/O,从而降低其性能。
2)setWriteBufferSize:Write Buffer Size 在 AutoFlush 为false 的时候起作用,默认是 2MB,也就是插入数据超过 2MB,就会自动提交到 Server。
示例代码如下。
HTable table = new HTable(hbaseConfig, tableName); table.setAutoFlush(autoFlush); List< Put> lp = new ArrayList< Put>(); int count = 10000; byte[] buffer = new byte[1024]; Random r = new Random(); for(int i = 1;i <= count;++i){ Put p = new Put(String.format("row%09d",i).getBytes()); r.nextBytes(buffer); p.add("f1".getBytes(), null, buffer); p.add("f2".getBytes(), null, buffer); p.add("f3".getBytes(), null, buffer); p.add("f4".getBytes(), null, buffer); p.setWriteToWAL(wal); lp.add(p); if(i%1000==0){ table.put(lp); lp.clear(); } }
7、 删除数据
HTable 通过 delete 方法删除数据。
public void delete(final Delete delete)
Delete 构造方法如下。
public Delete(byte [] row) public Delete(byte [] row, long timestamp, RowLock rowLock) public Delete(final Delete d)
Delete 常用方法有 deleteFamily/deleteColumn,用来指定要删除的 Family 或者 Column 的数据。 如果不调用任何这样的方法,将会删除整行。
示例代码如下。
HTable table = new HTable(hbaseConfig,"mytest"); Delete d = new Delete("row1".getBytes()); table.delete(d)
8、 切分表
HBaseAdmin 提供 split 方法将 table 进行切分。
public void split(final String tableNameOrRegionName)
如果提供的是 tableName,会将 table 所有 Region 进行切分;如果提供的是 RegionName,只会切分这个Region。Split 是一个异步操作,因此它并不能确切控制 Region 的个数。
示例代码如下。
public void split(String tableName,int number,int timeout) throws Exception{ Configuration HBASE_CONFIG = new Configuration(); HBASE_CONFIG.set("hbase.ZooKeeper.quorum",GlobalConf.ZOOKEEPER_QUORUM); HBaseConfiguration cfg = new HBaseConfiguration(HBASE_CONFIG); HBaseAdmin hAdmin = new HBaseAdmin(cfg); HTable hTable = new HTable(cfg,tableName); int oldsize = 0; long time = System.currentTimeMillis(); while(true){ int size = hTable.getRegionsInfo().size(); logger.info("the region number="+size); if(size>=number) break; if(size !=oldsize){ hAdmin.split(hTable.getTableName()); oldsize = size; }else if(System.currentTimeMillis()-time>timeout){ break; } Thread.sleep(1000*10); } }
MapReduce 操作 HBase
在 HBase 系统上运行批处理运算,最方便和实用的模型依然是 MapReduce,如下图所示。
HBase Table 和 Region 的关系类似 HDFS File 和 Block 的关系,HBase提供配套的 TableInputFormat 和 TableOutputFormat API,可以方便地将 HBase Table 作为 Hadoop MapReduce 的Source 和 Sink。对于 MapReduce Job 应用开发人员来说,基本不需要关注 HBase 系统本身的细节。
org.apache.hadoop.hbase.mapreduce 包中的类和工具用来将 HBase 作为 MapReduce 作业的输出
1、 HBaseConfiguration 类
org.apache.hadoop.hbase.HbaseConfiguration 继承了 org.apache.hadoop.conf.Configuration 类, 创建一个 HBaseConfiguration 对象实例,会返回读入 CLASSPATH 下 hbase-site.xml 文件和hbase-default.xml 文件中 HBase 配置信息的一个 Configuration,该Configuration 接下来会用于 创建 HBaseAdmin 和 HTable 实例。
HBaseAdmin 和 HTable 两个类在 org.apache.hadoop.hbase.client 包中,HBaseAdmin 用于管理 HBase 集群、添加和删除表,HTable 用于访问指定的表,Configuration 实例指向了执行这些代码的集群上的这些类。
2、 HtableDescriptor 类和 HColumnDescriptor 类
HBase 中表结构由 HTableDescriptor 描述(包括 HColumnDescriptor),对表的新增、修改、删除操作在接口 HMasterInterface 中定义,而该接口由 HMaster 实现。
HTabledescriptor包含:
1)表名、byte[] 和 String 格式。
2)表的元信息,以Key-Value形式存储。存储信息包括文件最大的大小(默认256MB)、是否只读、Flush 时内存占用大小(默认64MB)、是否 root 或 Meta Region、DEFERRED_LOG_FLUSH。
3)表的各 Family 描述 HColumnDescriptor。
HColumnDescriptor 描述 Column Family 的信息,包括:
1)压缩格式(不压缩或仅压缩 Value、压缩 Block 中的一系列记录)。
2)数据的版本数量。
3)Block 的大小。
4)是否在内存中。
5)是否 Cache Block。
6)是否使用 bloomfilter。
7)Cell 内容的存活时间。
8)是否复制。
当一个 Column Family 创建后,其参数不能修改,除非删除该 Column Family 后新建一个, 但删除 Column Family 也会删除该 Column Family 下的数据。另外,HTableDescriptor 中包含 ROOT_TABLEDESC 和 META_TABLEDESC 两个实例以描述-ROOT- 和 .META 表。
1)ROOT_TABLEDESC 包含一个 info 的 Column Family。
2)META_TABLEDESC 包含一个 info 和 historian,两个 Column Family。
3、 CreateTable 方法
如果指定 Split Key 为该 Table 按指定键初始创建多个 Region,否则仅创建一个 Region,过程如下。
1) 为 Table 创建 HRegionInfo
2) 判断是不是所有的 Meta Region 都 online(由 RegionManager 的 MetaScanner 扫描线程分配 Meta Region)。
3) 判断 ServerManager 是否有足够 Region Server 来创建 table。
从 RegionManager 的 online MetaRegion 查找该 HRegionInfo 应放入哪一个 MetaRegion 中,在 onlineMetaRegion 中查找仅比RegionName 小的 MetaRegion,而RegionName 由 tableName、起始 Key 和 regionId(root 为0,meta 为1,user 当前时间)组成,同时 Master 的 ServerConnection 获取 HRegionInterface 代理连接到该 MetaRegion,并查找对应该 Table 为 Key 的记录是否存在,若存在则报错该表已经存在,由 RegionManager 根据 HRegionInfo 创建新的 user。Region 在 rootDir 目录下 新建以 tableName 为名的目录,在 tableName 目录下新建一个 Region 的目录(经编码后的 RegionName),并新建一个 HRegion 对象。
disable、enable 和 delete 等操作封装在继承自 TableOperation 的类中,该类先获得要操作表的所有 MetaRegion,扫描这些 MetaRegion 中 所有该表的 user Region 信息并做相应处理,最后处理 MetaRegion。
4、 TableInputFormat 类
通过设置 conf.set(TableInputFormat.INPUT_TABLE,"tableName") 设定 HBase 的输入表,tableName 为表名。
设置 conf.set(TableInputFormat.SCAN,TableMRUtil.convertScanToString(scan)),设定对 HBase 输入表的 scan 方式。
setTable(new HTable(new Configuration(conf),tableName));
通过 TableInputFormat.setConf(Configuration conf) 方法初始化 scan 对象;scan 对象是从 Job 中设置的对象,以字符串的形式传给 TableInputFormat,在 TableInputFormat 内部将 scan 字符串转换为 scan 对象,操作如下。
scan = TableMapReduceUtil.convertStringtoScan(conf.get(SCAN))
TableInputFormat 继承 TableInputFormatBase 实现了 InputFormat 抽象类的两个抽象方法 getSplits() 和 createRecordReader()。
getSplits() 判定输入对象的切分原则,原则是对于 TableInputFormatBase,会遍历HBase 相应表的所有 HRegion,每一个 HRegion 都会被切分成一个 Split,所以切分的块数与表中 HRegion 的数目是相同的,代码如下。
InputSplit split = new TableSplit(table.getTableName(),splitStart,splitStop,regionLocation);
在 Split 中只会记载 HRegion 的起始 Row Key 和 结束Row Key,具体去读取这片区域的数据是 createRecordReader() 实现的。
对于一个 Map 任务,JobTracker 会考虑 TaskTracker 的网络位置,并选取一个距离其输入分片文件的最近的 TaskTracker。在理想情况下,任务是数据本地化的(data-local), 也就是任务运行在输入分片所在的节点上。同样,任务也可能是机器本地化的;任务和输入分片在同一个机架,但不在同一个节点上。
对于Reduce 任务,JobTracker 简单地从待运行的 Reduce 任务列表中选取下一个来运行,用不着考虑数据段本地化。
createRecordReader() 按照必然格式读取响应数据,接受 Split 块,返回读取记录的结果,操作代码如下。
public RecordReader< ImmutableBytesWritable,Result> createRecordReader(InputSplit split,TaskAttemptContext context){ Scan scan = new Scan(this.scan); scan.setStartRow(tableSplit.getStartRow()); scan.setStopRow(tableSplit.getEndRow()); tableRecorderReader.setScan(scan); tableRecorderReader.setHTable(table); tableRecorderReader.init(); return tableRecorderReader; }
tableRecorderReader.init() 返回的是这个分块的起始 Row Key 的记录;RecordReader 将一个 Split 解析成 < Key, Value> 的形式 提供给 map 函数,Key 就是Row Key,Value 就是对应的一行数据。
RecorderReader 用于在划分中读取 < Key,Value> 对。RecorderReader 有5 个虚方法,下面分别进行介绍。
1、initialize:初始化,输入参数包括该 Reader 工作的数据划分 InputSplit 和 Job 的上下文 context。
2、nextKey:得到输入的下一个 Key,如果数据划分已经没有新的记录,返回空。
3、nextValue:得到 Key 对应的 Value,必须在调用 nextKey 后调用。
4、 getProgress:得到现在的进度。
5、close:来自 java.io 的Closeable 接口,用于清理 RecorderReader。
在 MapReduce 驱动中调用 TableInputFormat 的类:
job.setInputFormatClass(TableInputFormat.class);
使用以下方法就不需要再单独定义。
tableMapReduceUtil.initTableReducerJob("daily_result", DailyReduce.class, job);
initTableReducerJob() 方法完成一系列操作。
1) job.setOutputFormatClass(TableOutputFormat.class);设置输出格式。 2)conf.set(TableOutputFormat.OUTPUT_TABLE,table);设置输出表。 3) 初始化 partition。
向 HBase 中写入数据
本节介绍利用 MapReduce 操作 HBase,首先介绍如何上传数据,借助最熟悉的 WordCount 案例,将 WordCount 的结果存储到 HBase 而不是 HDFS。
输入文件 test.txt 的内容为:
hello hadoop hadoop is easy
1) 编写 Mapper 函数。
public static class WordCountMapperHbase extends Mapper< Object, Text, ImmutableBytesWritable, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text word = new Text(); public void map(Object key, Text value, Context context) throws IOException, InterruptedException { StringTokenizer itr = new StringTokenizer(value.toString()); while (itr.hasMoreTokens()) { word.set(itr.nextToken()); //输出到hbase的key类型为ImmutableBytesWritable context.write(new ImmutableBytesWritable(Bytes.toBytes(word.toString())), one); } } }
2) 编写 Reducer 类。
public class WordCountReducerHbase extends TableReducer< ImmutableBytesWritable, IntWritable, ImmutableBytesWritable> { private IntWritable result = new IntWritable(); public void reduce(ImmutableBytesWritable key, Iterable< IntWritable> values, Context context) throws IOException, InterruptedException { int sum = 0; for (IntWritable val : values) { sum += val.get(); } Put put = new Put(key.get());//put 实例化 key代表主键,每个单词存一行 //三个参数分别为 列簇为content,列修饰符为count,列值为词频 put.add(Bytes.toBytes("content"), Bytes.toBytes("count"), Bytes.toBytes(String.valueOf(sum))); context.write(key , put); } }
3) 编写驱动类。
import java.io.IOException; import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil; import org.apache.hadoop.hbase.mapreduce.TableReducer; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; /** * * 将hdfs中的数据导入hbase * */ public class MapReduceWriteHbaseDriver { public static void main(String[] args)throws Exception { String tableName = "wordcount";//hbase 数据库表名 Configuration conf=HBaseConfiguration.create(); //实例化Configuration conf.set("hbase.zookeeper.quorum", "dajiangtai"); conf.set("hbase.zookeeper.property.clientPort", "2181");//端口号 //如果表已经存在就先删除 HBaseAdmin admin = new HBaseAdmin(conf); if(admin.tableExists(tableName)){ admin.disableTable(tableName); admin.deleteTable(tableName); } HTableDescriptor htd = new HTableDescriptor(tableName); HColumnDescriptor hcd = new HColumnDescriptor("content"); htd.addFamily(hcd);//创建列簇 admin.createTable(htd);//创建表 Job job=new Job(conf,"import from hdfs to hbase"); job.setJarByClass(MapReduceWriteHbaseDriver.class); job.setMapperClass(WordCountMapperHbase.class); //设置插入hbase时的相关操作 TableMapReduceUtil.initTableReducerJob(tableName, WordCountReducerHbase.class, job, null, null, null, null, false); job.setMapOutputKeyClass(ImmutableBytesWritable.class); job.setMapOutputValueClass(IntWritable.class); job.setOutputKeyClass(ImmutableBytesWritable.class); job.setOutputValueClass(Put.class); FileInputFormat.addInputPaths(job, "hdfs://dajiangtai:9000/dajiangtai/test.txt"); System.exit(job.waitForCompletion(true) ? 0 : 1); } }
数据插入hbase数据库后,查询结果如下所示。
hbase(main):003:0> scan 'wordcount' ROW COLUMN+CELL easy column=content:count, timestamp=1439181396202, value=1 hadoop column=content:count, timestamp=1439181396202, value=2 hello column=content:count, timestamp=1439181396202, value=1 is column=content:count, timestamp=1439181396202, value=1 4 row(s) in 0.3890 seconds
读取 HBase 中的数据
下面介绍如何读取 HBase 中的数据。读取数据比较简单,编写 Mapper 函数,读取 < Key,Value> 值,通过 Reducer 函数直接输出得到的结果就行了。
1) 编写 Mapper 函数。
public class WordCountHBaseMapper extends TableMapper< Text, Text> { @Override protected void map(ImmutableBytesWritable key, Result values, Context context) throws IOException, InterruptedException { StringBuffer sb = new StringBuffer(""); //获取列族content下面所有的值 for (java.util.Map.Entry< byte[], byte[]> value : values .getFamilyMap("content".getBytes()).entrySet()) { String str = new String(value.getValue()); if (str != null) { sb.append(str); } context.write(new Text(key.get()), new Text(new String(sb))); } } }
2) 实现 Reducer 类。
public class WordCountHBaseReducer extends Reducer< Text, Text, Text, Text> { private Text result = new Text(); public void reduce(Text key, Iterable< Text> values, Context context) throws IOException, InterruptedException { for (Text val : values) { result.set(val); context.write(key, result); } } }
3) 编写驱动类。
import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil; import org.apache.hadoop.hbase.mapreduce.TableMapper; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; /* * 读取hbase中的数据 */ public class MapReduceReaderHbaseDriver { public static void main(String[] args)throws Exception { String tableName = "wordcount";//hbase表名称 Configuration conf=HBaseConfiguration.create(); //实例化 Configuration conf.set("hbase.zookeeper.quorum", "dajiangtai"); //hbase服务地址 conf.set("hbase.zookeeper.property.clientPort", "2181");//端口号 Job job=new Job(conf,"import from hbase to hdfs"); job.setJarByClass(MapReduceReaderHbaseDriver.class); job.setReducerClass(WordCountHBaseReducer.class); //设置读取hbase时的相关操作 TableMapReduceUtil.initTableMapperJob(tableName, new Scan(), WordCountHBaseMapper.class, Text.class, Text.class, job, false); FileOutputFormat.setOutputPath(job, new Path("hdfs://dajiangtai:9000/dajiangtai/out")); System.exit(job.waitForCompletion(true) ? 0 : 1); } }
从hbase读取数据,输出到hdfs的数据如下所示。
easy 1 hadoop 2 hello 1 is 1
为什么要引入列族?
答:列族主要用来在存储上对相关的列分组,从而使得减少对无关列的访问来提高性能。
COLUMN和COLUMNS的区别
答:scan操作中的COLUMNS指定的是表的列族,get操作中的COLUMN指定的是特定的列,COLUMN的值实质上为“列族+:+列修饰符”。
HBase的索引有哪些
答:HBase是一个类似BigTable的分布式数据库,它是一个稀疏的长期存储的(在硬盘上)、多维度的、排序的映射表。这张表的索引是行关键字、列关键字和时间戳。HBase中的数据都是字符串,没有类型。
HBase架构中主要的组件
.Zookeeper
.管理目录表的主节点
.分区(Region)服务器
Memstore
WAL
快缓存
.分区
Memstore
HFile
.0个或多个文件:分区表示表中一个列族的文件集合
.布隆过滤
HBase具有的特点
答:线性和模块化可扩展性
严格一致的读取和写入
表的自动配置和分片
支持RegionServers之间的自动故障转移
方便的基类支持Hadoop的MapReduce作业与Apache HBase的表
易于使用的Java API的客户端访问
块缓存和布鲁姆过滤器实时查询
Thrift网关和REST-FUL Web服务支持XML、protobuf和二进制的数据编码选项
可扩展的基于JRuby(JIRB)的脚本
支持监控信息通过Hadoop子系统导出到文件或Ganglia
HBase 简介(强烈推荐看)的更多相关文章
- 【转帖】HBase简介(梳理知识)
HBase简介(梳理知识) https://www.cnblogs.com/muhongxin/p/9471445.html 一. 简介 hbase是bigtable的开源山寨版本.是建立的hdf ...
- HBase简介
HBase简介 HBase – Hadoop Database,是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群. HB ...
- HBase简介及原理
HBase简介 1.HBase是一个万亿行,百万列大表(Big Table),数据存放在hdfs集群中: 写操作使用MapReduce处理,将(增删改)处理结果放入HBase中,读就直接读HBase: ...
- hbase学习(一)hbase简介
1.hadoop生态系统 2.hbase简介 非关系型数据库知识面扩展 cassandra.hbase.mongodb.redis couchdb,文件存储数据库 Neo4j非关系型图数据库 3.hb ...
- HBase 学习之路(一)—— HBase简介
一.Hadoop的局限 HBase是一个构建在Hadoop文件系统之上的面向列的数据库管理系统. 要想明白为什么产生HBase,就需要先了解一下Hadoop存在的限制?Hadoop可以通过HDFS来存 ...
- HBase 系列(一)—— HBase 简介
一.Hadoop的局限 HBase 是一个构建在 Hadoop 文件系统之上的面向列的数据库管理系统. 要想明白为什么产生 HBase,就需要先了解一下 Hadoop 存在的限制?Hadoop 可以通 ...
- 1.Hbase简介
1. Hbase简介 1.1. 什么是hbase(面向列) HBASE是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBASE技术可在廉价PC Server上搭建起大规模 结构化存储集群 ...
- HBase简介(梳理知识)
一. 简介 hbase是bigtable的开源山寨版本.是建立的hdfs之上,提供高可靠性.高性能.列存储.可伸缩.实时读写的数据库系统.它介于nosql和RDBMS之间,仅能通过主键(row key ...
- HA简介以及HBase简介
HBase基础知识: 一,HMater节点:可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master运行 1,为Region server 分配r ...
随机推荐
- Java 对象属性的遍历
package com.cn.mybatis.test; import java.io.IOException; import java.io.InputStream; import java.lan ...
- CI 自动操作日志
在控制器中,继承一个总控制器,MY_Controller,让其他集成的控制器,继承my控制器 在MY_Controller控制器中,重写构造方法, 代码如下,测试pass! class MY_Cont ...
- OC基础-day04
#pragma mark - Day04_01_匿名对象 1. 如果函数有返回值 我们可以不使用变量接收返回值. 而是直接将函数写在要使用其返回值的地方. 2. 正常情况下.我创建对象. 是使用了1 ...
- 系统后台图表生成文档说明-javascript
1.引入jquery插件文件datas.js 2.各图表分类 表格 $('#'+tableId).mTable({ url:'', //数据来源,[必填] pageNum:1, //分页,默认为1,[ ...
- POJ 3181 Dollar Dayz(高精度 动态规划)
题目链接:http://poj.org/problem?id=3181 题目大意:用1,2...K元的硬币,凑成N元的方案数. Sample Input 5 3 Sample Output 5 分析: ...
- PHP Ajax简单实例
最近学习Jquery Ajax部分,通过简单例子,比较了下post,get方法的不同 HTML部分 <html> <head> <title>jQuery Ajax ...
- Apache server-status
1.找到apache配置文件:httpd.conf 2.打开模块: LoadModule status_module modules/mod_status.so 3.在文件末尾处加上以下代码: ...
- 2016030102 - Ubuntu软件安装与删除相关命令
apt-get, dkpg 常用命令: 安装软件命令: apt-get install softname1 softname2 softname3…… 卸载软件命令: apt-get remove s ...
- python相关的工具
在使用python的时候,发现Adaconda工具包真的很不错,里面集合了很多的工具,并且,自带了很多的python常用模块 另外,PyCharm编辑器也是不错的,界面清晰,可以实现数据的可视化
- spoj PARTIT
三维DP 第K字典序从左向右找 根据dp数组的值算出每一位该打印什么 代码: #include <cstdio> #include <cstring> using namesp ...