Apache SeaTunnel 的 Redis 连接器支持 string、hash、set、zset 类型的读取和写入,Redis 到 Redis 的数据搬运不用再手写脚本。本篇按数据类型给出四种同步配置,基于 SeaTunnel 2.3.13 的连接器行为,给有需要的同学一些参考。
| 数据类型 | 读取行为 | 典型写入方式 | 注意点 |
|---|---|---|---|
| string | 整值读取,format=json 时按 schema 解析字段 | 按 key 写入,同 key 覆盖 | key 模板支持 {字段} 占位符改写 |
| hash | 无 schema 时整 hash 序列化为一行 JSON | 追加到 list | hash 的 key 名不进数据行 |
| set | 每个元素独立成行 | 追加到 list | 无序、不去重 |
| zset | 每个元素独立成行 | 追加到 list | score 不读取,会丢失 |
String:结构化透传
data_type = string 把每个 key 的值整读出来,format = json 时按 schema 逐字段解析。sink 端 data_type = key 按 key 写入,同 key 后写覆盖先写,key 模板里的 {uid} 用行内字段值替换。适合缓存迁移、key 换前缀这类场景。
source { Redis { host = "redis-prod-01" port = 6379 auth = "redis-demo-pass" keys = "user:profile:*" data_type = string batch_size = 200 read_key_enabled = true key_field_name = key single_field_name = value format = json schema = { table = "UserDB.UserProfile" columns = [ { name = "key", type = "string" }, { name = "uid", type = "bigint" }, { name = "nickname", type = "string" }, { name = "age", type = "int" } ] } }}sink { Redis { host = "redis-prod-01" port = 6379 auth = "redis-demo-pass" key = "user:profile:v2:{uid}" support_custom_key = true data_type = key batch_size = 200 }}
测试数据:
SET user:profile:1001 '{"uid":1001,"nickname":"xiaoma","age":30}'SET user:profile:1002 '{"uid":1002,"nickname":"candy","age":25}'
跑完任务后验证:
GET user:profile:v2:1001"{\"key\":\"user:profile:1001\",\"uid\":1001,\"nickname\":\"xiaoma\",\"age\":30}"
数据原样落地,key 换上了 v2 前缀。没配 value_field,整行按 json 序列化写入;read_key_enabled = true 时 Redis key 本身也会作为一列带过去。
Hash:压平合并进 list
不配 schema 时,每个 hash 的全部 field-value 序列化为一个 JSON 作为一行,value 保持字符串,hash 的 key 名不进数据。sink 配成 list,多个 hash 就合并进了同一条队列,适合异构 hash 汇总。
source { Redis { host = "redis-prod-01" port = 6379 auth = "redis-demo-pass" keys = "user:ext:*" data_type = hash batch_size = 200 }}sink { Redis { host = "redis-prod-01" port = 6379 auth = "redis-demo-pass" key = "audit:user:ext:queue" data_type = list batch_size = 200 }}
测试数据:
HSET user:ext:1001 city Shanghai level goldHSET user:ext:1002 city Beijing level silver device iOS
验证:
LRANGE audit:user:ext:queue 0 -11) "{\"city\":\"Shanghai\",\"level\":\"gold\"}"2) "{\"city\":\"Beijing\",\"level\":\"silver\",\"device\":\"iOS\"}"
两个字段结构不一样的 hash 压平进了同一条 list。如果想拆成列而不是整体 JSON,配 schema 加 hash_key_parse_mode = kv,schema 第一个字段作为原 hash key 的列名,每个 kv 独立成行。
Set:逐元素展开进 list
set 中的每个元素都作为一行向下游发送,数据粒度从集合变成单值流。sink 配成 list 逐条追加。
source { Redis { host = "redis-prod-01" port = 6379 auth = "redis-demo-pass" keys = "tag:members:*" data_type = set batch_size = 200 }}sink { Redis { host = "redis-prod-01" port = 6379 auth = "redis-demo-pass" key = "tag:members:merged" data_type = list batch_size = 200 }}
测试数据:
SADD tag:members:vip 1001 1003 1007SADD tag:members:newuser 1002 1003
验证:
LRANGE tag:members:merged 0 -11) "1001"2) "1003"3) "1007"4) "1002"5) "1003"
注意两点:set 无序,上面顺序只是示意,不能当业务依据;不去重,1003 在两个源 set 里都有,list 里就出现两次。想合并去重,sink 换成 data_type = set。
Zset:逐元素展开,score 丢失
zset 和 set 一样逐元素成行,但 score 不读取、不进数据行,下游只拿到成员名单。
source { Redis { host = "redis-prod-01" port = 6379 auth = "redis-demo-pass" keys = "rank:board:*" data_type = zset batch_size = 200 }}sink { Redis { host = "redis-prod-01" port = 6379 auth = "redis-demo-pass" key = "rank:members:merged" data_type = list batch_size = 200 }}
测试数据:
ZADD rank:board:day 9800 1001 9500 1003ZADD rank:board:week 7200 1002
验证:
LRANGE rank:members:merged 0 -11) "1001"2) "1003"3) "1002"
三个成员的 score(9800、9500、7200)全部丢失。sink 端写 zset 时 score 也固定为 1,读写两边都不带真实 score。所以排行榜、优先级队列这类依赖 score 的场景不要走这条路。
五、总结
四种形态对应三种数据变化:string 结构化透传、hash 压平合并、set/zset 粒度展开。Redis 到 Redis 的搬运,大部分场景用连接器配置就能解决,不用引入另外的脚本。
但连接器的边界也很清楚:hash 的 key 名、set 的顺序、zset 的 score,这些边缘信息默认都会丢。业务一旦依赖这些信息,就得换自定义脚本或其他方案。