迁移键功能非常重要,有时候我们只想把部分数据从一个redis迁移到另一个redis(例如从生产环境迁移到测试环境) redis发展过程中提供了move、dump+restore、migrate三组迁移键的方法,它们的实现场景和使用场景不一样
move
move key db
- move命令只用于在redis内部进行迁移,redis内部有多个数据库,彼此之间在数据上是隔离的,move key db就是把指定的键从源数据库移动到目标数据库中
- 使用建议:不建议在生产环境中使用,因此这个命令只需要了解即可
dump+restore
dump key
restore key ttl value
dump+restore可以实现在不同的redis实例之间进行数据迁移,整个迁移过程分为两步:
- 在源redis上,dump命令会将键值序列化,格式采用的是rdb格式
- 在模板redis上,restore命令会将上面序列化的值进行复原,采用ttl代表过期时间,如果ttl=0代表没有过期时间
注意:
- 整个迁移过程不是原子性的,而是通过客户端分布完成的
- 迁移过程中是开启了两个客户端连接,所以dump的结构不是在源redis和目标redis之间进行传输
演示
- 在源redis上执行dump
redis-source> set hello world
OK
redis-source> dump hello
"\x00\x05world\x06\x00\x8f<T\x04%\xfcNQ"
- 在模板redis上执行restore
redis-target> get hello
(nil)
redis-target> restore hello 0 "\x00\x05world\x06\x00\x8f<T\x04%\xfcNQ"
OK
redis-target> get hello
"world"
上面2步对应的伪代码如下:
Redis sourceRedis = new Redis("sourceMachine", 6379);
Redis targetRedis = new Redis("targetMachine", 6379);
targetRedis.restore("hello", 0, sourceRedis.dump(key));
migrate(推荐)
语法
migrate host port key|"" destination-db timeout [copy] [replace] [keys key1 key2 ...]
- host:目标Redis的IP
- port:目标Redis的端口
- key|"":在Redis3.0.6版本之前,migrate只支持迁移一个键,所以此处是要迁移的键,但Redis3.0.6版本之后支持迁移多个键,如果当前需要迁移多个键,此处为空字符串""
- destination-db:目标Redis的数据库索引,例如0号数据库,这里就写0
- timeout:迁移的超时时间(毫秒)
- [copy]:如果添加此选项,迁移后不删除源键
- [replace]:替换目标实例上已存在的 key
- [keys key1 key2…]:迁移多个键,例如要迁移key1、key2,此处填keys key1 key2
作用
- 将key原子性的从当前实例传送到目标实例的指定数据库上,一旦传送成功,key保证会出现在目标实例上,而当前实例上的key会被删除
- 这个命令是一个原子操作,它在执行的时候会阻塞进行迁移的两个实例,直到下面任意结果发生:
- 迁移成功
- 迁移失败
- 等待超时
- 实际上migrate命令就是将dump、restore、del三个命令进行组合,命令的内部实现是这样的:
- 它在当前实例对给定key进行dump命令,将它序列化,然后传送到目标实例
- 目标实例再使用restore对数据进行反序列化,并将反序列化所得到的数据添加到数据库中
- 当前实例就像目标实例的客户端那样,只要看到 RESTORE 命令返回 OK ,它就会调用 DEL 删除自己数据库上的 key 。
- timeout 参数以毫秒为格式,指定当前实例和目标实例进行沟通的最大间隔时间。
- 这说明操作并不一定要在 timeout 毫秒内完成,只是说数据传送的时间不能超过这个 timeout 数。
- migrate命令需要在给定的时间内完成IO操作。如果在传送数据时发生IO错误,或者达到了超时时间,那么命令会停止执行,并返回一个特殊的错误: IOERR
- 当 IOERR 出现时,有以下两种可能:
- key 可能存在于两个实例
- key 可能只存在于当前实例
- 唯一不可能发生的情况就是丢失 key ,因此,如果一个客户端执行 MIGRATE 命令,并且不幸遇上 IOERR 错误,那么这个客户端唯一要做的就是检查自己数据库上的 key 是否已经被正确地删除。
- 如果有其他错误发生,那么 MIGRATE 保证 key 只会出现在当前实例中。(当然,目标实例的给定数据库上可能有和 key 同名的键,不过这和 MIGRATE 命令没有关系)。
返回值
迁移成功时返回 OK ,否则返回相应的错误。
VS dump+restore
- 整个过程都是原子执行的,不需要再多个Redis实例上开启客户端,只需在源Redis上执行migrate命令即可
- migrate命令的数据传输直接在源Redis和目标Redis上完成
- 目标Redis完成restore后会发送OK给源Redis,源Redis接收后会根据migrate对应的选项来决定是否要源Redis上删除对应的键
示例
实例
先启动两个 Redis 实例,一个使用默认的 6379 端口,一个使用 7777 端口
$ ./redis-server &
[1] 3557
...
$ ./redis-server --port 7777 &
[2] 3560
...
然后用客户端连上 6379 端口的实例,设置一个键,然后将它迁移到 7777 端口的实例上:
$ ./redis-cli
redis 127.0.0.1:6379> flushdb
OK
redis 127.0.0.1:6379> SET greeting "Hello from 6379 instance"
OK
redis 127.0.0.1:6379> MIGRATE 127.0.0.1 7777 greeting 0 1000
OK
redis 127.0.0.1:6379> EXISTS greeting # 迁移成功后 key 被删除
(integer) 0
使用另一个客户端,查看 7777 端口上的实例:
$ ./redis-cli -p 7777
redis 127.0.0.1:7777> GET greeting
"Hello from 6379 instance"
实例:
- 源Redis有键hello,目标Redis没有
127.0.0.1:6380> migrate 127.0.0.1 6379 hello 0 1000
OK
实例:
- 源Redis和目标Redis都有键hello
- 如果命令没有加replace选项会收到错误提示,加了replace会返回OK表示成功
127.0.0.1:6379> migrate 127.0.0.1 6379 hello 0 1000
(error) ERR Target instance replied with error: BUSYKEY Target key name already
127.0.0.1:6379> migrate 127.0.0.1 6379 hello 0 1000 replace
OK
实例
- 源Redis没有键hello
- 这种情况使用migrate会收到nokey的提示:
127.0.0.1:6379> migrate 127.0.0.1 6380 hello 0 1000
NOKEY
实例
Redis3.0.6版本后迁移多个键
127.0.0.1:6379> migrate 127.0.0.1 6380 "" 0 5000 keys key1 key2 key3
OK