Redis键总结整理

Redis默认有0-15共16个数据库(dbnum属性的值由服务配置的database选项决定),使用select命令来进入不同的数据库,默认进入0数据库。在服务器内部,客户端状态redisClient结构的db属性记录了客户端当前的目标数据库,通过修改redisClient.db指针,让它指向服务器中不同的数据库,从而实现切换目标数据库中的功能,这就是select命令的实现原理。到目前为止,redis仍然没有可以返回客户端目标数据库的命令(但redis-cli客户端会在输入符旁提示当前所使用的目标数据库),为了避免对数据库进行误操作,在执行redis命令特别是像flushdb之类的危险命令前,最好先执行一个select命令,显式地切换到指定的数据库,然后再执行别的命令。

数据库的键空间

Redis是一个键值对数据库服务器,每个数据库中的每个数据库都由一个redisDb结构表示,其中,redisDb结构的dict字典(键空间)保存了数据库中的所有键值对。

EXPIRE

模式:导航会话

假设一项 web 服务,打算根据用户最近访问的 N 个页面来进行物品推荐,并且假设用户停止阅览超过 60 秒,那么就清空阅览记录(为了减少物品推荐的计算量,并且保持推荐物品的新鲜度)。这些最近访问的页面记录,我们称之为『导航会话』,可以用 INCR 和 RPUSH 命令在 Redis 中实现它:每当用户阅览一个网页的时候,执行以下代码:

如果用户停止阅览超过 60 秒,那么它的导航会话就会被清空,当用户重新开始阅览的时候,系统又会重新记录导航会话,继续进行物品推荐。

SORT

用法:

1)SORT key 返回键值从小到大排序的结果。

2)SORT key DESC 返回键值从大到小排序的结果。

3)使用 ALPHA 修饰符对字符串进行排序

因为 SORT 命令默认排序对象为数字, 当需要对字符串进行排序时, 需要显式地在 SORT 命令之后添加 ALPHA 修饰符:如果系统正确地设置了 LC_COLLATE 环境变量的话,Redis能识别 UTF-8 编码。

4)使用 LIMIT 修饰符限制返回结果

排序之后返回元素的数量可以通过 LIMIT 修饰符进行限制, 修饰符接受 offset 和 count 两个参数:offset 指定要跳过的元素数量、count 指定跳过 offset 个指定的元素之后,要返回多少个对象。

5)使用外部 key 进行排序

可以使用外部 key 的数据作为权重,代替默认的直接对比键值的方式来进行排序。假设现在有用户数据如下:

uid user_name_{uid} user_level_{uid}
1 admin 9999
2 jack 10
3 peter 25
4 mary 70

BY 选项

默认情况下, SORT uid 直接按 uid 中的值排序:

通过使用 BY 选项,可以让 uid 按其他键的元素来排序。

比如说, 以下代码让 uid 键按照 user_level_{uid} 的大小来排序:

user_level_* 是一个占位符, 它先取出 uid 中的值, 然后再用这个值来查找相应的键。在对 uid 列表进行排序时, 程序就会先取出 uid 的值 1 、 2 、 3 、 4 , 然后使用 user_level_1 、 user_level_2 、 user_level_3和 user_level_4 的值作为排序 uid 的权重。

GET 选项

使用 GET 选项, 可以根据排序的结果来取出相应的键值。

比如说, 以下代码先排序 uid , 再取出键 user_name_{uid} 的值:

组合使用 BY 和 GET

通过组合使用 BY 和 GET , 可以让排序结果以更直观的方式显示出来。

比如说, 以下代码先按 user_level_{uid} 来排序 uid 列表, 再取出相应的 user_name_{uid} 的值:

现在的排序结果要比只使用 SORT uid BY user_level_* 要直观得多。

获取多个外部键

可以同时使用多个 GET 选项, 获取多个外部键的值。

以下代码就按 uid 分别获取 user_level_{uid} 和 user_name_{uid} :

GET 用 # 获取被排序键的值。

以下代码就将 uid 的值、及其相应的 user_level_* 和 user_name_* 都返回为结果:

获取外部键,但不进行排序

通过将一个不存在的键作为参数传给 BY 选项, 可以让 SORT 跳过排序操作, 直接返回结果:

通过将这种用法和 GET 选项配合, 就可以在不排序的情况下, 获取多个外部键, 相当于执行一个整合的获取操作(类似于 SQL 数据库的 join 关键字)。

