redis第三章:数据结构

redis数据结构

键管理

# 查看键数量:DBSIZE
127.0.0.1:6379> dbsize
(integer) 18

# 查询匹配的键(过时):KEYS pattern (支持glob风格通配符格)(生产环境禁止使用)[已被SCAN命令取代]
127.0.0.1:6379> keys *
 1) "session:2018"
 2) "ls"
 3) "ss2"
 4) "\xac\xed\x00\x05t\x00\x02k2"
 5) "ls2"
 6) "k"
 7) "foo"
 8) "user:2:followers"
 9) "uid"
10) "num"
11) "myzset"
12) "user:1:following"
13) "mylsalpha"
14) "dsts"
15) "ss"
16) "k2"
17) "preredis中key和value的存储大小限制
18) "key"

# 查询匹配的键(流行):SCAN cursor [MATCH pattern] [COUNT number] [TYPE type]
# 第一次scan返回结果为空,但第二次不为空,cursor为0表示已经扫描完所有键
127.0.0.1:6379> scan 0 match key count 10
1) "22"
2) (empty list or set)
127.0.0.1:6379> scan 22 match key count 10
1) "0"
2) 1) "key"

# 判断键是否存在: EXISTS key [key ...] 
# 存在返回1, 否则返回0, 多个键作为参数则返回,其中已经存在的键的数量
127.0.0.1:6379> exists foo myzset fakekey
(integer) 2
127.0.0.1:6379> exists foo
(integer) 1
127.0.0.1:6379> exists fakekey
(integer) 0127.0.0.1:6379> exists foo
(integer) 1
127.0.0.1:6379> exists fakekey
(integer) 0

# 删除键(小key): DEL key [key ...]
# DEL命令的参数不支持通配符,可以结合linux的管道和xargs命令实现删除所有符合规则的键
127.0.0.1:6379> del foo
(integer) 1
127.0.0.1:6379> del fakekey
(integer) 0

# 删除键(大key): UNLINK key [key ...]
127.0.0.1:6379> unlink bigkey
(integer) 1
127.0.0.1:6379> unlink fakekey
(integer) 0

# 查询键值的数据类型: TYPE key
127.0.0.1:6379> type fakekey
none
127.0.0.1:6379> type mystring
string
### 特别注意: 单个数字在redis中也是用string类型存储的
127.0.0.1:6379> type numStr
string
127.0.0.1:6379> type ls
list
127.0.0.1:6379> type myzset
zset
127.0.0.1:6379> type numSet
set
127.0.0.1:6379> type userMap
hash
127.0.0.1:6379> type hll # HyperLogLog
string
127.0.0.1:6379>  type nearRestaurants # GEO
zset

# 查询值对象的内部编码方式:OBJECT ENCODING [arguments [arguments ...]]
127.0.0.1:6379> object encoding fakekey
(nil)
127.0.0.1:6379> object encoding mystring
"embstr"
127.0.0.1:6379> object encoding ls
"quicklist"
127.0.0.1:6379> object encoding myzset
"ziplist"
127.0.0.1:6379> object encoding numSet
"intset"
127.0.0.1:6379> object encoding userMap
"ziplist"
127.0.0.1:6379> object encoding hll # HyperLogLog
"raw"
127.0.0.1:6379> object encoding nearRestaurants # GEO
"ziplist"

glob风格通配符规则:

符号 意义
? 匹配一个字符
* 匹配任意个(包括0个)字符
[] 匹配括号内任一字符,可以使用"-"表示一个范围,如:a[b-d]可以匹配ab,ac,ad
\x 匹配字符x,用于转义符号.如要匹配"?",就需要使用?

DEL命令技巧: 要删除所有"user:"开头的键,可以执行(强大的xargs)

redis-cli KEYS "user:*" | xargs redis-cli DEL

另外,由于DEL命令支持多个键作为参数,所以可以执行(性能更好)

redis-cli DEL `redis-cli KEYS "user:*"`

字符串类型

字符串类型是redis最基本的数据类型,它能存储任何形式的字符串(数字),包括二进制数据,一个字符串类型的键允许存储的最大数据是512MB (内存总共就那么几G,一个键值就能占512MB,有点可怕)

  • 内部编码: redis使用了三种不同的编码方式来存储字符串对象,并根据字符串值自动选择

    • int: 用于能够使用64位有符号整数表示的字符串
    • embstr:用于长度小于等于44字节的字符串
    • raw:用于长度大于44字节的字符串

object命令:http://www.redis.cn/commands/object.html

# 数字带不带“”, 都是int编码
127.0.0.1:6379> set numStrK 123
OK
127.0.0.1:6379> object encoding numStrK
"int"
127.0.0.1:6379> set numStrK "123"
OK
127.0.0.1:6379> object encoding numStrK
"int"

127.0.0.1:6379> set shortStrK "shortStr"
OK
127.0.0.1:6379> object encoding shortStrK
"embstr"

