[toc]

数据类型

  • Redis通用命令

  • String: 字符串类型

  • Hash: 哈希类型

  • List: 列表类型

  • Set: 集合类型

  • Sorted set: 顺序集合类型

Redis通用命令

连接Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 连接redis
redis-cli

# 退出连接
127.0.0.1:6379> quit
127.0.0.1:6379> exit

# 关闭redis
127.0.0.1:6379> shutdown
redis-cli shutdown

# redis启动读取配置文件
redis-server /root/redis-stable/redis.conf

# redis后台启动(开启守护进程后不需要输入"&")
redis-server &

# 指定端口之后连接
redis-cli -p 6380

# 指定连接redis的IP地址
redis-cli -h 10.0.0.51 -a 123

# 指定连接密码
redis-cli -a 123

库操作

1
2
# 切换库[1-15]
select number(0~15)

数据操作

  • 1
    2
    3
    4
    5
    # 追加数据(在value后追加指定值)
    127.0.0.1:6379> APPEND name BB
    (integer) 1
    127.0.0.1:6379> get name
    "ChenlinBB"
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 删除所在库的数据
    127.0.0.1:6379> flushdb

    # 删除所有库的数据
    127.0.0.1:6379> flushall

    # 貌似和del一样(根据value选择非阻塞删除仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。)
    127.0.0.1:6379> UNLINK ke
    (integer) 1

    # 删除一条"key"
    127.0.0.1:6379> del name
    (integer) 1

    # 同时删除多条"key"
    127.0.0.1:6379> del k1 k2 k3
    (integer) 3
  • 1
    2
    3
    4
    5
    6
    # 临时修改redis配置
    127.0.0.1:6379> CONFIG SET protected-mode yes

    # 修改"key"的名字(将chenlin改成cl)
    127.0.0.1:6379> RENAME chenlin cl
    OK
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # 查看所有的数据(注意!不要轻易使用,如想查看数据,先查看数据量DBSIZE)
    127.0.0.1:6379> keys *
    1) "name"

    # 查看数据量
    127.0.0.1:6379> DBSIZE
    (integer) 1

    # 查看单个数据(有数据)
    127.0.0.1:6379> KEYS name
    1) "name"

    # 查看单个数据(无数据)
    127.0.0.1:6379> KEYS xxx
    (empty array)

    # 查看所有的配置
    CONFIG GET *

    # 查看指定配置
    127.0.0.1:6379> CONFIG GET bind
    1) "bind"
    2) "10.0.0.51 172.16.1.51 127.0.0.1"

密码的配置与修改

  1. 配置文件配置密码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 修改配置文件
    vim redis.conf
    requirepass 123

    # 使用密码连接
    redis-cli -a 123

    # 登陆后输入密码
    redis-cli
    127.0.0.1:6379> AUTH 123
    OK
    127.0.0.1:6379> DBSIZE
    (integer) 1

    # redis连接后获取密码
    127.0.0.1:6379> CONFIG GET requirepass
    1) "requirepass"
    2) "123"
  2. redis连接后修改密码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 修改redis密码为234
    127.0.0.1:6379> CONFIG SET requirepass 234
    OK
    127.0.0.1:6379> exit

    # 测试登录
    redis-cli
    127.0.0.1:6379> DBSIZE
    (error) NOAUTH Authentication required.
    127.0.0.1:6379> auth 234
    OK

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 编辑配置文件
vim /root/redis-stable/redis.conf

# 守护进程模式
daemonize yes

# 启动redis不用加&也可以启动
redis-server /root/redis-stable/redis.conf

# 指定pid文件
pidfile /root/redis-stable/redis.pid

# 指定日志级别
loglevel notice

# 开启redis的日志,并设置日志存放路径
logfile "/var/log/redis/redis.log"

# 指定可连接的主机(监听地址)
bind 10.0.0.51 172.16.1.51 127.0.0.1

# 关闭protected(保护模式,是否只允许本地访问)
protected-mode yes -------> no

