Get the data out: migrate from Redis to KiviDB
There are four ways. All of them carry strings, lists, sets, sorted sets, hashes with per-field expiry, streams with their consumer groups and pending…
There are four ways. All of them carry strings, lists, sets, sorted sets, hashes with per-field expiry, streams with their consumer groups and pending entries, bitmaps, geo sets, RedisJSON documents and key TTLs, from Redis 6.2 to 8.10.
| Method | Downtime | Into self-hosted | Into KiviDB Cloud |
|---|---|---|---|
| Replicate | Seconds | Yes | No: a Cloud database cannot replicate from an outside server |
| RDB file | From the snapshot to the switch | Yes | Yes: import the file |
| Copy tool (RedisShake, RIOT) | Seconds with sync or live mode | Yes | Yes |
MIGRATE | For the keys you move | Yes | Possible, not recommended |
1. Replicate, then switch
KiviDB connects to Redis as a replica: it takes a full copy, then follows every
write until you promote it. Redis sees an ordinary replica, so this needs
PSYNC on the primary, which self-hosted Redis always allows.
kividb --port 6380 --dir /var/lib/kividb \
--replicaof redis.internal 6379 \
--masterauth "$REDIS_PASSWORD"
# with an ACL user on Redis, add: --masteruser replicator
The user needs the replication commands on Redis
(ACL SETUSER replicator on >… +psync +replconf +ping).
Watch it catch up:
redis-cli -p 6380 INFO replication | grep -E 'master_link_status|master_sync_in_progress|slave_repl_offset'
redis-cli -h redis.internal INFO replication | grep master_repl_offset
It has caught up when master_link_status:up, master_sync_in_progress:0, and
KiviDB's slave_repl_offset equals Redis's master_repl_offset. The cutover
steps are in the runbook.
For a large dataset with many writes, check client-output-buffer-limit replica on Redis is large enough for the writes made while the snapshot
transfers. If it is not, Redis drops the link and the full sync starts again.
2. An RDB file
Take a snapshot. BGSAVE writes it in the background; SAVE blocks Redis until
it is done. Taking it from a Redis replica keeps the work off the primary.
redis-cli -h redis.internal BGSAVE
redis-cli -h redis.internal INFO persistence | grep -E 'rdb_bgsave_in_progress|rdb_last_bgsave_status'
redis-cli -h redis.internal CONFIG GET dir # where dump.rdb is
redis-cli -h redis.internal CONFIG GET dbfilename
When rdb_bgsave_in_progress:0 and the status is ok, copy the file. Writes
made after the snapshot are not in it: stop writes first, or plan to replay
them, or use a method that follows writes.
redis-cli --rdb dump.rdb also fetches a snapshot over the network, from any
Redis that allows SYNC.
3. Copy with RedisShake
RedisShake (open source, MIT) reads from Redis and writes to KiviDB with ordinary commands, so it works into KiviDB Cloud too. Download the release for your platform, unpack it, and run it with a TOML file:
curl -LO https://github.com/tair-opensource/RedisShake/releases/download/v4.7.0/redis-shake-v4.7.0-linux-amd64.tar.gz
tar xzf redis-shake-v4.7.0-linux-amd64.tar.gz
./redis-shake shake.toml
It has three readers:
| Reader | How it reads | Follows new writes | Needs on the source |
|---|---|---|---|
sync_reader | Acts as a replica: a full snapshot, then the write stream | Yes, until you stop it | PSYNC |
scan_reader | SCAN, then DUMP each key | With ksn = true (keyspace notifications) | DUMP; for ksn, CONFIG SET notify-keyspace-events |
rdb_reader | Reads an RDB file | No | The file |
All three write with RESTORE, which keeps TTLs, field expiry and stream
consumer groups.
Sync (follows writes until you switch):
[sync_reader]
cluster = false
address = "redis.internal:6379"
username = "" # an ACL user, if you use one
password = "redis-password"
tls = false
[redis_writer]
cluster = false
address = "kividb.internal:6380"
username = ""
password = "kividb-password"
tls = false
[advanced]
dir = "shake-data"
log_file = "shake.log"
Its log reports progress every few seconds. Once it reads
syncing aof, diff=[0], KiviDB has the snapshot and every write since, and
stays up to date until you stop the tool.
Scan (where PSYNC is not allowed): replace the reader with:
[scan_reader]
cluster = false
address = "redis.internal:6379"
password = "redis-password"
tls = false
dbs = [] # all databases; or e.g. [0, 2]
scan = true
ksn = false # true: keep following writes through keyspace notifications
count = 1 # keys per SCAN call; 100 or more copies faster
With ksn = false it copies every key once and exits. With ksn = true it
also subscribes to keyspace notifications and copies each key again when it
changes, until you stop it. That means writing over keys it has already
copied, so add this, or it stops at the first changed key with a BUSYKEY
error:
[advanced]
rdb_restore_command_behavior = "rewrite"
Turn the notifications on at the source first
(CONFIG SET notify-keyspace-events KEA, or the provider's setting).
Keyspace notifications are fire-and-forget: a key changed while the tool is
disconnected is missed, so run the verify script after the switch.
Filters. The [filter] section skips keys or databases, for example module
keys KiviDB does not read:
[filter]
block_key_prefix = ["ts:", "bf:"]
block_db = [5]
Into an existing database. RedisShake stops at the first key that already
exists on KiviDB (Target key name is busy). Set
rdb_restore_command_behavior = "rewrite" under [advanced] to overwrite, or
"skip" to keep KiviDB's copy.
Copy with RIOT
RIOT (open source, Apache 2.0) does the same
from a single command. The riot-standalone-… downloads include their own Java
runtime.
riot replicate redis://:$REDIS_PASSWORD@redis.internal:6379 \
redis://kividb.internal:6380 --target-pass "$KIVIDB_PASSWORD" \
--compare NONE
--mode SCAN(the default) copies once;--mode LIVEkeeps following writes through keyspace notifications until you stop it.- It copies one logical database per run: add
/2to both URIs for database 2. --compare QUICKandFULLrun a Lua script on the target after the copy, so they need Lua there. Into the default build, or a KiviDB Cloud database without Lua, use--compare NONEand the verify script.
4. Copy keys with MIGRATE
To move some keys rather than everything, run MIGRATE on Redis with KiviDB
as the destination. It sends each key as a RESTORE, and KiviDB accepts
Redis's payloads:
redis-cli -h redis.internal MIGRATE kividb.internal 6380 "" 0 5000 COPY REPLACE \
AUTH "$KIVIDB_PASSWORD" KEYS user:1 user:2 user:3
# with a KiviDB user: AUTH2 <username> <password>
COPY leaves the key on Redis; without it Redis deletes the key once KiviDB
has it. The fourth argument is the destination database. The Redis server
itself opens the connection to KiviDB, and MIGRATE has no TLS options of its
own, so for KiviDB Cloud use RedisShake or RIOT instead.
To move a whole database this way, loop over SCAN and send batches:
redis-cli -h redis.internal --scan | xargs -n 100 \
redis-cli -h redis.internal MIGRATE kividb.internal 6380 "" 0 10000 COPY REPLACE AUTH "$KIVIDB_PASSWORD" KEYS
From Redis Cluster
KiviDB can't join a Redis cluster, and an RDB file is per shard, so use a copy tool that reads every shard.
Into a self-hosted KiviDB cluster: build the KiviDB cluster first (see Set up a cluster), then let RedisShake read each Redis shard and route each key to the KiviDB node that owns its slot:
[sync_reader]
cluster = true
address = "redis-node-1.internal:6379" # any node; it finds the rest
password = "redis-password"
[redis_writer]
cluster = true
address = "kividb-node-1.internal:6380" # any node
password = "kividb-password"
The KiviDB cluster does not need the same number of shards or the same slot
layout: keys are written to whichever node owns their slot. Use scan_reader
with cluster = true where PSYNC is blocked.
Into one KiviDB server (when the data fits on one machine): the same, with
cluster = false under [redis_writer]. Multi-key commands then work across
all keys, which they did not on the cluster.
With RIOT: add --source-cluster, and --target-cluster for a cluster
target.
Hash tags ({user:42}:cart) keep working on a KiviDB cluster: it uses the same
slot function.
From a Sentinel setup
Sentinel only decides which Redis server is the primary. Ask it, then use any method above against that address:
redis-cli -h sentinel-1.internal -p 26379 SENTINEL get-master-addr-by-name mymaster
Do not attach KiviDB as a replica of a Sentinel-monitored primary. Sentinel
discovers every replica of the primary and treats it as a failover candidate.
If the primary fails while KiviDB is attached, Sentinel can promote the KiviDB
server and reconfigure the other Redis replicas to follow it. In Redis you
would prevent that with replica-priority 0; KiviDB 1.2.0 accepts that setting
but does not report it to Sentinel, so it does not help.
Two ways that stay out of Sentinel's sight:
-
Replicate from a Redis replica rather than the primary. Sentinel learns replicas from the primary only, so it never sees KiviDB. Redis replicas serve their own replicas. When Sentinel promotes that replica, KiviDB keeps following it and continues with a partial resync, without a new full copy.
kividb --replicaof redis-replica-1.internal 6379 --masterauth "$REDIS_PASSWORD" -
Use RedisShake (
sync_reader) against the primary. It is a client, not a server Sentinel can connect to and promote. If Sentinel fails over during the copy, restart RedisShake against the new primary.
After the move, clients connect to KiviDB directly: Sentinel-aware clients
(Redis.sentinel in redis-py, sentinels in ioredis, JedisSentinelPool)
are replaced by plain connections, or by cluster clients if you move to a KiviDB
cluster. KiviDB has no Sentinel; for failover, run a KiviDB cluster with
replicas, the Kubernetes operator,
or KiviDB Cloud Pro.