127.0.0.1:6379> set longStrK "i am a long string whose length is more than 44 bytes"
OK
127.0.0.1:6379> object encoding longStrK
"raw"
  • # 赋值 SET key value
    127.0.0.1:6379> set existK v
    OK
    127.0.0.1:6379> get existK
    "v"
    127.0.0.1:6379> set existK override # 覆盖已经存在的key
    OK
    127.0.0.1:6379> get existK
    "override"
    
    # 批量赋值(减少RTT) MSET key value [key value ...]
    127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
    OK
    127.0.0.1:6379> mget k1 k2 k3
    1) "v1"
    2) "v2"
    3) "v3"
    
    ## 只新增 set + NX = insert 等价于 setNX
    127.0.0.1:6379> set existK overrideAgain NX # NX: 不覆盖已存在的key(当key不存在时才设值)
    (nil)
    127.0.0.1:6379> get existK
    "override"
    127.0.0.1:6379> setnx insertK insertV
    (integer) 1
    127.0.0.1:6379> setnx insertK insertV
    (integer) 0
    
    ## 只更新 set + XX = update
    127.0.0.1:6379> set noExistK v XX # XX: 只覆盖已经存在的key(在key存在时才设值)
    (nil)
    127.0.0.1:6379> set existK overrideAgain XX
    OK
    127.0.0.1:6379> get existK
    "overrideAgain"
    127.0.0.1:6379> get noExistK
    (nil)
    
    # 定时过期
    127.0.0.1:6379> set expKSec 5s EX 5 # 秒
    OK
    127.0.0.1:6379> ttl expKSec
    (integer) 3
    127.0.0.1:6379> ttl expKSec
    (integer) 2
    127.0.0.1:6379> ttl expKSec
    (integer) 1
    127.0.0.1:6379> ttl expKSec
    (integer) -2
    
    127.0.0.1:6379> set expKMillSec 100mills PX 100 # 毫秒
    OK
    127.0.0.1:6379> ttl expKMillSec
    (integer) -2
    
    # 递增: INCR key, 返回递增后的值; 键不存在,默认键值为0,进行递增
    127.0.0.1:6379> get numStr
    "1"
    127.0.0.1:6379> incr numStr
    (integer) 2
    127.0.0.1:6379> get numStr
    "2"
    
    # 递增升级: INCRBY key increment, 自定义步长,可以为负数,效果等同于decrby key decrement
    127.0.0.1:6379> incrby numStr 3
    (integer) 5
    127.0.0.1:6379> get numStr
    "5"
    127.0.0.1:6379> incrby numStr -2
    (integer) 3
    127.0.0.1:6379> get numStr
    "3"
    
    # 递增变型: INCRBYFLOAT key increment
    127.0.0.1:6379> incrbyfloat numStr 0.14159267
    "3.14159267"
    127.0.0.1:6379> get numStr
    "3.14159267"
    
  • # DECR key,DECR和INCR作用相反: 让键值递减
    # DECRBY key decrement
    # DECR没有DECRBYFLOAT, 通过INCRBYFLOAT实现相同效果
    127.0.0.1:6379> incrbyfloat numStr -0.14159267
    "3"
    127.0.0.1:6379> get numStr
    "3"
    127.0.0.1:6379> decr numStr
    (integer) 2
    127.0.0.1:6379> get numStr
    "2"
    127.0.0.1:6379> decrby numStr 2
    (integer) 0
    127.0.0.1:6379> get numStr
    "0"
    
  • # 追尾:APPEND key value (尾部追加值)
    
    ## 字符串 append 字符串
    127.0.0.1:6379> get mystring
    "stringVal"
    127.0.0.1:6379> append mystring " is not enough, append str now"
    (integer) 39
    127.0.0.1:6379> get mystring
    "stringVal is not enough, append str now"
    
    ## 数字 append 数字
    127.0.0.1:6379> get numStr
    "-1"
    127.0.0.1:6379> append numStr 100
    (integer) 5
    127.0.0.1:6379> get numStr
    "-1100"
    127.0.0.1:6379> incr numStr
    (integer) -1099
    
    ## 数字 append 字符串zifuc
    127.0.0.1:6379> get numStr
    "-1099"
    127.0.0.1:6379> append numStr " num change to str ??"
    (integer) 26
    127.0.0.1:6379> get numStr
    "-1099 num change to str ??"
    127.0.0.1:6379> incr numStr
    (error) ERR value is not an integer or out of range
    
    # 局部修改:SETRANGE key offset value,相比于整个值覆盖,节省了RTT
    127.0.0.1:6379> set myK "hello world"
    OK
    127.0.0.1:6379> setrange myK 6 "redis"
    (integer) 11
    127.0.0.1:6379> get myK 
    "hello redis"
    127.0.0.1:6379> setrange myK 5 @
    (integer) 11
    127.0.0.1:6379> getrange myK 0 -1
    "hello@redis"
    
    ## 如果offset比当前key对应string还要长,那这个string后面就补0以达到offset
    127.0.0.1:6379> setrange noEnoughLenK 3 value
    (integer) 8
    127.0.0.1:6379> get noEnoughLenK
    "\x00\x00\x00value"
    
    
  • # 取值: GET key,键不存在,返回空
    127.0.0.1:6379> get noExistK
    (nil)
    127.0.0.1:6379> get existK
    "overrideAgain"
    
    # 局部取值
    127.0.0.1:6379> getrange myK 0 5
    "hello "
    
    # 取长:STRLEN key(获取字符串长度): 中文UTF-8编码,一个字符长度是3
    127.0.0.1:6379> mset englishK hello chineseK 你好
    OK
    127.0.0.1:6379> strlen englishK 
    (integer) 5
    127.0.0.1:6379> strlen chineseK
    (integer) 6
    
    # 批量取值(减少RTT):MGET key [key ...],(同时获取多个键值)
    127.0.0.1:6379> mget englishK chineseK
    1) "hello"
    2) "\xe4\xbd\xa0\xe5\xa5\xbd"
    
    解决乱码: --raw
    neo@neo:~$ redis-cli --raw mget englishK chineseK
    hello
    你好
    

列表类型(队列、列表)

