Phoenix的几种方法调用： 批处理方式 命令行方式 GUI方式 JDBC调用方式 一、批处理方式 1、创建user_phoenix.sql文件 内容如下 CREATE TABLE IF NOT EXISTS user_phoenix ( state CHAR(2) NOT NULL, city VARCHAR NOT NULL, population BIGINT CONSTRAINT my_pk PRIMARY KEY (state, city) ); 2、创建user_phoenix.csv数据文件 NY,New York,8143197 CA,Los Angeles,3844829 IL,Chicago,2842518 TX,Houston,2016582 PA,Philadelphia,1463281 AZ,Phoenix,1461575 TX,San Antonio,1256509 CA,San Diego,1255540 TX,Dallas,1213825 CA,San Jose,912332 3、创建user_phoenix_query.sql文件 SELECT state as "State",count(city) as "City Count",sum(population) as "Population Sum" FROM user_phoenix GROUP BY state ORDER BY sum(population) DESC; 4、执行 python /export/servers/phoenix-4.14.0-cdh5.14.2/bin/psql.py node01:2181 user_phoenix.sql user_phoenix.csv user_phoenix_query.sql 这条命令同时做了三件事：创建表、插入数据、查询结果。 用Hbase shell 看下会发现多出来一个 USER_PHOENIX 表，用scan 命令查看一下这个表的数据 hbase(main):002:0> scan 'USER_PHOENIX' 结论： 1、之前定义的Primary key 为state,city, 于是Phoenix就把输入的state，city的值拼起来成为rowkey。 2、其他的字段还是按照列名去保存，默认的列族为0。 3、还有一个0:_0这个列是没有值的，这个是Phoenix处于性能方面考虑增加的一个列，不用管这个列。 二、命令行方式 1、执行命令 ./sqlline.py node01:2181 可以进入到命令行模式 2、相关命令 开头需要一个感叹号 使用help可以打印出所有命令 !all Execute the specified SQL against all the current connections !appconfig Set custom application configuration class name !autocommit Set autocommit mode on or off !batch Start or execute a batch of statements !brief Set verbose mode off !call Execute a callable statement !close Close the current connection to the database !closeall Close all current open connections !columns List all the columns for the specified table !commandhandler Add a command handler !commit Commit the current transaction (if autocommit is off) !connect Open a new connection to the database !dbinfo Give metadata information about the database !describe Describe a table !dropall Drop all tables in the current database !exportedkeys List all the exported keys for the specified table !go Select the current connection !help Print a summary of command usage !history Display the command history -d Print timestamps for each event (default for !history) -f Print full time date stamps in the US format -E Print full time date stamps in the European format -i Print full time date stamps in ISO8601 format -n Suppresses command numbers -r Reverses the order of the commands !importedkeys List all the imported keys for the specified table !indexes List all the indexes for the specified table !isolation Set the transaction isolation for this connection !list List the current connections !manual Display the SQLLine manual !metadata Obtain metadata information !nativesql Show the native SQL for the specified statement !nickname Create a friendly name for the connection (updates command prompt) !outputformat Set the output format for displaying results (table, vertical, csv, tsv, xmlattrs, xmlelements, json) !primarykeys List all the primary keys for the specified table !procedures List all the procedures !prompthandler Set custom prompt handler class name !properties Connect to the database specified in the properties file(s) !quit Exits the program !readonly Set readonly mode on or off !reconnect Reconnect to the database !record Record all output to the specified file !rehash Fetch table and column names for command completion !rerun Execute previous command from the history file !reset Reset a sqlline variable !rollback Roll back the current transaction (if autocommit is off) !run Run a script from the specified file !save Save the current variables and aliases !scan Scan for installed JDBC drivers !schemas List all the schemas in the database !script Start saving a script to a file !set List / set a sqlline variable !sql Execute a SQL command !tables List all the tables in the database !typeinfo Display the type map for the current connection !verbose Set verbose mode on 解释： !autocommit 打开或关闭自动提交模式 !batch 启动或执行一批语句 !brief 关闭详细模式 !call 执行可调用语句 !close 关闭与数据库的当前连接 !closeall 关闭所有当前打开的连接 !columns 列出指定表的所有列 !commit 提交当前事务（如果自动提交已关闭） !connect 打开与数据库的新连接。 !dbinfo 提供有关数据库的元数据信息 !describe 描述一个表 !dropall 删除当前数据库中的所有表 !exportedkeys 列出指定表的所有导出键 !go 选择当前连接 !help 打印命令使用情况摘要 !history 显示命令历史记录 !importedkeys 列出指定表的所有导入键 !indexes 列出指定表的所有索引 !isolation 为此连接设置事务隔离 !list 列出当前连接 !manual 显示sqlline手册 !metadata 获取元数据信息 !nativesql 显示指定语句的本机SQL !outputformat 设置显示结果的输出格式（表格、垂直、csv、tsv、xmlattrs、xmleelements） !primarykeys 列出指定表的所有主键 !procedures 列出所有程序 !properties 连接到属性文件中指定的数据库 !quit 退出程序 !reconnect 重新连接到数据库 !record 将所有输出记录到指定文件 !rehash 获取用于完成命令的表和列名称 !rollback 回滚当前事务（如果关闭自动提交） !run 从指定的文件运行脚本 !save 保存当前变量和别名 !scan 扫描已安装的JDBC驱动程序 !script 开始将脚本保存到文件 !set 设置sqlline变量 3、建立employee的映射表—数据准备 数据准备然后我们来建立一个映射表，映射我之前建立过的一个hbase表 employee.有2个列族 company、family。 create 'employee1','company','family' put 'employee1','row1','company:name','ted' put 'employee1','row1','company:position','worker' put 'employee1','row1','family:tel','136009123xx' put 'employee1','row2','company:name','michael' put 'employee1','row2','company:position','manager' put 'employee1','row2','family:tel','189422xxxx' scan 'employee1' 在建立映射表之前要说明的是，Phoenix是大小写敏感的，并且所有命令都是大写，如果你建的表名没有用双引号括起来，那么无论你输入的是大写还是小写，建立出来的表名都是大写的，如果你需要建立出同时包含大写和小写的表名和字段名，请把表名或者字段名用双引号括起来。 可以建立读写的表或者只读的表，他们的区别如下： 读写表：如果你定义的列簇不存在，会被自动建立出来，并且赋以空值。 只读表：你定义的列簇必须事先存在。 4、建立映射表 0: jdbc:phoenix:node01> CREATE TABLE IF NOT EXISTS "employee1" ( "no" VARCHAR(10) NOT NULL PRIMARY KEY, "company"."name" VARCHAR(30), "company"."position" VARCHAR(20), "family"."tel" VARCHAR(20), "family"."age" INTEGER ); 这个语句有几个注意点： 1、IF NOT EXISTS可以保证如果已经有建立过这个表，配置不会被覆盖。 2、作为rowkey的字段用 PRIMARY KEY标定。 3、列簇用 columnFamily.columnName 来表示。 4、family.age 是新增的字段，之前建立测试数据的时候没有建立这个字段的原因是在hbase shell下无法直接写入数字型，使用UPSERT 命令插入数据的时候就可以看到真正的数字型在hbase 下是如何显示的。 5、查询映射表数据 0: jdbc:phoenix:node01> SELECT * FROM "employee"; +-------+----------+-----------+--------------+-------+ | no | name | position | tel | age | +-------+----------+-----------+--------------+-------+ | row1 | ted | worker | 136009123xx | null | | row2 | michael | manager | 189422xxxx | null | +-------+----------+-----------+--------------+-------+ 6、入数据、更改数据 插入或者更改数据在phoenix中使用upsert关键字， 如果表中不存在该数据则插入,否则更新 插入： 0:jdbc:phoenix:node01> upsert into "employee" values('row3','billy','worker','1697468xxxx',33); 修改数据： 0: jdbc:phoenix:node01:2181> upsert into "employee" ("no","tel") VALUES ('row2','1358888xxxx'); 查询： 0: jdbc:phoenix:node01:2181> select * from "employee"; +-------+----------+-----------+--------------+-------+ | no | name | position | tel | age | +-------+----------+-----------+--------------+-------+ | row1 | ted | worker | 136009123xx | null | | row2 | michael | manager | 13588888888 | null | | row3 | billy | worker | 1697468xxxx | 33 | +-------+----------+-----------+--------------+-------+ 3 rows selected (0.06 seconds) 7、查询Hbase数据 hbase(main):056:0> scan 'employee' ROW COLUMN+CELL row1 column=company:_0, timestamp=1484730892661, value= row1 column=company:name, timestamp=1484730892527, value=ted row1 column=company:position, timestamp=1484730892565, value=worker row1 column=family:tel, timestamp=1484730892661, value=13600912345 row2 column=company:_0, timestamp=1484730892762, value= row2 column=company:name, timestamp=1484730892702, value=michael row2 column=company:position, timestamp=1484730892730, value=manager row2 column=family:tel, timestamp=1484730892762, value=13588888888 row3 column=company:_0, timestamp=1484809036587, value=x row3 column=company:name, timestamp=1484809036587, value=billy row3 column=company:position, timestamp=1484809036587, value=worker row3 column=family:age, timestamp=1484809036587, value=\x80\x00\x00! row3 column=family:tel, timestamp=1484809036587, value=16974681345 company:_0这个列是没有值的，这个是Phoenix处于性能方面考虑增加的一个列，不用管这个列。 参考资料： https://cloud.tencent.com/developer/article/1779519?areaSource=103001.15&traceId=sP1ZCWjDc2Fe9ovREXEnR
一、前言 HBase 由于它存储和读写的高性能，在 OLAP 即时分析中发挥着重要的作用。而 RowKey 作为 HBase 的核心知识点，其设计势必会影响到数据在 HBase 中的分布，还会影响我们查询效率，可以说 RowKey 的设计质量关乎了 HBase 的质量。 对于关系型数据库，数据定位可以理解为“二维坐标”；但在 HBase 中，定位一条数据（即一个 Cell）我们需要 4 个维度的限定：行键（RowKey）、列族（Column Family）、列限定符（Column Qualifier）、时间戳（Timestamp）。其中，RowKey 是最容易出现问题的。除了根据业务和查询需求来设计之外，还有很多地方需要我们注意。 二、RowKey 概念 HBase 中 RowKey 可以唯一标识一行记录，在 HBase 查询的时候有以下几种方式： 通过 get 方式，指定 RowKey 获取唯一一条记录。 通过 scan 方式，设置 startRow 和 stopRow 参数进行范围匹配。 全表扫描，即直接扫描整张表中所有行记录。 从字面意思来看，RowKey 就是行键的意思，在曾删改查的过程中充当了主键的作用。它可以是任意字符串，在 HBase 内部 RowKey 保存为字节数组。 HBase 中的数据是按照 Rowkey 的 ASCII 字典顺序进行全局排序的，有伙伴可能对 ASCII 字典序印象不够深刻，下面举例说明： 假如有 5 个 Rowkey："012", "0", "123", "234", "3"，按 ASCII 字典排序后的结果为："0", "012","123", "234", "3"。 因此设计 RowKey 时，需要充分利用排序存储这个特性，将经常一起读取的行存储放到一起，要避免做全表扫描，因为效率特别低。 三、什么是数据热点？ 3.1、热点现象产生 HBase 中的行是按照 Rowkey 的字典顺序排序的，这种设计优化了 scan 操作，可以将相关的行以及会被一起读取的行存取在临近位置，便于 scan。 然而糟糕的 Rowkey 设计是热点的源头。 热点发生在大量的 client 直接访问集群的一个或极少数个节点（访问可能是读，写或者其他操作）。 大量访问会使热点 region 所在的单个机器超出自身承受能力，引起性能下降甚至 region 不可用，这也会影响同一个 RegionServer 上的其他 region，由于主机无法服务其他 region 的请求，这样就造成数据热点现象。 （这一点其实和数据倾斜类似） 所以在向 HBase 中插入数据的时候，应优化 RowKey 的设计，使数据被写入集群的多个 region，而不是一个。尽量均衡地把记录分散到不同的 Region 中去，平衡每个 Region 的压力。 3.2、避免数据热点的方法 在日常使用中，主要有 3 个方法来避免热点现象，分别是反转，加盐和哈希，下面逐个举例分析： 3.2.1、反转（Reversing） 第一种咱们要分析的方法是反转，顾名思义它就是把固定长度或者数字格式的 RowKey 进行反转，反转分为一般数据反转和时间戳反转，其中以时间戳反转较常见。 反转固定格式的数值以手机号为例，手机号的前缀变化比较少（如152、185等），但后半部分变化很多。如果将它反转过来，可以有效地避免热点。不过其缺点就是失去了有序性。 反转时间这个操作严格来讲不算“打散”，但可以调整数据的时间排序。如果将时间按照字典序排列，最近产生的数据会排在旧数据后面。如果用一个大值减去时间（比如用 99999999 减去 yyyyMMdd，或者 Long.MAX_VALUE 减去 时间戳），最新的数据就可以排在前面了。 3.2.2、加盐（Salting） 这里的“加盐”与密码学中的“加盐”不是一回事。它是指在 RowKey 的前面增加一些前缀，加盐的前缀种类越多，RowKey 就被打得越散。 需要注意的是分配的随机前缀的种类数量应该和我们想把数据分散到的那些 region 的数量一致。只有这样，加盐之后的 rowkey 才会根据随机生成的前缀分散到各个 region 中，避免了热点现象。 3.2.3、哈希（Hashing） 其实哈希和加盐的使用场景类似，但前缀不可以是随机的，因为必须要让客户端能够完整地重构 RowKey。所以一般会拿原 RowKey 或其一部分计算 Hash 值，然后再对 Hash 值做运算作为前缀。 四、RowKey 的设计原则 通过前面的分析应该知道了 HBase 中 RowKey 设计的重要性了，为了帮助我们设计出完美的 RowKey，HBase 提出了 RowKey 的设计原则主要有以下四点：长度原则、唯一原则、排序原则、散列原则。 4.1、RowKey 长度原则 RowKey 是一个二进制码流，可以是任意字符串，最大长度 64kb ，实际应用中一般为 10-100bytes，以 byte[] 形式保存，一般设计成定长。建议越短越好，不要超过 16 个字节，原因如下： 在 HBase 的底层存储 HFile 中，RowKey 是 KeyValue 结构中的一个域。假设 RowKey 长度 100B，那么 1000 万条数据中，光 RowKey 就占用掉100*1000w=10亿个字节将近 1G 空间，这样会极大影响 HFile 的存储效率。 HBase 中设计有 MemStore 和 BlockCache，分别对应列族/Store 级别的写入缓存，和 RegionServer 级别的读取缓存。如果 RowKey 字段过长，内存的有效利用率就会降低，系统不能缓存更多的数据，这样会降低检索效率。 另外，我们目前使用的服务器操作系统都是 64 位系统，内存是按照 8B 对齐的，因此设计 RowKey 时一般做成 8B 的整数倍，如 16B 或者 24B，可以提高寻址效率。 同样地，列族、列名的命名在保证可读的情况下也应尽量短。value 永远和它的 key 一起传输的。当具体的值在系统间传输时，它的 RowKey，列名，时间戳也会一起传输（因此实际上列族命名几乎都用一个字母，比如‘c’或‘f’）。如果你的 RowKey 和列名和值相比较很大，那么你将会遇到一些有趣的问题。Hfile 中的索引最终占据了 HBase 分配的大量内存。 4.2、唯一原则 其实唯一原则咱们可以结合 HashMap 的源码设计或者主键的概念来理解，由于 RowKey 用来唯一标识一行记录，所以必须在设计上保证 RowKey 的唯一性。 需要注意：由于 HBase 中数据存储的格式是 Key-Value 对格式，所以如果向 HBase 中同一张表插入相同 RowKey 的数据，则原先存在的数据会被新的数据给覆盖掉（和 HashMap 效果相同）。 4.3、排序原则 RowKey 是按照字典顺序排序存储的，因此，设计 RowKey 的时候，要充分利用这个排序的特点，将经常读取的数据存储到一块，将最近可能会被访问的数据放到一块。 一个常见的数据处理问题是快速获取数据的最近版本，使用反转的时间戳作为 RowKey 的一部分对这个问题十分有用，可以用Long.Max_Value-timestamp追加到 key 的末尾。 例如key , [key]的最新值可以通过scan [key]获得[key]的第一条记录，因为 HBase 中 RowKey 是有序的，第一条记录是最后录入的数据。 4.4、散列原则 散列原则用大白话来讲就是咱们设计出的 RowKey 需要能够均匀的分布到各个 RegionServer 上。 比如设计 RowKey 的时候，当 Rowkey 是按时间戳的方式递增，就不要将时间放在二进制码的前面，可以将 Rowkey 的高位作为散列字段，由程序循环生成，可以在低位放时间字段，这样就可以提高数据均衡分布在每个 Regionserver 实现负载均衡的几率。 结合前面分析的热点现象的起因思考： 如果没有散列字段，首字段只有时间信息，那就会出现所有新数据都在一个 RegionServer 上堆积的热点现象，这样在做数据检索的时候负载将会集中在个别 RegionServer 上，降低查询效率。 五、举个栗子 实际业务中，有一部分是用户在日历上记录自己的行为。需要储存在 RowKey 中的维度有：用户 ID（uuid，不会超过十亿）、日历上的日期（date，yyyyMMdd 格式）、记录行为的类型（type，0~99 之间）。记录的详细数据则存储在列 f:data 中。根据查询逻辑，我们可以设计的 RowKey 格式如下： 9~79809782~05~0008839540 长度正好是 24B。以字符‘~’为分界（‘~’的 ASCII 码是最大的，方便），各个部分的含义如下： uuid.toString().hashCode() % 10 99999999 - date StringUtils.leftPad(type, 2, "0") StringUtils.leftPad(uuid, 10, "0")
一、简介 HBase scan 命令用来查询全表数据，使用时只需指定表名即可。 例如对于上面的 Student 表，使用下面的写法即可查询数据： scan 'Student' 同样地，还可以指定列族和列的名称，或指定输出行数，甚至指定输出行键范围。 scan 指定条件输出时，需要使用大括号将参数包含起来。 注意指定列族和列名称使用 COLUMN 限定符；指定输出行键范围使用 STARTROW 和 ENDROW 限定符，此时输出行不包括 ENDROW 行。例如，上图中 ENDROW=>0003，只会输出行键为 0002 的记录，不会输出 0003 记录。 上述限定条件也可以联合使用，中间用逗号隔开即可。 在 HBase 中，具有相同行键的单元格，无论其属于哪个列族，都可以将整体看作一个逻辑行， 使用 count 命令可以计算表的逻辑行数。 在关系型数据库中，有多少条记录就有多少行，表中的行数很容易统计。而在 HBase 里，计算逻辑行需要扫描全表的内容，重复的行键是不纳入计数的，且标记为 tombstone 的删除数据也不纳入计数。 执行 count 命令其实是一个开销较大的进程，特别是应用在大数据场景时，可能需要持续很长时间，因此，用户一般会结合 Hadoop 的 MapReduce 架构来进行分布式的扫描计数。 二、详解 hbase 中 scan 命令是经常使用到的，而 filter 的作用尤其强大。这里简要的介绍下 scan 下 filter 命令的使用。 2.1、插入 scan 命令需要的数据 这里模拟了部分微博评论的数据，然后使用代码插入数据到 hbase。 public class Comment { //1-->普通文章，2--->热点文章 Integer articleType; //文章id String articleId; String userId; long timestamp; //comment content,暂时只考虑文本 String commentContent; } hbase 的表名称为 zy_comment，列簇info下有 articleType 以及 commentInfo 两个列。commentInfo 的 value 为上面Comment 类的 json 字符串，插入的数据如下所示。 2.2、HBase数据顺序 HBase是三维有序存储的，是指 rowkey（行键），column key（column family和qualifier）和 TimeStamp（时间戳）这个三个维度是依照 ASCII 码表排序的。（比如A排在a前面）。 1、先 rowkey 升序排序， 2、rowkey 相同则 column key 升序排序 3、rowkey、column key 相同则 timestamp 降序排序 2.3、支持的Filter scan 命令经常会大量使用 Filter，hbase shell 提供的 filter 都可以在 hbase client 包中找到对应的类，它们都是 Filter 的子类，很多命令都是通过 filter 来进行实现的。 使用 show_filters 命令查看 shell 中定义了哪些 filter 常量，如果想要使用 shell 中未定义的常量，在使用的时候必须手动 import filter 的全路径。 2.4、scan 的用法 使用 help 'scan' 命令可以查看 scan 的语法以及用法，关于 scan 命令中 filter 的使用规则如下: scan 'tableName',{FILTER=>"FilterName(param1,param2,...,paramN)"} {}中的语法是 ruby 的 map 的语法，FILTER 必须大写，filter 的参数是根据构造方法来的，也就是相当于 java 中的 new Filter(‘param1’,‘param2’) 等，这里只是省略了 new 参数而已。 当然同样可以使用 ruby 中 new 对象的方式，只是那样就必须使用全限定名称。后面会举一个全限定名称的例子。 在使用Filter的过程中部分filter会用到比较器 CompareOperator.java 以及运算比较符 ByteArrayComparable.java。 @Public public enum CompareOperator { LESS, // < LESS_OR_EQUAL, // <= EQUAL, // = NOT_EQUAL, // <> GREATER_OR_EQUAL, // >= GREATER, // > NO_OP; // 没有任何操作 private CompareOperator() { } } 比较器主要有以下几种: BinaryComparator 按字节索引顺序比较指定字节数组，采用 Bytes.compareTo(byte[])，比如:binary:\x00\x00\x02 BinaryPrefixComparator 跟前面相同，只是比较左端的数据是否相同,比如:binaryprefix:\x00\x00 NullComparator 判断给定的是否为空，不常用 BitComparator 按位比较 a BitwiseOp class 做异或，与，并操作，不常用 RegexStringComparator 提供一个正则的比较器，仅支持 EQUAL 和非EQUAL,比如:regexstring:ab*add SubstringComparator 判断提供的子串是否出现在table的value中。比如：substring:great 三、scan例子 查询方式通过 rowKey 来进行查询是最快的，所以 rowkey 的设计一定要合理，如果不合理会很影响查询速率。但是有时候确实没有办法完全通过 rowkey 来查询，所以就要借助 scan。 scan 命令支持的修饰词除了列（COLUMNS）修饰词外，HBase还支持Limit（限制查询结果行数），STARTROW（ROWKEY起始行。会先根据这个key定位到region，再向后扫描）、STOPROW(结束行)、TIMERANGE（限定时间戳范围）、VERSIONS（版本数）、和FILTER（按条件过滤行）等。 1、查询 user 是zhangsan的用户的评论数据，最多只返回10条 scan 'zy_comment',{LIMIT=>10,FILTER=>"PrefixFilter('zhangsan')"} 2、通过startrow,stoprow来进行查询(这种也比较快，实际操作中如果不能通过rowkey) scan 'zy_comment',{STARTROW=>'b',STOPROW='e',LIMIT=>10} 3、查询评论内容为666的评论有哪些 scan 'zy_comment',{LIMIT=>10,FILTER=>"ValueFilter(=,'substring:666')"} 4、查询热点文章的数据有哪些。这里binary中的数据一定要是二进制字符串而不是具体的值。 scan 'zy_comment',{LIMIT=>10,FILTER="SingleColumnValueFilter('info','articleType',=,'binary: \x00\x00\x00\x00\x00\x00\x00\x02')"} 5、查看评论包含great的热点文章评论有哪些 scan 'zy_comment',{LIMIT=>3,FILTER=>"(SingleColumnValueFilter('info','articleType',=,'binary:\x00\x00\x00\x00\x00\x00\x00\x02')) AND (ColumnPrefixFilter('commentInfo') AND ValueFilter(=,'substring:great'))"} 6、查询评论的前两条 scan 'zy_comment',{FILTER=>"PageFilter(2)"}和LIMIT有异曲同工之妙 7、查询rowkey中包含特定前缀的数据 scan 'zy_comment',{FILTER=>"RowFilter(=,'substring:zhangsan')"} 8、使用全限定名称查询articleId是123456的数据 import org.apache.hadoop.hbase.filter.SingleColumnValueFilter import org.apache.hadoop.hbase.filter.CompareFilter import org.apache.hadoop.hbase.filter.SubstringComparator scan 'zy_comment', {COLUMNS=>['info:commentInfo'], FILTER => SingleColumnValueFilter.new(Bytes.toBytes('info'), Bytes.toBytes('commentInfo'), CompareFilter::CompareOp.valueOf('EQUAL'), SubstringComparator.new('123456'))} 注意： 如果 scan 中指定了 COLUMNS，则FILTER中所使用的列需要包含在所指定的 COLUMNS 中，否则，filter 不起作用。 HBase 中主要的操作对象是一个个的 cell，每个 cell 都可以有多个版本。如果使用过滤器 ValueFilter，就会只有那些符合条件的 cell 被查出来。跟关系数据库的查询不同，关系数据库查出来的结果中各行都有相同的列。而 HBase，查出来的结果中，不同的行会有不同的列。 filter 不会降低服务方的 IO，它会把符合条件的子集传给客户端。即，它是在对查出的结果进行过滤，而不是象原来 sql 中的 where 子句。所以，如果要查出的结果中不包含 filter 需要的列，则 filter 就不能发挥作用。
一、简介 HBase 的名字的来源于 Hadoop database，即 hadoop 数据库，不同于一般的关系数据库，它是非结构化数据存储的数据库，而且它是基于列的而不是基于行的模式。 HBase 是一个分布式的、面向列的、基于 Google Bigtable 的开源实现。 利用 Hadoop HDFS 作为其文件存储系统，利用 Hadoop MapReduce 来处理HBase中的海量数据，利用Zookeeper作为协同服务。 二、HBase重要概念 HBase 的表结构 HBase 以表的形式存储数据。表有行和列组成。列划分为若干个列族/列簇(column family)，每个列族/列簇下面可以有多个普通列。 表 Table HBase 是用表来存储数据的。 命名空间 namespace namespace 命名空间指对一组表的逻辑分组，类似 RDBMS 中的 database，方便对表在业务上划分。 HBase 系统默认定义了两个缺省的 namespace： hbase：系统内建表，包含 namespace 和 meta 表。 default：用户建表时未指定 namespace 的表都创建在此。 行键 Row Key 行键，每一行的主键列，每行的行键要唯一，行键的值为任意字符串(最大长度是 64KB，实际应用中长度一般为 10-100bytes)，在 HBase 内部，rowKey 保存为字节数组 byte[]。行的一次读写是原子操作 (不论一次读写多少列)。 区域 Region Table在行的方向上分割为多个Region。 Region是按大小分割的，每个表开始只有一个region，随着数据的增多，region不断增大，当增大到一个阀值的时候，region就会等分为两个新的region,之后会有越来越多的region。 Region是HBase中分布式存储和负载均衡的最小单元。不同的Region分布到不同的RegionServer上。 Region由一个或者多个Store组成, 每个Store保存一个column family, 每个Store又由一个MemStore(存储在内存中)和0到多个StoreFile(存储在HDFS上)组成。 列族 column family 列族是每个子列的父级，每个子列都属于一个列族，一个列族包含一个或者多个相关列，创建表的时候需要指定列族，而列不需要必须指定。通过 列族名:列名 来表示某个具体的子列。HBase 中的 Schema 就是 TableName + Column Family Name。 列限定符 就是列族下的每个子列名称，或者称为相关列，或者称为限定符，只是翻译不同。 通过 columnFamily:column 来定位某个子列。 存储单元 cell 外观看到的每个单元格其实都对应着多个存储单元，默认情况下一个单元格对应着一个存储单元，一个存储单元可以存储一份数据，如果一个单元格有多个存储单元就表示一个单元格可以存储多个值。可以通过 version 来设置存储单元个数。可以通过 rowKey + columnFamily + column + timestamp来唯一确定一个存储单元。cell 中的数据是没有类型的，全部是字节码形式存贮。 hbase 按照时间戳降序排列各时间版本，其他映射建按照升序排序。 时间戳版本号 timestamp 每个 cell 都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由 hbase(在数据写入时自动 )赋值，此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突，就必须自己生成具有唯一性的时间戳。每个 cell 中，不同版本的数据按照时间倒序排序，即最新的数据排在最前面。 为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担，hbase提供了两种数据版本回收方式。一是保存数据的最后 n 个版本，二是保存最近一段时间内的版本（比如最近七天）。用户可以针对每个列族进行设置。 三、HBase Shell help 查看命令的使用描述，help ‘命令名’ whoami 我是谁，whoami version 返回 hbase 版本信息，version status 返回 hbase 集群的状态信息，status table_help 查看如何操作表，table_help create 创建表，create ‘表名’, ‘列族名1’, ‘列族名2’, ‘列族名N’ alter 修改列族，添加一个列族：alter ‘表名’, ‘列族名’ 删除列族：alter ‘表名’, {NAME=> ‘列族名’, METHOD=> ‘delete’} describe 显示表相关的详细信息，describe ‘表名’ list 列出hbase中存在的所有表，list exists 测试表是否存在，exists ‘表名’ put 添加或修改的表的值，put ‘表名’, ‘行键’, ‘列族名’, ‘列值’ put ‘表名’, ‘行键’, ‘列族名:列名’, ‘列值’ scan 通过对表的扫描来获取对用的值，scan ‘表名’ 扫描某个列族： scan ‘表名’, {COLUMN=>‘列族名’} 扫描某个列族的某个列： scan ‘表名’, {COLUMN=>‘列族名:列名’} 查询同一个列族的多个列： scan ‘表名’, {COLUMNS => [ ‘列族名1:列名1’, ‘列族名1:列名2’, …]} get 获取行或单元（cell）的值，get ‘表名’, ‘行键’ get ‘表名’, ‘行键’, ‘列族名’ count 统计表中行的数量，count ‘表名’ incr 增加指定表行或列的值，incr ‘表名’, ‘行键’, ‘列族:列名’, 步长值 get_counter 获取计数器，get_counter ‘表名’, ‘行键’, ‘列族:列名’ delete 删除指定对象的值（可以为表，行，列对应的值，另外也可以指定时间戳的值），删除列族的某个列： delete ‘表名’, ‘行键’, ‘列族名:列名’ deleteall 删除指定行的所有元素值，deleteall ‘表名’, ‘行键’ truncate 重新创建指定表，truncate ‘表名’ enable 使表有效，enable ‘表名’ is_enabled 是否启用，is_enabled ‘表名’ disable 使表无效，disable ‘表名’ is_disabled 是否无效，is_disabled ‘表名’ drop 删除表，drop的表必须是disable的 disable ‘表名’ drop ‘表名’ shutdown 关闭 hbase 集群（与exit不同） tools 列出 hbase 所支持的工具 exit 退出 hbase shell HBase Shell 是官方提供的一组命令，用于操作 HBase。如果配置了 HBase 的环境变量了，就可以知己在命令行中输入 hbase shell 命令进入命令行。 help 命令： 可以通过 help '命名名称' 来查看命令行的具体使用，包括命令的作用和用法。 通过help hbase命名来查看 hbase shell 支持的所有命令，hbase 将命令进行分组，其中 ddl、dml 使用较多。 四、general 命名 1、显示集群状态 status 可以为 summary, simple, detailed, replication。默认为 summary。 hbase> status hbase> status 'simple' hbase> status 'summary' hbase> status 'detailed' hbase> status 'replication' hbase> status 'replication', 'source' hbase> status 'replication', 'sink' 2、查询数据库版本 version 3、显示当前用户与组 whoami 4、查看操作表的命令 table_help 5、退出HBase Shell exit 五、ddl命令 1、创建表 create 注意：创建表时只需要指定列族名称，不需要指定列名。 # 语法 create '表名', { NAME => '列族名1'}, { NAME => '列族名2'}, { NAME => '列族名3'} # 此种方式是上上面的简写方式，使用上面方式可以为列族指定更多的属性，如VERSIONS、TTL、BLOCKCACHE、CONFIGURATION等属性 create '表名', '列族名1', '列族名2', '列族名3' create '表名', { NAME => '列族名1', VERSIONS => 版本号, TTL => 过期时间, BLOCKCACHE => true} # 示例 create 'tbl_user', 'info', 'detail' create 't1', { NAME => 'f1', VERSIONS => 1, TTL => 2592000, BLOCKCACHE => true} 2、 修改(添加、删除)表结构 Schema alter 2.1、添加一个列族 # 语法 alter '表名', '列族名' # 示例 alter 'tbl_user', 'address' 2.2、删除一个列族 # 语法 alter '表名', { NAME=> '列族名', METHOD=> 'delete'} # 示例 alter 'tbl_user', { NAME=> 'address', METHOD=> 'delete'} 2.3、修改列族的属性 可以修改列族的VERSIONS、IN_MEMORY # 修改f1列族的版本为5 alter 't1', NAME => 'f1', VERSIONS => 5 # 修改多个列族，修改f2为内存，版本号为5 alter 't1', 'f1', { NAME => 'f2', IN_MEMORY => true}, { NAME => 'f3', VERSIONS => 5} # 也可以修改table-scope属性，例如MAX_FILESIZE, READONLY,MEMSTORE_FLUSHSIZE, DEFERRED_LOG_FLUSH等。 # 例如，修改region的最大大小为128MB： alter 't1', MAX_FILESIZE => '134217728' 3、异步修改Schema alter_async # change or add the 'f1' column family in table 't1' from defaults to instead keep a maximum of 5 cell VERSIONS alter_async 't1', NAME => 'f1', VERSIONS => 5 # delete the 'f1' column family in table 'ns1:t1' alter_async 'ns1:t1', NAME => 'f1', METHOD => 'delete' alter_async 'ns1:t1', 'delete' => 'f1' # change the max size of a family to 128MB alter 't1', METHOD => 'table_att', MAX_FILESIZE => '134217728' alter 't1', { NAME => 'f1'}, { NAME => 'f2', METHOD => 'delete'} 4、获取 alter_async 执行的状态 alter_status alter_status '表名' 5、获取表的描述 describe # 语法 describe '表名' # 示例 describe 'tbl_user' 6、列举所有表 list 7、表是否存在 exists # 语法 exists '表名' # 示例 exists 'tbl_user' 8、启用表 enable 和禁用表 disable 通过enable和disable来启用/禁用这个表,相应的可以通过is_enabled和is_disabled来检查表是否被禁用。 # 语法 enable '表名' is_enabled '表名' disable '表名' is_disabled '表名' # 示例 disable 'tbl_user' is_disabled 'tbl_user' enable 'tbl_user' is_enabled 'tbl_user' 9、禁用满足正则表达式的所有表disable_all .匹配除“\n”和”\r”之外的任何单个字符 *匹配前面的子表达式任意次 # 匹配以t开头的表名 disable_all 't.*' # 匹配指定命名空间ns下的以t开头的所有表 disable_all 'ns:t.*' # 匹配ns命名空间下的所有表 disable_all 'ns:.*' 10、启用满足正则表达式的所有表 enable_all enable_all 't.*' enable_all 'ns:t.*' enable_all 'ns:.*' 11、删除表 drop 需要先禁用表，然后再删除表，启用的表是不允许删除的 # 语法 disable '表名' drop '表名' # 示例 disable 'tbl_user' drop 'tbl_user' 12、删除满足正则表达式的所有表 drop_all drop_all 't.*' drop_all 'ns:t.*' drop_all 'ns:.*' 13、获取某个表赋值给一个变量 get_table 通过 var = get_table ‘表名’ 赋值给一个变量对象，然后对象.来调用，就像面向对象编程一样，通过对象.方法来调用，这种方式在操作某个表时就不必每次列举表名了。 14、获取 rowKey 所在的区 locate_region locate_region '表名', '行键' 15、显示 hbase 所支持的所有过滤器 show_filters 过滤器用于 get 和 scan 命令中作为筛选数据的条件，类型关系型数据库中的 where 的作用 六、namespace 命令 1、列举命名空间 list_namespace 2、获取命名空间描述 describe_namespace 3、查看命名空间下的所有表 list_namespace_tables 4、创建命名空间 create_namespace 5、修改命名空间的属性 # add/modify a property alter_namespace 'ns1', { METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'} # delete a property alter_namespace 'ns1', { METHOD => 'unset', NAME=>'PROPERTY_NAME'} 删除命名空间 drop_namespace drop_namespace '命名空间名称' 七、dml命令 1、插入或者修改数据 put # 语法 # 当列族中只有一个列时'列族名:列名'使用'列族名' put '表名', '行键', '列族名', '列值' put '表名', '行键', '列族名:列名', '列值' # 示例 # 创建表 create 'tbl_user', 'info', 'detail', 'address' # 第一行数据 put 'tbl_user', 'mengday', 'info:id', '1' put 'tbl_user', 'mengday', 'info:name', '张三' put 'tbl_user', 'mengday', 'info:age', '28' put 'tbl_user', 'mengday', 'detail:birthday', '1990-06-26' put 'tbl_user', 'mengday', 'detail:email', 'abc@163.com' put 'tbl_user', 'mengday', 'detail:create_time', '2019-03-04 14:26:10' put 'tbl_user', 'mengday', 'address', '上海市' # 第二行数据 put 'tbl_user', 'vbirdbest', 'info:id', '2' put 'tbl_user', 'vbirdbest', 'info:name', '李四' put 'tbl_user', 'vbirdbest', 'info:age', '27' put 'tbl_user', 'vbirdbest', 'detail:birthday', '1990-06-27' put 'tbl_user', 'vbirdbest', 'detail:email', 'xxx@gmail.com' put 'tbl_user', 'vbirdbest', 'detail:create_time', '2019-03-05 14:26:10' put 'tbl_user', 'vbirdbest', 'address', '北京市' # 第一行数据 put 'tbl_user', 'xiaoming', 'info:id', '3' put 'tbl_user', 'xiaoming', 'info:name', '王五' put 'tbl_user', 'xiaoming', 'info:age', '26' put 'tbl_user', 'xiaoming', 'detail:birthday', '1990-06-28' put 'tbl_user', 'xiaoming', 'detail:email', 'xyz@qq.com' put 'tbl_user', 'xiaoming', 'detail:create_time', '2019-03-06 14:26:10' put 'tbl_user', 'xiaoming', 'address', '杭州市' 2、全表扫描scan 获取表的所有数据 # 语法 scan '表名' # 示例 scan 'tbl_user' 注意：中文编码了 扫描整个列簇 # 语法 scan '表名', { COLUMN=>'列族名'} # 示例 scan 'tbl_user', { COLUMN=>'info'} 扫描整个列簇的某个列 # 语法 scan '表名', { COLUMN=>'列族名:列名'} # 示例 scan 'tbl_user', { COLUMN=>'info:age'} 3、获取数据 get # 语法 get '表名', '行键' # 示例 get 'tbl_user', 'mengday' 根据某一行某列族的数据 # 语法 get '表名', '行键', '列族名' # 示例 get 'tbl_user', 'mengday', 'info' 示例： # 创建表，c1版本为4， 元数据mykey=myvalue hbase(main):009:0> create 't1', { NAME => 'c1', VERSIONS => 4}, METADATA => { 'mykey' => 'myvalue' } 0 row(s) in 2.2810 seconds => Hbase::Table - t1 # 添加列族c2, c3 hbase(main):010:0> alter 't1', 'c2', 'c3' Updating all regions with the new schema... 1/1 regions updated. Done. Updating all regions with the new schema... 1/1 regions updated. Done. 0 row(s) in 3.8320 seconds # 出入数据，c1 插入4个版本的值 hbase(main):011:0> put 't1', 'r1', 'c1', 'v1' 0 row(s) in 0.1000 seconds hbase(main):012:0> put 't1', 'r1', 'c1', 'v11' 0 row(s) in 0.0180 seconds hbase(main):013:0> put 't1', 'r1', 'c1', 'v111' 0 row(s) in 0.0140 seconds hbase(main):014:0> put 't1', 'r1', 'c1', 'v1111' 0 row(s) in 0.0140 seconds # 插入c2、c3的值 hbase(main):015:0> put 't1', 'r1', 'c2', 'v2' 0 row(s) in 0.0140 seconds hbase(main):016:0> put 't1', 'r1', 'c3', 'v3' 0 row(s) in 0.0210 seconds # 获取rowKey=r1的一行记录 hbase(main):017:0> get 't1', 'r1' COLUMN CELL c1: timestamp=1552819382575, value=v1111 c2: timestamp=1552819392398, value=v2 c3: timestamp=1552819398244, value=v3 3 row(s) in 0.0550 seconds # 获取rowKey=r1并且 1552819392398 <= 时间戳范围 < 1552819398244 hbase(main):018:0> get 't1', 'r1', { TIMERANGE => [1552819392398, 1552819398244]} COLUMN CELL c2: timestamp=1552819392398, value=v2 1 row(s) in 0.0090 seconds # 获取指定列的值 hbase(main):019:0> get 't1', 'r1', { COLUMN => 'c1'} COLUMN CELL c1: timestamp=1552819382575, value=v1111 1 row(s) in 0.0160 seconds # 获取指定列的值，多个值使用数组表示 hbase(main):020:0> get 't1', 'r1', { COLUMN => ['c1', 'c2', 'c3']} COLUMN CELL c1: timestamp=1552819382575, value=v1111 c2: timestamp=1552819392398, value=v2 c3: timestamp=1552819398244, value=v3 3 row(s) in 0.0170 seconds # 获取c1的值，获取4个版本的值，默认是按照时间戳降续排序的 hbase(main):021:0> get 't1', 'r1', { COLUMN => 'c1', VERSIONS => 4} COLUMN CELL c1: timestamp=1552819382575, value=v1111 c1: timestamp=1552819376343, value=v111 c1: timestamp=1552819368993, value=v11 c1: timestamp=1552819362975, value=v1 4 row(s) in 0.0180 seconds # 获取c1的3个版本值 hbase(main):027:0* get 't1', 'r1', { COLUMN => 'c1', VERSIONS => 3} COLUMN CELL c1: timestamp=1552819382575, value=v1111 c1: timestamp=1552819376343, value=v111 c1: timestamp=1552819368993, value=v11 3 row(s) in 0.0090 seconds # 获取指定时间戳版本的列 hbase(main):022:0> get 't1', 'r1', { COLUMN => 'c1', TIMESTAMP => 1552819376343} COLUMN CELL c1: timestamp=1552819376343, value=v111 1 row(s) in 0.0170 seconds hbase(main):023:0> get 't1', 'r1', { COLUMN => 'c1', TIMESTAMP => 1552819376343, VERSIONS => 4} COLUMN CELL c1: timestamp=1552819376343, value=v111 1 row(s) in 0.0130 seconds # 获取rowKey=r1中的值等于v2的所有列 hbase(main):024:0> get 't1', 'r1', { FILTER => "ValueFilter(=, 'binary:v2')"} COLUMN CELL c2: timestamp=1552819392398, value=v2 1 row(s) in 0.0510 seconds hbase(main):025:0> get 't1', 'r1', { COLUMN => 'c1', ATTRIBUTES => { 'mykey'=>'myvalue'}} COLUMN CELL c1: timestamp=1552819382575, value=v1111 1 row(s) in 0.0100 seconds 4、删除某个列族中的某个列 delete # 语法 delete '表名', '行键', '列族名:列名' create 'tbl_test', 'columnFamily1' put 'tbl_test', 'rowKey1', 'columnFamily1:column1', 'value1' put 'tbl_test', 'rowKey1', 'columnFamily1:column2', 'value2' delete 'tbl_test', 'rowKey1', 'columnFamily1:column1' 5、删除某行数据 deleteall # 语法 deleteall '表名', '行键' # 示例 deleteall 'tbl_test', 'rowKey1' 6、清空整个表的数据 truncate 先disable表，然后再drop表，最后重新create表 truncate '表名' 7、查询表中有多少行 count # 语法 count '表名' # 示例 count 'tbl_user' 8、自增 incr # 语法 incr '表名', '行键', '列族:列名', 步长值 # 示例 # 注意：incr 可以对不存的行键操作，如果行键已经存在会报错，如果使用put修改了incr的值再使用incr也会报错 # ERROR: org.apache.hadoop.hbase.DoNotRetryIOException: Field is not a long, it's 2 bytes wide incr 'tbl_user', 'xiaohong', 'info:age', 1 9、计数器 get_counter # 点击量：日、周、月 create 'counters', 'daily', 'weekly', 'monthly' incr 'counters', '20110101', 'daily:hits', 1 incr 'counters', '20110101', 'daily:hits', 1 get_counter 'counters', '20110101', 'daily:hits' 10、修饰词 10.1、COLUMNS: 查询同一个列族的多个列 # 语法 scan '表名', { COLUMNS => [ '列族名1:列名1', '列族名1:列名2', ...]} # 示例 scan 'tbl_user', { COLUMNS => [ 'info:id', 'info:age']} 10.2、TIMESTAMP 指定时间戳 scan 't1', { COLUMNS => 'c2', TIMESTAMP=> 1552819392398} 10.3、TIMERANGE表示的是”>=开始时间 and <结束时间“ # 语法 scan '表名',{ TIMERANGE=>[timestamp1, timestamp2]} # 示例 scan 'tbl_user',{ TIMERANGE=>[1551938004321, 1551938036450]} 10.4、VERSIONS 默认情况下一个列只能存储一个数据，后面如果修改数据就会将原来的覆盖掉，可以通过指定VERSIONS时HBase一列能存储多个值。 create 'tbl_test', 'columnFamily1' describe 'tbl_test' # 修改列族版本号 alter 'tbl_test', { NAME=>'columnFamily1', VERSIONS=>3 } put 'tbl_test', 'rowKey1', 'columnFamily1:column1', 'value1' put 'tbl_test', 'rowKey1', 'columnFamily1:column1', 'value2' put 'tbl_test', 'rowKey1', 'columnFamily1:column1', 'value3' # 默认返回最新的一条数据 get 'tbl_test','rowKey1','columnFamily1:column1' # 返回3个 get 'tbl_test','rowKey1',{ COLUMN=>'columnFamily1:column1', VERSIONS=>3} # 返回2个 get 'tbl_test','rowKey1',{ COLUMN=>'columnFamily1:column1', VERSIONS=>2} 10.5、STARTROW ROWKEY起始行。会先根据这个key定位到region，再向后扫描 # 语法 scan '表名', { STARTROW => '行键名'} # 示例 scan 'tbl_user', { STARTROW => 'vbirdbest'} 10.6、STOPROW ：截止到 STOPROW 行，STOPROW 行之前的数据，不包括 STOPROW 这行数据 # 语法 scan '表名', { STOPROW => '行键名'} # 示例 scan 'tbl_user', { STOPROW => 'vbirdbest'} 10.7、LIMIT 返回的行数 # 语法 scan '表名', { LIMIT => 行数} # 示例 scan 'tbl_user', { LIMIT => 2 } 11、FILTER 条件过滤器 过滤器之间可以使用 AND、OR 连接多个过滤器。 ValueFilter 值过滤器 # 语法：binary 等于某个值 scan '表名', FILTER=>"ValueFilter(=,'binary:列值')" # 语法 substring:包含某个值 scan '表名', FILTER=>"ValueFilter(=,'substring:列值')" # 示例 scan 'tbl_user', FILTER=>"ValueFilter(=, 'binary:26')" scan 'tbl_user', FILTER=>"ValueFilter(=, 'substring:6')" ColumnPrefixFilter 列名前缀过滤器 # 语法 substring:包含某个值 scan '表名', FILTER=>"ColumnPrefixFilter('列名前缀')" # 示例 scan 'tbl_user', FILTER=>"ColumnPrefixFilter('birth')" # 通过括号、AND和OR的条件组合多个过滤器 scan 'tbl_user', FILTER=>"ColumnPrefixFilter('birth') AND ValueFilter(=,'substring:26')" rowKey 字典排序 Table中的所有行都是按照row key的字典排序的 八、HBase 常见错误 hbase shell 在使用的时候经常会报错，这里列举了几个错误： 1、HBase创建不存在的表报已经存在错误 ERROR: Table already exists: ! 解决办法： # 1. 进入zookeeper client模式 cd /usr/local/Cellar/hbase/1.2.9/bin hbase zkcli # 2. 在zk client模式下输入ls /hbase/table命令看到zombie table ls /hbase/table # 3. 删除表，TABLE_NAME为要删除的表名 rmr /hbase/table/TABLE_NAME # 4. 重启hbase ./stop-hbase.sh ./start-hbase.sh 2、ERROR: org.apache.hadoop.hbase.ipc.ServerNotRunningYetException: Server is not running yet # 使用jps查看是否有datanode服务 jps 删除hadoop 的临时目录 /usr/local/Cellar/hadoop/3.1.1/libexec/tmp 3、ERROR: Can’t get master address from ZooKeeper; znode data == null # 重启hbase ./stop-hbase.sh ./start-hbase.sh 4、ERROR: org.apache.hadoop.hbase.PleaseHoldException: Master is initializing 这个错误出现的原因很多，这里说一下我的错误原因是hbase-site.xml中的有个属性名配置错误, 网上有的是hbase.rootdir, name配置成这个有的时候会报错有的时候不会报错，报错的时候格式化一下hadoop然后再重新启动，这种方式有的时候能解决问题，有的时候解决不了。这里我将hbase.rootdir改为hbase.root.dir 就不报错了，不知道是不是版本不同属性名配置不同，我的hadoop版本为3.1.1，hbase版本为1.2.9 <property> <name>hbase.root.dir</name> <value>hdfs://localhost:8020/hbase</value> </property> 参考： https://cloud.tencent.com/developer/article/2103719?from=15425&areaSource=102001.1&traceId=4UaenE3kfOsFOaU-YFny7
一、使用场景 如果大量的数据已经存放在 HBase 上面，需要对已经存在的数据进行数据分析处理，那么 Phoenix 并不适合做特别复杂的 SQL 处理，此时可以使用 hive 映射 HBase 的表格，之后写 HQL 进行分析处理。 二、HBase 与 Hive 集成使用 在 hive-site.xml 中添加 zookeeper 的属性，如下： <property> <name>hive.zookeeper.quorum</name> <value>hadoop102,hadoop103,hadoop104</value> </property> <property> <name>hive.zookeeper.client.port</name> <value>2181</value> </property> 2.1、案例一 目标：建立 Hive 表，关联 HBase 表，插入数据到 Hive 表的同时能够影响 HBase 表。 分步实现： （1）在 Hive 中创建表同时关联 HBase。 CREATE TABLE hive_hbase_emp_table( empno int, ename string, job string, mgr int, hiredate string, sal double, comm double, deptno int ) STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:co mm,info:deptno") TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table"); 提示：完成之后，可以分别进入 Hive 和 HBase 查看，都生成了对应的表。 （2）在 Hive 中创建临时中间表，用于 load 文件中的数据。 提示：不能将数据直接 load 进 Hive 所关联 HBase 的那张表中。 CREATE TABLE emp( empno int, ename string, job string, mgr int, hiredate string, sal double, comm double, deptno int ) row format delimited fields terminated by '\t'; （3）向 Hive 中间表中 load 数据。 hive> load data local inpath '/opt/software/emp.txt' into table emp; （4）通过 insert 命令将中间表中的数据导入到 Hive 关联 Hbase 的那张表中。 hive> insert into table hive_hbase_emp_table select * from emp; （5）查看 Hive 以及关联的 HBase 表中是否已经成功的同步插入了数据。 Hive： hive> select * from hive_hbase_emp_table; HBase： Hbase> scan 'hbase_emp_table' 2.2、案例二 目标：在 HBase 中已经存储了某一张表 hbase_emp_table，然后在 Hive 中创建一个外部表来关联 HBase 中的 hbase_emp_table 这张表，使之可以借助 Hive 来分析 HBase 这张表中的数据。 注：该案例 2 紧跟案例 1 的脚步，所以完成此案例前，请先完成案例 1。 分步实现： （1）在 Hive 中创建外部表。 CREATE EXTERNAL TABLE relevance_hbase_emp( empno int, ename string, job string, mgr int, hiredate string, sal double, comm double, deptno int ) STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:co mm,info:deptno") TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table"); （2）关联后就可以使用 Hive 函数进行一些分析操作了。 hive (default)> select deptno,avg(sal) monery from relevance_hbase_emp group by deptno ;
六、整合Phoenix 6.1、Phoenix 简介 6.1.1、Phoenix 定义 Phoenix 是 HBase 的开源 SQL 皮肤。可以使用标准 JDBC API 代替 HBase 客户端 API来创建表，插入数据和查询 HBase 数据。 6.1.2、为什么使用 Phoenix 官方给的解释为：在 Client 和 HBase 之间放一个 Phoenix 中间层不会减慢速度，因为用户编写的数据处理代码和 Phoenix 编写的没有区别（更不用说你写的垃圾的多），不仅如此Phoenix 对于用户输入的 SQL 同样会有大量的优化手段（就像 hive 自带 sql 优化器一样）。 Phoenix 在 5.0 版本默认提供有两种客户端使用瘦客户端和胖客户端，在 5.1.2 版本安装包中删除了瘦客户端，本文也不再使用瘦客户端。而胖客户端和用户自己写 HBase 的 API 代码读取数据之后进行数据处理是完全一样的。 6.2、Phoenix 快速入门 6.2.1、安装 1）官网地址 http://phoenix.apache.org/ 2）Phoenix 部署 （1）上传并解压 tar 包 [atguigu@hadoop102 software]$ tar -zxvf phoenix-hbase-2.4-5.1.2-bin.tar.gz -C /opt/module/ [atguigu@hadoop102 module]$ mv phoenix-hbase-2.4-5.1.2-bin/ phoenix （2）复制 server 包并拷贝到各个节点的 hbase/lib [atguigu@hadoop102 module]$ cd /opt/module/phoenix/ [atguigu@hadoop102 phoenix]$ cp phoenix-server-hbase-2.4-5.1.2.jar /opt/module/hbase/lib/ [atguigu@hadoop102 phoenix]$ xsync /opt/module/hbase/lib/ phoenixserver-hbase-2.4-5.1.2.jar （3）配置环境变量 sudo vim /etc/profile.d/my_env.sh #phoenix export PHOENIX_HOME=/opt/module/phoenix export PHOENIX_CLASSPATH=$PHOENIX_HOME export PATH=$PATH:$PHOENIX_HOME/bin source /etc/profile.d/my_env.sh （4）重启 HBase [atguigu@hadoop102 ~]$ stop-hbase.sh [atguigu@hadoop102 ~]$ start-hbase.sh （5）连接 Phoenix [atguigu@hadoop101 phoenix]$ /opt/module/phoenix/bin/sqlline.py hadoop102,hadoop103,hadoop104:2181 （6）错误解决 出现下面错误的原因是之前使用过 phoenix，建议删除之前的记录 警告: Failed to load history java.lang.IllegalArgumentException: Bad history file syntax! The history file /home/atguigu/.sqlline/history may be an older history: please remove it or use a different history file. 解决方法：在 /home/atguigu 目录下删除.sqlline文件夹 [atguigu@hadoop102 ~]$ rm -rf .sqlline/ 6.2.2、Phoenix Shell 操作 6.2.2.1、table 关于 Phoenix 的语法建议使用时直接查看官网： https://phoenix.apache.org/language/index.html 1）显示所有表 !table 或 !tables 2）创建表 直接指定单个列作为 RowKey CREATE TABLE IF NOT EXISTS student( id VARCHAR primary key, name VARCHAR, age BIGINT, addr VARCHAR); 在 phoenix 中，表名等会自动转换为大写，若要小写，使用双引号，如"us_population"。 指定多个列的联合作为 RowKey CREATE TABLE IF NOT EXISTS student1 ( id VARCHAR NOT NULL, name VARCHAR NOT NULL, age BIGINT, addr VARCHAR CONSTRAINT my_pk PRIMARY KEY (id, name)); 注：Phoenix 中建表，会在 HBase 中创建一张对应的表。为了减少数据对磁盘空间的占用，Phoenix 默认会对 HBase 中的列名做编码处理。具体规则可参考官网链接： https://phoenix.apache.org/columnencoding.html 若不想对列名编码，可在建表语句末尾加上 COLUMN_ENCODED_BYTES = 0; 3）插入数据 upsert into student values('1001','zhangsan', 10, 'beijing'); 4）查询记录 select * from student; select * from student where id='1001'; 5）删除记录 delete from student where id='1001'; 6）删除表 drop table student; 7）退出命令行 !quit 6.2.2.2、表的映射 1）表的关系 默认情况下， HBase 中已存在的表，通过 Phoenix 是不可见的。如果要在 Phoenix 中操作 HBase 中已存在的表，可以在 Phoenix 中进行表的映射。映射方式有两种：视图映射和表映射。 2）命令行中创建表 test HBase 中 test 的表结构如下，两个列族 info1、info2 （1）启动 HBase Shell [atguigu@hadoop102 ~]$ /opt/module/hbase/bin/hbase shell （2）创建 HBase 表 test hbase(main):001:0> create 'test','info1','info2' 3）视图映射 Phoenix 创建的视图是只读的，所以只能用来做查询，无法通过视图对数据进行修改等操作。在 phoenix 中创建关联 test 表的视图。 0: jdbc:phoenix:hadoop101,hadoop102,hadoop103> create view "test"(id varchar primary key,"info1"."name" varchar, "info2"."address" varchar); 4）表映射 在 Pheonix 创建表去映射 HBase 中已经存在的表，是可以修改删除 HBase 中已经存在的数据的。而且，删除 Phoenix 中的表，那么 HBase 中被映射的表也会被删除。 注：进行表映射时，不能使用列名编码，需将 column_encoded_bytes 设为 0。 0: jdbc:phoenix:hadoop101,hadoop102,hadoop103> create table "test"(id varchar primary key,"info1"."name" varchar, "info2"."address" varchar) column_encoded_bytes=0; 6.2.2.3、数字类型说明 HBase 中的数字，底层存储为补码，而 Phoenix 中的数字，底层存储为在补码的基础上，将符号位反转。故当在 Phoenix 中建表去映射 HBase 中已存在的表，当 HBase 中有数字类型的字段时，会出现解析错误的现象。 Hbase 演示： create 'test_number','info' put 'test_number','1001','info:number',Bytes.toBytes(1000) scan 'test_number',{COLUMNS => 'info:number:toLong'} ROW COLUMN+CELL 1001 column=info:number, timestamp=2023-03-05T18:08:15.765, value=1000 phoenix 演示： create view "test_number"(id varchar primary key,"info"."number" bigint); select * from "test_number"; +------+----------------------+ | ID | number | +------+----------------------+ | 1001 | -9223372036854774808 | +------+----------------------+ 解决上述问题的方案有以下两种： （1）Phoenix 种提供了 unsigned_int，unsigned_long 等无符号类型，其对数字的编码解码方式和 HBase 是相同的，如果无需考虑负数，那在 Phoenix 中建表时采用无符号类型是最合适的选择。 phoenix 演示： drop view "test_number"; create view "test_number"(id varchar primary key,"info"."number" unsigned_long); select * from "test_number"; +------+--------+ | ID | number | +------+--------+ | 1001 | 1000 | +------+--------+ （2）如需考虑负数的情况，则可通过 Phoenix 自定义函数，将数字类型的最高位，即符号位反转即可，自定义函数可参考如下链接：https://phoenix.apache.org/udf.html 6.2.3、Phoenix JDBC 操作 此处演示一个标准的JDBC连接操作，实际开发中会直接使用别的框架内嵌的Phoenix连接。 详见代码。 6.3、Phoenix 二级索引 6.3.1、二级索引配置文件 添加如下配置到 HBase 的 HRegionserver 节点的 hbase-site.xml。 <property> <name>hbase.regionserver.wal.codec</name> <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCod ec</value> </property> 6.3.2、全局索引 global index Global Index 是默认的索引格式，创建全局索引时，会在 HBase 中建立一张新表。也就是说索引数据和数据表是存放在不同的表中的，因此全局索引适用于多读少写的业务场景。 写数据的时候会消耗大量开销，因为索引表也要更新，而索引表是分布在不同的数据节点上的，跨节点的数据传输带来了较大的性能消耗。 在读数据的时候 Phoenix 会选择索引表来降低查询消耗的时间。 创建单个字段的全局索引。 CREATE INDEX my_index ON my_table (my_col); #例如 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> create index my_index on student1(age); 删除索引 DROP INDEX my_index ON my_table 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> drop index my_index on student1; 查看二级索引是否有效，可以使用 explainPlan 执行计划，有二级索引之后会变成范围扫描 。 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> explain select id,name from student1 where age = 10; 如果想查询的字段不是索引字段的话索引表不会被使用，也就是说不会带来查询速度的提升。 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> explain select id,name,addr from student1 where age = 10; 若想解决上述问题，可采用如下方案： （1）使用包含索引 （2）使用本地索引 6.3.3、包含索引 covered index 创建携带其他字段的全局索引（本质还是全局索引）。 CREATE INDEX my_index ON my_table (v1) INCLUDE (v2); 先删除之前的索引： 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> drop index my_index on student1; #创建包含索引 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> create index my_index on student1(age) include (addr); 之后使用执行计划查看效果 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> explain select id,name,addr from student1 where age = 10; 6.3.4、本地索引 local index Local Index 适用于写操作频繁的场景。 索引数据和数据表的数据是存放在同一张表中（且是同一个 Region），避免了在写操作的时候往不同服务器的索引表中写索引带来的额外开销。 my_column 可以是多个。 CREATE LOCAL INDEX my_index ON my_table (my_column); 本地索引会将所有的信息存在一个影子列族中，虽然读取的时候也是范围扫描，但是没有全局索引快，优点在于不用写多个表了。 #删除之前的索引 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> drop index my_index on student1; #创建本地索引 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> CREATE LOCAL INDEX my_index ON student1 (age,addr); #使用执行计划 0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> explain select id,name,addr from student1 where age = 10;
四、HBase进阶 4.1、Master 架构 Master 主要进程，具体实现类为 HMaster，通常部署在 namenode 上。 1）Meta 表格介绍：(警告：不要去改这个表) 全称 hbase:meta，只是在 list 命令中被过滤掉了，本质上和 HBase 的其他表格一样。 RowKey： ([table],[region start key],[region id]) 即表名，region 起始位置和 regionID。 列： info:regioninfo 为 region 信息，存储一个 HRegionInfo 对象。 info:server 当前 region 所处的 RegionServer 信息，包含端口号。 info:serverstartcode 当前 region 被分到 RegionServer 的起始时间。 如果一个表处于切分的过程中，即 region 切分，还会多出两列 info:splitA 和 info:splitB，存储值也是 HRegionInfo 对象，拆分结束后，删除这两列。 注意：在客户端对元数据进行操作的时候才会连接 master，如果对数据进行读写，直接连接 zookeeper 读取目录/hbase/meta-region-server 节点信息，会记录 meta 表格的位置。直接读取即可，不需要访问 master，这样可以减轻 master 的压力，相当于 master 专注 meta 表的写操作，客户端可直接读取 meta 表。 在 HBase 的 2.3 版本更新了一种新模式：Master Registry。客户端可以访问 master 来读取 meta 表信息。加大了 master 的压力，减轻了 zookeeper 的压力。 4.2、RegionServer 架构 主要进程，具体实现类为 HRegionServer。通常部署在 dataNode 上。 除了主要的组件，还会启动多个线程监控一些必要的服务： 1、Region 拆分 2、Region 合并 3、MemStory 刷写 4、WAL 预写日志滚动 1、MemStore 写缓存，由于 HFile 中的数据要求是有序的，所以数据是先存储在 MemStore 中，排好序后，等到达刷写时机才会刷写到 HFile，每次刷写都会形成一个新的 HFile，写入到对应的文件夹 store 中。 2、WAL 由于数据要经 MemStore 排序后才能刷写到 HFile，但把数据保存在内存中会有很高的概率导致数据丢失，为了解决这个问题，数据会先写在一个叫做 Write-Ahead logfile 的文件中，然后再写入 MemStore 中。所以在系统出现故障的时候，数据可以通过这个日志文件重建。 3、BlockCache 读缓存，每次查询出的数据会缓存在 BlockCache 中，方便下次查询。 4.3、写流程 写流程顺序正如 API 编写顺序，首先创建 HBase 的重量级连接。 （1）首先访问 zookeeper，获取 hbase:meta 表位于哪个 Region Server （2）访问对应的 Region Server，获取 hbase:meta 表，将其缓存到连接中，作为连接属性 MetaCache，由于 Meta 表格具有一定的数据量，导致了创建连接比较慢； 之后使用创建的连接获取 Table，这是一个轻量级的连接，只有在第一次创建的时候会检查表格是否存在访问 RegionServer，之后在获取 Table 时不会访问 RegionServer； （3）调用 Table 的 put 方法写入数据，此时还需要解析 RowKey，对照缓存的 MetaCache，查看具体写入的位置有哪个 RegionServer； （4）将数据顺序写入（追加）到 WAL，此处写入是直接落盘的，并设置专门的线程控制 WAL 预写日志的滚动（类似 Flume）； （5）根据写入命令的 RowKey 和 ColumnFamily 查看具体写入到哪个 MemStory，并且在 MemStory 中排序； （6）向客户端发送 ack； （7 ）等达到 MemStore 的刷写时机后，将数据刷写到对应的 story 中。 4.4、MemStore Flush MemStore 刷写由多个线程控制，条件互相独立： 主要的刷写规则是控制刷写文件的大小，在每一个刷写线程中都会进行监控 （1）当某个 memstroe 的大小达到了 hbase.hregion.memstore.flush.size（默认值 128M），其所在 region 的所有 memstore 都会刷写。 当 memstore 的大小达到了 hbase.hregion.memstore.flush.size（默认值 128M） hbase.hregion.memstore.block.multiplier（默认值 4） 时，会刷写同时阻止继续往该 memstore 写数据（由于线程监控是周期性的，所有有可能面对数据洪峰，尽管可能性比较小）。 （2）由 HRegionServer 中的属性 MemStoreFlusher 内部线程 FlushHandler 控制。标准为 LOWER_MARK（低水位线）和 HIGH_MARK（高水位线），意义在于避免写缓存使用过多的内存造成 OOM。 当 region server 中 memstore 的总大小达到低水位线： java_heapsize *hbase.regionserver.global.memstore.size（默认值 0.4） *hbase.regionserver.global.memstore.size.lower.limit（默认值 0.95） region 会按照其所有 memstore 的大小顺序（由大到小）依次进行刷写。直到 region server中所有 memstore 的总大小减小到上述值以下。 当 region server 中 memstore 的总大小达到高水位线： java_heapsize *hbase.regionserver.global.memstore.size（默认值 0.4） 时，会同时阻止继续往所有的 memstore 写数据。 （3）为了避免数据过长时间处于内存之中，到达自动刷写的时间，也会触发 memstore flush。由 HRegionServer 的属性 PeriodicMemStoreFlusher 控制进行，由于重要性比较低，5min 才会执行一次。 自动刷新的时间间隔由该属性进行配置 hbase.regionserver.optionalcacheflushinterval（默认1 小时）。 （4）当 WAL 文件的数量超过 hbase.regionserver.max.logs，region 会按照时间顺序依次进行刷写，直到 WAL 文件数量减小到 hbase.regionserver.max.log 以下（该属性名已经废弃，现无需手动设置，最大值为 32）。 4.5、读流程 4.5.1、HFile 结构 在了解读流程之前，需要先知道读取的数据是什么样子的。 HFile 是存储在 HDFS 上面每一个 store 文件夹下实际存储数据的文件。里面存储多种内容。包括数据本身（keyValue 键值对）、元数据记录、文件信息、数据索引、元数据索引和一个固定长度的尾部信息（记录文件的修改情况）。 键值对按照块大小（默认 64K）保存在文件中，数据索引按照块创建，块越多，索引越大。每一个 HFile 还会维护一个布隆过滤器（就像是一个很大的地图，文件中每有一种 key，就在对应的位置标记，读取时可以大致判断要 get 的 key 是否存在 HFile 中）。 KeyValue 内容如下: rowlength -----------→ key 的长度 row -----------------→ key 的值 columnfamilylength --→ 列族长度 columnfamily --------→ 列族 columnqualifier -----→ 列名 timestamp -----------→ 时间戳（默认系统时间） keytype -------------→ Put 由于 HFile 存储经过序列化，所以无法直接查看。可以通过 HBase 提供的命令来查看存储在 HDFS 上面的 HFile 元数据内容。 [atguigu@hadoop102 hbase]$ bin/hbase hfile -m -f /hbase/data/命名空间/表名/regionID/列族/HFile 名 4.5.2、读流程 创建连接同写流程。 （1）创建 Table 对象发送 get 请求。 （2）优先访问 Block Cache，查找是否之前读取过，并且可以读取 HFile 的索引信息和布隆过滤器。 （3）不管读缓存中是否已经有数据了（可能已经过期了），都需要再次读取写缓存和store 中的文件。 （4）最终将所有读取到的数据合并版本，按照 get 的要求返回即可。 4.5.3、合并读取数据优化 每次读取数据都需要读取三个位置，最后进行版本的合并。效率会非常低，所有系统需要对此优化。 （1）HFile 带有索引文件，读取对应 RowKey 数据会比较快。 （2）Block Cache 会缓存之前读取的内容和元数据信息，如果 HFile 没有发生变化（记录在 HFile 尾信息中），则不需要再次读取。 （3）使用布隆过滤器能够快速过滤当前 HFile 不存在需要读取的 RowKey，从而避免读取文件。（布隆过滤器使用 HASH 算法，不是绝对准确的，出错会造成多扫描一个文件，对读取数据结果没有影响） 4.6、StoreFile Compaction 由于 memstore 每次刷写都会生成一个新的 HFile，文件过多读取不方便，所以会进行文件的合并，清理掉过期和删除的数据，会进行 StoreFile Compaction。 Compaction 分为两种，分别是 Minor Compaction 和 Major Compaction。Minor Compaction会将临近的若干个较小的 HFile 合并成一个较大的 HFile，并清理掉部分过期和删除的数据，有系统使用一组参数自动控制，Major Compaction 会将一个 Store 下的所有的 HFile 合并成一个大 HFile，并且会清理掉所有过期和删除的数据，由参数 hbase.hregion.majorcompaction控制，默认 7 天。 Minor Compaction 控制机制： 参与到小合并的文件需要通过参数计算得到，有效的参数有 5 个 （1）hbase.hstore.compaction.ratio（默认 1.2F）合并文件选择算法中使用的比率。 （2）hbase.hstore.compaction.min（默认 3） 为 Minor Compaction 的最少文件个数。 （3）hbase.hstore.compaction.max（默认 10） 为 Minor Compaction 最大文件个数。 （4）hbase.hstore.compaction.min.size（默认 128M）为单个 Hfile 文件大小最小值，小于这个数会被合并。 （5）hbase.hstore.compaction.max.size（默认 Long.MAX_VALUE）为单个 Hfile 文件大小最大值，高于这个数不会被合并。 小合并机制为拉取整个 store 中的所有文件，做成一个集合。之后按照从旧到新的顺序遍历。 判断条件为： 1、过小合并，过大不合并。 2、文件大小/ hbase.hstore.compaction.ratio < (剩余文件大小和) 则参与压缩。所有把比值设置过大，如 10 会最终合并为 1 个特别大的文件，相反设置为 0.4，会最终产生 4 个 storeFile。不建议修改默认值 3、满足压缩条件的文件个数达不到个数要求（3 <= count <= 10）则不压缩。 4.7、Region Split Region 切分分为两种，创建表格时候的预分区即自定义分区，同时系统默认还会启动一个切分规则，避免单个 Region 中的数据量太大。 4.7.1、预分区（自定义分区） 每一个 region 维护着 startRow 与 endRowKey，如果加入的数据符合某个 region 维护的rowKey 范围，则该数据交给这个 region 维护。那么依照这个原则，我们可以将数据所要投放的分区提前大致的规划好，以提高 HBase 性能。 1）手动设定预分区 create 'staff1','info', SPLITS => ['1000','2000','3000','4000'] 2）生成 16 进制序列预分区 create 'staff2','info',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'} 3）按照文件中设置的规则预分区 （1）创建 splits.txt 文件内容如下： aaaa bbbb cccc dddd （2）然后执行： create 'staff3', 'info',SPLITS_FILE => 'splits.txt' 4）使用 JavaAPI 创建预分区 4.7.2、系统拆分 Region 的拆分是由 HRegionServer 完成的，在操作之前需要通过 ZK 汇报 master，修改对应的 Meta 表信息添加两列 info：splitA 和 info：splitB 信息。之后需要操作 HDFS 上面对应的文件，按照拆分后的 Region 范围进行标记区分，实际操作为创建文件引用，不会挪动数据。刚完成拆分的时候，两个 Region 都由原先的 RegionServer 管理。之后汇报给 Master，由 Master 将修改后的信息写入到 Meta 表中。等待下一次触发负载均衡机制，才会修改 Region 的管理服务者，而数据要等到下一次压缩时，才会实际进行移动。 不管是否使用预分区，系统都会默认启动一套 Region 拆分规则。不同版本的拆分规则有差别。系统拆分策略的父类为 RegionSplitPolicy。0.94 版本之前 => ConstantSizeRegionSplitPolicy 。 （ 1 ） 当 1 个 region 中 的 某 个 Store 下 所 有 StoreFile 的总大小超过hbase.hregion.max.filesize （10G），该 Region 就会进行拆分。 0.94 版本之后，2.0 版本之前 => IncreasingToUpperBoundRegionSplitPolicy。 （ 2 ） 当1个 region中的某个 Store 下所有 StoreFile 的总大小超过 Min(initialSize * R^3 , hbase.hregion.max.filesize")，该 Region 就会进行拆分。其中 initialSize 的默认值为 2 * hbase.hregion.memstore.flush.size，R 为当前 Region Server 中属于该 Table 的 Region 个数（0.94 版本之后）。 具体的切分策略为： 第一次 split：1^3 * 256 = 256MB 第二次 split：2^3 * 256 = 2048MB 第三次 split：3^3 * 256 = 6912MB 第四次 split：4^3 * 256 = 16384MB > 10GB，因此取较小的值 10GB 后面每次 split 的 size 都是 10GB 了。 2.0 版本之后 => SteppingSplitPolicy。 （3）Hbase 2.0 引入了新的 split 策略：如果当前 RegionServer 上该表只有一个 Region，按照 2 * hbase.hregion.memstore.flush.size 分裂，否则按照 hbase.hregion.max.filesize 分裂。 五、HBase 优化 5.1、RowKey 设计 一条数据的唯一标识就是 rowkey，那么这条数据存储于哪个分区，取决于 rowkey 处于哪个一个预分区的区间内，设计rowkey 的主要目的 ，就是让数据均匀的分布于所有的 region 中，在一定程度上防止数据倾斜。接下来谈一谈 rowkey 常用的设计方案。 生成随机数、hash、散列值 时间戳反转 字符串拼接 需求：使用 hbase 存储下列数据，要求能够通过 hbase 的 API 读取数据完成两个统计需求。 （1）统计张三在 2021 年 12 月份消费的总金额 （2）统计所有人在 2021 年 12 月份消费的总金额 5.1.1、实现需求 1 为了能够统计张三在 2021 年 12 月份消费的总金额，我们需要用 scan 命令能够得到张三在这个月消费的所有记录，之后在进行累加即可。Scan 需要填写 startRow 和 stopRow： scan : startRow -> ^A^Azhangsan2021-12 endRow -> ^A^Azhangsan2021-12. 注意点： （1）避免扫描数据混乱，解决字段长度不一致的问题，可以使用相同阿斯卡码值的符号进行填充，框架底层填充使用的是 ASCII 值为 1 的^A。 （2）最后的日期结尾处需要使用 ASCII 略大于’-’的值 最终得到 rowKey 的设计为： //注意 rowkey 相同的数据会视为相同数据覆盖掉之前的版本 rowKey: userdate(yyyy-MM-dd HH:mm:SS) 5.1.2、实现需求 2 问题提出：按照需要 1 的 rowKey 设计，会发现对于需求 2，完全没有办法写 rowKey 的扫描范围。此处能够看出 hbase 设计 rowKey 使用的特点为： 适用性强，泛用性差，能够完美实现一个需求，但是不能同时完美实现多个需要。 如果想要同时完成两个需求，需要对 rowKey 出现字段的顺序进行调整。 调整的原则为：可枚举的放在前面。其中时间是可以枚举的，用户名称无法枚举，所以必须把时间放在前面。 最终满足 2 个需求的设计 可以穷举的写在前面即可 rowKey 设计格式 => date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms) （1）统计张三在 2021 年 12 月份消费的总金额 scan: startRow => 2021-12^A^Azhangsan stopRow => 2021-12^A^Azhangsan. （2）统计所有人在 2021 年 12 月份消费的总金额 scan: startRow => 2021-12 stopRow => 2021-12. 5.1.3、添加预分区优化 预分区的分区号同样需要遵守 rowKey 的 scan 原则。所有必须添加在 rowKey 的最前面，前缀为最简单的数字。同时使用 hash 算法将用户名和月份拼接决定分区号。（单独使用用户名会造成单一用户所有数据存储在一个分区） 添加预分区优化 startKey stopKey 001 001 002 002 003 ... 119 120 分区号 => `hash(user+date(MM)) % 120` 分区号填充 如果得到 1 => 001 rowKey 设计格式 => 分区号 date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms) 缺点：实现需求 2 的时候，由于每个分区都有 12 月份的数据，需要扫描 120 个分区。 解决方法：提前将分区号和月份进行对应。 提前将月份和分区号对应一下 000 到 009 分区 存储的都是 1 月份数据 010 到 019 分区 存储的都是 2 月份数据 ... 110 到 119 分区 存储的都是 12 月份数据 是 9 月份的数据 分区号=> hash(user+date(MM)) % 10 + 80 分区号填充 如果得到 85 => 085 得到 12 月份所有人的数据 扫描 10 次 scan: startRow => 1102021-12 stopRow => 1102021-12. ... startRow => 1122021-12 stopRow => 1122021-12. .. startRow => 1192021-12 stopRow => 1192021-12. 5 5.2、参数优化 1）Zookeeper 会话超时时间 hbase-site.xml 属性：zookeeper.session.timeout 解释：默认值为 90000 毫秒（90s）。当某个 RegionServer 挂掉，90s 之后 Master 才 能察觉到。可适当减小此值，尽可能快地检测 regionserver 故障，可调整至 20-30s。 看你能有都能忍耐超时，同时可以调整重试时间和重试次数 hbase.client.pause（默认值 100ms） hbase.client.retries.number（默认 15 次） 2）设置 RPC 监听数量 hbase-site.xml 属性：hbase.regionserver.handler.count 解释：默认值为 30，用于指定 RPC 监听的数量，可以根据客户端的请求数进行调整，读写请求较多时，增加此值。 3）手动控制 Major Compaction hbase-site.xml 属性：hbase.hregion.majorcompaction 解释：默认值：604800000 秒（ 7 天）， Major Compaction 的周期，若关闭自动 Major Compaction，可将其设为 0。如果关闭一定记得自己手动合并，因为大合并非常有意义 4）优化 HStore 文件大小 hbase-site.xml 属性：hbase.hregion.max.filesize 解释：默认值 10737418240（10GB），如果需要运行 HBase 的 MR 任务，可以减小此值， 因为一个 region 对应一个 map 任务，如果单个 region 过大，会导致 map 任务执行时间过长。该值的意思就是，如果 HFile 的大小达到这个数值，则这个 region 会被切分为两 个 Hfile。 5）优化 HBase 客户端缓存 属性：hbase.client.write.buffer 解释：默认值 2097152bytes（2M）用于指定 HBase 客户端缓存，增大该值可以减少 RPC 调用次数，但是会消耗更多内存，反之则反之。一般我们需要设定一定的缓存大小，以达到 减少 RPC 次数的目的。 6）指定 scan.next 扫描 HBase 所获取的行数 hbase-site.xml 属性：hbase.client.scanner.caching 解释：用于指定 scan.next 方法获取的默认行数，值越大，消耗内存越大。 7）BlockCache 占用 RegionServer 堆内存的比例 hbase-site.xml 属性：hfile.block.cache.size 解释：默认 0.4，读请求比较多的情况下，可适当调大 8）MemStore 占用 RegionServer 堆内存的比例 hbase-site.xml 属性：hbase.regionserver.global.memstore.size 解释：默认 0.4，写请求较多的情况下，可适当调大 Lars Hofhansl（拉斯·霍夫汉斯）大神推荐 Region 设置 20G，刷写大小设置 128M，其它默认。 5.3、JVM 调优 JVM 调优的思路有两部分：一是内存设置，二是垃圾回收器设置。 垃圾回收的修改是使用并发垃圾回收，默认 PO+PS 是并行垃圾回收，会有大量的暂停。理由是 HBsae 大量使用内存用于存储数据，容易遭遇数据洪峰造成 OOM，同时写缓存的数据是不能垃圾回收的，主要回收的就是读缓存，而读缓存垃圾回收不影响性能，所以最终设置的效果可以总结为：防患于未然，早洗早轻松。 1）设置使用 CMS 收集器： -XX:+UseConcMarkSweepGC 2）保持新生代尽量小，同时尽早开启 GC，例如： //在内存占用到 70%的时候开启 GC -XX:CMSInitiatingOccupancyFraction=70 //指定使用 70%，不让 JVM 动态调整 -XX:+UseCMSInitiatingOccupancyOnly //新生代内存设置为 512m -Xmn512m //并行执行新生代垃圾回收 -XX:+UseParNewGC // 设置 scanner扫描结果占用内存大小，在 hbase-site.xml中，设置 hbase.client.scanner.max.result.size(默认值为 2M)为 eden空间的 1/8 （大概在 64M） // 设置多个与 max.result.size * handler.count 相乘的结果小于 Survivor Space(新生代经过垃圾回收之后存活的对象) 5.4、HBase 使用经验法则 官方给出了权威的使用法则： 1、Region 大小控制 10-50G 2、cell 大小不超过 10M（性能对应小于 100K 的值有优化），如果使用 mob（Mediumsized Objects 一种特殊用法）则不超过 50M。 3、1 张表有 1 到 3 个列族，不要设计太多。最好就 1 个，如果使用多个尽量保证不会同时读取多个列族。 4、1 到 2 个列族的表格，设计 50-100 个 Region。 5、列族名称要尽量短，不要去模仿 RDBMS（关系型数据库）具有准确的名称和描述。 6、如果 RowKey 设计时间在最前面，会导致有大量的旧数据存储在不活跃的 Region中，使用的时候，仅仅会操作少数的活动 Region，此时建议增加更多的 Region 个数。 7、如果只有一个列族用于写入数据，分配内存资源的时候可以做出调整，即写缓存不会占用太多的内存。
一、HBase简介 1.1、HBase 定义 Apache HBase 是以 HDFS 为数据存储的，一种分布式、可扩展的 NoSQL 数据库。 1.2、HBase 数据模型 HBase 的设计理念依据 Google 的 BigTable 论文，论文中对于数据模型的首句介绍。 Bigtable 是一个稀疏的、分布式的、持久的多维排序 map。 之后对于映射的解释如下： 该映射由行键、列键和时间戳索引；映射中的每个值都是一个未解释的字节数组。 最终 HBase 关于数据模型和 BigTable 的对应关系如下： HBase 使用与 Bigtable 非常相似的数据模型。用户将数据行存储在带标签的表中。数据行具有可排序的键和任意数量的列。该表存储稀疏，因此如果用户喜欢，同一表中的行可以具有疯狂变化的列。 最终理解 HBase 数据模型的关键在于稀疏、分布式、多维、排序的映射。其中映射 map指代非关系型数据库的 key-Value 结构。 1.2.1、HBase 逻辑结构 HBase 可以用于存储多种结构的数据，以 JSON 为例，存储的数据原貌为： { "row_key1":{ "personal_info":{ "name":"zhangsan", "city":"北京", "phone":"131********" }, "office_info":{ "tel":"010-1111111", "address":"atguigu" } }, "row_key11":{ "personal_info":{ "city":"上海", "phone":"132********" }, "office_info":{ "tel":"010-1111111" } } } HBase 逻辑结构： 存储数据稀疏，数据存储多维，不同的行具有不同的列。 数据存储整体有序，按照 RowKey 的字典序排列，RowKey 为 Byte 数组。 1.2.2、HBase 物理存储结构 物理存储结构即为数据映射关系，而在概念视图的空单元格，底层实际根本不存储。 1.2.3、数据模型 1、Name Space 命名空间，类似于关系型数据库的 database 概念，每个命名空间下有多个表。HBase 两个自带的命名空间，分别是 hbase 和 default，hbase 中存放的是 HBase 内置的表，default 是用户默认使用的命名空间。 2、Table 类似于关系型数据库的表概念。不同的是，HBase 定义表时只需要声明列族即可，不需要声明具体的列。因为数据存储时稀疏的，所有往 HBase 写入数据时，字段可以动态、按需指定。因此，和关系型数据库相比，HBase 能够轻松应对字段变更的场景。 3、Row HBase 表中的每行数据都由一个 RowKey 和多个 Column（列）组成，数据是按照 RowKey 的字典顺序存储的，并且查询数据时只能根据 RowKey 进行检索，所以 RowKey 的设计十分重要。 4、Column HBase 中的每个列都由 Column Family(列族)和 Column Qualifier（列限定符）进行限定，例如 info：name , info：age。建表时，只需指明列族，而列限定符无需预先定义。 5、TimeStamp 用于标识数据的不同版本（version），每条数据写入时，系统会自动为其加上该字段，其值为写入 HBase 的时间。 6、Cell 由{rowkey, column Family：column Qualifier, timestamp} 唯一确定的单元。cell 中的数据全部是字节码形式存贮。 1.3、HBase 基本架构 Master 主要进程，具体实现类为 HMaster，通常部署在namenode上。 功能：负责通过 ZK 监控 RegionServer 进程状态，同时是所有元数据变化的接口。内部启动监控执行 region 的故障转移和拆分的线程。 RegionServer 主要进程，具体实现类为 HRegionServer，部署在datanode上。 功能：主要负责数据 cell 的处理。同时在执行区域的拆分和合并的时候， 由 RegionServer 来实际执行。 架构角色： 1.3.1、Master 实现类为 HMaster，负责监控集群中所有的 RegionServer 实例。主要作用如下： （1）管理元数据表格 hbase:meta，接收用户对表格创建修改删除的命令并执行。 （2）监控 region 是否需要进行负载均衡，故障转移和 region 的拆分。 通过启动多个后台线程监控实现上述功能： 1、LoadBalancer 负载均衡器 周期性监控 region 分布在 regionServer 上面是否均衡，由参数 hbase.balancer.period 控制周期时间，默认 5 分钟。 2、CatalogJanitor 元数据管理器 定期检查和清理 hbase:meta 中的数据。meta 表内容在进阶中介绍。 3、MasterProcWAL master 预写日志处理器 把 master 需要执行的任务记录到预写日志 WAL 中，如果 master 宕机，让 backupMaster 读取日志继续干。 1.3.2、Region Server Region Server 实现类为 HRegionServer，主要作用如下: （1）负责数据 cell 的处理，例如写入数据 put，查询数据 get 等。 （2）拆分合并 region 的实际执行者，有 master 监控，有 regionServer 执行。 1.3.3、Zookeeper HBase 通过 Zookeeper 来做 master 的高可用、记录 RegionServer 的部署信息、并且存储有 meta 表的位置信息。 HBase 对于数据的读写操作时直接访问 Zookeeper 的，在 2.3 版本推出 Master Registry 模式，客户端可以直接访问 master。使用此功能，会加大对 master 的压力，减轻对 Zookeeper 的压力。 1.3.4、HDFS HDFS 为 Hbase 提供最终的底层数据存储服务，同时为 HBase 提供高容错的支持。 二、HBase入门 2.1、HBase 安装部署 2.1.1、Zookeeper 正常部署 首先保证 Zookeeper 集群的正常部署，并启动之。 [atguigu@hadoop102 zookeeper-3.5.7]$ bin/zkServer.sh start [atguigu@hadoop103 zookeeper-3.5.7]$ bin/zkServer.sh start [atguigu@hadoop104 zookeeper-3.5.7]$ bin/zkServer.sh start 2.1.2、Hadoop 正常部署 Hadoop 集群的正常部署并启动。 [atguigu@hadoop102 hadoop-3.1.3]$ sbin/start-dfs.sh [atguigu@hadoop103 hadoop-3.1.3]$ sbin/start-yarn.sh 2.1.3、HBase 的解压 1）解压 Hbase 到指定目录 [atguigu@hadoop102 software]$ tar -zxvf hbase-2.4.11-bin.tar.gz -C /opt/module/ [atguigu@hadoop102 software]$ mv /opt/module/hbase-2.4.11 /opt/module/hbase 2）配置环境变量 sudo vim /etc/profile.d/my_env.sh 添加 #HBASE_HOME export HBASE_HOME=/opt/module/hbase export PATH=$PATH:$HBASE_HOME/bin 3）使用 source 让配置的环境变量生效 source /etc/profile.d/my_env.sh 2.1.4、HBase 的配置文件 1）hbase-env.sh 修改内容，可以添加到最后： # 不启用HBase自带的Zookeeper集群 export HBASE_MANAGES_ZK=false 2）hbase-site.xml 修改内容： <property> <name>hbase.zookeeper.quorum</name> <value>hadoop102,hadoop103,hadoop104</value> <description>The directory shared by RegionServers. </description> </property> <!-- <property>--> <!-- <name>hbase.zookeeper.property.dataDir</name>--> <!-- <value>/export/zookeeper</value>--> <!-- <description> 记得修改 ZK 的配置文件 --> <!-- </description>--> <!-- </property>--> <property> <name>hbase.rootdir</name> <value>hdfs://hadoop102:8020/hbase</value> <description>The directory shared by RegionServers. </description> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> 3）regionservers文件 hadoop102 hadoop103 hadoop104 4）解决 HBase 和 Hadoop 的 log4j 兼容性问题，修改 HBase 的 jar 包，使用 Hadoop 的 jar 包 [atguigu@hadoop102 hbase]$ mv /opt/module/hbase/lib/client-facing-thirdparty/slf4j-reload4j-1.7.33.jar /opt/module/hbase/lib/client-facing-thirdparty/slf4j-reload4j-1.7.33.jar.bak 2.1.5、HBase 远程发送到其他集群 [atguigu@hadoop102 module]$ xsync hbase/ 2.1.6、HBase 服务的启动 1）单点启动 [atguigu@hadoop102 hbase]$ bin/hbase-daemon.sh start master [atguigu@hadoop102 hbase]$ bin/hbase-daemon.sh start regionserver 2）群启 [atguigu@hadoop102 hbase]$ bin/start-hbase.sh 3）对应的停止服务 [atguigu@hadoop102 hbase]$ bin/stop-hbase.sh 2.1.7、查看 HBase 页面 启动成功后，可以通过 http://hadoop102:16010 来访问 HBase 管理页面(HBase Master)，例如： 2.1.8、高可用（可选） 在 HBase 中 HMaster 负责监控 HRegionServer 的生命周期，均衡 RegionServer 的负载，如果 HMaster 挂掉了，那么整个 HBase 集群将陷入不健康的状态，并且此时的工作状态并不会维持太久。所以 HBase 支持对 HMaster 的高可用配置。 1）关闭 HBase 集群（如果没有开启则跳过此步） [atguigu@hadoop102 hbase]$ bin/stop-hbase.sh 2）在 conf 目录下创建 backup-masters 文件 [atguigu@hadoop102 hbase]$ touch conf/backup-masters 3）在 backup-masters 文件中配置高可用 HMaster 节点 [atguigu@hadoop102 hbase]$ echo hadoop103 > conf/backup-masters 4）将整个 conf 目录 scp 到其他节点 [atguigu@hadoop102 hbase]$ xsync conf 5）重启 hbase，打开页面测试查看http://hadooo102:16010 2.1.9、HBase单机部署 hbase-site.xml 修改内容： <configuration> <property> <name>hbase.rootdir</name> <value>hdfs://hadoop102:8020/hbase</value> <description>The directory shared by RegionServers. </description> </property> <property> <name>hbase.tmp.dir</name> <value>/opt/module/hbase/tmp</value> </property> <property> <name>hbase.cluster.distributed</name> <value>false</value> </property> </configuration> 单点启动 bin/start-hbase.sh 或者 [atguigu@hadoop102 hbase]$ bin/hbase-daemon.sh start master [atguigu@hadoop102 hbase]$ bin/hbase-daemon.sh start regionserver 2.2、HBase Shell 操作 2.2.1、基本操作 1）进入 HBase 客户端命令行 [atguigu@hadoop102 hbase]$ bin/hbase shell 2）查看帮助命令 能够展示 HBase 中所有能使用的命令，主要使用的命令有 namespace 命令空间相关，DDL 创建修改表格，DML 写入读取数据。 hbase:001:0> help HBase Shell, version 2.4.11, r7e672a0da0586e6b7449310815182695bc6ae193, Tue Mar 15 10:31:00 PDT 2022 Type 'help "COMMAND"', (e.g. 'help "get"' -- the quotes are necessary) for help on a specific command. Commands are grouped. Type 'help "COMMAND_GROUP"', (e.g. 'help "general"') for help on a command group. COMMAND GROUPS: Group name: general Commands: processlist, status, table_help, version, whoami Group name: ddl Commands: alter, alter_async, alter_status, clone_table_schema, create, describe, disable, disable_all, drop, drop_all, enable, enable_all, exists, get_table, is_disabled, is_enabled, list, list_regions, locate_region, show_filters Group name: namespace Commands: alter_namespace, create_namespace, describe_namespace, drop_namespace, list_namespace, list_namespace_tables Group name: dml Commands: append, count, delete, deleteall, get, get_counter, get_splits, incr, put, scan, truncate, truncate_preserve Group name: tools Commands: assign, balance_switch, balancer, balancer_enabled, catalogjanitor_enabled, catalogjanitor_run, catalogjanitor_switch, cleaner_chore_enabled, cleaner_chore_run, cleaner_chore_switch, clear_block_cache, clear_compaction_queues, clear_deadservers, clear_slowlog_responses, close_region, compact, compact_rs, compaction_state, compaction_switch, decommission_regionservers, flush, get_balancer_decisions, get_balancer_rejections, get_largelog_responses, get_slowlog_responses, hbck_chore_run, is_in_maintenance_mode, list_deadservers, list_decommissioned_regionservers, major_compact, merge_region, move, normalize, normalizer_enabled, normalizer_switch, recommission_regionserver, regioninfo, rit, snapshot_cleanup_enabled, snapshot_cleanup_switch, split, splitormerge_enabled, splitormerge_switch, stop_master, stop_regionserver, trace, unassign, wal_roll, zk_dump Group name: replication Commands: add_peer, append_peer_exclude_namespaces, append_peer_exclude_tableCFs, append_peer_namespaces, append_peer_tableCFs, disable_peer, disable_table_replication, enable_peer, enable_table_replication, get_peer_config, list_peer_configs, list_peers, list_replicated_tables, remove_peer, remove_peer_exclude_namespaces, remove_peer_exclude_tableCFs, remove_peer_namespaces, remove_peer_tableCFs, set_peer_bandwidth, set_peer_exclude_namespaces, set_peer_exclude_tableCFs, set_peer_namespaces, set_peer_replicate_all, set_peer_serial, set_peer_tableCFs, show_peer_tableCFs, update_peer_config Group name: snapshots Commands: clone_snapshot, delete_all_snapshot, delete_snapshot, delete_table_snapshots, list_snapshots, list_table_snapshots, restore_snapshot, snapshot Group name: configuration Commands: update_all_config, update_config Group name: quotas Commands: disable_exceed_throttle_quota, disable_rpc_throttle, enable_exceed_throttle_quota, enable_rpc_throttle, list_quota_snapshots, list_quota_table_sizes, list_quotas, list_snapshot_sizes, set_quota Group name: security Commands: grant, list_security_capabilities, revoke, user_permission Group name: procedures Commands: list_locks, list_procedures Group name: visibility labels Commands: add_labels, clear_auths, get_auths, list_labels, set_auths, set_visibility Group name: rsgroup Commands: add_rsgroup, alter_rsgroup_config, balance_rsgroup, get_namespace_rsgroup, get_rsgroup, get_server_rsgroup, get_table_rsgroup, list_rsgroups, move_namespaces_rsgroup, move_servers_namespaces_rsgroup, move_servers_rsgroup, move_servers_tables_rsgroup, move_tables_rsgroup, remove_rsgroup, remove_servers_rsgroup, rename_rsgroup, show_rsgroup_config SHELL USAGE: Quote all names in HBase Shell such as table and column names. Commas delimit command parameters. Type <RETURN> after entering a command to run it. Dictionaries of configuration used in the creation and alteration of tables are Ruby Hashes. They look like this: {'key1' => 'value1', 'key2' => 'value2', ...} and are opened and closed with curley-braces. Key/values are delimited by the '=>' character combination. Usually keys are predefined constants such as NAME, VERSIONS, COMPRESSION, etc. Constants do not need to be quoted. Type 'Object.constants' to see a (messy) list of all constants in the environment. If you are using binary keys or values and need to enter them in the shell, use double-quote'd hexadecimal representation. For example: hbase> get 't1', "key\x03\x3f\xcd" hbase> get 't1', "key\003\023\011" hbase> put 't1', "test\xef\xff", 'f1:', "\x01\x33\x40" The HBase shell is the (J)Ruby IRB with the above HBase-specific commands added. For more on the HBase Shell, see http://hbase.apache.org/book.html 2.2.2、namespace 1）创建命名空间 使用特定的 help 语法能够查看命令如何使用。 hbase:002:0> help 'create_namespace' 2）创建命名空间 bigdata hbase:003:0> create_namespace 'bigdata' 3）查看所有的命名空间 hbase:004:0> list_namespace NAMESPACE bigdata default hbase 2.2.3、DDL 1）创建表 在 bigdata 命名空间中创建表格 student，两个列族。info 列族数据维护的版本数为 5 个，如果不写默认版本数为 1。 hbase:005:0> create 'bigdata:student', {NAME => 'info', VERSIONS => 5}, {NAME => 'msg'} 如果创建表格只有一个列族，没有列族属性，可以简写。 如果不写命名空间，使用默认的命名空间 default。 hbase:009:0> create 'student1','info' 2）查看表 查看表有两个命令：list 和 describe list：查看所有的表名 hbase:013:0> list describe：查看一个表的详情 hbase:014:0> describe 'student1' hbase:013:0> describe 'bigdata:student' Table bigdata:student is ENABLED bigdata:student COLUMN FAMILIES DESCRIPTION {NAME => 'info', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS => '5', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', COMPRESSION => 'NONE', TTL => 'FOREVER', MIN_VE RSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} {NAME => 'msg', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS => '1', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', COMPRESSION => 'NONE', TTL => 'FOREVER', MIN_VER SIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} 3）修改表 表名创建时写的所有和列族相关的信息，都可以后续通过 alter 修改，包括增加删除列族。 （1）增加列族和修改信息都使用覆盖的方法 hbase:015:0> alter 'student1', {NAME => 'f1', VERSIONS => 3} （2）删除信息使用特殊的语法 hbase:015:0> alter 'student1', NAME => 'f1', METHOD => 'delete' hbase:016:0> alter 'student1', 'delete' => 'f1' 4）删除表 shell 中删除表格,需要先将表格状态设置为不可用。 hbase:017:0> disable 'student1' hbase:018:0> drop 'student1' 2.2.4、DML 1）写入数据 在 HBase 中如果想要写入数据，只能添加结构中最底层的 cell。可以手动写入时间戳指定 cell 的版本，推荐不写默认使用当前的系统时间。 hbase:019:0> put 'bigdata:student','1001','info:name','zhangsan' hbase:020:0> put 'bigdata:student','1001','info:name','lisi' hbase:021:0> put 'bigdata:student','1001','info:age','18' 如果重复写入相同 rowKey，相同列的数据，会写入多个版本进行覆盖。 2）读取数据 读取数据的方法有两个：get 和 scan。 get 最大范围是一行数据，也可以进行列的过滤，读取数据的结果为多行 cell。 hbase:022:0> get 'bigdata:student','1001' COLUMN CELL info:age timestamp=2023-02-26T16:59:34.149, value=18 info:name timestamp=2023-02-26T16:59:27.238, value=lisi 1 row(s) hbase:023:0> get 'bigdata:student','1001' , {COLUMN => 'info:name'} COLUMN CELL info:name timestamp=2023-02-26T16:59:27.238, value=lisi 1 row(s) 也可以修改读取 cell 的版本数，默认读取一个。最多能够读取当前列族设置的维护版本数。 hbase:024:0>get 'bigdata:student','1001' , {COLUMN => 'info:name', VERSIONS => 6} COLUMN CELL info:name timestamp=2023-02-26T16:59:27.238, value=lisi info:name timestamp=2023-02-26T16:59:18.745, value=zhangsan 1 row(s) scan 是扫描数据，能够读取多行数据，不建议扫描过多的数据，推荐使用 startRow 和stopRow 来控制读取的数据，默认范围左闭右开。 hbase:025:0> scan 'bigdata:student',{STARTROW => '1001',STOPROW => '1002'} ROW COLUMN+CELL 1001 column=info:age, timestamp=2023-02-26T16:59:34.149, value=18 1001 column=info:name, timestamp=2023-02-26T16:59:27.238, value=lisi 1 row(s) 实际开发中使用 shell 的机会不多，所有丰富的使用方法到 API 中介绍。 3）删除数据 删除数据的方法有两个：delete 和 deleteall。 delete 表示删除一个版本的数据，即为 1 个 cell，不填写版本默认删除最新的一个版本。 hbase:026:0> delete 'bigdata:student','1001','info:name' deleteall 表示删除所有版本的数据，即为当前行当前列的多个 cell。（执行命令会标记数据为要删除，不会直接将数据彻底删除，删除数据只在特定时期清理磁盘时进行） hbase:027:0> deleteall 'bigdata:student','1001','info:name' 三、HBase API 详见代码。