Redis 5大数据类型(一)

前言

Reids安装请看:Linux环境下安装Redis

这里说的数据类型是value的数据类型,key的类型都是字符串。

5种数据类型:

  1. 字符串String
  2. 列表List
  3. 集合Set
  4. 哈希表 Hash
  5. 有序集合Zset

Redis 常用数据类型操作命令: Redis命令中心

Redis键(key)的相关命令:

  • keys * : 查看当前库所有的key
  • exists key : 判断某个key是否存在
  • type key : 查看key的类型
  • del key: 删除指定的key数据
  • unlink key : 根据value删除非阻塞删除,仅仅将keys从keyspace元数据中删除,真正的删除会在后续异步中操作
  • expire key 10: 设置key的有效时间为10秒
  • ttl key:查看指定的key还有多少秒过期,-1:表示永不过期,-2:表示已过期
  • select dbindex:切换数据库【0-15】,默认为0
  • dbsize:查看当前数据库key的数量
  • flushdb:清空当前库
  • flushall:清空全部库

1. 字符串String

String是Redis最基本的类型,可以理解为和Memcached一样的类型,一个key 对应一个value。

String类型是二进制,意味着可以包含任何数据,比如jpg图片或者序列化对象。一个Redis字符串value最多可以是512M

1.1 常用命令

  1. set: 添加键值对

    1
    127.0.0.1:6379> set key value [EX seconds|PX milliseconds|EXAT timestamp|PXAT milliseconds-timestamp|KEEPTTL] [NX|XX] [GET]
    • NX:当数据库中key不存在时,可以将key-value添加到数据库
    • XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥
    • EX:key的超时秒数
    • PX:key的超时毫秒数,与EX互斥
    • value中若包含空格、特殊字符,需用双引号包裹
    1
    2
    127.0.0.1:6379> set xiaoyuge 180 NX 
    OK
  2. get: 获取值

    1
    get <key>

    示例:

    1
    2
    127.0.0.1:6379> get xiaoyuge
    "180"
  3. append: 追加值

    1
    append <key> <value>

    将给定的value追加到原值的末尾。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    127.0.0.1:6379> set xiaoyuge 180
    OK
    127.0.0.1:6379> get xiaoyuge
    "180"
    127.0.0.1:6379> append xiaoyuge cm
    (integer) 5
    127.0.0.1:6379> get xiaoyuge
    "180cm"
  4. strlen: 获取值的长度

    1
    strlen <key>

    示例:

    1
    2
    127.0.0.1:6379> strlen xiaoyuge
    (integer) 8
  5. setnx: key不存在时,设置key的值

    1
    setnx <key> <value>

    示例:

    1
    2
    3
    4
    5
    127.0.0.1:6379> setnx ygb cool      ## ygb不存在,返回1,表示设置成功
    (integer) 1
    127.0.0.1:6379> setnx ygb cool ## 再次通过setnx设置ygb,由于已经存在了,所以设置失败,返回0
    (integer) 0
    127.0.0.1:6379>
  6. incr: 原子递增1

    1
    incr <key>

    将key中存储的值增加1, 只能对数值操作,如果key不存在,则会新建一个,值为1

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> flushdb #清空db,方便测试
    OK
    127.0.0.1:6379> set age 18 #age值为18
    OK
    127.0.0.1:6379> incr age #age增加1,返回19
    (integer) 19
    127.0.0.1:6379> get age #获取age的值
    "19"
    127.0.0.1:6379> incr salary #salary不存在,自动创建一个,值为1
    (integer) 1
    127.0.0.1:6379> get salary #获取salary的值
  7. decr: 原子递减1

    1
    decr <key>

    将key中存储的值减1,只能对数值操作,如果为空,新增值为-1

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> set age 18
    OK
    127.0.0.1:6379> get age
    "18"
    127.0.0.1:6379> decr age
    (integer) 17
    127.0.0.1:6379> keys age1
    (empty array)
    127.0.0.1:6379> decr age1
    (integer) -1
  8. incrby/decrby: 递增或递减指定的数字

    1
    2
    incrby <key> <步长>
    decrby <key> <步长>

    将key中存储的数字值递增指定的步长,若key不存在,则相当于在原值为0的值上递增指定的步长。

    示例:

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> incrby age 18
    (integer) 18
    127.0.0.1:6379> decrby age 2
    (integer) 16
    127.0.0.1:6379> get age
    "16"
  9. mset: 同时设置多个key-value

    1
    2
    3
    4
    mset <key1> <value1> <key2> <value2> ...

    # 设置name = xiaoyuge, age = 18
    # 127.0.0.1:6379> mset name xiaoyuge age 18
  10. mget: 获取多个key对应的值

    1
    2
    3
    4
    5
    6
    mget <key1> <key2> ....

    #示例
    127.0.0.1:6379> mget name age
    #1) "xiaoyuge"
    #2) "18"
  11. msetnx: 当多个key都不存在时,则设置成功

    1
    msetnx <key1> <value1> <key2> <value2> ...

    原子性,要么都成功,或者都失败

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    127.0.0.1:6379> flushdb   #清空
    OK
    127.0.0.1:6379> set k1 v1 #先设置k1
    OK
    127.0.0.1:6379> msetnx k1 v1 k2 v2 #当k1和k2都不存在,同时设置k1,k2,由于k1已经存在,所以操作失败
    (integer) 0
    127.0.0.1:6379> mget k1 k2 # 获取k1、k2, k2不存在
    1) "v1"
    2) (nil)
    127.0.0.1:6379> msetnx k2 v2 k3 v3 #当k2和k3都不存在,同时设置k2,k3,设置成功
    (integer) 1
    127.0.0.1:6379> mget k2 k3 # 获取k2、k3
    1) "v2"
    2) "v3"
  12. getrange: 获取值的范围,类似于Java的substring

    1
    getrange key <start> <end>

    获取[start, end]之间的字符,返回为字符串

    示例:

    1
    2
    3
    4
    127.0.0.1:6379> set k1 xiaoyuge
    OK
    127.0.0.1:6379> getrange k1 0 4
    "xiaoy"
  13. setrange: 覆盖自定位置的值

    1
    setrange <key> <start> <value>

    从 start 位置开始覆盖,覆盖的长度为value的长度, 总长度不变

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    127.0.0.1:6379> get k1
    "xiaoyuge"
    127.0.0.1:6379> setrange k1 0 nb
    (integer) 8
    127.0.0.1:6379> get k1
    "nbaoyuge"
    127.0.0.1:6379> get k1
    "nbaoyuge"
    127.0.0.1:6379> setrange k1 0 xiao
    (integer) 8
    127.0.0.1:6379> get k1
    "xiaoyuge"
  14. setex: 设置键值&过期时间(秒)

    1
    setex <key> <过期时间s> <value>

    示例:

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> setex k1 120 v1 #设置k1的值为v1,有效期120秒
    OK
    127.0.0.1:6379> get k1 #获取k1的值
    "v1"
    127.0.0.1:6379> ttl k1 #获取k1还有多少秒失效
    (integer) 113
  15. getset: 以新换旧,设置新值同时返回旧值

    1
    getset <key> <value>

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> set name xiaoyuge         #设置name为xiaoyuge
    OK
    127.0.0.1:6379> getset name xiaoyuge666 #设置name为xiaoyuge666,返回name的旧值
    "xiaoyuge"
    127.0.0.1:6379> getset age 18 #设置age为18,age未设置过,返回age的旧值为null
    (nil)
    127.0.0.1:6379> get name #获取现在的name,返回xiaoyuge666
    "xiaoyuge666"
    127.0.0.1:6379> get age #获取age
    "18"