内部使用双向链表实现, 至多包含2^32-1个元素,可以作为列表、队列、栈

  • 内部编码: redis使用quicklist存储列表,有两个配置可以调整存储逻辑

    • list-max-ziplist-size: 内部节点的最大size,一般使用默认值即可(quicklist每个节点都是一个ziplist)
    • list-compress-depth: 列表压缩策略。如果我们会用到redis队列的首尾元素,用这个配置可以获得更好的压缩比(该参数表示quicklist两端不被压缩的节点的个数,当列表很多的时候最可能被访问的数据是位于列表两端的数据,因此对这个参数进行精确调优可以实现压缩比和其他元素之间的平衡)
  • # 从列表左端插入元素: LPUSH key value [value ...]
    127.0.0.1:6379> lpush lsK 1 2 3 4 5
    (integer) 5
    127.0.0.1:6379> lrange lsK 0 -1
    1) "5"
    2) "4"
    3) "3"
    4) "2"
    5) "1"
    
    # 从列表右端插入元素: RPUSH key value [value ...]
    127.0.0.1:6379> rpush lsK -1 -2 -3 -4 -5
    (integer) 10 
    127.0.0.1:6379> lrange lsK 0 -1
     1) "5"
     2) "4"
     3) "3"
     4) "2"
     5) "1"
     6) "-1"
     7) "-2"
     8) "-3"
     9) "-4"
    10) "-5"
    
    # 坐标插入法:LINSERT key BEFORE|AFTER pivot value (前到后:即左到右)
    ## 以pivot元素为坐标,在其前面插入新元素
    127.0.0.1:6379> linsert lsK before 1 66
    (integer) 11
    127.0.0.1:6379> lrange lsK 0 -1
     1) "5"
     2) "4"
     3) "3"
     4) "2"
     5) "66"
     6) "1"
     7) "-1"
     8) "-2"
     9) "-3"
    10) "-4"
    11) "-5"
    
    ## 以pivot元素为坐标,在其后面插入新元素
    127.0.0.1:6379> linsert lsK after -1 -66
    (integer) 12
    127.0.0.1:6379> lrange lsK 0 -1
     1) "5"
     2) "4"
     3) "3"
     4) "2"
     5) "66"
     6) "1"
     7) "-1"
     8) "-66"
     9) "-2"
    10) "-3"
    11) "-4"
    12) "-5"
    
    # 只更新,不新增:LPUSHX,RPUSHX
    # LPUSH,RPUSH,LINSERT操作不存在的key时,会自动创建,也可使用LPUSHX,RPUSHX只在key存在时操作
    127.0.0.1:6379> lpushx noExistLsK 1 2 3
    (integer) 0
    127.0.0.1:6379> exists noExistLsK
    (integer) 0
    127.0.0.1:6379> rpushx noExistLsK -1 -2 -3
    (integer) 0
    127.0.0.1:6379> exists noExistLsK
    (integer) 0
    
    • # 从列表左端弹出元素:LPOP key (队列式)
      127.0.0.1:6379> lrange lsK 0 -1
       1) "5"
       2) "4"
       3) "3"
       4) "2"
       5) "66"
       6) "1"
       7) "-1"
       8) "-66"
       9) "-2"
      10) "-3"
      11) "-4"
      12) "-5"
      127.0.0.1:6379> lpop lsK
      "5"
      127.0.0.1:6379> lrange lsK 0 -1
       1) "4"
       2) "3"
       3) "2"
       4) "66"
       5) "1"
       6) "-1"
       7) "-66"
       8) "-2"
       9) "-3"
      10) "-4"
      11) "-5"
      
      # 从列表左端弹出元素:RPOP key(队列式)
      127.0.0.1:6379> lrange lsK 0 -1
       1) "4"
       2) "3"
       3) "2"
       4) "66"
       5) "1"
       6) "-1"
       7) "-66"range lsK 0 -1
      1) "3"
      2) "4"
      3) "3"
      4) "1"
      5) "-1"
      6) "-3"
      7) "-4"
      8) "-3"
      127.0.0.1:6379> lset
       8) "-2"
       9) "-3"
      10) "-4"
      11) "-5"
      127.0.0.1:6379> rpop lsK
      "-5"
      127.0.0.1:6379> lrange lsK 0 -1
       1) "4"
       2) "3"
       3) "2"
       4) "66"
       5) "1"
       6) "-1"
       7) "-66"
       8) "-2"
       9) "-3"
      10) "-4"
      
      # 阻塞版本LPOP,RPOP: BLPOP key [key ...] timeout , BRPOP key [key ...] timeout
      # 在队列为空时,阻塞指定时间; (timeout,单位:秒,0表示一直阻塞)
      ## 一个客户端进入阻塞
      127.0.0.1:6379> brpop job_queue 0
      ## 一个客户端压入一个元素
      127.0.0.1:6379> lpush job_queue task1
      (integer) 1
      ## 阻塞的客户端收到数据,并且退出阻塞(每次只能消费一个元素)
      127.0.0.1:6379> brpop job_queue 0
      1) "job_queue"
      2) "task1"
      (29.83s)
      
      ## 客户端进入阻塞
      127.0.0.1:6379> brpop job_queue 0
      ## 客户端压入两个元素
      127.0.0.1:6379> lpush job_queue task2 task3
      (integer) 2
      ## 阻塞的客户端收到数据,并且退出阻塞(每次只能消费一个元素)
      127.0.0.1:6379> brpop job_queue 0
      1) "job_queue"
      2) "task2"
      (19.81s)
      127.0.0.1:6379> lrange job_queue 0 -1 # 队列中还有一个元素没有消费
      1) "task3"
      
      # 删除列表中指定的值: LREM key count value
      # 删除列表中前count个值为value的元素,返回值是实际删除的元素个数
      ##  1) count > 0, 从列表左边开始删(列表式)
      127.0.0.1:6379> lrange lsK 0 -1
       1) "4"
       2) "3"
       3) "2"
       4) "66"
       5) "1"
       6) "-1"
       7) "-66"
       8) "-2"
       9) "-3"
      10) "-4"
      127.0.0.1:6379> lrem lsK 1 66
      (integer) 1
      127.0.0.1:6379> lrange lsK 0 -1
      1) "4"
      2) "3"
      3) "2"
      4) "1"
      5) "-1"
      6) "-66"
      7) "-2"
      8) "-3"
      9) "-4"
      
      ##  2) count < 0, 从列表右边开始删(列表式)
      127.0.0.1:6379> lrange lsK 0 -1
      1) "4"
      2) "3"
      3) "2"
      4) "1"
      5) "-1"
      6) "-66"
      7) "-2"
      8) "-3"
      9) "-4"
      127.0.0.1:6379> lrem lsK -1 -66
      (integer) 1
      127.0.0.1:6379> lrange lsK 0 -1
      1) "4"
      2) "3"
      3) "2"
      4) "1"
      5) "-1"
      6) "-2"
      7) "-3"
      8) "-4"
      
      ##  3) count = 0, 删除所有值为value的元素(列表式)
      127.0.0.1:6379> lrange lsK 0 -1
       1) "3"
       2) "2"
       3) "4"
       4) "3"
       5) "2"
       6) "1"
       7) "-1"
       8) "-2"
       9) "-3"
      10) "-4"
      11) "-2"
      12) "-3"
      127.0.0.1:6379> lrem lsK 0 2
      (integer) 2
      127.0.0.1:6379> lrem lsK 0 -2
      (integer) 2
      127.0.0.1:6379> lrange lsK 0 -1
      1) "3"
      2) "4"
      3) "3"
      4) "1"
      5) "-1"
      6) "-3"
      7) "-4"
      8) "-3"
      
    • # 通过索引修改列表元素:LSET key index value(列表式)
      127.0.0.1:6379> lrange lsK 0 -1
      1) "3"
      2) "4"
      3) "3"
      4) "1"
      5) "-1"
      6) "-3"
      7) "-4"
      8) "-3"
      127.0.0.1:6379> lset lsK 0 33
      OK
      127.0.0.1:6379> lrange lsK 0 -1
      1) "33"
      2) "4"
      3) "3"
      4) "1"
      5) "-1"
      6) "-3"
      7) "-4"
      8) "-3"
      
      # 裁剪列表: LTRIM key start end
      # 只留下索引范围内的元素(包括start,end); 常结合LPUSH命令来限制元素数量(列表式)
      # eg: LPUSH logs newLog; LTRIM logs 0 99; #每次新加入元素时调用LTRIM,来保留最新100条log
      127.0.0.1:6379> lrange lsK 0 -1
      1) "33"
      2) "4"
      3) "3"
      4) "1"
      5) "-1"
      6) "-3"
      7) "-4"
      8) "-3"
      127.0.0.1:6379> ltrim lsK 2 -3
      OK
      127.0.0.1:6379> lrange lsK 0 -1
      1) "3"
      2) "1"
      3) "-1"
      4) "-3"
      
      # 元素转移: RPOPLPUSH source destination 【没有lpoprpush】(实现循环队列)
      # 先从source右侧弹出,后从destination左侧插入(source、destination可以是同一列表)(队列式)
      127.0.0.1:6379> lrange lsK 0 -1
      1) "3"
      2) "1"
      3) "-1"
      4) "-3"
      127.0.0.1:6379> rpoplpush lsK lsK
      "-3"
      127.0.0.1:6379> lrange lsK 0 -1
      1) "-3"
      2) "3"
      3) "1"
      4) "-1"
      
    • # 查询列表元素个数: LLEN key
      127.0.0.1:6379> lrange lsK 0 -1
      1) "-3"
      2) "3"
      3) "1"
      4) "-1"
      127.0.0.1:6379> llen lsK
      (integer) 4
      
      # 获取列表片段:LRANGE key start stop, (包含两端,左端起始索引为0;右端为-1)
      ##  1) 如果start位置比stop靠后, 返回空列表
      127.0.0.1:6379> lrange lsK 0 -1
      1) "-3"
      2) "3"
      3) "1"
      4) "-1"
      
      ## 2) 如果stop大于实际索引范围, 返回最右边元素
      127.0.0.1:6379> lrange lsK 2 -3
      (empty list or set)
      
      # 通过索引查询列表元素:LINDEX key index
      127.0.0.1:6379> lrange lsK 0 -1
      1) "-3"
      2) "3"
      3) "1"
      4) "-1"
      127.0.0.1:6379> lindex lsK 2
      "1"
      127.0.0.1:6379> lindex lsK -2
      "1"
      

