我们将使用目前最主流和推荐的 Python 客户端库:HappyBase。

目录
- 环境准备
- HBase 环境
- Python 环境
- HappyBase 安装
- 核心概念与连接
- HBase 表结构
- 连接到 HBase
- 完整代码范例
- 建立连接
- 创建表
- 增、删、改、查 操作
- 批量操作
- 扫描表
- 删除表
- 高级特性:过滤器
- 使用
RowFilter进行前缀匹配查询
- 使用
- 总结与最佳实践
环境准备
a. HBase 环境
您需要一个正在运行的 HBase 集群,为了方便开发和测试,最简单的方式是使用 Docker 运行一个单节点的 HBase。
# 拉取 HBase 镜像 docker pull hbase/hbase # 运行 HBase 容器 # -p 2181:2181 映射 ZooKeeper 端口 # -p 16010:16010 映射 HBase Master UI 端口 # -p 9090:9090 映射 Thrift Server 端口 (HappyBase 通过 Thrift 与 HBase 通信) docker run -d --name hbase -p 2181:2181 -p 16010:16010 -p 9090:9090 hbase/hbase
启动后,您可以通过 http://localhost:16010 查看 HBase 的 Web UI。
b. Python 环境
确保您的系统已安装 Python 3.x。
c. HappyBase 安装
HappyBase 依赖于 thrift 库与 HBase 的 Thrift 服务器通信。

# 安装 HappyBase pip install happybase # 如果安装失败,可能需要先安装 thrift # pip install thrift
核心概念与连接
a. HBase 表结构
在操作之前,我们需要了解 HBase 的基本数据模型:
- 表: 数据的集合。
- 行键: 每行的唯一标识符,HBase 中的行是按照行键的字典序排序的。
- 列族: 行中的一组列,列族必须在创建表时定义,并且相对固定。
- 列限定符: 列族下的具体列,列限定符是动态的,可以在写入数据时任意指定。
- 单元格: 行、列、时间戳交汇处的值。
- 时间戳: 数据的版本信息。
一个 users 表,可能有 info 和 contact 两个列族。
| Row Key (用户ID) | Column Family: info | Column Family: contact |
|---|---|---|
| name (列) | email (列) | |
| age (列) | phone (列) | |
user_001 |
name: "Alice" |
email: "alice@example.com" |
age: "30" |
phone: "13800138000" |
|
user_002 |
name: "Bob" |
email: "bob@example.com" |
age: "25" |
phone: "13900139000" |
b. 连接到 HBase
HappyBase 通过 Thrift 协议连接到 HBase,默认情况下,它会连接到本地 (localhost:9090) 的 Thrift 服务器。
import happybase
# 1. 建立连接
# Thrift 服务器不在默认端口,需要指定 host 和 port
# connection = happybase.Connection('your-hbase-host', port=9090)
connection = happybase.Connection()
print("成功连接到 HBase!")
print(f"可用表: {connection.tables()}")
# 2. 关闭连接 (非常重要!)
# 在程序结束时,务必关闭连接,释放资源
connection.close()
完整代码范例
下面是一个完整的 Python 脚本,演示了对 HBase 表的常用操作。