1.2 数据结构

String的数据结构为简单动态字符串(Simple Dynamic String,简写SDS)。是可以修改的字符串,内部结构类似于Java中的ArrayList,采用分配冗余空间的方式来减少内存的频繁分配

如图所示,内部为当前字符串实际分配的空间capacity,一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次会多扩容1M的空间

要注意的是字符串最大长度为512M

2. 列表List

列表List是简单的字符串列表,按照插入顺序排序,添加元素时可以插入列表的头部或者尾部。

它的底层实现实际上使用双向链表实现的,对两端的操作性能很高,通过索引下标操作中间节点性能会较差。

2.1 常用命令

  1. lupsh/rpush: 从左边或者右边插入一个或多个值

    1
    2
    lpush <key1> <value1> <value2> <value3> ...
    rpush <key1> <value1> <value2> <value3> ...

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> rpush k v1 v2 v3  #列表k的右边插入3个元素
    (integer) 3
    127.0.0.1:6379> lpush k v4 v5 v6 #列表k的左边插入3个元素
    (integer) 6
    127.0.0.1:6379> lrange k 0 6 #输出[0,6]范围内的元素
    1) "v6"
    2) "v5"
    3) "v4"
    4) "v1"
    5) "v2"
    6) "v3"
  2. lrange: 从列表左边获取指定范围内的值

    1
    lrange <key> <start> <end>

    返回列表key中指定的区间的元素,区间偏移量start ,end指定。

    下标index参数start和end都从0开始,也可以使用负数下标,以 -1表示列表最后一个元素,-2表示倒数第二个元素…..

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    127.0.0.1:6379> lpush n v1 v2 v3 v4 v5 v6
    (integer) 6
    127.0.0.1:6379> lrange n 0 -1 #取出n集合中所有元素 start 0表示从第一个开始, end -1表示最后一个
    1) "v6"
    2) "v5"
    3) "v4"
    4) "v3"
    5) "v2"
    6) "v1"
    127.0.0.1:6379> lrange n -3 -1 #获取倒数第三至倒数一个元素 start 一定要小于end
    1) "v3"
    2) "v2"
    3) "v1"
    127.0.0.1:6379>
  3. lpop/rpop: 从左边或者右边弹出多个元素

    1
    lpop/rpop <key> <count>

    count: 可以省略,默认为1

    lpop/rpop操作之后,弹出的值会从列表中删除,当所有的值都删除后,键就删除

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> rpush k v1 v2 v3  #集合k 右边添加3个元素
    (integer) 3
    127.0.0.1:6379> lpop k #左边弹出1个元素
    "v1"
    127.0.0.1:6379> rpop k 2 # 右边弹出2个元素
    "v3"
    "v2"
    127.0.0.1:6379> exists k # 查询key是否存在
    (integer) 0
  4. rpoplpush: 从一个列表右边弹出一个元素放到另外一个列表中

    1
    rpoplpush <source> <destination>

    从source的右边弹出一个元素放到destination列表的左边

    示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    127.0.0.1:6379> rpush k 1 2 3   #列表k的右边添加3个元素[1,2,3]
    (integer) 3
    127.0.0.1:6379> lrange k 0 -1 #从左到右输出k列表中的元素
    1) "1"
    2) "2"
    3) "3"
    127.0.0.1:6379> rpush k1 4 5 6 #列表k1的右边添加3个元素[4,5,6]
    (integer) 3
    127.0.0.1:6379> lrange k1 0 -1 #从左到右输出k1列表中的元素
    1) "4"
    2) "5"
    3) "6"
    127.0.0.1:6379> rpoplpush k k1 #从k的右边弹出一个元素放到k1的左边
    "3"
    127.0.0.1:6379> lrange k 0 -1 #k中剩下2个元素了
    1) "1"
    2) "2"
    127.0.0.1:6379> lrange k1 0 -1 #k1变成来4个元素
    1) "3"
    2) "4"
    3) "5"
    4) "6"
  5. lindex: 获取指定索引位置的元素(从左到右)

    1
    lindex <key> <index>

    返回列表key中下标为index的元素。

    下标从0开始,也可以是负数,-1 表示列表最后一个元素,-2表示列表倒数第二个元素…..

    如果key不是列表类型,返回一个错误

    如果index超出了列表的长度范围,返回 nil

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> lrange k1 0 -1
    1) "3"
    2) "4"
    3) "5"
    4) "6"
    127.0.0.1:6379> lindex k1 2 #返回索引位置2的元素
    "5"
    127.0.0.1:6379> lindex k1 6 #返回索引位置6的元素,超出了列表长度
    (nil)
    127.0.0.1:6379> lindex k1 -1 #返回最后一个元素
    "6"
  6. llen: 获取列表长度

    1
    llen <key>

    返回列表长度,如果key不存在,则被解释为一个空列表,返回0;

    如果key不是列表类型,返回一个错误

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    127.0.0.1:6379> lrange k1 0 -1
    1) "3"
    2) "4"
    3) "5"
    4) "6"
    127.0.0.1:6379> llen k1 #返回k1集合的长度
    (integer) 4
    127.0.0.1:6379> set name xiaoyuge #设置字符串
    OK
    127.0.0.1:6379> type name #查看name的类型
    string
    127.0.0.1:6379> llen name #获取name的长度,因为name为String类型,所以报错
    (error) WRONGTYPE Operation against a key holding the wrong kind of value
    127.0.0.1:6379> strlen name #字符串的长度使用strlen
    (integer) 8
  7. linsert: 在某个值的前或者后插入一个值

    1
    linsert <key> before|after <value> <newvalue>

    将值newvalue插入到列表key中,位于value值之前或者之后

    当value不存在列表key中,不执行任何操作,返回 -1

    当key不存在时,key被视为空列表,不执行任何操作, 返回 0

    如果命令执行成功,返回插入操作完成之后,列表的长度

    如果key不是列表类型,返回一个错误

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    127.0.0.1:6379> rpush k 1 2 3   #列表k中添加3个元素
    (integer) 3
    127.0.0.1:6379> lrange k 0 -1 #输出k全部元素
    1) "1"
    2) "2"
    3) "3"
    127.0.0.1:6379> linsert k before 1 0 #在1前面添加0,添加成功,返回列表长度
    (integer) 4
    127.0.0.1:6379> lrange k 0 -1 #输出k全部元素
    1) "0"
    2) "1"
    3) "2"
    4) "3"
    127.0.0.1:6379> linsert k before 4 5 #在4前面添加5,由于元素4不存在,插入失败返回-1
    (integer) -1
    127.0.0.1:6379> linsert k1 before 4 5 #在列表k1中元素4前面插入5,由于列表k1不存在,返回0
    (integer) 0
  8. lrem: 删除指定数量的某个相同的元素

    1
    lrem <key> <count> <value>

    根据count的值,移除列表中与参数value相等的count个元素

    count的值可以是以下几种

    1. count > 0: 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count

    2. count < 0: 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。

    3. count = 0: 移除列表总所有与value相等的值

    因为不存在的 key 被视作空表(empty list),所以当 key 不存在时,总是返回 0 。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    127.0.0.1:6379> flushdb #清空db,方便测试
    OK
    127.0.0.1:6379> rpush k1 v1 v2 v3 v2 v2 v1 #k1列表中插入6个元素
    (integer) 6
    127.0.0.1:6379> lrange k1 0 -1 #输出k1集合中所有元素
    1) "v1"
    2) "v2"
    3) "v3"
    4) "v2"
    5) "v2"
    6) "v1"
    127.0.0.1:6379> lrem k1 2 v2 #k1集合中从左边删除2个v2
    (integer) 2
    127.0.0.1:6379> lrange k1 0 -1 #输出列表,列表中还有1个v2,前面2个v2干掉了
    1) "v1"
    2) "v3"
    3) "v2"
    4) "v1"
  9. lset: 替换指定位置的值

    1
    lset <key> <index> <value>

    将列表key下标为index的元素替换为value, 当index参数超出范围,或者对一个空列表进行操作时,返回一个错误

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    127.0.0.1:6379> flushdb              #清空db,方便测试
    OK
    127.0.0.1:6379> rpush k v1 v2 v3 #k集合中放入3个元素
    (integer) 3
    127.0.0.1:6379> lrange k 0 -1 #输出k集合元素
    1) "v1"
    2) "v2"
    3) "v3"
    127.0.0.1:6379> lset k 1 vv #将k集合中第2个元素替换为vv
    OK
    127.0.0.1:6379> lrange k 0 -1
    1) "v1"
    2) "vv"
    3) "v3"
    127.0.0.1:6379> lset k 10 vv #将k集合中第11个元素替换为vv,由于集合长度小于10,报错
    (error) ERR index out of range
    127.0.0.1:6379> lset k1 1 vv #k1不存在,报错
    (error) ERR no such key