集合类型

集合中元素都是唯一且无序的,一个集合类型的键可以存储至多2^32-1个字符串;
内部实现是值为空的散列表,所以增/删/判断元素是否存在,时间复杂度都是O(1);
最方便的是集合之间还可以进行并/交/差的运算

  • 内部编码

    • intset:如果元素都是整数,且元素个数小于set-max-intset-entries(默认512)配置,将采用该编码。intset对于较小的集合可以节省空间

    • hashtable: intset不适用时的默认编码

  • 集合类型和列表类型对比:

集合类型 列表类型
存储内容 至多2^32-1个字符串 至多2^32-1个字符串
有序性
唯一性
  • # 添加元素:SADD key member [member ...]
    127.0.0.1:6379> sadd setK a b c
    (integer) 3
    127.0.0.1:6379> smembers setK
    1) "b"
    2) "c"
    3) "a"
    
  • # 删除元素:SREM key member [member ...]
    127.0.0.1:6379> smembers setK
    1) "b"
    2) "c"
    3) "a"
    127.0.0.1:6379> srem setK a b
    (integer) 2
    127.0.0.1:6379> smembers setK
    1) "c"
    
    # 弹出元素(随机:模拟随机开奖):SPOP key [count]
    127.0.0.1:6379> smembers setK
    1) "b"
    2) "c"
    3) "a"
    127.0.0.1:6379> spop setK 2
    1) "b"
    2) "c"
    127.0.0.1:6379> smembers setK
    1) "a"
    
  • #将member从source集合移动到destination集合中: SMOVE source destination member
    127.0.0.1:6379> sscan setK1 0
    1) "0"
    2) 1) "1"
       2) "2"
       3) "3"
    127.0.0.1:6379> sscan setK2 0
    1) "0"
    2) 1) "2"
       2) "3"
       3) "4"
    127.0.0.1:6379> smove setK1 setK2 1
    (integer) 1
    127.0.0.1:6379> sscan setK1 0
    1) "0"
    2) 1) "2"
       2) "3"
    127.0.0.1:6379> sscan setK2 0
    1) "0"
    2) 1) "1"
       2) "2"
       3) "3"
       4) "4"
    
    

    # 获得所有元素(阻塞,不推荐):SMEMBERS key
    127.0.0.1:6379> smembers setK
    1) "b"
    2) "c"
    3) "a"
    4) "d"
    5) "e"
    6) "f"
    
    # 获得所有元素(不阻塞,推荐):SSCAN key cursor [MATCH pattern] [COUNT count]
    127.0.0.1:6379> sscan setK 0 
    1) "0"
    2) 1) "d"
       2) "c"
       3) "f"
       4) "b"
       5) "e"
       6) "a"
    
    # 判断元素是否存在:SISMEMBER key member
    127.0.0.1:6379> smembers setK
    1) "a"
    127.0.0.1:6379> sismember setK q
    (integer) 0
    127.0.0.1:6379> sismember setK a
    (integer) 1
    
    # 获取元素个数:SCARD key
    127.0.0.1:6379> smembers setK
    1) "a"
    127.0.0.1:6379> scard setK
    (integer) 1
    
    # 随机获取元素: SRANDMEMBER key [count],可能返回数据并不是非常随机,为什么 => 和散列表有关
    ## 1) count > 0, 随机获取count个不重复元素,count大于集合元素个数,返回全部元素
    127.0.0.1:6379> sadd setK a b c d e f g h i j k
    (integer) 10
    127.0.0.1:6379> srandmember setK 3
    1) "e"
    2) "f"
    3) "k"
    127.0.0.1:6379> srandmember setK 3
    1) "d"127.0.0.1:6379> sadd setK a b c d e f g h i j k
    2) "g"
    3) "e"
    127.0.0.1:6379> srandmember setK 3
    1) "d"
    2) "e"
    3) "a"
    
    ## 2) count < 0, 随机获取|count|个元素,元素可能重复
    127.0.0.1:6379> sadd setK a b c d e f g h i j k
    (integer) 10
    127.0.0.1:6379> srandmember setK -3
    1) "j"
    2) "j"
    3) "j"
    127.0.0.1:6379> srandmember setK -3
    1) "j"
    2) "e"
    3) "d"
    127.0.0.1:6379> srandmember setK -3
    1) "j"
    2) "a"
    3) "g"
    
    
  • 集合运算

    # 并集:SUNION key [key ...], {1,2,3} U {2,3,4} = {1,2,3,4};
    127.0.0.1:6379> sadd setK1 1 2 3
    (integer) 3
    127.0.0.1:6379> sadd setK2 2 3 4
    (integer) 3
    127.0.0.1:6379> sunion setK1 setK2
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    
    # 交集:SINTER key [key ...], {1,2,3} n {2,3,4} = {2,3};
    127.0.0.1:6379> sadd setK1 1 2 3
    (integer) 3
    127.0.0.1:6379> sadd setK2 2 3 4
    (integer) 3
    127.0.0.1:6379> sinter setK1 setK2
    1) "2"
    2) "3"
    
    # 差集: SDIFF key [key ...], {1,2,3} - {2,3,4} = {1}; {2,3,4} - {1,2,3} = {4};
    127.0.0.1:6379> sadd setK1 1 2 3
    (integer) 3
    127.0.0.1:6379> sadd setK2 2 3 4
    (integer) 3
    127.0.0.1:6379> sdiff setK1 setK2
    1) "1"
    127.0.0.1:6379> sdiff setK2 setK1
    1) "4"
    
    # 并集结果存储为新key(如果key已经存在,会被覆盖), SUNIONSTORE destination key [key ...]
    127.0.0.1:6379> sadd setK1 1 2 3
    (integer) 3
    127.0.0.1:6379> sadd setK2 2 3 4
    (integer) 3
    127.0.0.1:6379> sunionstore setK1UnionsetK2 setK1 setK2 
    (integer) 4
    127.0.0.1:6379> sscan setK1UnionsetK2 0
    1) "0"
    2) 1) "1"
       2) "2"
       3) "3"
       4) "4"
     
    # 交集结果存储为新key(如果key已经存在,会被覆盖), SINTERSTORE destination key [key ...]
    127.0.0.1:6379> sadd setK1 1 2 3
    (integer) 3
    127.0.0.1:6379> sadd setK2 2 3 4
    (integer) 3
    127.0.0.1:6379> sinterstore setK1IntersetK2 setK1 setK2  
    (integer) 2
    127.0.0.1:6379> sscan setK1IntersetK2 0
    1) "0"
    2) 1) "2"
       2) "3"
    
    # 差集结果存储为新key(如果key已经存在,会被覆盖), SDIFFSTORE destination key [key ...]
    127.0.0.1:6379> sdiffstore setK1DiffsetK2 setK1 setK2
    (integer) 1
    127.0.0.1:6379> sscan setK1DiffsetK2 0
    1) "0"
    2) 1) "1"
    127.0.0.1:6379> sdiffstore setK2DiffsetK1 setK2 setK1
    (integer) 1
    127.0.0.1:6379> sscan setK2DiffsetK1 0
    1) "0"
    2) 1) "4"
    
    