# 设置连接密码
requirepass 123

# 开启redis的日志,并设置日志存放路径
logfile "/var/log/redis/redis.log"

Redis数据结构之字符串(String)

数据结构

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

img

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

  • String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
  • String类型是二进制安全的。意味着Redis的string可以包含任何数据,比如jpg图片或者序列化的对象。
  • String类型可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB

命令行模式

1
2
3
4
5
6
7
8
9
# 进入命令行模式:
redis-cli

# 选项
-a password # 输入登陆密码
--raw # raw 避免中文显示乱码
-p port # 指定端口之后连接
-h 10.0.0.51 # 指定连接redis的IP地址
.conf的绝对路径 # 指定读取的配置文件

查看命令帮助

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> help @string

APPEND key value
summary: Append a value to a key
since: 2.0.0

BITCOUNT key [start end]
summary: Count set bits in a string
since: 2.6.0
......

String数据结构中键(key)操作命令

    • set

      • 语法格式

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        # 语法格式
        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互斥
        * NX:key不存在时创建,已存在则忽略。
        * XX:key存在则更新,不存在则忽略。

        # 增加数据的命令有以下几个:
        * set :标准命令,"key"不存在时创建,已存在则更新。
        * mset :批量创建"key",所有"key"不存在时创建,已存在则更新。
        * setex :增加key并设置生存时间(单位为秒)
        * psetex:增加key并设置生存时间(以毫秒为单位,1s=1000ms)
        * setnx :"key"不存在时创建,已存在则忽略。
        * msetnx:批量创建"key",所有"key"key不存在时创建,已存在则忽略。
      • 基础用法

        1
        2
        3
        4
        5
        6
        7
        # 增加key
        127.0.0.1:6379> set name cl
        OK

        # 设置多个key
        127.0.0.1:6379> MSET name1 cl age1 18
        OK
      • 使用set命令结合”EX”,”PX”的用法

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        # 增加key并设置生存时间(单位为秒)
        127.0.0.1:6379> set name cl ex 10
        OK

        # "set"和"ex"合并成"setex"
        127.0.0.1:6379> SETEX name 10 cl
        OK

        # 增加key并设置生存时间(以毫秒为单位,1s=1000ms)
        127.0.0.1:6379> set name cl px 10
        OK

        # "set"和"px"合并成"psetex"
        127.0.0.1:6379> PSETEX name 15 cl
        OK
      • 使用set命令结合”NX”,”XX”的用法

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        # 使用"nx"后:"key"不存在时创建,已存在则忽略。
        127.0.0.1:6379> set name cl nx
        OK
        127.0.0.1:6379> set name cl1 nx
        (nil)
        127.0.0.1:6379> get name
        "cl"

        # "set"与"nx"合并成“setnx”
        127.0.0.1:6379> SETNX name cl1
        (integer) 0
        127.0.0.1:6379> get name
        "cl"
      • 批量增加多个”key”,同时定义多个键值对,”mset”也可以和”nx”一起用。

        1
        2
        3
        4
        5
        # 批量创建"key",所有"key"不存在时创建,已存在则更新。
        mset key1 value1 [key2 value2...]

        # 批量创建"key",所有"key"不存在时创建,已存在则忽略。
        msetnx key1 value1 [key2 value2...]
    • 追加数据

      • 基础用法

        1
        2
        3
        4
        5
        6
        7
        8
        9
        # 使用setrange在第"num"个字符处更新数据(从0开始算起)
        127.0.0.1:6379> SETRANGE name 3 cccccc
        (integer) 9
        127.0.0.1:6379> get name
        "123cccccc"

        # 在指定"key"的"value"后追加字符,如果"key"不存在则创建
        127.0.0.1:6379> append name BB
        "chenlinBB"
    • 计数

      • 整数递增

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        # 使用规则
        * 如果key不存在,则初始值为0,incr之后其值为1
        * 如果value非整型(如hello),则执行失败并返回错误信息。
        * 步长increment为整数,当为负数时效果等于递减

        # 对指定的"key"的"value"递增,递增步长默认1
        127.0.0.1:6379> INCR num
        1
        127.0.0.1:6379> INCR num
        2

        # 对指定的"key"的"value"递增,并指定递增步长
        127.0.0.1:6379> incrby num 2
        4
        127.0.0.1:6379> incrby num 2
        6

        # 对指定的"key"的"value"递增,并指定递增步长
        127.0.0.1:6379> incrbyfloat num 0.2
        0.4
        127.0.0.1:6379> incrbyfloat num 0.2
        0.6
      • 小数递增

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        # 使用规则
        * 如果key不存在,则初始值为0,incr之后其值为1
        * 如果value非整型(如hello),则执行失败并返回错误信息。
        * 步长increment为小数,当为负数时效果等于递减

        # 对指定的"key"的"value"递增,并指定递增步长
        127.0.0.1:6379> incrbyfloat num 0.2
        0.4
        127.0.0.1:6379> incrbyfloat num 0.2
        0.6
      • 整数递减

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        # 使用规则
        * 如果key不存在,则初始值为0,incr之后其值为1
        * 如果value非整型(如hello),则执行失败并返回错误信息。
        * 步长increment为整数,当为负数时效果等于递减

        # 对指定的"key"的"value"递减,递减步长默认1
        127.0.0.1:6379> DECR num
        1
        127.0.0.1:6379> DECR num
        0

        # 对指定的"key"的"value"递减,并指定递减步长
        127.0.0.1:6379> decrby num 2
        8
        127.0.0.1:6379> decrby num 2
        6
    • 修改”key”和”value”

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      # 将"key"的值(将"cl"改成"chenlin")
      127.0.0.1:6379> set name chenlin
      OK

      # 使用getset命令修改数据
      ## 等同于"get"+"set",先执行"get",再执行"set"
      127.0.0.1:6379> GETSET name chenlin
      "cl"
      127.0.0.1:6379> get name
      "chenlin"
    • 修改生存时间

      1
      2
      3
      4
      5
      6
      7
      # 修改生存时间(将指定key的生存时间修改为"200")
      127.0.0.1:6379> EXPIRE cl 200
      (integer) 1

      # 取消生存时间(将指定key的生存时间取消)
      127.0.0.1:6379> PERSIST jh
      (integer) 1
    • 查看”key”和”value”值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      # 查看所有的数据(注意!不要轻易使用,如想查看数据,先查看数据量DBSIZE)
      127.0.0.1:6379> keys *
      1) "name"

      # 查看数据量
      127.0.0.1:6379> DBSIZE
      (integer) 1

      # 查看"key"的值"value"
      127.0.0.1:6379> get name
      "cl"

      # 批量查看"key"的值"value"
      127.0.0.1:6379> mget k1 k2 k3
      1) "a"
      2) "b"
      3) "c"

      # 匹配key(键)cl
      127.0.0.1:6379> keys *cl
      1) "cl"

      # 随机返回一个key
      127.0.0.1:6379> RANDOMKEY
      "cl2"

      # 使用setrange在第"num"个字符处更新数据(从0开始算起)
      # 从指定的字符范围截取字符串(从0开始算起)
      getrange key start end
      127.0.0.1:6379> GETRANGE name 1 4
      "henl"
    • 查看生存时间

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      # 以秒为单位查看key生存时间倒计时
      ## "-1"=未设置生存时间,永不过期
      ## "-2"=生存时间已到期
      127.0.0.1:6379> TTL cl
      (integer) 16

      # 以毫秒为单位查看key生存时间倒计时
      ## "-1"=未设置生存时间,永不过期
      ## "-2"=生存时间已到期
      127.0.0.1:6379> PTTL cl
      (integer) 12199
    • 查看”key”,”value”属性

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      # 查看key是否存在(存在:1 不存在:0)
      127.0.0.1:6379> EXISTS k1
      (integer) 1
      127.0.0.1:6379> EXISTS k2
      (integer) 0

      # 查看指定key数据类型
      127.0.0.1:6379> TYPE cl
      string

      # 查看字符的长度
      127.0.0.1:6379> strlen name
      (integer) 2