以下代码演示了如何在不引起排序的情况下,使用 SORT 、 BY 和 GET 获取多个外部键:

将哈希表作为 GET 或 BY 的参数

除了可以将字符串键之外, 哈希表也可以作为 GET 或 BY 选项的参数来使用。

比如说,对于前面给出的用户信息表:

uid user_name_{uid} user_level_{uid}
1 admin 9999
2 jack 10
3 peter 25
4 mary 70

我们可以不将用户的名字和级别保存在 user_name_{uid} 和 user_level_{uid} 两个字符串键中, 而是用一个带有 name 域和 level 域的哈希表 user_info_{uid} 来保存用户的名字和级别信息:

之后, BY 和 GET 选项都可以用 key->field 的格式来获取哈希表中的域的值, 其中 key 表示哈希表键, 而 field 则表示哈希表的域:

保存排序结果

默认情况下, SORT 操作只是简单地返回排序结果,并不进行任何保存操作。通过给 STORE 选项指定一个 key 参数,可以将排序结果保存到给定的键上。如果被指定的 key 已存在,那么原有的值将被排序结果覆盖。

可以通过将 SORT 命令的执行结果保存,并用 EXPIRE 为结果设置生存时间,以此来产生一个 SORT 操作的结果缓存。这样就可以避免对 SORT 操作的频繁调用:只有当结果集过期时,才需要再调用一次 SORT 操作。

另外,为了正确实现这一用法,你可能需要加锁以避免多个客户端同时进行缓存重建(也就是多个客户端,同一时间进行 SORT 操作,并保存为结果集),具体参见 SETNX 命令。

如何避免多个客户端同时进行sort命令实现缓存重建:

1)判断缓存是否存在。setnx cache_{uid}
2)解决缓存不存在时,多个客户端并发生成缓存。通过独占锁。通过getset lock_{uid} 1。如果返回值为1,则认为获取锁失败,其他客户端正在生成缓存,如果获取为nil,则认为成功获取锁。
3)设定锁的超时机制
4)生成缓存
5)删除锁机制

SCAN

SCAN cursor [MATCH pattern] [COUNT count]

SCAN 及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代一集元素:

  • SCAN 命令用于迭代当前数据库中的数据库键。
  • SSCAN 命令用于迭代集合键中的元素。
  • HSCAN 命令用于迭代哈希键中的键值对。
  • ZSCAN 命令用于迭代有序集合中的元素(包括元素成员和元素分值)。

以上的四个命令都支持增量式迭代, 它们每次执行都只会返回少量元素, 所以这些命令可以用于生产环境, 而不会出现像 KEYS命令、 SMEMBERS 命令带来的问题 —— 当 KEYS 命令被用于处理一个大的数据库时, 又或者 SMEMBERS 命令被用于处理一个大的集合键时, 它们可能会阻塞服务器达数秒之久。不过, 增量式迭代命令也不是没有缺点的:使用 SMEMBERS 命令可以返回集合键当前包含的所有元素, 但是对于 SCAN 这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证。

注意:

SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第一个参数总是一个数据库键。而 SCAN 命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。

SCAN 命令的基本用法

SCAN 命令是一个基于游标的迭代器: SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。

SCAN 命令的回复是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则是一个数组, 这个数组中包含了所有被迭代的元素。当命令返回了游标 0 , 这表示迭代已经结束, 整个数据集已经被完整遍历过了。以 0 作为游标开始一次新的迭代, 一直调用 SCAN 命令,直到命令返回游标 0,我们称这个过程为一次完整遍历。增量式迭代命令并不保证每次执行都返回某个给定数量的元素。甚至可能会返回零个元素, 但只要命令返回的游标不是 0 , 应用程序就不应该将迭代视作结束。不过命令返回的元素数量总是符合一定规则的, 对于一个大数据集来说, 增量式迭代命令每次最多可能会返回数十个元素;而对于一个足够小的数据集来说, 如果这个数据集的底层表示为编码数据结构(适用于是小集合键、小哈希键和小有序集合键), 那么增量迭代命令将在一次调用中返回数据集中的所有元素。用户可以通过增量式迭代命令提供的 COUNT 选项来指定每次迭代返回元素的最大值。

COUNT 选项