有序集合

有序集合,英文名:Sorted set,意味着每个元素都有一个用于排序的权重(即为每个元素带上一个分数),虽然元素都是唯一的,但分数可以相同;这种原生有序特性使得我们可以按顺序从集合中获取元素,默认升序

  • 内部编码
    • ziplist: 对于长度小于zset-max-ziplist-entries(默认128)配置且所有元素size小于zset-max-ziplist-value配置(默认64字节)的有序集合,采用此编码。ziplist对于较小集合比较节省空间
    • skiplist: 当ziplist不适用时使用的默认编码

有序集合和列表类型对比:

  • 相同点:

    • 都是有序的
    • 都可以获取某个范围的元素
  • 不同点:

    • 列表类型是通过链表实现的,获取两端数据极快,但访问中间数据较慢. 因此更适合"新鲜事"或"日志"这样很少访问中间数据的场景
    • 有序集合是通过散列表和跳跃表(skip list)实现的,所以读取中间数据也很快: O(log(N))
    • 列表不能随意调整元素位置,但是有序集合可以(更改元素分数)
    • 有序集合比列表类型更耗费内存
  • # 添加元素:ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
    # XX: 只修改元素不新增; NX: 只新增元素不修改
    # CH: 让ZADD返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。
    # INCR: 当ZADD指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作。
    127.0.0.1:6379> zadd sortedsetK 1 jordan 2 kobe 3 james 4 wade 5 bosh
    (integer) 5
    127.0.0.1:6379> zrange sortedsetK 0 -1
    1) "jordan"
    2) "kobe"
    3) "james"
    4) "wade"
    5) "bosh"
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
     1) "jordan"
     2) "1"
     3) "kobe"
     4) "2"
     5) "james"
     6) "3"
     7) "wade"
     8) "4"
     9) "bosh"
    10) "5"
    
    # 只新增元素
    127.0.0.1:6379> zadd sortedsetK NX 6 culi
    (integer) 1
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
     1) "jordan"
     2) "1"
     3) "kobe"
     4) "2"
     5) "james"
     6) "3"
     7) "wade"
     8) "4"
     9) "bosh"
    10) "5"
    11) "culi"
    12) "6"
    # 已经存在的元素不会被修改
    127.0.0.1:6379> zadd sortedsetK NX 8 kobe
    (integer) 0
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
     1) "jordan"
     2) "1"
     3) "kobe"
     4) "2"
     5) "james"
     6) "3"
     7) "wade"
     8) "4"
     9) "bosh"
    10) "5"
    11) "culi"
    12) "6"
    
    # 只更新
    # 不存在的元素不会被新增
    127.0.0.1:6379> zadd sortedsetK XX 7 durant 
    (integer) 0
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
     1) "jordan"
     2) "1"
     3) "kobe"
     4) "2"
     5) "james"
     6) "3"
     7) "wade"
     8) "4"
     9) "bosh"
    10) "5"
    11) "culi"
    12) "6"
    # 为什么修改成功后,返回0,因为有序集合默认返回新增元素个数,使用ch选项可以返回被修改元素个数
    127.0.0.1:6379> zadd sortedsetK XX 8 kobe
    (integer) 0
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
     1) "jordan"
     2) "1"
     3) "james"
     4) "3"
     5) "wade"
     6) "4"
     7) "bosh"
     8) "5"
     9) "culi"
    10) "6"
    11) "kobe"
    12) "8"
    
    # CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。更改的元素是新添加的成员,已经存在的成员更新分数。 所以在命令中指定的成员有相同的分数将不被计算在内。注:在通常情况下,ZADD返回值只计算新添加成员的数量。
    127.0.0.1:6379> zadd sortedsetK ch 24 kobe
    (integer) 1
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
     1) "jordan"
     2) "1"
     3) "james"
     4) "3"
     5) "wade"
     6) "4"
     7) "bosh"
     8) "5"
     9) "culi"
    10) "6"
    11) "kobe"
    12) "24"
    # 已经存在相同分数相同元素,则不算修改
    127.0.0.1:6379> zadd sortedsetK ch 24 kobe
    (integer) 0
    
    # 给指定元素加分 (相当于:ZINCRBY sortedsetK 22 jordan)
    127.0.0.1:6379> zadd sortedsetK incr 22 jordan
    "23"
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
     1) "james"
     2) "3"
     3) "wade"
     4) "4"
     5) "bosh"
     6) "5"
     7) "culi"
     8) "6"
     9) "jordan"
    10) "23"
    11) "kobe"
    12) "24"
    
    # 如果元素的分值相同,按照字典顺序排序(相同元素,后面分数覆盖前面分数)
    127.0.0.1:6379> zadd zsetK 0 a 0 c 0 f 0 b 0 d 2 a 2 c
    (integer) 5
    127.0.0.1:6379> zscan zsetK 0
    1) "0"
    2)  1) "b"
        2) "0"
        3) "d"
        4) "0"
        5) "f"
        6) "0"
        7) "a"
        8) "2"
        9) "c"
       10) "2"
    
  • # 删除元素: ZREM key member [member ...]
    127.0.0.1:6379> zrem sortedsetK culi bosh
    (integer) 2
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "james"
    2) "3"
    3) "wade"
    4) "4"
    5) "jordan"
    6) "23"
    7) "kobe"
    8) "24"
    
    # 按照排名删除元素: ZREMRANGEBYRANK key start stop
    # 将指定排名(rank)区间内的元素删除。0处是分数最小的那个元素。-1是分数最大的元素。
    # 删除分数最低的两个元素
    127.0.0.1:6379> zremrangebyrank sortedsetK 0 1
    (integer) 2
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "jordan"
    2) "23"
    3) "kobe"
    4) "24"
    # 删除最高和最低分的两个元素,需要两次操作,因为:zremrangebyrank sortedsetK 0 -1表示删除全部
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "james"
    2) "3"
    3) "wade"
    4) "4"
    5) "jordan"
    6) "23"
    7) "kobe"
    8) "24"
    # 删除最低分元素
    127.0.0.1:6379> zremrangebyrank sortedsetK 0 0
    (integer) 1
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "wade"
    2) "4"
    3) "jordan"
    4) "23"
    5) "kobe"
    6) "24"
    # 删除最高分元素
    127.0.0.1:6379> zremrangebyrank sortedsetK -1 -1
    (integer) 1
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "wade"
    2) "4"
    3) "jordan"
    4) "23"
    
    # 按照分数删除元素: ZREMRANGEBYSCORE key min max
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "james"
    2) "3"
    3) "wade"
    4) "4"
    5) "jordan"
    6) "23"
    7) "kobe"
    8) "24"
    127.0.0.1:6379> zremrangebyscore sortedsetK 1 10
    (integer) 2
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "jordan"
    2) "23"
    3) "kobe"
    4) "24"
    
  • # 给元素加/减分:ZINCRBY key increment member
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "james"
    2) "3"
    3) "wade"
    4) "4"
    5) "jordan"
    6) "23"
    7) "kobe"
    8) "24"
    127.0.0.1:6379> zincrby sortedsetK 3 james #加分
    "6"
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "wade"
    2) "4"
    3) "james"
    4) "6"
    5) "jordan"
    6) "23"
    7) "kobe"
    8) "24"
    127.0.0.1:6379> zincrby sortedsetK -1 wade #减分
    "3"
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "wade"
    2) "3"
    3) "james"
    4) "6"
    5) "jordan"
    6) "23"
    7) "kobe"
    8) "24"
    
  • # 获取元素分数:ZSCORE key member
    127.0.0.1:6379> zscore sortedsetK kobe
    "24"
    
    # 取分数最高的count个元素: ZPOPMAX key [count]
    # 取分数最低的count个元素: ZPOPMIN key [count]
    # 还有阻塞版本:BZPOPMAX key [key ...] timeout , BZPOPMIN key [key ...] timeout
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
     1) "ai"
     2) "3"
     3) "james"
     4) "3"
     5) "wade"
     6) "4"
     7) "jordan"
     8) "23"
     9) "brant"
    10) "24"
    11) "kobe"
    12) "24"
    127.0.0.1:6379> zpopmax sortedsetK 2
    1) "kobe"
    2) "24"
    3) "brant"
    4) "24"
    127.0.0.1:6379> zpopmin sortedsetK 2
    1) "ai"
    2) "3"
    3) "james"
    4) "3"
    
    # 获取某个范围的元素
    # ZRANGE key start stop [WITHSCORES] #正序
    # ZREVRANGE key start stop [WITHSCORES] #反序
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores 
    1) "wade"
    2) "3"
    3) "james"
    4) "6"
    5) "jordan"
    6) "23"
    7) "kobe"
    8) "24"
    
    127.0.0.1:6379> zrevrange sortedsetK 0 -1 withscores
    1) "kobe"
    2) "24"
    3) "jordan"
    4) "23"
    5) "james"
    6) "6"
    7) "wade"
    8) "3"
    
    # 获取指定分数范围的元素:ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
    #区间:min和max可以是-inf(负无穷)和+inf(正无穷)
    #默认,区间的取值使用闭区间(小于等于或大于等于),可以使用(符号改成开区间(小于或大于)
    eg: ZRANGEBYSCORE scoreboard 80 100 #[80,100]
    eg: ZRANGEBYSCORE scoreboard 80 (100 #[80,100)
    eg: ZRANGEBYSCORE scoreboard (80 +inf #(80,正无穷)
    
    # 获取元素个数:ZCARD key
    127.0.0.1:6379> zcard sortedsetK 
    (integer) 4
    
    # 统计某个分数范围内的元素个数:ZCOUNT key min max
    127.0.0.1:6379> zcount sortedsetK 1 10
    (integer) 2
    
    # 返回指定元素区间内的元素: ZRANGEBYLEX key min max [LIMIT offset count]
    
    # 分数必须相同! 如果有序集合中的成员分数有不一致的,返回的结果就不准。
    # 成员字符串作为二进制数组的字节数进行比较。
    # 默认是以ASCII字符集的排列。如果成员包含utf-8这类字符集的内容,就会影响返回结果,所以建议不要使用。
    # 默认, “max” 和 “min” 参数前必须加 “[” 符号作为开头。”[” 符号与成员之间不能有空格, 返回成员结果集会包含参数 “min” 和 “max” 。
    # “max” 和 “min” 参数前可以加 “(“ 符号作为开头表示小于, “(“ 符号与成员之间不能有空格。返回成员结果集不会包含 “max” 和 “min” 成员。
    # 可以使用 “-“ 和 “+” 表示得分最小值和最大值
    # “min” 和 “max” 不能反, “max” 放前面 “min”放后面会导致返回结果为空
    # 与ZRANGEBYLEX获取顺序相反的指令是ZREVRANGEBYLEX。
    # 源码中采用C语言中 memcmp() 函数, 从字符的第0位到最后一位进行排序,如果前面部分相同,那么较长的字符串比较短的字符串排序靠后
    127.0.0.1:6379> zadd zset 0 a 0 aa 0 abc 0 apple 0 b 0 c 0 d 0 d1 0 dd 0 dobble 0 z 0 z1
    (integer) 12
    127.0.0.1:6379> zrangebylex zset - +
     1) "a"
     2) "aa"
     3) "abc"
     4) "apple"
     5) "b"
     6) "c"
     7) "d"
     8) "d1"
     9) "dd"
    10) "dobble"
    11) "z"
    12) "z1"
    127.0.0.1:6379> zrangebylex zset - + limit 0 3
    1) "a"
    2) "aa"
    3) "abc"
    127.0.0.1:6379> zrangebylex zset - + limit 3 3
    1) "apple"
    2) "b"
    3) "c"
    # 闭区间
    127.0.0.1:6379> zrangebylex zset [a [c
    1) "a"
    2) "aa"
    3) "abc"
    4) "apple"
    5) "b"
    6) "c"
    # 开区间
    127.0.0.1:6379> zrangebylex zset (a (c
    1) "aa"
    2) "abc"
    3) "apple"
    4) "b"
    
    #zrangebylex应用场景
    # 电话号码排序
    127.0.0.1:6379> zadd phone 0 13100111100 0 13110114300 0 13132110901
    (integer) 3
    127.0.0.1:6379> zadd phone 0 13200111100 0 13210414300 0 13252110901
    (integer) 3
    127.0.0.1:6379> zadd phone 0 13300111100 0 13310414300 0 13352110901
    (integer) 3
    # 获取132,133段手机号
    127.0.0.1:6379> zrangebylex phone [132 (134
    1) "13200111100"
    2) "13210414300"
    3) "13252110901"
    4) "13300111100"
    5) "13310414300"
    6) "13352110901"
    # 获取132段手机号
    127.0.0.1:6379> zrangebylex phone [132 (133
    1) "13200111100"
    2) "13210414300"
    3) "13252110901"
    
    # 姓名排序
    127.0.0.1:6379> zadd names 0 Toumas 0 Jake 0 Bluetuo 0 Gaodeng 0 Aimini 0 Aidehua
    (integer) 6
    127.0.0.1:6379> zrangebylex names [A (B
    1) "Aidehua"
    2) "Aimini"
    127.0.0.1:6379> zrangebylex names [C [Z
    1) "Gaodeng"
    2) "Jake"
    3) "Toumas"
    
    
    
    # 获得元素的排名(正反都从0开始)
    # ZRANK key member #正序
    # ZREVRANK key member #反序
    127.0.0.1:6379> zrange sortedsetK 0 -1 withscores
    1) "wade"
    2) "3"
    3) "james"
    4) "6"
    5) "jordan"
    6) "23"
    7) "kobe"
    8) "24"
    127.0.0.1:6379> zrank sortedsetK james
    (integer) 1
    127.0.0.1:6379> zrevrank sortedsetK james
    (integer) 2
    
  • 集合运算

    # 1.交集: ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
    # ZINTERSTORE命令可以计算多个有序集合的交集,并将结果存储在destination键中(同样以有序集合类型存储),返回值为destination键中的元素个数,destination键中的元素分数有AGGREGATE参数决定
    #  WEIGHTS参数可以设置每个集合的权重,每个集合在运算时,元素分数会被乘上对应权重
    
    #  1)当AGGREGATE是SUM (默认值),则相同元素,分数累加
    #  2)当AGGREGATE是MIN,则相同元素,分数取小值
    #  3)当AGGREGATE是MAX,则相同元素,分数取大值
    127.0.0.1:6379> zrange zset1 0 -1 withscores
    1) "bei"
    2) "1"
    3) "shang"
    4) "2"
    5) "guang"
    6) "3"
    127.0.0.1:6379> zrange zset2 0 -1 withscores
    1) "guang"
    2) "30"
    3) "sheng"
    4) "40"
    5) "hang"
    6) "50"
    # (zset1*1) n (zset2*0.1)
    127.0.0.1:6379> zinterstore zset1Interzset2 2 zset1 zset2 weights 1 0.1 aggregate sum
    (integer) 1
    127.0.0.1:6379> zrange zset1Interzset2 0 -1 withscores
    1) "guang"
    2) "6"
    
    # 2.并集:(同交集) ZUINONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
    
    # (zset1*1) U (zset2*0.1)
    127.0.0.1:6379> zunionstore zset1Uionzset2 2 zset1 zset2 weights 1 0.1 aggregate sum
    (integer) 5
    127.0.0.1:6379> zrange zset1Uionzset2 0 -1 withscores
     1) "bei"
     2) "1"
     3) "shang"
     4) "2"
     5) "sheng"
     6) "4"
     7) "hang"
     8) "5"
     9) "guang"
    10) "6"
    

