|
敲下 get 'student', '001',HBase Shell 返回三行结果。就三行。RowKey 是 001,列族 info 下两个列限定符,name 和 age,值分别是 "ZhangSan" 和 "20"。没有多余字段,没有 schema 声明,没有表结构定义。这就是 HBase 的 get——按 RowKey 精确取一行数据,简单到让人怀疑它是不是漏了什么。 它确实没漏。HBase 的 get 跟 MySQL 的 SELECT * FROM student WHERE id = '001' 在语义上等价,但底层走的路完全不同。MySQL 要解析 SQL、优化执行计划、可能走 B+ 树索引;HBase 的 get 直接定位到 RowKey 所在的 Region,从 MemStore 和 HFile 里把对应行捞出来。没有查询优化器,没有执行计划,就是一次定向查找。 get 的语法比你想的灵活 最基础的用法就是 get '表名', 'RowKey'。但实际用起来,经常需要控制返回哪些列。比如只想看 name: get 'student', '001', { COLUMN => 'info:name'} 想同时拿多个列,用数组: get 'student', '001', { COLUMN => ['info:name', 'info:age']} 还能限定版本数。HBase 每列默认存 3 个版本,get 默认返回最新那个。要看历史版本: get 'student', '001', { COLUMN => 'info:name', VERSIONS => 3} 时间戳范围也能卡: get 'student', '001', { TIMERANGE => [1600000000000, 1700000000000]} 这些参数写在一对大括号里,用逗号隔开,像 Ruby 的哈希字面量——因为 HBase Shell 本来就是 JRuby 写的。第一次用容易把 => 写成 =,然后报语法错误,别问我怎么知道的。 一个容易踩的坑 RowKey 是二进制字节数组,不是字符串。Shell 里写 get 'student', '001',HBase 会把 '001' 当成 UTF-8 字符串转成字节再去查。如果你的 RowKey 是其他编码或者有特殊字节,Shell 里的写法就得注意。曾经有个同事用 Bytes.toBytes(123) 写入 RowKey,在 Shell 里用 get 'table', '123' 死活查不到——因为一个是整数的字节表示,一个是字符串 "123" 的字节表示,长度都不一样。 还有个细节:get 返回的结果里,列族和列限定符之间用冒号连接,比如 info:name。但如果列族下没有列限定符(少见但合法),显示就是 info:。时间戳默认显示的是毫秒级的长整型,看着像 1609459200000 这种。Shell 不会帮你转成日期,得自己算。 跟 scan 比,get 快在哪 scan 是全表或范围扫描,get 是点查。RegionServer 处理 get 时,直接通过 RowKey 定位到目标 Region,然后在 BlockCache、MemStore、HFile 三层里按顺序找。scan 则要遍历多个 Region,维护 scanner 状态,分批返回。数据量大的时候,一个 get 通常几毫秒,一个全表 scan 可能跑几分钟——前提是你没设 limit。 但 get 也不是没代价。如果 RowKey 设计得不好,比如热点集中在某几个 Region,大量 get 请求会把那几个 RegionServer 压垮。RowKey 散列设计(加盐、哈希前缀)就是为了解决这个。Shell 里做 get 测试的时候感觉不到,生产环境几千 QPS 打过来,热点问题立刻暴露。 Shell 里的 get 还能这么玩 加 { FILTER => "..."} 可以塞过滤器,虽然点查场景下用得少。比如 get 'student', '001', { FILTER => "ValueFilter(=, 'binary:ZhangSan')"},只有 name 或 age 的值等于 ZhangSan 才返回。这相当于把过滤逻辑下推到 RegionServer,减少网络传输。不过点查本来就没多少数据,过滤器更多是 scan 的搭档。 还有个冷门用法:get 'student', '001', { ATTRIBUTES => { 'VISIBILITY' => 'PRIVATE'}},配合 HBase 的 cell 级可见性标签用。大多数集群没开这个特性,写了也白写。 日常运维里,get 最常出现在排查数据问题的场景。用户说“这条记录查不到”,你登 Shell 敲一个 get,返回 0 row(s),那就确认是写入侧的问题,不是读取侧。如果返回了数据但跟预期不符,再看时间戳和版本。比翻日志快得多。 一行命令,三行结果。HBase 的 get 没什么花哨的,但把参数用对了,能省不少事。 |