虽然增量式迭代命令不保证每次迭代所返回的元素数量, 但我们可以使用 COUNT 选项, 对命令的行为进行一定程度上的调整。基本上, COUNT 选项的作用就是让用户告知迭代命令, 在每次迭代中应该从数据集里返回多少元素。COUNT 参数的默认值为 10 。

  • 在迭代一个足够大的、由哈希表实现的数据库、集合键、哈希键或者有序集合键时, 如果用户没有使用 MATCH 选项, 那么命令返回的元素数量通常和 COUNT 选项指定的一样, 或者比 COUNT 选项指定的数量稍多一些。
  • 在迭代一个编码为整数集合(intset,一个只由整数值构成的小集合)、 或者编码为压缩列表(ziplist,由不同值构成的一个小哈希或者一个小有序集合)时, 增量式迭代命令通常会无视 COUNT 选项指定的值, 在第一次迭代就将数据集包含的所有元素都返回给用户。并非每次迭代都要使用相同的 COUNT 值。

用户可以在每次迭代中按自己的需要随意改变 COUNT 值, 只要记得将上次迭代返回的游标用到下次迭代里面就可以了。

MATCH 选项

和 KEYS 命令一样, 增量式迭代命令也可以通过提供一个 glob 风格的模式参数, 让命令只返回和给定模式相匹配的元素, 这一点可以通过在执行增量式迭代命令时, 通过给定 MATCH <pattern> 参数来实现。需要注意的是, 对元素的模式匹配工作是在命令从数据集中取出元素之后, 向客户端返回元素之前的这段时间内进行的, 所以如果被迭代的数据集中只有少量元素和模式相匹配, 那么迭代命令或许会在多次执行中都不返回任何元素。

 SCAN 命令的保证

SCAN 命令,以及其他增量式迭代命令,在进行完整遍历的情况下可以为用户带来以下保证: 从完整遍历开始直到完整遍历结束期间, 一直存在于数据集内的所有元素都会被完整遍历返回; 这意味着, 如果有一个元素, 它从遍历开始直到遍历结束期间都存在于被遍历的数据集当中, 那么 SCAN 命令总会在某次迭代中将这个元素返回给用户。然而因为增量式命令仅仅使用游标来记录迭代状态, 所以同一个元素可能会被返回多次。 处理重复元素的工作交由应用程序负责。如果一个元素是在迭代过程中被添加到数据集的, 又或者是在迭代过程中从数据集中被删除的, 那么这个元素可能会被返回,也可能不会,这是未定义的。

迭代过程中可能出现的情况:

  • 并发执行多个迭代

在同一时间, 可以有任意多个客户端对同一数据集进行迭代, 客户端每次执行迭代都需要传入一个游标, 并在迭代执行之后获得一个新的游标, 而这个游标就包含了迭代的所有状态, 因此, 服务器无须为迭代记录任何状态。

  • 中途停止迭代

因为迭代的所有状态都保存在游标里面, 而服务器无须为迭代保存任何状态, 所以客户端可以在中途停止一个迭代, 而无须对服务器进行任何通知。即使有任意数量的迭代在中途停止, 也不会产生任何问题。

  • 使用错误的游标进行增量式迭代

使用间断的、负数、超出范围或者其他非正常的游标来执行增量式迭代并不会造成服务器崩溃, 但可能会让命令产生未定义的行为。未定义行为指的是, 增量式命令对返回值所做的保证可能会不再为真。只有两种游标是合法的:在开始一个新的迭代时, 游标必须为 0 。增量式迭代命令在执行之后返回的, 用于延续迭代过程的游标。

迭代终结的保证

增量式迭代命令所使用的算法只保证在数据集的大小有界的情况下, 迭代才会停止, 换句话说, 如果被迭代数据集的大小不断地增长的话, 增量式迭代命令可能永远也无法完成一次完整迭代。从直觉上可以看出, 当一个数据集不断地变大时, 想要访问这个数据集中的所有元素就需要做越来越多的工作, 能否结束一个迭代取决于用户执行迭代的速度是否比数据集增长的速度更快。

可用版本:

>= 2.8.0

时间复杂度:

增量式迭代命令每次执行的复杂度为 O(1) , 对数据集进行一次完整迭代的复杂度为 O(N) , 其中 N 为数据集中的元素数量。

返回值:

SCAN 命令、 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都返回一个包含两个元素的 multi-bulk 回复: 回复的第一个元素是字符串表示的无符号 64 位整数(游标), 回复的第二个元素是另一个 multi-bulk 回复, 这个 multi-bulk 回复包含了本次被迭代的元素。

  • SCAN 命令返回的每个元素都是一个数据库键。
  • SSCAN 命令返回的每个元素都是一个集合成员。
  • HSCAN 命令返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。
  • ZSCAN 命令返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员和一个分值组成。