为什么有序集合只有ZINTERSTORE / ZUINONSTORE, 而没有ZINTER / ZUNION

有序集合常用的使用场景是大数据排序: 游戏的玩家排行榜,所以很少会需要获得键中的全部数据

Redis设计者认为在做完交集 、并集后大部分情况是将结果存入新的键中以便后续处理

# 可以通过事务实现ZINTER
MULTI
ZINTERSTORE tempkey ...
ZRANGE tempkey ...
DEL tempKey
EXEC

散列类型

散列表示字段和值之间的映射关系,类型java中hashmap;为了与redis的键区分,我们使用"字段"来表示散列中值对象所关联的键,散列类型对于存储对象属性是一种完美的数据类型,至多包含2^32-1个字段

  • 内部编码

    • ziplist: 对于长度小于hash-max-ziplist-entries(默认512)配置,且所以元素size小于hash-max-ziplist-value(默认64字节)配置,采用此编码,对于较小散列对象可以节省空间
    • hashtable: 当ziplist不适用时,使用的默认编码
  • # 单字段赋值: HSET key field value (插入-返回1, 更新-返回0)
    127.0.0.1:6379> hset user_1 name mike
    (integer) 1
    127.0.0.1:6379> hget user_1 name
    "mike"
    127.0.0.1:6379> hset user_1 name jordan
    (integer) 0
    127.0.0.1:6379> hgetall user_1
    1) "name"
    2) "jordan"
    
    # 多字段赋值:HMSET key field value [field value ...]
    127.0.0.1:6379> hmset user_1 sex man age 666 nickname god
    OK
    127.0.0.1:6379> hgetall user_1
    1) "name"
    2) "jordan"
    3) "sex"
    4) "man"
    5) "age"
    6) "666"
    7) "nickname"
    8) "god"
    
    # 条件式新增: HSETNX key field value, 字段不存在,则新增; 字段已存在,则不做任何操作
    127.0.0.1:6379> hsetnx user_1 name mk
    (integer) 0
    127.0.0.1:6379> hgetall user_1
    1) "name"
    2) "jordan"
    3) "sex"
    4) "man"
    5) "age"
    6) "666"
    7) "nickname"
    8) "god"
    127.0.0.1:6379> hsetnx user_1 role player
    (integer) 1
    127.0.0.1:6379> hgetall user_1
     1) "name"
     2) "jordan"
     3) "sex"
     4) "man"
     5) "age"
     6) "666"
     7) "nickname"
     8) "god"
     9) "role"
    10) "player"
    
    # 字段递增:HINCRBY key field increment [散列类型没有HINCR命令,通过HINCRBY key field 1实现]
    127.0.0.1:6379> hget user_1 age
    "666"
    127.0.0.1:6379> hincrby user_1 age 4
    (integer) 670
    127.0.0.1:6379> hget user_1 age
    "670"
    
    
  • # 删除单个或多个字段:HDEL key field [field ...]
    127.0.0.1:6379> hdel user_1 nickname
    (integer) 1
    127.0.0.1:6379> hgetall user_1
    1) "name"
    2) "jordan"
    3) "sex"
    4) "man"
    5) "age"
    6) "670"
    7) "role"
    8) "player"
    
  • # 取单字段:HGET key field
    
    # 取多字段:HMGET key field [field ...]
    
    # 取全部字段(阻塞):HGETALL key,
    
    # 取全部字段(非阻塞):HSCAN key cursor [MATCH pattern] [COUNT count]
    # count指明每次希望返回元素个数,但redis不保证返回的就是count个,count的默认值是10
    127.0.0.1:6379> hscan user_1 0 match n* count 10
    1) "0"
    2) 1) "name"
       2) "jordan"
    
    # 只获取字段名或字段值:HKEYS key , KVALS key
    127.0.0.1:6379> hkeys user_1
    1) "name"
    2) "sex"
    3) "age"
    4) "role"
    127.0.0.1:6379> hvals user_1
    1) "jordan"
    2) "man"
    3) "670"
    4) "player"
    
    # 字段是否存在:HEXISTS key field
    127.0.0.1:6379> hexists user_1 name
    (integer) 1
    127.0.0.1:6379> hexists user_1 address
    (integer) 0
    
    # 获取字段数量:HLEN key
    127.0.0.1:6379> hlen user_1
    (integer) 4
    