2.2 数据结构

List的数据结构为快速链表quickList

  1. 首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也就是压缩列表。 它将所有的元素紧挨着一起存储,分配的是一块连续的内存。

  2. 当就比较多的时候才会改成quickList。

因为普通的链表需要的附加指针空间太大,会比较浪费空间,比如这个列表里存储的只是int类型的书,结构上还需要2个额外的指针prev和next。

redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用,这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。

3. 集合Set

Redis set对外提供的功能与list类似,是一个列表的功能,特殊之处在于set可以自动去重

set时String类型的无序集合,它的底层实际上时一个value为null的hash表,添加、删除、查找复杂度都是O(1)

一个算法,如果时间复杂度是O(1),那么随着数据的增加,查找数据的时间不变,也就是不管数据多少,查找时间都是一样的。

3.1 常用命令

  1. sadd: 添加一个或者多个元素

    1
    2
    3
    4
    5
    6
    sadd <key> <value1> <value2> ...

    #示例:
    127.0.0.1:6379> sadd s1 v1 v2 v3
    (integer) 3
    127.0.0.1:6379>
  2. smembers取出所有的元素

    1
    2
    3
    4
    5
    6
    7
    smembers <key>

    #示例:
    127.0.0.1:6379> smembers s1 #输出结果为无序
    1) "v3"
    2) "v1"
    3) "v2"
  3. sismember: 判断集合中是否存在某个值

    1
    2
    3
    4
    sismember <key> <value>

    #判断集合key是否包含元素value, 1:有, 0:没有
    #sismember: set is member(是否是set中的成员)
  4. scard: 返回集合中元素的个数

    1
    scard <key>

    返回集合key中元素个数,当key不存在返回0

  5. srem: 删除多个元素

    1
    srem <key> <member> [member ...]

    移除集合key中的一个或多个member元素,不存在的元素会被忽略,当key不是集合类型,返回一个错误

    返回被成功移除的元素数量,不包括被忽略的元素

  6. spop: 随机弹出多个值

    1
    spop <key> <count>

    随机从key集合中弹出count个元素,count默认为1,返回被移除的元素

    当count大于元素集合个数的时候,弹出全部

    当key不存在或者空集时,返回nil

  7. srandmember: 随机获取多个元素,不会从集合中删除

    1
    srandmember <key> <count>

    从key指定的集合中随机返回count个元素,count可以不指定,默认值是1。

    srandmember 和 spop的区别: 都可以随机获取多个元素,srandmember 不会删除元素,而spop会删除元素。

    返回值: 1. 只提供 key 参数时,返回一个元素;如果集合为空,返回 nil 。 2. 如果提供了count参数,那么返回一个数组;如果集合为空,返回空数组。

  1. smove: 将某个元素从一个集合移到另一个集合

    1
    smove <source> <destination> <member>

    将member元素从source集合移动到destination集合。

    smove是原子性操作,如果source集合不存在或者不保护指定的member元素,则smove命令不执行任何操作,仅返回0。
    否则,member元素从source集合中移除,并添加到destination集合中去(destination集合不存在的话,会自动添加一个)

    当destination已经包含member元素,smove只是删除source中的member元素

    如果member被成功删除,返回1;如果member不是source集合成员,并且没有任何对destination的操作,那么返回0

  2. *sinter 取多个集合的交集

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    sinter key [key ...]

    #示例
    127.0.0.1:6379> smembers s #查询s集合的所有元素
    1) "v3"
    2) "v4"
    3) "v2"
    127.0.0.1:6379> sadd s2 v1 v2 v3 #集合s2添加三个元素
    (integer) 3
    127.0.0.1:6379> smembers s2 #查询s2集合的所有元素
    1) "v3"
    2) "v1"
    3) "v2"
    127.0.0.1:6379> sinter s s2 #获取集合s s2的交集
    1) "v3"
    2) "v2"
  3. sinterstore: 将多个集合的交集放到一个新的集合中

    1
    sinterstore destination key [key ...]

    这个命令类似于sinter命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。 返回结果集中的成员数量。

  4. sunion: 取多个集合的并集,自动去重

    1
    2
    3
    4
    5
    6
    7
    sunion key [key ...]
    #示例:
    127.0.0.1:6379> sunion s s2
    1) "v1"
    2) "v3"
    3) "v2"
    4) "v4"
  5. sunionstore: 将多个集合的并集放到一个新的集合中

    1
    sinterstore destination key [key ...]

    这个命令类似于 sunion 命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。 返回值:结果集中的成员数量。

  6. sdiff: 取多个集合的差集

    1
    sdiff key [key ...]

    返回一个集合的全部成员,该集合是所有给定集合之间的差集。 不存在的 key 被视为空集。

  7. sdiffstore: 将多个集合的差集放到一个新的集合中

    1
    sdiffstore destination key [key ...]

    这个命令类似于 sdiff 命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。

