HBase插入数据 #

一、PUT语法 #

1.1 基本语法 #

ruby
# 基本语法
put '表名', '行键', '列族:列限定符', '值'

# 示例
put 'user', 'user001', 'info:name', '张三'
put 'user', 'user001', 'info:age', '25'
put 'user', 'user001', 'info:email', 'zhangsan@example.com'

1.2 完整语法 #

ruby
# 完整语法
put '表名', '行键', '列族:列限定符', '值', 时间戳

# 示例
put 'user', 'user001', 'info:name', '张三', 1704067200000

1.3 参数说明 #

参数 说明 示例
表名 目标表名称 ‘user’
行键 RowKey ‘user001’
列族:列限定符 列标识 ‘info:name’
数据值 ‘张三’
时间戳 版本时间戳(可选) 1704067200000

二、基本插入操作 #

2.1 插入单列数据 #

ruby
# 插入单列数据
put 'user', 'user001', 'info:name', '张三'

# 插入多列数据(需要多次put)
put 'user', 'user001', 'info:age', '25'
put 'user', 'user001', 'info:email', 'zhangsan@example.com'
put 'user', 'user001', 'info:phone', '13812345678'

2.2 插入多列族数据 #

ruby
# 插入info列族数据
put 'user', 'user001', 'info:name', '张三'
put 'user', 'user001', 'info:age', '25'

# 插入settings列族数据
put 'user', 'user001', 'settings:theme', 'dark'
put 'user', 'user001', 'settings:language', 'zh-CN'

# 插入history列族数据
put 'user', 'user001', 'history:login', '2024-01-01 10:00:00'

2.3 插入命名空间中的表 #

ruby
# 在命名空间中插入数据
put 'myapp:user', 'user001', 'info:name', '张三'

三、版本控制 #

3.1 自动时间戳 #

ruby
# 不指定时间戳,系统自动生成
put 'user', 'user001', 'info:name', '张三'

# 系统自动使用当前时间戳

3.2 手动指定时间戳 #

ruby
# 指定时间戳插入
put 'user', 'user001', 'info:name', '张三', 1704067200000
put 'user', 'user001', 'info:name', '张三丰', 1704067260000

# 查询所有版本
get 'user', 'user001', {COLUMN => 'info:name', VERSIONS => 3}

3.3 版本覆盖 #

ruby
# 相同时间戳会覆盖
put 'user', 'user001', 'info:name', '张三', 1704067200000
put 'user', 'user001', 'info:name', '李四', 1704067200000

# 结果:只有李四的值(张三被覆盖)

3.4 版本数限制 #

ruby
# 创建表时指定版本数
create 'user', {NAME => 'info', VERSIONS => 3}

# 插入多个版本
put 'user', 'user001', 'info:name', '版本1'
put 'user', 'user001', 'info:name', '版本2'
put 'user', 'user001', 'info:name', '版本3'
put 'user', 'user001', 'info:name', '版本4'

# 只保留最新3个版本
get 'user', 'user001', {COLUMN => 'info:name', VERSIONS => 10}

四、批量插入 #

4.1 使用Shell脚本 #

ruby
# 创建批量插入脚本 batch_put.rb
import org.apache.hadoop.hbase.util.Bytes
import org.apache.hadoop.hbase.client.Put
import org.apache.hadoop.hbase.TableName

table = connection.getTable(TableName.valueOf('user'))
puts_list = []

(1..1000).each do |i|
    rowkey = "user%04d" % i
    put = Put.new(Bytes.toBytes(rowkey))
    put.addColumn(Bytes.toBytes('info'), Bytes.toBytes('name'), Bytes.toBytes("用户#{i}"))
    put.addColumn(Bytes.toBytes('info'), Bytes.toBytes('age'), Bytes.toBytes((18 + i % 50).to_s))
    puts_list << put
end

table.put(puts_list)
puts "Inserted #{puts_list.size} rows"