HLL类型

redis我们可以使用集合来进行唯一计数,但是,当数据量增大到上千万时,就需要考虑内存消耗和性能下降问题。如果不需要获取数据集的内容,只是想要得到不同值的个数,就可以使用HyperLogLog(HLL)数据类型来优化使用集合类型时的内存和性能问题

HLL命令都是PF开头,向HLL的发明者Philippe Flajolet致敬;HLL的优势在于能够使用固定数量的内存(每个HLL键占用12kb内存,却可以计算出最多2^64个不同元素的基数)和常数时间复杂度(每个键O(1))进行唯一计数。不过HLL算法返回的基数可能不准确(标准差1%,HLL是cardinality counting基数计数的一种)

  • 内部编码

    HLL实际被当做字符串存储,内部使用了两种方式存储HLL对象

    • 稀疏:对于长度小于hll-sparse-max-bytes(默认3000)配置的HLL对象,采用此编码,存储效率高
    • 稠密:稀疏不适用时的默认编码
# 想要统计餐厅一个星期的独立访客数,可以每天生成一个HLL,然后用pfmerge把7天数据合并为一个
127.0.0.1:6379> pfadd myresturant_1 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10
(integer) 1
127.0.0.1:6379> pfadd myresturant_2 u6 u7 u8 u9 u10 u11 u12 u13 u14 u15
(integer) 1
127.0.0.1:6379> pfadd myresturant_3 u1 u2 u3 u4 u5 u10 u20 u30 u40 u50
(integer) 1
127.0.0.1:6379> pfmerge myresturant_week myresturant_1 myresturant_2 myresturant_3
OK
127.0.0.1:6379> pfcount myresturant_week
(integer) 19