3.2 数据结构

set数据机构是字典,字典是用hash表实现的。

Java中的hashSet的内部实现使用HashMap,只不过所有的value都指向同一个对象。

Redis的set结构也是一样的,它的内部也使用hash结构,所有的value都指向同一个内部值。

4. 哈希表 Hash

Redis hash是一个键值对集合

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 类似于java里面的Map<String,Object>

4.1 常用命令

  1. hset: 设置多个field值

    1
    2
    3
    4
     hset key field value [field value ...]

    127.0.0.1:6379> hset set1 name xiaoyuge age 18
    (integer) 2

    将哈希表 key 中的域 field 的值设为 value 。 如果 key 不存在,一个新的哈希表被创建并进行 hset 操作。 如果域 field 已经存在于哈希表中,旧值将被覆盖。

    返回值: 如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。 如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0 。

  2. hget: 获取指定field的值

    1
    2
    3
    4
    5
    6
    hget <key> <field>

    127.0.0.1:6379> hget set1 name
    "xiaoyuge"
    127.0.0.1:6379> hget set1 age
    "18"
  3. hgetall: 返回hash表所有的fileld和value

    1
    2
    3
    4
    5
    6
    7
    hgetall <key>

    127.0.0.1:6379> hgetall set1
    1) "name"
    2) "xiaoyuge"
    3) "age"
    4) "18"
  4. hexists: 判断给定的field是否存在,1:存在,0:不存在

    1
    hexists key field

    查看哈希表 key 中,给定域 field 是否存在。 返回值: 如果哈希表含有给定域,返回 1 。 如果哈希表不含有给定域,或 key 不存在,返回 0 。

  1. hkeys: 列出所有的filed
    1
    hkeys key
  2. hvals: 列出所有的value
    1
    hvals key
  3. hlen: 返回field的数量
    1
    hlen key
  4. hincrby: filed的值加上指定的增量
    1
    hincrby key field increment

    为哈希表 key 中的域 field 的值加上增量 increment 。 增量也可以为负数,相当于对给定域进行减法操作。 如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。 如果域 field 不存在,那么在执行命令前,域的值被初始化为 0 。 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。

    返回值: 执行 hincrby 命令之后,哈希表 key 中域 field 的值。

    示例:
    1
    2
    3
    4
    5
    127.0.0.1:6379> hincrby set1 age 3
    (integer) 21
    127.0.0.1:6379> hvals set1
    1) "xiaoyuge"
    2) "21"
  5. hsetnx: 当filed不存在的时候,设置filed的值
    1
    hsetnx key field value

    将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。 若域 field 已经存在,该操作无效。 如果 key 不存在,一个新哈希表被创建并执行 hsetnx 命令。

    返回值: 设置成功,返回 1 。 如果给定域已经存在且没有操作被执行,返回 0