Redis数据结构之哈希(Hash)

数据结构

Redis hash 是一个键值对集合。

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。类似于python中的dict和java中的map集合。

  • 应用场景:
    • 存储部分变更的数据,如用户信息,商品信息等。
    • 最接近表结构的一种类型。
  • 数据结构
    • Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。

用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:

  • 方法一
    • img
    • 每次修改用户的某个属性需要,先反序列化改好后再序列化回去。开销较大。
  • 方法二
    • img
    • 用户ID数据冗余
  • Hash方案
    • img
    • 通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题

查看命令帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> help @hash


APPEND key value
summary: Append a value to a key
since: 2.0.0

DECR key
summary: Decrement the integer value of a key by one
since: 1.0.0

DECRBY key decrement
summary: Decrement the integer value of a key by the given number
since: 1.0.0
......

String数据结构中键(key)操作命令

    • 增加数据

      • 语法格式

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        # 语法格式
        HSET key field value [field value ...]
        HSETNX key field value

        * NX:当数据库中key不存在时,可以将key-value添加数据库

        # 增加数据的命令有以下几个:
        * hset :标准命令,"key"不存在时创建,已存在则更新。
        * hmset :批量创建"key",等同于"hset"
        * hsetnx :单个创建,"hash表""key"不存在则创建,已存在则忽略。
      • 基础用法

        1
        2
        3
        4
        5
        6
        7
        # 增加hash类型的单个field或增加多个field
        127.0.0.1:6379> hset test id 1 name cl company 上海
        (integer) 3

        # 单个创建,"hash表"或"key"不存在则创建,已存在则忽略。
        127.0.0.1:6379> HSETNX test id 1
        (integer) 0
    • 计数

      • hincrby

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        # hincrby可以对hash对象的指定的field的value做递增操作

        # 语法格式
        HINCRBY key field increment
        * 如果key不存在,则初始值为0,incr之后其值为1
        * 对hash对象的指定的field的value做递增操作
        * value必须是整型,否则执行失败并返回错误信息。
        * 步长increment必须是整数,当increment为负数时为递减操作

        # 示例
        127.0.0.1:6379> HINCRBY test id 10
        11
      • hincrbyfloat

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        # hincrbyfloat可以对hash对象的指定的field的value做递增操作

        # 语法格式
        HINCRBYFLOAT key field increment
        * 如果key不存在,则初始值为0,incr之后其值为1
        * 对hash对象的指定的field的value做递增操作
        * value必须是数字类型,否则执行失败并返回错误信息。
        * 步长increment必须是整数或浮点数,当increment为负数时为递减操作

        # 示例
        127.0.0.1:6379> HINCRBYFLOAT test id 10.9
        21.9
    • 过期策略(类似于生存时间)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      # hash类型可以使用全局函数expire做过期策略。

      # 语法格式
      expire key seconds

      # 示例
      127.0.0.1:6379> EXPIRE test 1000
      1
      127.0.0.1:6379> ttl test
      993
    • 基础用法

      1
      2
      3
      4
      5
      6
      7
      8
      # 使用hdel命令删除hash类型的value中的fields,可批量或单个删除

      # 语法格式
      HDEL key field1 [field2 ...]

      # 示例
      127.0.0.1:6379> HDEL test name
      1
    • 基础用法

      1
      2
      3
      4
      5
      6
      7
      8
      # 修改hash类型中指定的field的值

      # 语法格式
      HSET key field value [field value ...]

      # 示例
      127.0.0.1:6379> HSET test name cl
      0
    • 查询”field”或”value”值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      # 查询指定hash类型的field对应的值
      127.0.0.1:6379> Hget test name
      cl

      # 查询指定hash类型的多个field对应的值
      127.0.0.1:6379> hmget test name company
      1) "cl"
      2) "上海"

      # 查询指定hash类型对象的全部field和对应的value值
      127.0.0.1:6379> HGETALL test
      1) "id"
      2) "1"
      3) "name"
      4) "chenlin"
      5) "company"
      6) "上海"

      # 查询指定hash类型对象的全部field
      127.0.0.1:6379> HKEYS test
      1) "id"
      2) "name"
      3) "company"

      # 查询指定hash类型对象的全部field的values值
      127.0.0.1:6379> HVALS test
      1) "1"
      2) "chenlin"
      3) "上海"
    • 查询”field”或”value”属性

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      # 查看hash类型的key中指定的field是否存在(存在:1 不存在:0)
      127.0.0.1:6379> HEXISTS test name
      1

      # 查询hash类型中field的数量
      127.0.0.1:6379> HLEN test
      3

      # 查询hash类型中指定filed对应的value的字符长度
      127.0.0.1:6379> HSTRLEN test name
      7