总结

优点:

  • 提供键空间的遍历操作,支持游标,复杂度O(1), 整体遍历一遍只需要O(N);
  • 提供结果模式匹配;
  • 支持一次返回的数据条数设置,但仅仅是个hints,有时候返回的会多;
  • 弱状态,所有状态只需要客户端需要维护一个游标;

缺点:

  • 无法提供完整的快照遍历,也就是中间如果有数据修改,可能有些涉及改动的数据遍历不到;
  • 每次返回的数据条数不一定,极度依赖内部实现;
  • 返回的数据可能有重复,应用层必须能够处理重入逻辑;

Redis过期键删除策略

定时删除:

在设置键的过期时间时,创建一个定时事件,当过期时间到达时,由事件处理器自动执行键的删除操作。定时删除策略对内存是最友好的: 因为它保证过期键会在第一时间被删除, 过期键所消耗的内存会立即被释放。

这种策略的缺点是, 它对 CPU 时间是最不友好的: 因为删除操作可能会占用大量的 CPU 时间 —— 在内存不紧张、但是 CPU 时间非常紧张的时候 (比如说,进行交集计算或排序的时候), 将 CPU 时间花在删除那些和当前任务无关的过期键上, 这种做法毫无疑问会是低效的。除此之外, 目前 Redis 事件处理器对时间事件的实现方式 —— 无序链表, 查找一个时间复杂度为 O(N) —— 并不适合用来处理大量时间事件。

惰性删除:

放任键过期不管,但是在每次从 dict 字典中取出键值时,要检查键是否过期,如果过期的话,就删除它,并返回空;如果没过期,就返回键值。惰性删除对 CPU 时间来说是最友好的: 它只会在取出键时进行检查, 这可以保证删除操作只会在非做不可的情况下进行 —— 并且删除的目标仅限于当前处理的键, 这个策略不会在删除其他无关的过期键上花费任何 CPU 时间。

惰性删除的缺点:它对内存是最不友好的: 如果一个键已经过期, 而这个键又仍然保留在数据库中, 那么 dict 字典和 expires 字典都需要继续保存这个键的信息, 只要这个过期键不被删除, 它占用的内存就不会被释放。在使用惰性删除策略时, 如果数据库中有非常多的过期键, 但这些过期键又正好没有被访问的话, 那么它们就永远也不会被删除(除非用户手动执行), 这对于性能非常依赖于内存大小的 Redis 来说,会有影响。

定期删除:

每隔一段时间,对 expires 字典进行检查,删除里面的过期键。从上面对定时删除和惰性删除的讨论来看, 这两种删除方式在单一使用时都有明显的缺陷: 定时删除占用太多 CPU 时间, 惰性删除浪费太多内存。定期删除是这两种策略的一种折中:它每隔一段时间执行一次删除操作,并通过限制删除操作执行的时长和频率,籍此来减少删除操作对 CPU 时间的影响。另一方面,通过定期删除过期键,它有效地减少了因惰性删除而带来的内存浪费。

过期键对 AOF 、RDB 和复制的影响

在创建新的 RDB 文件时,程序会对键进行检查,过期的键不会被写入到更新后的 RDB 文件中。因此,过期键对更新后的 RDB 文件没有影响。在键已经过期,但是还没有被惰性删除或者定期删除之前,这个键不会产生任何影响,AOF 文件也不会因为这个键而被修改。当过期键被惰性删除、或者定期删除之后,程序会向 AOF 文件追加一条 DEL 命令,来显式地记录该键已被删除。当进行 AOF 重写时, 程序会对键进行检查, 过期的键不会被保存到重写后的 AOF 文件。当服务器带有附属节点时, 过期键的删除由主节点统一控制:

1)如果服务器是主节点,那么它在删除一个过期键之后,会显式地向所有附属节点发送一个 DEL 命令。

2)如果服务器是附属节点,那么当它碰到一个过期键的时候,它会向程序返回键已过期的回复,但并不真正的删除过期键。因为程序只根据键是否已经过期、而不是键是否已经被删除来决定执行流程,所以这种处理并不影响命令的正确执行结果。当接到从主节点发来的 DEL 命令之后,附属节点才会真正的将过期键删除掉。附属节点不自主对键进行删除是为了和主节点的数据保持绝对一致, 因为这个原因, 当一个过期键还存在于主节点时,这个键在所有附属节点的副本也不会被删除。这种处理机制对那些使用大量附属节点,并且带有大量过期键的应用来说,可能会造成一部分内存不能立即被释放,但是,因为过期键通常很快会被主节点发现并删除。