import happybase
import time
# --- 配置 ---
# HBase Thrift 服务器地址
HBASE_HOST = 'localhost'
HBASE_PORT = 9090
# 要操作的表名
TABLE_NAME = 'users'
# 列族定义
COLUMN_FAMILIES = {
'info': dict(), # 列族 'info' 没有特殊属性
'contact': dict() # 列族 'contact' 也没有特殊属性
}
def main():
"""
主函数,演示 HBase 的完整操作流程
"""
# 1. 建立连接
print("正在连接到 HBase...")
try:
connection = happybase.Connection(host=HBASE_HOST, port=HBASE_PORT)
print("连接成功!")
except Exception as e:
print(f"连接失败: {e}")
return
# 2. 创建表 (如果表不存在)
if TABLE_NAME.encode() not in connection.tables():
print(f"表 '{TABLE_NAME}' 不存在,正在创建...")
connection.create_table(TABLE_NAME, COLUMN_FAMILIES)
print(f"表 '{TABLE_NAME}' 创建成功!")
else:
print(f"表 '{TABLE_NAME}' 已存在。")
# 3. 获取表的连接对象
table = connection.table(TABLE_NAME)
# 4. 增、删、改、查 操作
# 4.1 写入数据 (Put)
print("\n--- 写入数据 ---")
user_id_1 = 'user_001'
data_1 = {
'info:name': 'Alice',
'info:age': '30',
'contact:email': 'alice@example.com',
'contact:phone': '13800138000'
}
table.put(user_id_1, data_1)
print(f"已写入数据: {user_id_1} -> {data_1}")
user_id_2 = 'user_002'
data_2 = {
'info:name': 'Bob',
'info:age': '25',
'contact:email': 'bob@example.com',
'contact:phone': '13900139000'
}
table.put(user_id_2, data_2)
print(f"已写入数据: {user_id_2} -> {data_2}")
# 4.2 读取数据 (Get)
print("\n--- 读取数据 ---")
# 读取整行
row = table.row(user_id_1)
print(f"读取 {user_id_1} 的整行数据: {row}")
# 读取特定列
row_specific = table.row(user_id_1, columns=['info:name', 'contact:email'])
print(f"读取 {user_id_1} 的特定列: {row_specific}")
# 4.3 更新数据 (也是 Put)
# HBase 的更新本质上是覆盖,直接 Put 新值即可
print("\n--- 更新数据 ---")
update_data = {'info:age': '31'}
table.put(user_id_1, update_data)
print(f"已更新 {user_id_1} 的年龄: {update_data}")
print(f"更新后 {user_id_1} 的年龄: {table.row(user_id_1)['info:age']}")
# 4.4 删除数据 (Delete)
print("\n--- 删除数据 ---")
# 删除指定列
table.delete(user_id_1, columns=['contact:phone'])
print(f"已删除 {user_id_1} 的 phone 列")
print(f"删除后 {user_id_1} 的数据: {table.row(user_id_1)}")
# 5. 批量操作
print("\n--- 批量操作 ---")
# 使用 batch 可以将多个操作打包,提高性能
with table.batch() as b:
b.put('user_003', {'info:name': 'Charlie', 'info:age': '35'})
b.put('user_004', {'info:name': 'David', 'info:age': '40'})
# 删除操作也可以放在 batch 中
b.delete('user_002')
print("批量操作完成: 添加了 user_003, user_004,并删除了 user_002。")
# 6. 扫描表 (Scan)
print("\n--- 扫描表 ---")
# 扫描整个表
print("扫描所有行:")
for key, data in table.scan():
print(f"Row Key: {key.decode('utf-8')}, Data: {data}")
# 带前缀的扫描
print("\n扫描行键以 'user_' 开头的所有行:")
for key, data in table.scan(row_prefix=b'user_'):
print(f"Row Key: {key.decode('utf-8')}, Data: {data}")
# 7. 删除表 (需要先禁用表)
print("\n--- 删除表 ---")
# 注意:HBase 不能直接删除正在使用的表,需要先禁用
if TABLE_NAME.encode() in connection.tables():
print(f"正在禁用表 '{TABLE_NAME}'...")
connection.disable_table(TABLE_NAME)
print(f"表 '{TABLE_NAME}' 已禁用。")
print(f"正在删除表 '{TABLE_NAME}'...")
connection.delete_table(TABLE_NAME)
print(f"表 '{TABLE_NAME}' 已删除。")
# 8. 关闭连接
print("\n操作完成,正在关闭连接...")
connection.close()
print("连接已关闭。")
if __name__ == '__main__':
main()
高级特性:过滤器
过滤器是 HBase 查询的强大功能,允许你在服务器端对数据进行过滤,只返回符合条件的数据,大大减少了网络传输,HappyBase 通过 scan() 方法的 filter 参数来使用过滤器。
下面是一个使用 RowFilter 进行前缀匹配的例子。
import happybase
# 假设我们已经按照上面的例子创建了 'users' 表并插入了数据
HBASE_HOST = 'localhost'
HBASE_PORT = 9090
TABLE_NAME = 'users'
def scan_with_filter():
connection = happybase.Connection(host=HBASE_HOST, port=HBASE_PORT)
table = connection.table(TABLE_NAME)
print(f"--- 使用过滤器扫描以 'user_00' 开头的行 ---")
# HBase 过滤器语法
# 'PrefixFilter' 是一个内置过滤器
# 它需要一个二进制字符串作为参数
prefix_filter = "PrefixFilter('user_00')"
# 使用 scan 的 filter 参数
scanner = table.scan(filter=prefix_filter)
count = 0
for key, data in scanner:
print(f"Row Key: {key.decode('utf-8')}, Data: {data}")
count += 1
print(f"共找到 {count} 条匹配的记录。")
connection.close()
# 运行这个函数来测试过滤器
# scan_with_filter()
其他常用过滤器示例:
SingleColumnValueFilter: 按列的值进行过滤。# 找出年龄大于 30 的用户 # 注意: HBase 是字符串比较,'31' > '30' filter = "SingleColumnValueFilter('info', 'age', >, 'binary:30')" scanner = table.scan(filter=filter)ColumnPrefixFilter: 按列限定符的前缀进行过滤。# 找出所有 'info' 列族下,列名以 'na' 开头的列 filter = "ColumnPrefixFilter('na')" scanner = table.scan(filter=filter, columns=[b'info:']) # 指定列族
总结与最佳实践
-
连接管理:
- 使用
try...finally或with语句来确保连接总是被正确关闭,防止资源泄露。 - 对于高并发应用,不要为每个请求创建和销毁连接,而是使用连接池,HappyBase 本身不提供连接池,但可以配合
pyhbase等库或使用第三方库如happybase.pool。
- 使用
-
设计 Row Key:
- Row Key 是 HBase 查询性能的关键。避免热点问题(不要用时间戳作为 Row Key 的开头,导致所有写请求都集中在同一个 Region Server)。
- 好的设计原则是让 Row Key 的读写请求均匀分布到集群的不同节点上,可以使用
用户ID的哈希值或反转的手机号等方式。
-
使用批量操作:
- 当需要插入或更新大量数据时,务必使用
batch()操作,它将多个请求打包成一个 RPC 调用,能显著提升性能。
- 当需要插入或更新大量数据时,务必使用
-
善用过滤器:
尽量在 HBase 服务器端使用过滤器完成数据过滤,而不是把所有数据拉到客户端再处理,这样可以极大地减少网络 I/O 和客户端的内存消耗。
-
注意数据类型:
- HBase 中所有数据都是字节数组 (
bytes),HappyBase 会自动帮你进行编码和解码(字符串会变成b'...'),但在处理复杂类型(如数字、JSON)时,需要自己负责序列化和反序列化,存入数字30,实际存入的是字节串b'30',读取时也需要知道它原本是字符串。
- HBase 中所有数据都是字节数组 (