Redis数据结构之列表(List)

单键多值
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

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

img

数据结构

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

首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。

img

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

查看命令帮助

1
2
3
4
5
6
7
8
9
10
help @list

BLPOP key [key ...] timeout
summary: Remove and get the first element in a list, or block until one is available
since: 2.0.0

BRPOP key [key ...] timeout
summary: Remove and get the last element in a list, or block until one is available
since: 2.0.0
......

操作命令

    • 增加元素

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      # 从左边插入元素,从左边依次追加进栈,先进后出,后进先出
      127.0.0.1:6379> LPUSH name xxx yyy
      (integer) 2
      127.0.0.1:6379> LPUSH name zzz
      (integer) 3
      127.0.0.1:6379> LRANGE name 0 -1
      1) "zzz"
      2) "yyy"
      3) "xxx"

      # 从右边插入元素,从右边依次追加进队列,先进先出,后进后出。
      127.0.0.1:6379> rpush name xxx yyy
      (integer) 2
      127.0.0.1:6379> rpush name zzz
      (integer) 3
      127.0.0.1:6379> LRANGE name 0 -1
      1) "xxx"
      2) "yyy"
      3) "zzz"

      # 从左边追加元素,只有list存在时才会执行
      127.0.0.1:6379> LPUSHX name cl
      (integer) 4
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "cl"
      2) "xxx"
      3) "yyy"
      4) "zzz"

      # 从右边追加元素,只有list存在时才会执行
      127.0.0.1:6379> LPUSHX name cl
      (integer) 4
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "xxx"
      2) "yyy"
      3) "zzz"
      4) "cl"
    • 插入元素

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      # 从list中指定的元素的前/后 插入一个新元素
      127.0.0.1:6379> LRANGE name 0 -1
      1) "xxx"
      2) "yyy"
      3) "zzz"

      # 在某个KEY值之前插入一个值
      127.0.0.1:6379> LINSERT name before zzz cl
      (integer) 4
      127.0.0.1:6379> LRANGE name 0 -1
      1) "xxx"
      2) "yyy"
      3) "cl"
      4) "zzz"

      # 在某个KEY值后面添加一个值
      127.0.0.1:6379> LINSERT name after xxx chenlin
      (integer) 5
      127.0.0.1:6379> LRANGE name 0 -1
      1) "xxx"
      2) "chenlin"
      3) "yyy"
      4) "cl"
      5) "zzz"
    • 消费和消费追加

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      # 从左边消费列表中的元素,消费完之后从列表中删除。
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "19"
      2) "1616"
      3) "1615"
      4) "jh"
      5) "python"
      6) "linux"
      127.0.0.1:6379> LPOP mylist
      "19"
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "1616"
      2) "1615"
      3) "jh"
      4) "python"
      5) "linux"

      # 从右边消费列表中的元素,消费完之后从列表中删除。
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "1616"
      2) "1615"
      3) "jh"
      4) "python"
      5) "linux"
      127.0.0.1:6379> RPOP mylist
      "linux"
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "1616"
      2) "1615"
      3) "jh"
      4) "python"

      # 消费列表A的最右边的元素返回,然后追加到列表B的最左边。
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "1616"
      2) "1615"
      3) "jh"
      4) "python"
      127.0.0.1:6379> RPOPLPUSH mylist mylist
      "python"
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "python"
      2) "1616"
      3) "1615"
      4) "jh"

      # 从列表中左侧查询元素,返回列表的key和左侧第一个元素,若所有查询的列表中都没有元素,则会阻塞等待至设置的timeout秒之后返回空,若在这期间,这些列表新增了元素,则会立刻消费并返回该元素。
      127.0.0.1:6379> BLPOP mylist 10
      1) "mylist"
      2) "1616"
      127.0.0.1:6379> BLPOP mylist 10
      1) "mylist"
      2) "1615"
      127.0.0.1:6379> BLPOP mylist 10
      1) "mylist"
      2) "jh"
    • 删除数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      # 从列表左侧,删除指定"个数"的"值"
      127.0.0.1:6379> LRANGE name 0 -1
      1) "xxx"
      2) "chenlin"
      3) "yyy"
      4) "cl"
      5) "zzz"
      127.0.0.1:6379> LREM name 1 cl
      (integer) 1
      127.0.0.1:6379> LRANGE name 0 -1
      1) "xxx"
      2) "chenlin"
      3) "yyy"
      4) "zzz"

      # 从列表左侧,删除指定范围之外的数据
      127.0.0.1:6379> LRANGE name 0 -1
      1) "xxx"
      2) "chenlin"
      3) "yyy"
      127.0.0.1:6379> LTRIM name 0 1
      OK
      127.0.0.1:6379> LRANGE name 0 -1
      1) "xxx"
      2) "chenlin"
    • 修改元素

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # 修改元素内容(将左边第3个元素修改为19)
      127.0.0.1:6379> LSET mylist 2 19
      OK
      127.0.0.1:6379> LRANGE mylist 0 -1
      1) "Shanghai7"
      2) "shanghai"
      3) "19"
      4) "1616"
      5) "1615"
      6) "jh"
      7) "python"
      8) "linux"
    • 查询数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      # 查看列表中元素的个数
      127.0.0.1:6379> LLEN name
      (integer) 4

      # 根据起止下标范围查询列表元素
      ## 查询全部元素
      127.0.0.1:6379> lrange mylist 0 -1
      1) "19"
      2) "1616"
      3) "1615"
      4) "jh"
      5) "python"
      6) "linux"
      ## 查询最右边的元素
      127.0.0.1:6379> lrange mylist -1 -1
      1) "linux"

      # 根据指定的index下标查看列表中的元素
      127.0.0.1:6379> LINDEX mylist 3
      "jh"