4.2 执行脚本 #

bash
# 在Shell中执行
hbase shell batch_put.rb

# 或在Shell内执行
exec 'batch_put.rb'

4.3 使用Java API批量插入 #

java
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import java.util.ArrayList;
import java.util.List;

public class BatchPut {
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionFactory.createConnection();
        Table table = connection.getTable(TableName.valueOf("user"));
        
        List<Put> puts = new ArrayList<>();
        
        for (int i = 1; i <= 1000; i++) {
            Put put = new Put(Bytes.toBytes("user" + String.format("%04d", i)));
            put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), 
                         Bytes.toBytes("用户" + i));
            put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), 
                         Bytes.toBytes(String.valueOf(18 + i % 50)));
            puts.add(put);
        }
        
        table.put(puts);
        System.out.println("Inserted " + puts.size() + " rows");
        
        table.close();
        connection.close();
    }
}

五、原子操作 #

5.1 检查并插入(CAS) #

ruby
# 检查并插入
# 语法:checkAndPut '表名', '行键', '列族:列', '期望值', Put对象

# 示例:只有当age为25时才更新name
put 'user', 'user001', 'info:age', '25'
checkAndPut 'user', 'user001', 'info:age', '25', 'info:name', '张三丰'

5.2 追加操作 #

ruby
# 追加数据到现有值
append 'user', 'user001', 'info:tags', ',vip'

# 示例
put 'user', 'user001', 'info:tags', 'normal'
append 'user', 'user001', 'info:tags', ',vip'
# 结果:normal,vip

5.3 增量操作 #

ruby
# 计数器增量
incr 'user', 'user001', 'info:login_count'

# 指定增量值
incr 'user', 'user001', 'info:login_count', 5

# 获取计数器值
get_counter 'user', 'user001', 'info:login_count'

六、数据类型处理 #

6.1 字符串 #

ruby
# 字符串直接插入
put 'user', 'user001', 'info:name', '张三'
put 'user', 'user001', 'info:email', 'zhangsan@example.com'

6.2 数值 #

ruby
# 数值以字符串形式存储
put 'user', 'user001', 'info:age', '25'
put 'user', 'user001', 'info:score', '95.5'

# 使用计数器存储数值
incr 'user', 'user001', 'info:login_count'

6.3 布尔值 #

ruby
# 布尔值以字符串存储
put 'user', 'user001', 'info:active', 'true'
put 'user', 'user001', 'info:verified', 'false'

6.4 日期时间 #

ruby
# 日期时间以字符串存储
put 'user', 'user001', 'info:created_at', '2024-01-01 10:00:00'
put 'user', 'user001', 'info:updated_at', '2024-01-02 15:30:00'

# 或使用时间戳
put 'user', 'user001', 'info:created_ts', '1704067200000'

6.5 JSON数据 #

ruby
# JSON数据以字符串存储
put 'user', 'user001', 'info:address', '{"city":"北京","district":"朝阳"}'
put 'user', 'user001', 'info:preferences', '{"theme":"dark","language":"zh-CN"}'

七、插入示例 #

7.1 用户信息插入 #

ruby
# 创建表
create 'user', {NAME => 'info', VERSIONS => 1}

# 插入用户信息
put 'user', 'user001', 'info:name', '张三'
put 'user', 'user001', 'info:age', '25'
put 'user', 'user001', 'info:email', 'zhangsan@example.com'
put 'user', 'user001', 'info:phone', '13812345678'
put 'user', 'user001', 'info:created_at', '2024-01-01 10:00:00'

# 验证插入
get 'user', 'user001'

7.2 订单信息插入 #

ruby
# 创建表
create 'order', {NAME => 'detail', VERSIONS => 1}

# 插入订单信息
put 'order', 'order001', 'detail:user_id', 'user001'
put 'order', 'order001', 'detail:amount', '100.00'
put 'order', 'order001', 'detail:status', 'paid'
put 'order', 'order001', 'detail:created_at', '2024-01-01 10:00:00'