GEO类型

排序

SORT命令可对列表类型、集合类型、有序集合类型进行排序,可完成与关系数据库中的连接查询相类似的任务

# 对集合进行排序
127.0.0.1:6379> SADD uid 6 12 66 86 70 2
(integer) 6
127.0.0.1:6379> SORT uid
1) "2"
2) "6"
3) "12"
4) "66"
5) "70"
6) "86"
# 当集合元素都是整数,Redis第这种情况进行了优化:元素是有序的
127.0.0.1:6379> SMEMBERS uid
1) "2"
2) "6"
3) "12"
4) "66"
5) "70"
6) "86"

# 对列表进行排序
127.0.0.1:6379> LPUSH ls 4 2 6 1 3 7
(integer) 6
127.0.0.1:6379> LRANGE ls 0 -1
1) "7"
2) "3"
3) "1"
4) "6"
5) "2"
6) "4"
127.0.0.1:6379> SORT ls
1) "1"
2) "2"
3) "3"
4) "4"
5) "6"
6) "7"
#(结合前面的LRANGE结果,可以看出SORT并不会影响原列表)
127.0.0.1:6379> LRANGE ls 0 -1
1) "7"
2) "3"
3) "1"
4) "6"
5) "2"
6) "4"

# 对字符进行排序,要加上ALPHA参数
127.0.0.1:6379> LPUSH ls2 a c e d B C A
(integer) 7
127.0.0.1:6379> LRANGE ls2 0 -1
1) "A"
2) "C"
3) "B"
4) "d"
5) "e"
6) "c"
7) "a"
127.0.0.1:6379> SORT ls2
(error) ERR One or more scores can't be converted into double
127.0.0.1:6379> SORT ls2 ALPHA
1) "a"
2) "A"
3) "B"
4) "c"
5) "C"
6) "d"
7) "e"

# 反序
127.0.0.1:6379> SORT uid DESC
1) "86"
2) "70"
3) "66"
4) "12"
5) "6"
6) "2"
127.0.0.1:6379> SORT ls2 ALPHA DESC
1) "e"
2) "d"
3) "C"
4) "c"
5) "B"
6) "A"
7) "a"

# LIMIT offset count (top3)
127.0.0.1:6379> SORT ls DESC
1) "7"
2) "6"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> SORT ls DESC LIMIT 0 3
1) "7"
2) "6"
3) "4"
127.0.0.1:6379> 

限制

key:512MB

String类型:一个String类型的value最大可以存储512M

List类型:list的元素个数最多为2^32-1个,也就是4294967295个。

Set类型:元素个数最多为2^32-1个,也就是4294967295个。

Hash类型:键值对个数最多为2^32-1个,也就是4294967295个。

Sorted set类型:跟Set类型一致

问题

1、为什么redis基本类型里没有数字类型

2、为什么有set类型还要有HyperLogLog

3、如何利用redis实现循环队列

4、如何利用redis实现随机开奖

5、为什么有序集合修改成功后,返回0

6、删除有序集合分数最高和最低两个元素可以一次操作完成吗

7、为什么有序集合只有ZINTERSTORE / ZUINONSTORE, 而没有ZINTER / ZUNION

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容