Redis数据结构之无序集合(Set)

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变。集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

数据结构

Set数据结构是dict字典,字典是用哈希表实现的。

Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。

查看帮助命令

1
2
# 使用Redis命令手册查看
127.0.0.1:6379> help @set

操作命令

    • 增加数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      # 给集合内新增成员,若集合不存在则创建集合并新增成员。
      SADD key member [member ...]

      # 增加一条member
      127.0.0.1:6379> SADD fan 1
      (integer) 1
      127.0.0.1:6379> SMEMBERS fan
      1) "1"

      # 增加多条member
      127.0.0.1:6379> SADD fan 2 3 4 5
      (integer) 4
      127.0.0.1:6379> SMEMBERS fan
      1) "1"
      2) "2"
      3) "3"
      4) "4"
      5) "5"
    • 删除数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      # 从集合中删除指定的成员,返回删除的个数。
      SREM key member [member ...]

      # 示例
      127.0.0.1:6379> SREM myset 2 4
      (integer) 2
      127.0.0.1:6379> SMEMBERS myset
      1) "1"
      2) "3"
    • 修改数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      #1.移动数据
      SMOVE source destination member

      #2.示例
      127.0.0.1:6379> SADD myset1 1 2 3 4 5
      (integer) 5

      127.0.0.1:6379> SADD myset2 one two three
      (integer) 3

      127.0.0.1:6379> SMOVE myset1 myset2 4
      (integer) 1

      127.0.0.1:6379> SMEMBERS myset1
      1) "1"
      2) "2"
      3) "3"
      4) "5"

      127.0.0.1:6379> SMEMBERS myset2
      1) "three"
      2) "two"
      3) "4"
      4) "one"
    • 查看数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      # 查看集合中所有的成员
      127.0.0.1:6379> SMEMBERS myset1
      1) "1"
      2) "2"
      3) "3"
      4) "5"

      # 返回集合中成员的个数
      127.0.0.1:6379> SCARD myset1
      (integer) 4

      # 从集合中随机返回指定个数的成员
      127.0.0.1:6379> SRANDMEMBER myset1 2
      1) "3"
      2) "2"

      127.0.0.1:6379> SRANDMEMBER myset1 2
      1) "5"
      2) "3"


      #4.判断对象是否是集合中的成员,1表示true,0表示false
      SISMEMBER key member

      127.0.0.1:6379> SISMEMBER myset1 6
      (integer) 0
      127.0.0.1:6379> SISMEMBER myset1 2
      (integer) 1

      #5.随机返回一个成员,从集合中随机弹出一个成员,返回该成员并从集合中删除该成员。

      SPOP key

      127.0.0.1:6379> SPOP myset2
      "one"
      127.0.0.1:6379> SPOP myset2
      "two"
      127.0.0.1:6379> SPOP myset2
      "three"
      127.0.0.1:6379> SMEMBERS myset2
      1) "4"
    • 集合

      • 交集

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
          #1.取多个集合的交集,返回这些集合中共同拥有的成员。

        SINTER key [key ...]

        127.0.0.1:6379> sadd myseta 1 2 3 4 5
        (integer) 5
        127.0.0.1:6379> sadd mysetb 4 5 6 7 8
        (integer) 5
        127.0.0.1:6379> sadd mysetc 5 6 7 8 9
        (integer) 5
        127.0.0.1:6379> SINTER myseta mysetb mysetc
        1) "5"

        #2.将多个集合的交集的结果保存为一个新的集合destination,返回新集合的成员个数。
        SINTERSTORE destination key [key ...]

        127.0.0.1:6379> SINTERSTORE mysetc mysetb myseta
        (integer) 2

        127.0.0.1:6379> SMEMBERS mysetc
        1) "4"
        2) "5"
      • 并集

        1
        2
        3
        #1.取多个集合的并集,相同的成员会被去重。
        SUNION key [key ...]

      127.0.0.1:6379> SMEMBERS mysetb

      1. “4”
      2. “5”
      3. “6”
      4. “7”
      5. “8”

      127.0.0.1:6379> SMEMBERS myseta

      1. “1”
      2. “2”
      3. “3”
      4. “4”
      5. “5”

      127.0.0.1:6379> SUNION myseta mysetb

      1. “1”
      2. “2”
      3. “3”
      4. “4”
      5. “5”
      6. “6”
      7. “7”
      8. “8”

      #2.将多个集合的并集的结果保存为一个新的集合
      SUNIONSTORE destination key [key …]

      127.0.0.1:6379> SUNIONSTORE mysetc myseta mysetb
      (integer) 8
      127.0.0.1:6379> SMEMBERS mysetc

      1. “1”
      2. “2”
      3. “3”
      4. “4”
      5. “5”
      6. “6”
      7. “7”
      8. “8”
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
          
        - 差集

        ```bash
        # 取多个集合的差集,以最左边的为主集合,返回左集合中有而其他集合没有的成员。
        127.0.0.1:6379> SDIFF myseta mysetb
        1) "1"
        2) "2"
        3) "3"

        #将多个集合的差集的结果保存为一个新的集合 ,返回新集合的成员个数 。
        SDIFFSTORE destination key [key ...]

        127.0.0.1:6379> SDIFFSTORE mysetc myseta mysetb
        (integer) 3
        127.0.0.1:6379> SMEMBERS mysetc
        1) "1"
        2) "2"
        3) "3"

模拟发朋友圈

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> lpush wechat 'zhouyi'
(integer) 1
127.0.0.1:6379> lpush wechat 'zhouer'
(integer) 2
127.0.0.1:6379> lpush wechat 'zhousan'
(integer) 3
127.0.0.1:6379> LRANGE wechat 0 100
1) "zhousan"
2) "zhouer"
3) "zhouyi"

Redis数据结构之有序集合Zset(sorted set)

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。

数据结构

SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。

  • zset底层使用了两个数据结构:
    1. hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
    2. 跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。

跳跃表(跳表)

  • 简介

    • 有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组不便元素的插入、删除;平衡树或红黑树虽然效率高但结构复杂;链表查询需要遍历所有效率低。Redis采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单。
  • 实例

    • 对比有序链表和跳跃表,从链表中查询出51

      1. 有序链表

        • 要查找值为51的元素,需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。

          img

      2. 跳跃表

        • 从第2层开始,1节点比51节点小,向后比较。

        • 21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层

        • 在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下

        • 在第0层,51节点为要查找的节点,节点被找到,共查找4次。

        • 从此可以看出跳跃表比有序链表效率要高

          img

查看命令帮助

1
127.0.0.1:6379> help @sorted_set

操作命令

    • 增加数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      # 往有序集合中新增成员,需要指定该成员的分数,分数可以是整形或浮点型,当分数相同时候,索引下标按照字典排序。

      # 语法格式
      ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]

      # 增加一条数据
      127.0.0.1:6379> ZADD myzset 1 jh
      1

      # 增加多条数据
      127.0.0.1:6379> ZADD myzset 2 shanghai 3 beijing
      2
      127.0.0.1:6379> ZRANGE myzset 0 -1
      jh
      shanghai
      beijing
    • 删除数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      #1.移除指定的成员
      ZREM key member [member ...]

      127.0.0.1:6379> ZRANGE myzset 0 -1
      jh
      shanghai
      beijing
      127.0.0.1:6379> ZREM myzset shanghai
      1
      127.0.0.1:6379> ZRANGE myzset 0 -1
      jh
      beijing
    • 修改数据

      1
          
    • 查看数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      # 获取有序集合的成员数
      ZCARD key

      127.0.0.1:6379> ZCARD myzset
      3

      # 获取指定分数区间内的成员数
      从有序集合内获取指定分数区间内的成员数。
      ZCOUNT key min max

      127.0.0.1:6379> ZRANGE myzset 0 -1
      jh
      shanghai
      beijing

      127.0.0.1:6379> ZCOUNT myzset 1 2
      2

      #3.字典排序
      根据字典排序返回min ,max之间的数据量.
      ZLEXCOUNT key min max

      127.0.0.1:6379> ZRANGE myzset 0 -1
      jh
      shanghai
      beijing

      127.0.0.1:6379> ZLEXCOUNT myzset [sh [zzz
      0

      #4.获取成员的分数值
      返回有序集中,成员的分数值,不存在的成员返回空。

      ZSCORE key member

      127.0.0.1:6379> ZRANGE myzset 0 -1
      jh
      shanghai
      beijing
      127.0.0.1:6379> ZSCORE myzset shanghai
      2

      #5.迭代有序集合中的元素(包括元素成员和元素分值)
      127.0.0.1:6379> ZSCAN myzset 0 match "sh*"
      0
      shanghai
      2
      127.0.0.1:6379> ZSCAN myzset 0 match "j*"
      0
      jh
      1