关于redis数据类型操作的应用场景及时间复杂度

  • String类型命令时间复杂度和应用场景
  • string类型高时间复杂度的命令汇总
  • List类型的命令的时间复杂度和应用场景
  • 列表数据类型命令时间复杂度高的命令
  • SET类型的基本命令时间复杂度及应用场景
  • ZSET类型的命令的时间复杂度及应用场景
  • HASH类型命令及其时间复杂度


String类型命令时间复杂度和应用场景

  • set key value 时间复杂度: O(1)
  • SETNX key value 时间复杂度: O(1)

SetNX用来实现分布式锁

  • SETEX key seconds value 时间复杂度: O(1)
  • PSETEX key milliseconds value 时间复杂度: O(1)
  • GET key 时间复杂度: O(1)
  • GETSET key value 时间复杂度: O(1)
  • STRLEN key 时间复杂度: O(1)
  • APPEND key value 平摊O(1)
  • SETRANGE key offset value 时间复杂度:对于长度较短的字符串,命令的平摊复杂度O(1);对于长度较大的字符串,命令的复杂度为 O(M) ,其中 M 为 value 的长度。
  • GETRANGE key start end 时间复杂度: O(N),其中 N 为被返回的字符串的长度。
  • INCR key 时间复杂度: O(1)

INCR 系列的命令可以用来实现计数器的功能

  • INCRBY key increment 时间复杂度: O(1)
  • INCRBYFLOAT key increment 时间复杂度: O(1)
  • DECR key 时间复杂度: O(1)
  • MSET key value [key value …] 时间复杂度: O(N),其中 N 为被设置的键数量。
  • MSETNX key value [key value …] 时间复杂度: O(N), 其中 N 为被设置的键数量。
  • MGET key [key …] 时间复杂度: O(N) ,其中 N 为给定键的数量。

string类型高时间复杂度的命令汇总

m系列的命令 和 getrange 命令 以及一定情况下的setrange 命令

List类型的命令的时间复杂度和应用场景

  • LPUSH key value [value …] 时间复杂度: O(1)
  • LPUSHX key value 时间复杂度: O(1)
  • RPUSH key value [value …] 时间复杂度: O(1)
  • RPUSHX key value 时间复杂度: O(1)
  • LPOP key 时间复杂度: O(1)
  • RPOP key 时间复杂度: O(1)
  • RPOPLPUSH source destination 时间复杂度: O(1)
  • LREM key count value 时间复杂度: O(N)
  • LLEN key 时间复杂度: O(1)
  • LINDEX key index 时间复杂度: O(N)
  • LINSERT key BEFORE|AFTER pivot value 时间复杂度: O(N), N 为寻找 pivot 过程中经过的元素数量。
  • LSET key index value 时间复杂度:对头元素或尾元素进行 LSET 操作,复杂度为 O(1)。其他情况下,为 O(N), N 为列表的长度。
  • LRANGE key start stop 时间复杂度: O(S+N), S 为偏移量 start , N 为指定区间内元素的数量。
  • LTRIM key start stop 时间复杂度为 O(N) ,N为删除的元素

LTRIM 命令通常和 LPUSH key value [value …] 命令或 RPUSH key value [value …] 命令配合使用,举个例子:LPUSH log newest_log
LTRIM log 0 99
这个例子模拟了一个日志程序,每次将最新日志 newest_log 放到 log 列表中,并且只保留最新的 100 项。注意当这样使用 LTRIM 命令时,时间复杂度是O(1),因为平均情况下,每次只有一个元素被移除。

  • BLPOP key [key …] timeout 时间复杂度: O(1)
    阻塞队列

有时候,为了等待一个新元素到达数据中,需要使用轮询的方式对数据进行探查。
另一种更好的方式是,使用系统提供的阻塞原语,在新元素到达时立即进行处理,而新元素还没到达时,就一直阻塞住,避免轮询占用资源。
对于 Redis ,我们似乎需要一个阻塞版的 SPOP key 命令,但实际上,使用 BLPOP 或者 BRPOP key [key …] timeout 就能很好地解决这个问题。

  • BRPOPLPUSH source destination timeout 时间复杂度: O(1)
    安全队列

Redis的列表经常被用作队列(queue),用于在不同程序之间有序地交换消息(message)。一个客户端通过 LPUSH key value [value …] 命令将消息放入队列中,而另一个客户端通过 RPOP key 或者 BRPOP key [key …] timeout 命令取出队列中等待时间最长的消息。不幸的是,上面的队列方法是『不安全』的,因为在这个过程中,一个客户端可能在取出一个消息之后崩溃,而未处理完的消息也就因此丢失。使用 RPOPLPUSH 命令(或者它的阻塞版本 BRPOPLPUSH source destination timeout )可以解决这个问题:因为它不仅返回一个消息,同时还将这个消息添加到另一个备份列表当中,如果一切正常的话,当一个客户端完成某个消息的处理之后,可以用 LREM key count value 命令将这个消息从备份表删除。最后,还可以添加一个客户端专门用于监视备份表,它自动地将超过一定处理时限的消息重新放入队列中去(负责处理该消息的客户端可能已经崩溃),这样就不会丢失任何消息了。

循环队列