4.2 数据结构

Hash类型对应的数据结构是2种:ziplist(压缩列表)hashtable(哈希表)

当field-value长度较短个数较少时,使用ziplist,否则使用hashtable。

5. 有序集合Zset

有序集合zset和普通集合set非常相似,是一个没有重复元素的字符串集合,不同之处是有序集合的每一个成员都关联一个评分(score),
这个评分被用来按照从最低分到最高分的方式排序集合中的成员。

成员是唯一的,但是评分可以重复。因为元素是有序的,所以可以很快的根据评分score或者次序position来获取一个范围的元素,访问有序集合中的中间元素也是非常快的。

5.1 常用命令

  1. zadd: 添加元素
    1
    zadd <key> <score1> <member1> <score2> <member2> ...

    将一个或多个member元素及其score值加入到有序集 key 当中。 如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。 score 值可以是整数值或双精度浮点数。 如果 key 不存在,则创建一个空的有序集并执行 zadd 操作。

    当 key 存在但不是有序集类型时,返回一个错误。 被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。

  1. zrange: score生序,获取指定索引范围的元素

    1
    zrange key start top [withscores]

    返回存储在有序集合key中的指定范围的元素。 返回的元素可以认为是按score从最低到最高排列,如果得分相同,将按字典排序。

    下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。 你也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。

    zrange key 0 -1:可以获取所有元素

    withscores:让成员和它的 score 值一并返回,返回列表以 value1,score1, …, valueN,scoreN 的格式表示

    时间复杂度:O(log(N)+M), N 为有序集的基数,而 M 为结果集的基数

  2. zrevrange: score降序,获取指定索引范围的元素

    1
    zrevrange key start stop [WITHSCORES]

    返回存储在有序集合key中的指定范围的元素。 返回的元素可以认为是按score最高到最低排列, 如果得分相同,将按字典排序。

    下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。 你也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。

    withscores:让成员和它的 score 值一并返回,返回列表以 value1,score1, …, valueN,scoreN 的格式表示

  1. zrangebyscore:按照score升序,返回指定score范围内的数据
    1
    zrangebyscore key min max [WITHSCORES] [LIMIT offset count]

    返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。

    具有相同 score 值的成员按字典序来排列(该属性是有序集提供的,不需要额外的计算)。
    可选的 LIMIT 参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count ),注意当 offset 很大时,定位 offset 的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N) 时间。

  1. zrevrangebyscore:按照score降序,返回指定score范围内的数据

    1
    zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]

    返回有序集 key 中, score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从大到小)的次序排列。

    具有相同 score 值的成员按字典序的逆序排列。

    除了成员按 score 值递减的次序排列这一点外, zrevrangebyscore 命令的其他方面和 zrangebyscore 命令一样。

  2. zincrby:为指定元素的score加上指定的增量

    1
    zincrby key increment member

    可以通过传递一个负数值 increment ,让 score 减去相应的值,比如 ZINCRBY key -5 member ,就是让 member 的 score 值减去 5 。

    当 key 不存在,或 member 不是 key 的成员时, ZINCRBY key increment member 等同于 ZADD key increment member

  3. zrem:删除集合中多个元素

    1
    zrem key member [member ...]

    移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。 当 key 存在但不是有序集类型时,返回一个错误。

  1. zremrangebyrank 根据索引范围删除元素
    1
    zremrangebyrank key start stop

    移除有序集 key 中,指定排名(rank)区间内的所有成员。 区间分别以下标参数 start 和 stop 指出,包含 start 和 stop 在内。 下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。 你也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。

  1. zremrangebyscore:根据score的范围删除元素
    1
    zremrangebyscore key min max

    移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员

  1. zcount:统计指定score范围内的元素个数
    1
    zcount key min max

    返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量

  1. zrank:按照score生序,返回某个元素在集合中的排名
    1
    zrank key member

    返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。 排名以 0 为底,也就是说, score 值最小的成员排名为 0 。

  1. zrevrank:按照score将许,返回某个元素在集合中的排名
    1
    zrevrank key member

    返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。排名以 0 为底,也就是说, score 值最大的成员排名为 0

  1. zscore:返回集合中指定元素的score
    1
    zscore key member

    返回有序集 key 中,成员 member 的 score 值。 如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil

5.2 数据结构

SortedSet(zset)是redis提供的一个非常特别的数据结构,内部使用到了2种数据结构。

1. hash表

类似于java中的Map<String,score>,key为集合中的元素,value为元素对应的score,可以用来快速定位元素定义的score,时间复杂度为O(1)

2. 跳表

跳表(skiplist)是一个非常有限的数据结构,实现简单,插入、删除、查找的复杂度均为O(logN)

类似于Java中的ConcurrentSkipListSet,根据score的值排序后生成一个跳表,可以快速按照位置的顺序或者score的顺序查找元素。

来看一下跳表的原理:
首先从考虑一个有序列表开始:

从该有序表中搜索元素 < 23, 43, 59 > ,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数为 2 + 4 + 6 = 12 次。有没有优化的算法吗? 链表是有序的,但不能使用二分查找。类似二叉搜索树,我们把一些节点提取出来,作为索引。得到如下结构:

这里我们把 < 14, 34, 50, 72 > 提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。我们还可以再从一级索引提取一些元素出来,作为二级索引,变成如下结构:

如果元素足够多,这种索引结构就能体现出优势来了。