# 验证插入
get 'order', 'order001'

7.3 时序数据插入 #

ruby
# 创建表
create 'sensor', {NAME => 'data', VERSIONS => 1, COMPRESSION => 'SNAPPY'}

# 插入传感器数据
put 'sensor', 'device001_1704067200', 'data:temp', '25.5'
put 'sensor', 'device001_1704067200', 'data:humidity', '60'
put 'sensor', 'device001_1704067200', 'data:pressure', '1013'

# 验证插入
get 'sensor', 'device001_1704067200'

八、性能优化 #

8.1 批量插入 #

text
批量插入优化
├── 使用批量Put减少网络开销
├── 合理设置批量大小(100-1000)
├── 关闭WAL(牺牲安全性)
└── 预分区避免热点

8.2 写入优化配置 #

xml
<!-- hbase-site.xml -->

<!-- 增大MemStore -->
<property>
    <name>hbase.hregion.memstore.flush.size</name>
    <value>268435456</value>  <!-- 256MB -->
</property>

<!-- 增加Handler数量 -->
<property>
    <name>hbase.regionserver.handler.count</name>
    <value>50</value>
</property>

8.3 关闭WAL(谨慎使用) #

java
// Java API关闭WAL
Put put = new Put(Bytes.toBytes("user001"));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("张三"));
put.setDurability(Durability.SKIP_WAL);  // 关闭WAL
table.put(put);

九、常见问题 #

9.1 数据类型问题 #

ruby
# 问题:数值比较不正确
put 'user', 'user001', 'info:score', '100'
put 'user', 'user001', 'info:score', '9'

# 查询时按字典序排序
# 结果:100 < 9(字典序)

# 解决:使用固定长度或补零
put 'user', 'user001', 'info:score', '100'
put 'user', 'user001', 'info:score', '009'

9.2 热点问题 #

ruby
# 问题:顺序写入导致热点
put 'user', 'user001', 'info:name', '张三'
put 'user', 'user002', 'info:name', '李四'
put 'user', 'user003', 'info:name', '王五'

# 解决:使用散列RowKey
put 'user', 'a1b2_user001', 'info:name', '张三'
put 'user', 'c3d4_user002', 'info:name', '李四'
put 'user', 'e5f6_user003', 'info:name', '王五'

9.3 版本数限制 #

ruby
# 问题:版本数超过限制
create 'user', {NAME => 'info', VERSIONS => 3}
put 'user', 'user001', 'info:name', 'v1'
put 'user', 'user001', 'info:name', 'v2'
put 'user', 'user001', 'info:name', 'v3'
put 'user', 'user001', 'info:name', 'v4'

# 结果:只保留最新3个版本(v2, v3, v4)

十、最佳实践 #

10.1 数据设计 #

text
数据设计建议
├── 合理设计RowKey避免热点
├── 列名简短减少存储
├── 使用压缩减少存储空间
└── 设置合理的版本数和TTL

10.2 写入优化 #

text
写入优化建议
├── 使用批量插入
├── 预分区避免热点
├── 合理设置MemStore大小
└── 低峰期大量写入

10.3 数据一致性 #

text
数据一致性建议
├── 使用CAS操作保证原子性
├── 重要数据开启WAL
├── 合理设计版本策略
└── 定期备份重要数据

十一、总结 #

本节介绍了HBase插入数据:

操作 语法
基本插入 put ‘表’, ‘row’, ‘cf:col’, ‘value’
指定时间戳 put ‘表’, ‘row’, ‘cf:col’, ‘value’, ts
批量插入 table.put(puts_list)
原子插入 checkAndPut
追加 append ‘表’, ‘row’, ‘cf:col’, ‘value’
计数器 incr ‘表’, ‘row’, ‘cf:col’

下一步,让我们学习查询数据!

最后更新:2026-03-27