键值相关命令总结:

命令原型 时间复杂度 命令描述 返回值
KEYS pattern O(N) 获取所有匹配pattern参数的keys。在正常操作中应该尽量避免对该命令的调用,对大型数据库而言,该命令是非常耗时的,对Redis服务器的性能影响也是比较大的。pattern支持glob-style的通配符格式。 匹配模式的键列表。
DEL key [key …] O(N) 从数据库删除中参数中指定的keys,如果指定键不存在,则直接忽略。如果指定的Key关联的数据类型不是String类型,而是List、Set、Hashes和Sorted Set等容器类型,该命令删除每个键的时间复杂度为O(M),其中M表示容器中元素的数量。而对于String类型的Key,其时间复杂度为O(1)。 实际被删除的Key数量。
EXISTS key O(1) 判断指定键是否存在。 1表示存在,0表示不存在。
MOVE key db O(1) 将当前数据库中指定的键Key移动到参数中指定的数据库中。如果该Key在目标数据库中已经存在,或者在当前数据库中并不存在,该命令将不做任何操作并返回0。 移动成功返回1,否则0。
RENAME key newkey O(1) 为指定指定的键重新命名,如果参数中的两个Keys的命令相同,或者是源Key不存在,该命令都会返回相关的错误信息。如果newKey已经存在,则直接覆盖。
RENAMENX key newkey O(1) 如果新值不存在,则将参数中的原值修改为新值。其它条件和RENAME一致。 1表示修改成功,否则0。
PERSIST key O(1) 如果Key存在过期时间,该命令会将其过期时间消除,使该Key不再有超时,而是可以持久化存储。 1表示Key的过期时间被移出,0表示该Key不存在或没有过期时间。
EXPIRE key seconds O(1) 该命令为参数中指定的Key设定超时的秒数,在超过该时间后,Key被自动的删除。如果该Key在超时之前被修改,与该键关联的超时将被移除。 1表示超时被设置,0则表示Key不存在,或不能被设置。
EXPIREAT key timestamp O(1) 该命令的逻辑功能和EXPIRE完全相同,唯一的差别是该命令指定的超时时间是绝对时间,而不是相对时间。该时间参数是Unix timestamp格式的,即从1970年1月1日开始所流经的秒数。 1表示超时被设置,0则表示Key不存在,或不能被设置。
TTL key O(1) 获取该键所剩的超时描述。 返回所剩描述,如果该键不存在或没有超时设置,则返回-1。
PTTL O(1) 这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而不是像 TTL 命令那样,以秒为单位 当 key 不存在时,返回 -2 。当 key 存在但没有设置剩余生存时间时,返回 -1 。否则,以毫秒为单位,返回 key 的剩余生存时间。
RANDOMKEY O(1) 从当前打开的数据库中随机的返回一个Key。 返回的随机键,如果该数据库是空的则返回nil。
TYPE key O(1) 获取与参数中指定键关联值的类型,该命令将以字符串的格式返回。 返回的字符串为string、list、set、hash和zset,如果key不存在返回none。
SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC|DESC] [ALPHA] [STORE destination] O(N+M*log(M)) 排序 返回排序后的原始列表。
RESTORE key ttl serialized-value [REPLACE] 查找给定键的复杂度为 O(1) ,对键进行反序列化的复杂度为 O(N*M) ,其中 N 是构成 key 的 Redis 对象的数量,而 M 则是这些对象的平均大小。有序集合(sorted set)的反序列化复杂度为 O(N*M*log(N)) ,因为有序集合每次插入的复杂度为 O(log(N)) 。如果反序列化的对象是比较小的字符串,那么复杂度为 O(1) 。 反序列化给定的序列化值,并将它和给定的 key 关联。参数 ttl 以毫秒为单位为 key 设置生存时间;如果 ttl 为 0 ,那么不设置生存时间。RESTORE 在执行反序列化之前会先对序列化值的 RDB 版本和数据校验和进行检查,如果 RDB 版本不相同或者数据不完整的话,那么RESTORE 会拒绝进行反序列化,并返回一个错误。如果键 key 已经存在, 并且给定了 REPLACE 选项, 那么使用反序列化得出的值来代替键 key 原有的值; 相反地, 如果键 key 已经存在, 但是没有给定 REPLACE 选项, 那么命令返回一个错误。  如果反序列化成功那么返回 OK ,否则返回一个错误。
DUMP key 查找给定键的复杂度为 O(1) ,对键进行序列化的复杂度为 O(N*M) ,其中 N 是构成 key 的 Redis 对象的数量,而 M 则是这些对象的平均大小。如果序列化的对象是比较小的字符串,那么复杂度为 O(1) 。 序列化给定 key ,并返回被序列化的值,使用 RESTORE 命令可以将这个值反序列化为 Redis 键。序列化生成的值有以下几个特点:它带有 64 位的校验和,用于检测错误, RESTORE 在进行反序列化之前会先检查校验和。值的编码格式和 RDB 文件保持一致。RDB 版本会被编码在序列化值当中,如果因为 Redis 的版本不同造成 RDB 格式不兼容,那么 Redis 会拒绝对这个值进行反序列化操作。