通过使用相同的 key 作为 RPOPLPUSH 命令的两个参数,客户端可以用一个接一个地获取列表元素的方式,取得列表的所有元素,而不必像 LRANGE key start stop 命令那样一下子将所有列表元素都从服务器传送到客户端中(两种方式的总复杂度都是 O(N))。
以上的模式甚至在以下的两个情况下也能正常工作:有多个客户端同时对同一个列表进行旋转(rotating),它们获取不同的元素,直到所有元素都被读取完,之后又从头开始。
有客户端在向列表尾部(右边)添加新元素。这个模式使得我们可以很容易实现这样一类系统:有 N 个客户端,需要连续不断地对一些元素进行处理,而且处理的过程必须尽可能地快。一个典型的例子就是服务器的监控程序:它们需要在尽可能短的时间内,并行地检查一组网站,确保它们的可访问性。
这个模式使得我们可以很容易实现这样一类系统:有 N 个客户端,需要连续不断地对一些元素进行处理,而且处理的过程必须尽可能地快。一个典型的例子就是服务器的监控程序:它们需要在尽可能短的时间内,并行地检查一组网站,确保它们的可访问性。注意,使用这个模式的客户端是易于扩展(scala)且安全(reliable)的,因为就算接收到元素的客户端失败,元素还是保存在列表里面,不会丢失,等到下个迭代来临的时候,别的客户端又可以继续处理这些元素了。

列表数据类型命令时间复杂度高的命令

LREM key count value 时间复杂度: O(N)  
LTRIM key start stop  时间复杂度为 O(N) ,N为删除的元素
LSET 操作,复杂度为 O(1)。其他情况下,为 O(N), N 为列表的长度。
LRANGE key start stop  时间复杂度: O(S+N), S 为偏移量 start , N 为指定区间内元素的数量。
LINDEX key index   时间复杂度: O(N)

SET类型的基本命令时间复杂度及应用场景

  • SADD key member [member …] 时间复杂度: O(N), N 是被添加的元素的数量。
  • SISMEMBER key member 时间复杂度: O(1)
  • SPOP key 时间复杂度: O(1)
  • SRANDMEMBER key [count] 时间复杂度: 只提供 key 参数时为 O(1) 。如果提供了 count 参数,那么为 O(N) ,N 为返回数组的元素个数。
  • SREM key member [member …] 时间复杂度: O(N), N 为给定 member 元素的数量。
  • SMOVE source destination member 时间复杂度: O(1)
  • SCARD key 时间复杂度: O(1)
  • SMEMBERS key 时间复杂度: O(N), N 为集合的基数。
  • SINTER key [key …] 时间复杂度: O(N * M), N 为给定集合当中基数最小的集合, M 为给定集合的个数。

实现关注模型,可能认识的人

  • SINTERSTORE destination key [key …] 时间复杂度: O(N * M), N 为给定集合当中基数最小的集合, M 为给定集合的个数。
  • SUNION key [key …] 时间复杂度: O(N), N 是所有给定集合的成员数量之和。
  • SUNIONSTORE destination key [key …] 时间复杂度: O(N), N 是所有给定集合的成员数量之和。
  • SDIFF key [key …] 时间复杂度: O(N), N 是所有给定集合的成员数量之和。

ZSET类型的命令的时间复杂度及应用场景

redis的zset天生是用来做排行榜的、好友列表, 去重, 历史记录等业务需求。

  • ZADD key score member [[score member] [score member] …] 时间复杂度: O(M*log(N)), N 是有序集的基数, M 为成功添加的新成员的数量。
  • ZSCORE key member 时间复杂度: O(1)
  • ZINCRBY key increment member 时间复杂度: O(log(N))
  • ZCOUNT key min max 时间复杂度: O(log(N)), N 为有序集的基数。
  • ZRANGE key start stop [WITHSCORES] 时间复杂度: O(log(N)+M), N 为有序集的基数,而 M 为结果集的基数。
  • ZREVRANGE key start stop [WITHSCORES] 时间复杂度: O(log(N)+M), N 为有序集的基数,而 M 为结果集的基数。
  • ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 时间复杂度: O(log(N)+M), N 为有序集的基数, M 为被结果集的基数。
  • ZRANK key member 时间复杂度: O(log(N))
  • ZREVRANK key member 时间复杂度: O(log(N))
  • ZREM key member [member …] 时间复杂度: O(M*log(N)), N 为有序集的基数, M 为被成功移除的成员的数量。
  • ZREMRANGEBYRANK key start stop 时间复杂度: O(log(N)+M), N 为有序集的基数,而 M 为被移除成员的数量。
  • ZREMRANGEBYSCORE key min max 时间复杂度: O(log(N)+M), N 为有序集的基数,而 M 为被移除成员的数量。
  • ZRANGEBYLEX key min max [LIMIT offset count] 时间复杂度:O(log(N)+M), 其中 N 为有序集合的元素数量, 而 M 则是命令返回的元素数量 命令会使用 C 语言的 memcmp() 函数, 对集合中的每个成员进行逐个字节的对比(byte-by-byte compare), 并按照从低到高的顺序, 返回排序后的集合成员。 如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大
  • ZLEXCOUNT key min max 时间复杂度: O(log(N)),其中 N 为有序集合包含的元素数量。
  • ZREMRANGEBYLEX key min max 时间复杂度: O(log(N)+M), 其中 N 为有序集合的元素数量, 而 M 则为被移除的元素数量。

HASH类型命令及其时间复杂度

  • HSET hash field value 时间复杂度: O(1)
  • HSETNX hash field value 时间复杂度: O(1)
  • HGET hash field 时间复杂度: O(1)
  • HEXISTS hash field 时间复杂度: O(1)
  • HDEL key field [field …] 时间复杂度:O(N), N 为要删除的域的数量。
  • HLEN key 时间复杂度:O(1)
  • HSTRLEN key field 时间复杂度:O(1)
  • HINCRBY key field increment 时间复杂度:O(1)
  • HMSET key field value [field value …] 时间复杂度:O(N),N 为 field-value 对的数量。
  • HMGET key field [field …] 时间复杂度:O(N), N 为给定域的数量。
  • HKEYS key 时间复杂度:O(N), N 为哈希表的大小。
  • HVALS key 时间复杂度:O(N)
  • HGETALL key 时间复杂度:O(N)