序列化的值不包括任何生存时间信息。

 

如果 key 不存在,那么返回 nil 。否则,返回序列化之后的值。
SCAN cursor [MATCH pattern] [COUNT count] 增量式迭代命令每次执行的复杂度为 O(1) , 对数据集进行一次完整迭代的复杂度为 O(N) , 其中 N 为数据集中的元素数量。 SCAN 命令、 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都返回一个包含两个元素的 multi-bulk 回复: 回复的第一个元素是字符串表示的无符号 64 位整数(游标), 回复的第二个元素是另一个 multi-bulk 回复, 这个 multi-bulk 回复包含了本次被迭代的元素。SCAN 命令返回的每个元素都是一个数据库键。SSCAN 命令返回的每个元素都是一个集合成员。HSCAN 命令返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。ZSCAN 命令返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。

 

MIGRATE host port key destination-db timeout [COPY] [REPLACE] 将 key 原子性地从当前实例传送到目标实例的指定数据库上,一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。这个命令是一个原子操作,它在执行的时候会阻塞进行迁移的两个实例,直到以下任意结果发生:迁移成功,迁移失败,等到超时。命令的内部实现是这样的:它在当前实例对给定 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 命令没有关系)。

可选项:

COPY :不移除源实例上的 key 。

REPLACE :替换目标实例上已存在的 key 。

 

迁移成功时返回 OK ,否则返回相应的错误。
OBJECT subcommand [arguments [arguments]] O(1) OBJECT 命令允许从内部察看给定 key 的 Redis 对象。它通常用在除错(debugging)或者了解为了节省空间而对 key 使用特殊编码的情况。当将Redis用作缓存程序时,你也可以通过 OBJECT 命令中的信息,决定 key 的驱逐策略(eviction policies)。OBJECT 命令有多个子命令:OBJECT REFCOUNT <key> 返回给定 key 引用所储存的值的次数。此命令主要用于除错。

OBJECT ENCODING <key> 返回给定 key 锁储存的值所使用的内部表示(representation)。

OBJECT IDLETIME <key> 返回给定 key 自储存以来的空闲时间(idle, 没有被读取也没有被写入),以秒为单位。

对象可以以多种方式编码:

字符串可以被编码为 raw (一般字符串)或 int (为了节约内存,Redis 会将字符串表示的 64 位有符号整数编码为整数来进行储存)。

列表可以被编码为 ziplist 或 linkedlist 。 ziplist 是为节约大小较小的列表空间而作的特殊表示。

集合可以被编码为 intset 或者 hashtable 。 intset 是只储存数字的小集合的特殊表示。

哈希表可以编码为 zipmap 或者 hashtable 。 zipmap 是小哈希表的特殊表示。

有序集合可以被编码为 ziplist 或者 skiplist 格式。 ziplist 用于表示小的有序集合,而 skiplist 则用于表示任何大小的有序集合。

假如你做了什么让 Redis 没办法再使用节省空间的编码时(比如将一个只有 1 个元素的集合扩展为一个有 100 万个元素的集合),特殊编码类型(specially encoded types)会自动转换成通用类型(general type)。

 

REFCOUNT 和 IDLETIME 返回数字。ENCODING 返回相应的编码类型。

参考资料:

http://redisdoc.com/

《redis设计与实现》

http://www.bubuko.com/infodetail-826677.html

http://blog.csdn.net/htl258/article/details/5695391

http://www.open-open.com/lib/view/open1398408820593.html

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">