Redis

NoSQL

什么是NoSQL

NoSQL全称是Not only SQL(不仅仅是SQL),它属于非关系型数据库(Not-Relational DB)。

NoSQl的存储结构主要有两个特点:

  • 数据之间是无关系的:关系型数据库有主外键约束,而NoSQL弱化了这个概念
  • 数据的结构是松散的、可变的:在关系型数据库中,如果表有5个“列”。那么最多只能存储5个列的值;而在NoSQL中没有所谓固定的列数,甚至连“列”的概念都没有,所以存储数据的类型、数据的多少都是可变的,是不固定的。

NoSQL是一类数据库的统称。并不是某一个具体的数据库产品名称,就像关系型数据库管理系统(Relational Database Management System,RDBMS)一样

RDBMS包括Oracle、MySQL以及MS SQL Server,NoSQL包括Redis、MangoDB等。

为什么使用NoSQL

RDBMS的缺点如下:

  • 因为RDBMS无法应对每秒上万次的读写请求,无法处理大量集中的高并发操作,所以在电商项目中,不是从RDBMS中直接读取数据来展示给客户,而是先将数据放入类似Redis的NoSQL中进行保存,实现缓存的作用,再从Redis中加载数据展示给客户,以减少对RDBMS的访问,提高运行效率。
  • 表中存储信息是有限制的。

列数有限:常见的RDBMS允许一张表最大支持的列数是有限制的,其中Oracle最多支持1000列

行数有限:在RDBMS中,如果一张表中的行数达到百万级别时,那么读写的速度会呈断崖式下降

在使用RDBMS时,面对海量数据必须使用主从复制、分库分表。这样的系统架构是难以维护的,其维护成本较高,因为它增加了程序员在开发和运维时的工作量,而且在海量数据下使用Select查询语句效率极低,查询时间会呈指数级增长。

  • RDBMS无法简单地通过增加硬件、服务器节点地方式来提高系统性能,因为性能得瓶颈在RDBMS上,而不是在高性能服务器上。
  • 关系型数据库大多时收费的,而且对硬件的要求较高,。软件和硬件的使用成本比较大,但NoSQL可以解决上面四个问题。
  • NoSQL支持每秒上万次的读写。
  • 数据存储格式灵活。
  • 在单机的环境下NoSQL性能就很好,在多台计算机的环境下性能更高。
  • NoSQL大多数时免费的、开源的。

NoSQL有自己的优势和使用场景,在软件公司中应用比较多。

NoSQL的优势

NoSQL的优势可以总结为如下4点:

  • 面对海量数据时依然保持良好性能。

    NoSQL具有非常良好的读写性能,尤其在面对海量数据时表现同样优秀。这得益于它的非关系性和结构简单。Redis的作者在设计Redis时,最先考虑的就是性能。

  • 灵活的数据格式

    使用NoSQL时不需要创建列,它的数据格式比较灵活。

  • 高可用

    NoSQL具有主从复制、支持集群的特点,大大增加了软件项目的高可用性。如果某一台计算机宕机,那么其他的计算机会接手任务,不至于出现系统无法访问的情况。

  • 低成本

    这是大多数NoSQL共有的特点,因为多数NoSQL时免费开源的,所以没有高昂的授权成本。

    RDBMS和NoSQL都有各自的优势和使用场景,两者需要结合使用。让关系型数据库关注在关系上,让NoSQL关注在存储上。

    “一针见血”总结NoSQL优势:因为RDBMS太慢,所以用NoSQL!

NoSQL的劣势

一个事务有优势就有劣势,NoSQL的劣势可以总结为如下5点:

  • 数据之间是无关系的。
  • 不支持标准的SQL,没有工人的NoSQL标准。
  • 没有关系型数据库的约束,如主键约束、主外键约束和取值范围约束等,大多数也没有索引的概念。
  • 没有事务回滚的概念,由于优先考虑性能,因此不能完全实现ACID特性。
  • 没有丰富的数据类型

Redis介绍及使用场景

​ Redis的作者是萨尔瓦托雷 · 圣菲利波(Salvatore Sanfilippo),他来自意大利的西西里岛,被称为Redis之父。

​ Redis全称是Remote Dictionary Server,它是现阶段极为流行的NoSQL之一,它以键-值(Key-Value)对的形式在内存中保存数据。Redis的读写性能非常高,如果硬件环境非常优秀,可以实现每秒插入10万条记录,因此Redis常用于存储、缓存等场景。

​ Redis可以将内存中的数据持久化到硬盘上,防止因为出现断电、死机等问题造成数据丢失,还支持Key超时、发布订阅、流水线(批处理)以及Lua脚本等。

​ Redis主要有如下特点:

  • 速度快:Redis中的数据被放入内存,读写速度快。Redis使用C语言实现,更接近底层。Redis是”单线程模型“,避免了因抢锁而影响运行效率,但Redis6.0 开始支持”多线程模型“,运行命令时还是遵守单线程模型。
  • 使用简单,运行稳定:Redis使用key-value对组织数据,学习成本非常低,就像学习Java中的HashMap一样简单,并且Redis的源代码已经经过了大量优化,在速度和稳定性上非常优秀。曾经有人评价Redis的源代码是艺术和技术的集成者。
  • 功能丰富:支持五种常见的基本数据类型,分别是字符串(String)、散列(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set).
  • 支持多种客户端:可以使用Java、C、C++、PHP、Python以及Node.js等编程语言来对Redis进行操作。
  • 支持持久化:可以将内存中的数据持久化到硬盘上,达到数据备份的目的。
  • 支持主从复制:实现Redis服务的副本,保证数据的完整性。
  • 支持分布式:从Redis3.0 开始正式支持分布式,实现多台服务器共同工作。
  • 支持高可用:哨兵模式就是解决方案。

Redis在软件系统的位置如图:

image-20220324155750136

Redis篇

Linux安装

Redis官网下载

image-20220324191257115

  1. 上传至home目录下,进入文件夹解压
1
tar -zxvf redis-6.2.6.tar.gz
  1. 安装gcc
1
yum intall gcc
  1. 进入Redis解压后目录
1
2
make
make install

image-20220324192118224

image-20220324192151792

  1. 复制配置文件,Redis安装目录为usr/local/bin,进入目录新建配置文件夹
1
mkdir zconfig
  1. 复制一份redis解压后的配置文件到zconfig中
1
cp /opt/redis-6.2.6/redis.conf zconfig
  1. Redis默认不是后台启动,进入配置文件修改为默认后台启动
1
vim redis.conf 

image-20220324192931493

  1. 使用对应配置文件(zconfig)启动redis
1
redis-server zconfig/redis.conf

image-20220324193048767

  1. 使用redis-cli连接测试
1
redis-cli -p 6379
  1. 查看redis的进程是否开启

image-20220324193526768

  1. 如何关闭redis服务

    1
    shutdouwn

    image-20220324193708071

扩展:更改Redis服务端口

  1. 在命令行中指定

    1
    redis-server --port 8888
  2. 使用如下命令指定端口的Redis服务

    1
    redis-cli -p 8888 shutdown

测试性能

redis-benchmark是一个压力测试工具!

官方自带的性能测试工具!

redis-benchmark命令参数!

redis性能测试工具可选参数如下所示:

序号 选项 描述 默认值
1 -h 指定服务器主机名 127.0.0.1
2 -p 指定服务器端口 6379
3 -s 指定服务器 socket
4 -c 指定并发连接数 50
5 -n 指定请求数 10000
6 -d 以字节的形式指定 SET/GET 值的数据大小 2
7 -k 1=keep alive 0=reconnect 1
8 -r SET/GET/INCR 使用随机 key, SADD 使用随机值
9 -P 通过管道传输 请求 1
10 -q 强制退出 redis。仅显示 query/sec 值
11 –csv 以 CSV 格式输出
12 ***-l*(L 的小写字母)** 生成循环,永久执行测试
13 -t 仅运行以逗号分隔的测试命令列表。
14 ***-I*(i 的大写字母)** Idle 模式。仅打开 N 个 idle 连接并等待。

简单测试

1
2
#测试 100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

image-20220324195927432

基础知识

redis有16个数据库

image-20220324200558479

默认使用的是第0个

可以使用select进行切换

1
2
3
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]>

查看当前数据库的键值队的数量DBSIZE

1
2
3
127.0.0.1:6379> DBSIZE
(integer) 5
127.0.0.1:6379>

image-20220324200915061

清除当前数据库flushdb

1
2
3
4
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)

image-20220324201248741

清楚全部数据库的内容FLUSHALL

1
2
3
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379>

image-20220324201358068

Redis是单线程的!

Redis的瓶颈是根据机器的内存和网络带宽。

Redis 为社么单线程还这么块?

  1. 误区1:高性能的服务器一定比单线程效率高?
  2. 误区2:多线程(CPU上下文会切换)一定比单线程效率高?

核心:redis是将所有数据全部放在内存中的,所以说使用单线程去操作效率是最高的,多线陈(CPU上下文会切换:耗时操作)对于内存系统来说,如果没有上下文切换效率就是最高的?多次读写都是在一个CPU上的,在内存情况下,这就是最佳的方案。

Redis的五大 数据类型

Connection类型命令

  • echo命令

    使用格式如下:

    echo message

    该命令用于输出指定的消息message

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    127.0.0.1:6379> echo "a b c"
    "a b c"
    127.0.0.1:6379> echo abc
    "abc"
    127.0.0.1:6379> echo true
    "true"
    127.0.0.1:6379> echo false
    "false"
    127.0.0.1:6379> echo 123
    "123"
    127.0.0.1:6379> echo null
    "null"
  • ping命令

    使用格式如下:

    ping

    客户端向Redis服务器发送ping命令,用于测试于Redis服务器的连接是否有效。如果连接到Redis服务器,则会返回pong命令;如果连接不到Redis服务器,则会出现如下异常。

    Could not connect to Redis at 127.0.0.1:6739: Connection refused

    使用ping命令可以实现自定义“心跳”,检测Redis服务器中实例的存活情况

    测试案例

    1
    2
    127.0.0.1:6379> ping
    PONG
  • quit命令

    使用格式如下:

    quit

    该命令用于请求Redis服务器断开与当前客户端的连接

    测试案例

    1
    2
    127.0.0.1:6379> quit
    [root@iZwz91ojxikfjqb3lvllllZ bin]#
  • select命令

    Redis没有数据库名称,而是使用索引代替

    使用格式如下:

    select index

    该命令用于选择目标数据库,数据库索引index用数字值指定,以0作为起始索引值,默认使用0号数据库。

    ​ Redis默认有16个数据库配置。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    127.0.0.1:6379> keys *
    (empty array)
    127.0.0.1:6379> set a 0
    OK
    127.0.0.1:6379> select 1
    OK
    127.0.0.1:6379[1]> set a 1
    OK
    127.0.0.1:6379[1]> get a
    "1"
    127.0.0.1:6379[1]> select 0
    OK
    127.0.0.1:6379> get a
    "0"
    127.0.0.1:6379>
  • swapdb命令

    使用格式如下:

    swapdb index index

    该命令用于交换两个数据库的索引

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    127.0.0.1:6379> flushall
    OK
    127.0.0.1:6379> set username username0
    OK
    127.0.0.1:6379> get username
    "username0"
    127.0.0.1:6379> select 1
    OK
    127.0.0.1:6379[1]> set username username1
    OK
    127.0.0.1:6379[1]> get username
    "username1"
    127.0.0.1:6379[1]> swapdb 0 1
    OK
    127.0.0.1:6379[1]> get username
    "username0"
    127.0.0.1:6379[1]> select 0
    OK
    127.0.0.1:6379> get username
    "username1"
    127.0.0.1:6379>
  • Redis-key

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
127.0.0.1:6379> keys *  #查看所有的key
(empty array)
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> exits name #判断当前key是否存在
(error) ERR unknown command `exits`, with args beginning with: `name`,
127.0.0.1:6379> exists name
(integer) 0
127.0.0.1:6379> set name zhang
OK
127.0.0.1:6379> expore name 10
(error) ERR unknown command `expore`, with args beginning with: `name`, `10`,
127.0.0.1:6379> expire name 10 #设置key的过期时间单位是s秒
(integer) 1
127.0.0.1:6379> ttl name #查看该key的过期剩余时间单位是s秒
(integer) 6
127.0.0.1:6379> ttl name
(integer) 4
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) 0
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> type name #c查看key的类型
string

String(字符串)

​ String类型的命令主要用于处理字符串,可以处理JSON或XML等类型的复杂字符串,可以处理整数、浮点数,甚至是二进制的数据,包括视频、音频和图片等资源。每一个Key对应的value最大可以存储512M的数据。

​ String数据类型常用于存储JSON字符串,使用方式是将数据库中的数据使用JDBC存入实体类,然后将实体类转成JSON字符串保存在Redis的String数据类型中,以后获取这条数据时,直接从Redis中获取,速度比RDBMS快得多。

​ String数据类型的存储形式如图:

image-20220324214315381

  • append命令

    使用格式如下:

    append key value

    如果key已经存在并且是一个字符串,则append命令将value追加到key原来值的末尾;如果key不存在,则等同于执行set key value

    ​ 返回值代表操作后的字符串长度。

    测试案例

    1
    2
    3
    4
    5
    127.0.0.1:6379> append name world
    (integer) 10
    127.0.0.1:6379> get name
    "helloworld"
    127.0.0.1:6379>
  • incr命令

    使用格式如下:

    incr key

    ​ 该命令用于将key对应的整数值自加1.如果key不存在,那么key的value会先被初始化为0,然后执行incr命令。如果value包括错误的类型,或者字符串的value不能表示为整数,那么返回一个错误。value的限制是64位(bit)有符号整数。

    ​ incr命令是一个针对字符串的命令,因为Redis没有专用的整数类型,所以key中存储的字符串被解释为10进制64bit有符号整数来执行incr命令。

    Redis使用单线程模型,如果有两个客户端同时执行incr命令时不会出现错误的结果。不同客户端发送的命令按执行的顺序进入Redis服务器的命令队列中,Redis命令从命令队列中按顺序执行命令。不会出现多条命令同时执行的情况,而是一条接着一条按顺序执行。这就可能出现如果某一个命令需要花费大量的时间来执行,则其他命令会阻塞,影响系统运行效率。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    127.0.0.1:6379> keys *
    1) "name"
    2) "username"
    127.0.0.1:6379> incr name
    (error) ERR value is not an integer or out of range
    127.0.0.1:6379> set key1 3
    OK
    127.0.0.1:6379> incr key1
    (integer) 4
    127.0.0.1:6379> get key1
    "4"
    127.0.0.1:6379>

  • incrby命令

    使用格式如下:

    incrby key increment

    该命令用于将key对应的value加上增量increment。

    如果key不存在,那么key的value会被初始化为0,然后执行incrby命令。

    如果value包括错误的数据类型,或字符串的value不能表示成整数,那么返回一个错误。

    value的限制时64bit有符号整数。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    127.0.0.1:6379> get key1
    "4"
    127.0.0.1:6379> incrby key1 5
    (integer) 9
    127.0.0.1:6379> get key1
    "9"
    127.0.0.1:6379>

  • incrbyfloat命令

    使用格式如下:

    incrbyfloat key inctement

    ​ 该命令用于为key对应的value加上浮点数增量increment。

    ​ 如果key不存在,那么incrbyfloat命令会先将key的value设为0,再执行加法操作。

    ​ 如果命令执行成功,那么key的value会被更新为(执行加法之后的)新的value,并且新value会以字符串的形式返回给调用者。

    ​ 无论是key的value,还是增量increment,都可以使用像2.0e、3e5、90e-2这样的指数符号(Exponential Notation)来表示。但是,执行incrbyfloat命令之后的value总是以一个数字、一个小数点(可选的)和一个任意位的小数部分组成(如:3.14、68.354)。小数部分最后的0会被删除,如果有需要的话,还会浮点数改为整数(如3.0会被保存为3)。

    ​ 除此之外,无论加法计算所得到的浮点数的实际精度有多长,incrbyfloat命令的计算精度为小数点的后17位。

    注意:Redis中的整数和浮点数都以字符串形式保存,它们都属于字符串类型

    如果key的value不能转换成数字,则执行incr、incrby、incrbyfloat 等数字计算命令会出现异常,如value是List或Set数据类型,或者存储的value是abc或123abc等,都不能正确执行incr、incrby、incrbyfloat 等命令。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> set mykey 100
    OK
    127.0.0.1:6379> get mykey
    "100"
    127.0.0.1:6379> incrbyfloat mykey 19.123
    "119.123"
    127.0.0.1:6379> get mykey
    "119.123"
    127.0.0.1:6379>
  • decr命令

    使用格式如下:

    decr by

    该命令用于将key对应的整数值自减1.如果key不存在,那么key的value会被初始化为0,然后执行decr命令。如果value包括错误的类型,或字符串的value不能表示为整数,那么返回一个错误。value的限制是64bit有符号整数。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    127.0.0.1:6379> set username usernamevalue
    OK
    127.0.0.1:6379> get username
    "usernamevalue"
    127.0.0.1:6379> decr username
    (error) ERR value is not an integer or out of range
    127.0.0.1:6379> set num1 100
    OK
    127.0.0.1:6379> decr num1
    (integer) 99
    127.0.0.1:6379> decr num1
    (integer) 98
    127.0.0.1:6379>
  • decrby命令

    使用格式如下:

    decrby key decrement

    该命令用于将key对应的value减去减量decrement。

    如果key不存在,那么key的value会被初始化为0,然后执行decrby命令。

    如果value包括错误的类型,或字符串的value不能表示为整数,那么返回一个错误。value的限制是64bit有符号整数。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> set key11 100
    OK
    127.0.0.1:6379> get key11
    "100"
    127.0.0.1:6379> decrby key11 4
    (integer) 96
    127.0.0.1:6379> get key11
    "96"
    127.0.0.1:6379>

  • set和get命令

    set命令的使用格式如下:

    set key value [Ex seconds] [PX milliseconds] [NX|XX]

    该命令用于将字符串的value关联到key。如果key已经拥有旧value,则将新的value覆盖旧的value。对某个带有生存空间(Time To Live,TTL)的key来说,当set命令成功在这个key上执行,这个key原有的TTL将被清楚。

    set命令的行为可以通过一系列参数来修改:

    • EX second:设置key的TTL为second(单位为s)。set key EX second的运行效果等同于setex key second value。
    • PX millisecond:设置key的TTL为millisecond(单位是ms).set key value PX millisecond的运行效果等同于psetex key millisecond value。
    • NX:只在key不存在时,才对key进行设置操作,常用于添加操作。set key value NX的效果等同于setnx key value。
    • XX:只在key已经存在时,才对key进行设置操作,常用于更新操作。

    get命令的使用格式如下:

    get key

    获取key对应的value 。如果key不存在,则返回特殊值nil;如果key存在,并且key对应的value不是字符串,则返回错误,因为get命令只处理字符串。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    127.0.0.1:6379> keys *
    1) "name"
    2) "num1"
    3) "username"
    4) "key1"
    5) "key11"
    6) "mykey"
    127.0.0.1:6379> get name
    "helloworld"
    127.0.0.1:6379> set num1 world
    OK
    127.0.0.1:6379> get num1
    "world"
    127.0.0.1:6379>
  • strlen命令

    使用格式如下:

    strlen key

    该命令用于返回key所存储字符串的长度。当key存储的不是字符串时,返回一个错误。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    127.0.0.1:6379> strlen name
    (integer) 0
    127.0.0.1:6379> strlen username
    (integer) 13
    127.0.0.1:6379> get username
    "usernamevalue"
    127.0.0.1:6379>

  • setrange命令

    使用格式如下:

    setrange key offset value

    该命令用value从偏移量offset开始将给定key所存储的字符串进行覆盖。offset以B为单位,值从0开始。

    如果给定key原来存储的字符串长度比offset小(如字符串只有5个字符长,但是设置的offset是10),那么源字符串和offset之间的空白将用零字节(Zero Bytes,即\x00)来填充。

    注意:能使用的最大offset是$2 ^ {29}$-1(536870911),因为Redis字符串的大小被限制在512MB以内,如果需要使用比这更大的空间,可以使用多个key。

    当生成一个很长的字符串时,Redis需要分配内存空间,该操作有可能会造成服务器阻塞(blok)。

    setrange和getrange命令可以将字符串作为线性数组,这是一个非常快速和高效的存储结构。

    返回值代表被setrange命令修改之后字符串的长度。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> flushall
    OK
    127.0.0.1:6379> set a 12345
    OK
    127.0.0.1:6379> setrange a 7 678
    (integer) 10
    127.0.0.1:6379> get a
    "12345\x00\x00678"
    127.0.0.1:6379>

  • getrange命令

    使用格式如下:

    getrange key start end

    该命令用于返回key中字符串的子字符串,字符串的截取范围由start和end两个偏移量决定(包括start和end在内)。start和end以B为单位,值从0开始。

    负数偏移量表示从字符串末尾开始计数,-1表示最后一个字符,-2表示倒数第二个字符,以此类推。

    返回值就是截取出的子字符串。该命令的作用和java中的subString()方法相识。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> set a 123456789
    OK
    127.0.0.1:6379> getrange a 0 4
    "12345"
    127.0.0.1:6379> getrange a 0 100
    "123456789"
    127.0.0.1:6379> getrange a 0 -1
    "123456789"
    127.0.0.1:6379>

  • setbit和getbit命令

    setbit命令使用格式如下:

    setbit key offset value

    该命令用于将key存储的value作为二进制,然后在指定offset上的位设置值。

    位的值取决于value,可以时0,也可以是1.当key不存在时,自动生成一个新的字符串。

    字符串会进行伸展(Grown)以确保它可以将value保存在指定的offset上。当字符串进行伸展时,空白位置以0填充。

    offset必须大于或等于0,并且小于$2 ^{32}$(位映射被限制在512MB之内)。对使用大offset的setbit命令来说,内存分配可能造成Redis服务器被阻塞。

    返回值代表指定offset原来存储位对应的值。

    getbit命令的使用格式如下:

    getbit key offset

    该命令用于将key存储的value作为二进制,获取指定offset上的位值。

    当offset比字符串的长度大,或者key不存在时,返回0。

    如果想在Redis中将存储的汉字正确显示出来,需要在执行redis-cli命令时添加 –raw参数。命令如下:

    redis-cli -p 7777 -a accp –raw

    测试用例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    127.0.0.1:6379> set key1 中
    OK
    127.0.0.1:6379> getbit key1 7
    (integer) 0
    127.0.0.1:6379> setbit key1 7 1
    (integer) 0
    127.0.0.1:6379> getbit key1 15
    (integer) 0
    127.0.0.1:6379> setbit key1 15 1
    (integer) 0
    127.0.0.1:6379> getbit key1 23
    (integer) 1
    127.0.0.1:6379> setbit key1 23 0
    (integer) 1
    127.0.0.1:6379> get key1
    "\xe5\xb9\xac"
    127.0.0.1:6379>
  • bitcount命令

    使用格式如下:

    bitcount key [start] [end]

    该命令用于计算给定字符串转换成二进制后值为1的位的个数。一般情况下,给定的整个字符串都会被计数,通过指定额外的star或end参数,可以在指定的字符范围内进行计数。

    start和end参数的设置和getrange命令类似,都可以使用负数值,如-1表示最后一个字节,-2表示倒数第二个字节,依此类推。参数start和end代表字节,不是位。

    不存在的key被当成空字符串来处理,因此对一个不存在的key执行bitcount命令,结果为0。

    可以使用bitcount命令统计出某个人一年内登录网站的总次数。

    测试用例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> set username 中
    OK
    127.0.0.1:6379> bitcount username
    (integer) 13
    127.0.0.1:6379> bitcount username 0 0
    (integer) 4
    127.0.0.1:6379> bitcount username 0 1
    (integer) 8
    127.0.0.1:6379>
  • bitop命令

    使用格式如下:

    bitop operation destkey key [key ….]

    该命令用于将一个或多个key中的字符串转换成二进制,并对这些转化后的key进行位运算,然后将计算结果保存到destkey上。

    operation可以是and、or、xor、not这四种操作中的任意一种。

    • bitop and destkey key [key ….]。

      对一个或多个key求逻辑并,然后将结果保存到destkey。

    • bitop or destkey key [key ….]。

      对一个或多个key求逻辑或,然后将结果保存到destkey。

    • bitop xor destkey key [key ….]。

      对一个或多个key求逻辑异或,然后将结果保存到destkey。XOR操作是指如果a和b两个值不相同,则异或结果为1;如果a和b两个值相同,则异或结果为0;

    • bitop not destkey key。

      对给定key求逻辑非,1转换成0、0转换成 1,然后将结果保存到destkey

    除not操作之外,其他操作都可以接收一个或多个key作为参数。

    当bitop命令处理不同长度字符串时,较短的字符串所缺少的部分会被看作0。

    空的key也被看作是包括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
    127.0.0.1:6379> setbit key2 0 1
    (integer) 0
    127.0.0.1:6379> setbit key2 1 0
    (integer) 0
    127.0.0.1:6379> setbit key2 2 1
    (integer) 0
    127.0.0.1:6379> setbit key2 3 0 #以上生成二进制数1010
    (integer) 0
    127.0.0.1:6379> setbit key3 0 1
    (integer) 0
    127.0.0.1:6379> setbit key3 1 0
    (integer) 0
    127.0.0.1:6379> setbit key3 2 0
    (integer) 0
    127.0.0.1:6379> setbit key3 3 1 #以上生成二进制数1001
    (integer) 0
    127.0.0.1:6379> bitop and key4 key2 key3 #对两个二进制数进行and操作,结果在key4
    (integer) 1
    127.0.0.1:6379> getbit key4 0
    (integer) 1
    127.0.0.1:6379> getbit key4 1
    (integer) 0
    127.0.0.1:6379> getbit key4 2
    (integer) 0
    127.0.0.1:6379> getbit key4 3
    (integer) 0
    127.0.0.1:6379>
    • or操作

      or操作对一个或多个key求逻辑或,然后保存到destkey

    • xor操作

      xor操作对一个或多个key求逻辑异或,然后将结果保存到destkey,xor操作是指如果a和b两个值不相同,则异或结果为1;如果a和b两个值相同,则异或结果为0;

    • not操作

      not操作对给定的key求逻辑非,1转换成0、0转换成1,然后将结果保存到destkey。

    • and操作对一个或多个key求逻辑并,然后将结果保存到destkey。

  • getset命令

    使用格式如下:

    getset key value

    该命令用于原子性(Atomic)地将给定的key的新值赋为value,并返回key的旧值。

    原子性是指不可分割的操作或命令。也就是赋值新值和返回值这两个操作不能其他的命令所干扰,这两个操作都完成了,才会执行其他的命令。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> keys *
    1) "key2"
    2) "key4"
    3) "key8"
    4) "key3"
    5) "key1"
    127.0.0.1:6379> getset key2 keyvalue2
    "\xa0"
    127.0.0.1:6379> get key2
    "keyvalue2"
    127.0.0.1:6379>
  • msetnx命令

    使用格式如下:

    metnx key value [key value]

    该命令用于同时设置一个或多个key-value对,当且仅当所有给定的key都不存在时,才会批量执行set命令;即使只有一个给定的key存在,msetnx,命令也会拒绝执行所有给定key的set命令。

    msetnx命令是原子性的,所有key-value对可以全被设置,也可以全不被设置。

    当所有给定key都成功设置是返回1.如果所有给定key都设置失败(至少有一个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> msetnx name lily age 12 sex male
    (integer) 1
    127.0.0.1:6379> keys *
    1) "key2"
    2) "name"
    3) "sex"
    4) "key4"
    5) "key8"
    6) "age"
    7) "key3"
    8) "key1"
    127.0.0.1:6379> get name
    "lily"
    127.0.0.1:6379> get age
    "12"
    127.0.0.1:6379> get sex
    "male"
    127.0.0.1:6379>
  • mset命令

    使用格式如下:

    mset key value [key value]

    该命令用于同时设置一个或多个key-value对。

    如果某个给定key已经存在,那么mset命令会用新值覆盖原来的旧值。如果这不是所希望的效果,请考虑使用msetnx命令,它只会在所有给定key都不存在的情况下才执行set命令。

    mset命令是一个原子性命令,所有给定key都会在同一时间内设置,客户端不会看到某些key已更新,而其他key保持不变的效果。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    127.0.0.1:6379> mset username tom pssword 123456
    OK
    127.0.0.1:6379> get username
    "tom"
    127.0.0.1:6379> get pssword
    "123456"
    127.0.0.1:6379>
  • mget命令

    使用格式如下:

    mget key [key ….]

    该命令用于返回所有(一个或多个)给定key的value值。

    如果给定的多个key中某个key不存在,那么这个key返回特殊值nil

    测试案例

    1
    2
    3
    4
    5
    127.0.0.1:6379> mget username pssword address
    1) "tom"
    2) "123456"
    3) (nil)
    127.0.0.1:6379>
  • bitfield命令

    使用格式如下:

    bitfield key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FALL]

    前文介绍过setbit和getbit命令,这两个命只会对一个位进行操作,而bitfield命令可以将多个位当成一个“组”,对这个组中的数据进行操作。

    bitfield命令可以将一个Redis字符串看作一个由二进制组成的数组,可以对数组中的数据进行“分组”访问。如将数组中的“某一部分数据”当作整数,对这个整数进行加法和减法操作,并且这些操作可以通过设置某些参数妥善地处理计算时出现地溢出情况。

    注意一下几点:

    • 使用get命令对超出字符串当前范围地二进制进行访问(包括key不存在地情况)超出部分地二进制位的值将被当作0。
    • 使用set子命令或incrby子命令对超出字符串当前范围地二进制进行访问将导致字符串被扩展,被扩展的部分会使用值为0的二进制进行位进行填充。在对字符串进行扩展时,命令会根据字符串目前已有的最远端二进制位计算出执行操作所需要的最小长度。

    以下是bitfield命令支持的子命令:

    • get type offset:返回指定offset处的type的值

    • set type offset value:对指定offset处设置type的值,并返回它的旧值。

    • incrby type offset increment:对指定offset处的type进行加法操作,并返回它的旧值。

      用户可以通过increment参数传入负值来实现相应的减法操作,并返回它的旧值。

    • overflow [WRAP|SAT|FAIL]:可以改变之后执行的increby子命令在发生溢出情况时的行为。

    当对offset处的数据进行操作时,可以在type的前面添加 i 来表示有符号整数,如使用i16来表示16位长的有符号整数。或者使用 u 来表示无符号整数,如可以使用u8来表示8位长的无符号整数。

    bitfield命令最大支持64位长的有符号整数个63位长的无符号整数,其中无符号整数的63位长度限制是由于Redis协议目前还无法返回64位长的无符号整数。

    测试案例

    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
    127.0.0.1:6379> clear
    127.0.0.1:6379> bitfield key19 set i8 0 -123
    1) (integer) 0
    127.0.0.1:6379> getbit key19 0
    (integer) 1
    127.0.0.1:6379> getbit key19 1
    (integer) 0
    127.0.0.1:6379> getbit key19 2
    (integer) 0
    127.0.0.1:6379> getbit key19 3
    (integer) 0
    127.0.0.1:6379> getbit key19 4
    (integer) 0
    127.0.0.1:6379> getbit key19 5
    (integer) 1
    127.0.0.1:6379> getbit key19 6
    (integer) 0
    127.0.0.1:6379> getbit key19 7
    (integer) 1
    127.0.0.1:6379> bitfield key19 set i8 0 -124
    1) (integer) -123
    127.0.0.1:6379> bitfield key19 get i8 0
    1) (integer) -124
    127.0.0.1:6379> bitfield key19 incrby i8 0 20
    1) (integer) -104
    127.0.0.1:6379>
  • 使用#方便处理“组数据”

    用户有两种方法来设置offse。

    • 如果用户给定的offset是一个没有任何前缀的数字,那么这个数字指示的就是以0为开始的offset,也就是 位所对应的索引值。

    • 如果用户给定的是一个带有#的offset,那么命令将使用这个offset于=与被设置的数字类型的长度相乘,从而计算出正真的offset,如下命令:

      bitfield mystring set i8 #0 100 i8 #1 200

      命令会把mystring里面第一个i8长度的二级制位的值设置为100,并把第二个i8长度的二进制的值设置为200.当把key对应的value当作数组来使用,并且数组中存储的都是固定长度的整数时,使用#可以免去手动计算二进制位所在的offset的麻烦。

      测试案例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      127.0.0.1:6379> clear
      127.0.0.1:6379> bitfield a set i8 #0 10
      1) (integer) 0
      127.0.0.1:6379> bitfield a set i8 #1 20
      1) (integer) 0
      127.0.0.1:6379> bitfield a get i8 #0
      1) (integer) 10
      127.0.0.1:6379> bitfield a get i8 #1
      1) (integer) 20
      127.0.0.1:6379> bitfield a incrby i8 #0 40
      1) (integer) 50
      127.0.0.1:6379> bitfield a get i8 #0
      1) (integer) 50
      127.0.0.1:6379>
  • overflow子命令的测试

    用户可以通过overflow子命令结合以下3个参数来决定在执行自增或自减操作时遇到的向上溢出(Overflow)或者向下溢出(Underflow)时的行为。

    • WRAP:使用环绕(Wrap Around)方法处理有符号整数和无符号整数的溢出情况。对无符号整数来说,类似于将始终指针向前拨或向后拨。对有符号整数来说,上溢将导致数字重新从最小的负值开始计算,而下溢将导致数字重新从最大的正数开始计算。比如我们对一个值为127的i8执行加1操作,那么将得到结果-128。在默认情况下,incrby命令使用WRAP来处理溢出情况。
    • SAT:使用饱和计算(Saturation Arithmetic)方法处理溢出情况,也就是说,下溢计算的结果为最小的整数,而上溢计算的结果为最大的整数值。举个例子,如果我们对一个值为120的i8整数执行加10操作,那么命令的结果将为i8类型所能存储的最大整数127。与此相反,如果一个针对i8整数的计算结果造成了下溢,那么这个i8整数将被设置为-127。
    • FAIL:在这一方法下,命令将拒绝执行那些会导致上溢或者下溢情况出现的计算,并向用户返回空值表示计算未被执行。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    127.0.0.1:6379> bitfield a set i8 #0 120
    1) (integer) 0
    127.0.0.1:6379> bitfield a overflow wrap incrby i8 #0 10
    1) (integer) -126
    127.0.0.1:6379> bitfield a get i8 #0
    1) (integer) -126
    127.0.0.1:6379> bitfield b set i8 #0 -120
    1) (integer) 0
    127.0.0.1:6379> bitfield b overflow wrap incrby i8 #0 -10
    1) (integer) 126
    127.0.0.1:6379> bitfield a get i8 #0
    1) (integer) -126
    127.0.0.1:6379>
    127.0.0.1:6379> bitfield c overflow sat incrby i8 #0 10
    1) (integer) 127
    127.0.0.1:6379>
  • bitpos命令

    使用格式如下:

    bitpos key bit [start] [end]

    该命令用于返回设置为1或0的第一个位的索引值。

    注意:参数start、end中的值以B(字节)为单位,而不是bit(位)

    测试案例

    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> setbit key1 0 0
    (integer) 0
    127.0.0.1:6379> setbit key1 1 0
    (integer) 0
    127.0.0.1:6379> setbit key1 2 0
    (integer) 0
    127.0.0.1:6379> setbit key1 3 0
    (integer) 0
    127.0.0.1:6379> setbit key1 4 0
    (integer) 0
    127.0.0.1:6379> setbit key1 5 0
    (integer) 0
    127.0.0.1:6379> setbit key1 6 0
    (integer) 0
    127.0.0.1:6379> setbit key1 7 0 #key1的二级制值是0000000
    (integer) 0
    127.0.0.1:6379> setbit key2 0 1
    (integer) 0
    127.0.0.1:6379> setbit key2 1 1
    (integer) 0
    127.0.0.1:6379> setbit key2 2 1
    (integer) 0
    127.0.0.1:6379> setbit key2 3 1
    (integer) 0
    127.0.0.1:6379> setbit key2 4 1
    (integer) 0
    127.0.0.1:6379> setbit key2 5 1
    (integer) 0
    127.0.0.1:6379> setbit key2 6 1
    (integer) 0
    127.0.0.1:6379> setbit key2 7 1 #key2的二级制值是11111111
    (integer) 0
    127.0.0.1:6379> bitpos key1 0
    (integer) 0
    127.0.0.1:6379> bitpos key1 1
    (integer) -1
    127.0.0.1:6379> bitpos key2 0
    (integer) 8
    127.0.0.1:6379> bitpos key2 1
    (integer) 0
    127.0.0.1:6379>
  • 处理慢查询

    当key对应的value存储大量数据,查询时会减慢Redis的响应速度,导致Redis发生阻塞,最终可能会引起整个Redis服务器不可用,所以要找到那些导致慢查询的有关命令。

    在redis.conf配置文件中,主要有两处与慢查询有关的配置。

    • slowlog-log-slower-than:当命令执行时间(不包括排队时间)超时时会被记录下来,单位是us。如下命令就可以记录执行时间超过30ms的命令。

      config set slowlog-log-slower-than 30000

      上面的这个命令也可以在redis.conf配置文件中进行配置。

    • slowlog-max-len:可以记录慢查询命令的总数。通过以下命令可以记录最近200条慢查询命令。

      config set slowlog-max-len 200

      上面这个命令也可以在redis.conf配置中进行配置。

      可以使用如下两条命令获取慢查询的命令。

      • slowlog get[len]:获取指定长度的慢查询列表。
      • slowlog reset:清空慢查询日志队列。

      测试案例

      1
      2
      3
      127.0.0.1:6379> slowlog get
      (empty array)
      127.0.0.1:6379>

Hash类型命令

Redis中的Hash映射是key和value的映射,其中value包括“field-value对”的映射。

Hash数据类型的存储形式如图:

image-20220325114223843

Hash数据类型保持key-value对结构,它的key-value对个数最多为2^32-1个。Hash数据类型中的key与普通的key一样,具有TTL的功能,但field没有这个功能。

  • hset和hget命令

    hset命令的使用格式如下:

    hset key field value

    该命令作用和Java中的HashMap.put(key,value)方法相似。

    hget命令的使用格式如下:

    hget key field

    该命令作用和Java中的HashMap.get(key)相似。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    127.0.0.1:6379> del key1
    (integer) 1
    127.0.0.1:6379> hget key1 a
    (nil)
    127.0.0.1:6379> hset key1 a aa
    (integer) 1
    127.0.0.1:6379> hget key1 a
    "aa"
    127.0.0.1:6379> hset key1 a aaNewValue
    (integer) 0
    127.0.0.1:6379> hget key1 a
    "aaNewValue"
    127.0.0.1:6379>
  • hmset和hmget命令

    hmset命令使用格式如下:

    hmset key field vlaue [field value….]

    该命令用于批量添加field和value。

    hmget命令使用格式如下;

    hmget key field [field…]

    该命令用于批量获取field对应的value。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> del key1
    (integer) 1
    127.0.0.1:6379> hmset key1 a aa b bb c cc
    OK
    127.0.0.1:6379> hmget key1 a b c
    1) "aa"
    2) "bb"
    3) "cc"
    127.0.0.1:6379>
  • hlen命令

    使用格式如下:

    hlen key

    该命令用于返回field的个数。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> hmset key1 a aa b bb c cc
    OK
    127.0.0.1:6379> hmget key1 a b c
    1) "aa"
    2) "bb"
    3) "cc"
    127.0.0.1:6379> hlen key1
    (integer) 3
    127.0.0.1:6379>
  • hdel命令

    使用格式如下:

    hdel key field [field…]

    该命令用于删除key中的field。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    127.0.0.1:6379> hdel key1 a
    (integer) 1
    127.0.0.1:6379> hmget key1 a b c
    1) (nil)
    2) "bb"
    3) "cc"
    127.0.0.1:6379>
  • hexists命令

    使用格式如下:

    hexists key field

    如果key对应的value中包含指定的field,则返回1;否则返回0。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> hmget key1 a b c
    1) (nil)
    2) "bb"
    3) "cc"
    127.0.0.1:6379> hexists key1 a
    (integer) 0
    127.0.0.1:6379> hexists key1 b
    (integer) 1
    127.0.0.1:6379>
  • hincrby和hincrbyfloat命令

    hincrby命令使用格式如下:

    hincrby key field increment

    该命令用于对field的值进行整数自增increment,field的范围是64bit有符号整数。

    hincrbyfloat命令使用格式如下:

    hincrbyfloat key field increment

    该命令用于对field的值进行浮点数自增increment。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> hset key1 a 123
    (integer) 1
    127.0.0.1:6379> hincrby key1 a 1000
    (integer) 1123
    127.0.0.1:6379> hget key1 a
    "1123"
    127.0.0.1:6379> hincrbyfloat key1 a 0.234
    "1123.23399999999999999"
    127.0.0.1:6379> hget key1 a
    "1123.23399999999999999"
    127.0.0.1:6379>
  • hgetall命令

    使用格式如下:

    hgetall key

    该命令用于取得所有field和对应的value。如果field和value个数很多,则该命令会阻塞Redis服务器。建议field的个数不要超过500。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> hmset key1 a aa b bb c cc
    OK
    127.0.0.1:6379> hgetall key1
    1) "a"
    2) "aa"
    3) "b"
    4) "bb"
    5) "c"
    6) "cc"
    127.0.0.1:6379>
  • hkeys和hvals命令

    hkeys命令使用格式如下:

    hkeys key

    该命令用于取得所有的field,field应该称为hfields更恰当。

    hvals命令的使用格式如下:

    hvals key

    该命令用于取得key对应的所有value

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> hmset key1 a aa b bb c cc
    OK
    127.0.0.1:6379> hkeys key1
    1) "a"
    2) "b"
    3) "c"
    127.0.0.1:6379> hvals key1
    1) "aa"
    2) "bb"
    3) "cc"
    127.0.0.1:6379>
  • hsetnx命令

    使用格式如下:

    hsetnx key field value

    只有当field不存在时,才保存value;如果field不在,则不进行任何操作。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    127.0.0.1:6379> hmset key1 a aa b bb c cc
    OK
    127.0.0.1:6379> hkeys key1
    1) "a"
    2) "b"
    3) "c"
    127.0.0.1:6379> hvals key1
    1) "aa"
    2) "bb"
    3) "cc"
    127.0.0.1:6379> hsetnx key1 a aanew
    (integer) 0
    127.0.0.1:6379> hget key1 a
    "aa"
    127.0.0.1:6379> hsetnx key1 d aanew
    (integer) 1
    127.0.0.1:6379> hget key1 d
    "aanew"
    127.0.0.1:6379>
  • hstrlen命令

    使用格式如下:

    hstrlen key field

    该命令用用户返回field存储的value字符串长度。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    127.0.0.1:6379> hsetnx key1 d aanew
    (integer) 1
    127.0.0.1:6379> hget key1 d
    "aanew"
    127.0.0.1:6379> hstrlen key1 d
    (integer) 5
    127.0.0.1:6379>
  • hscan命令

    使用格式如下:

    hscan key cursor [MATCH pattern] [COUNT count]

    该命令用于以多次迭代的方式将Hash(散列)中的数据取出。

    hscan命令是一个基于游标的迭代器,代表在每次调用次命令时服务器都会返回一个更新的游标值,用户需要使用该游标值作为游标参数才可以执行下一次迭代。当游标值设置为0时开始迭代,当服务器返回的游标值为0时停止迭代。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> hscan key1 0
    1) "0"
    2) 1) "a"
    2) "aa"
    3) "b"
    4) "bb"
    5) "c"
    6) "cc"
    7) "d"
    8) "aanew"
    127.0.0.1:6379>
  • 使用sort命令对散列进行排序

    对散列指定的field进行排序。

    测试案例

    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
    127.0.0.1:6379> rpush uerId 1 2 3
    (integer) 3
    127.0.0.1:6379> hmset hashkey1 name A age 100
    OK
    127.0.0.1:6379> hmset hashkey2 name B age 50
    OK
    127.0.0.1:6379> hmset hashkey3 name C age 1
    OK
    127.0.0.1:6379> sort userId by hashkey*->age
    (empty array)
    127.0.0.1:6379> sort uerId by hashkey*->age
    1) "3"
    2) "2"
    3) "1"
    127.0.0.1:6379> sort uerId by hashkey*->age get hashkey*->name
    1) "C"
    2) "B"
    3) "A"
    127.0.0.1:6379> sort uerId by hashkey*->age get # get hashkey*->name DESC
    1) "1"
    2) "A"
    3) "2"
    4) "B"
    5) "3"
    6) "C"
    127.0.0.1:6379>

List类型命令

List类型命令主要用于处理列表,相当于java中的LinkedList,其插入和删除速度非常快,但根据索引的定位速度就很慢。

以List数据类型存储的元素具有有序性,元素可以重复,可以对列表的头部和尾部进行元素的添加和弹出,可以向前或向后进行双向遍历。

List数据类型可以用作对列:先进先出,具有FIFO特性。

List数据类型可以用作栈,先进后出,具有FILO特性。

List数据类型可以用作任务对列,将需要延后处理的任务放入队列中,使用新的线程按顺序读取列表中任务并进行处理。任务队列可以使用具有阻塞特性的blpop或prpop命令实现。

一个List数据类型最多可以存储2^32-1个元素。

List数据类型的存储形式如图:

image-20220325140342666

  • rpush、llen和lrange命令

    rpush命令的使用格式如下:

    rpush key value [value …]

    该命令用于向队列尾部添加一个或多个元素,类似于Java中的ArrayList.add(object)方法,但是rpush命令一次可以添加多个元素。

    llen命令的使用格式如下:

    llen key

    该命令用于获取列表中元素的个数。

    lrange命令使用格式如下:

    lrange key start stop

    该命令用于使用偏移量返回列表的全部或部分元素,偏移量的值从0开始作为索引值,其中0代表列表的第一个元素,1代表下一个元素,依此类推。

    偏移量也可以是负数,代表从列表尾部开始的偏移量,如-1代表列表最后一个元素,-2代表倒数第二个元素,依此类推。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    127.0.0.1:6379> rpush key1 a b c
    (integer) 3
    127.0.0.1:6379> llen key1
    (integer) 3
    127.0.0.1:6379> rpush key1 d
    (integer) 4
    127.0.0.1:6379> llen key1
    (integer) 4
    127.0.0.1:6379> lrange key1 0 3
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    127.0.0.1:6379>
  • rpushx命令

    使用格式如下:

    rpushx key value

    该命令用于仅在key已经存在并被包括在列表中的情况下,才在列表尾部插入元素。与rpush命令相反,当key不存在时,rpushx命令不执行任何操作。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> lrange key1 0 3
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    127.0.0.1:6379> rpushx key1 e
    (integer) 5
    127.0.0.1:6379> rpushx key2 a
    (integer) 0
    127.0.0.1:6379>
  • lpush命令

    使用格式如下:

    lpush key value [value ….]

    该命令用于向队列头部添加一个或多个元素。

    测试案例

    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> flushdb
    OK
    127.0.0.1:6379> keys *
    (empty array)
    127.0.0.1:6379> rpush key1 a b c
    (integer) 3
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "b"
    3) "c"
    127.0.0.1:6379> lpush key1 3 2 1
    (integer) 6
    127.0.0.1:6379> lrange key1 0 -1
    1) "1"
    2) "2"
    3) "3"
    4) "a"
    5) "b"
    6) "c"
    127.0.0.1:6379>
  • lpushx命令

    使用格式如下:

    lpush key value

    该命令用于仅在key已经存在并且包括在列表中的情况下,才在列表头部插入元素。与lpush命令相反,当key不存在时,lpushx命令将不执行任何操作。

    测试案例

    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> del key1
    (integer) 1
    127.0.0.1:6379> lpushx key1 3 2 1
    (integer) 0
    127.0.0.1:6379> lpush key1 3 2 1
    (integer) 3
    127.0.0.1:6379> lrange key1 0 -1
    1) "1"
    2) "2"
    3) "3"
    127.0.0.1:6379> lpushx key1 c b a
    (integer) 6
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "1"
    5) "2"
    6) "3"
    127.0.0.1:6379>
  • rpop命令

    使用格式如下:

    rpop key

    该命令用于删除并返回存于key对应的列表的最后一个元素。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "1"
    5) "2"
    6) "3"
    127.0.0.1:6379> rpop key1
    "3"
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "1"
    5) "2"
    127.0.0.1:6379>
  • lpop命令

    使用格式如下:

    lpop key

    该命令用于删除并且返回存于key对应列表的第一个元素。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "1"
    5) "2"
    127.0.0.1:6379> rpop key1
    "2"
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "1"
    127.0.0.1:6379>
  • rpoplpush命令

    使用格式如下:

    rpoplpush source destination

    该命令用于原子性的返回并删除存储在源列表的最后一个元素,并且返回的元素存储在目标列表的第一个位置。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    127.0.0.1:6379> del key1
    (integer) 1
    127.0.0.1:6379> del key2
    (integer) 0
    127.0.0.1:6379> rpush key1 a b c
    (integer) 3
    127.0.0.1:6379> rpush key2 1 2 3
    (integer) 3
    127.0.0.1:6379> rpoplpush key1 key2
    "c"
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "b"
    127.0.0.1:6379> lrange key2 0 -1
    1) "c"
    2) "1"
    3) "2"
    4) "3"
    127.0.0.1:6379>
  • lrem命令

    使用格式如下:

    lrem key count value

    该命令用于从key对应的列表里删除前count次出现的值为value的元素。

    参数count可以有如下几种用法:

    • count>0:从头到尾删除值为value的count个元素。
    • count<0:从尾到头删除值为value的count个元素。
    • count=0:删除所有值为value的元素

    使用如下命令会从列表中删除最后出现的两个hello元素:

    lrem list -2 “hello”

    lrem 命令删除时采用绝对等于方式

    测试案例

    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
    127.0.0.1:6379> rpush mykey a hello b hello c hello d hello e hello f hello g hello
    (integer) 14
    127.0.0.1:6379> llen mykey
    (integer) 14
    127.0.0.1:6379> lrem mykey 3 hello
    (integer) 3
    127.0.0.1:6379> lrange mykey 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    5) "hello"
    6) "e"
    7) "hello"
    8) "f"
    9) "hello"
    10) "g"
    11) "hello"
    127.0.0.1:6379> lrem mykey -2 hello
    (integer) 2
    127.0.0.1:6379> lrange mykey 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    5) "hello"
    6) "e"
    7) "hello"
    8) "f"
    9) "g"
    127.0.0.1:6379> lrem mykey 0 hello
    (integer) 2
    127.0.0.1:6379> lrange mykey 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    5) "e"
    6) "f"
    7) "g"
  • lset命令

    使用格式如下:

    lset key index value

    该命令用于在指定index处放置的元素,相当于新元素更新旧元素。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    127.0.0.1:6379> del key1
    (integer) 1
    127.0.0.1:6379> rpush key1 1 2 3
    (integer) 3
    127.0.0.1:6379> lrange key1 0 -1
    1) "1"
    2) "2"
    3) "3"
    127.0.0.1:6379> lset key1 0 a
    OK
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "2"
    3) "3"
    127.0.0.1:6379>
  • ltrim命令

    使用格式如下:

    ltrim key start stop

    该命令类似于String.substring()方法。

    参数start和stop也可以是负数,代表列表末尾的偏移量,其中-1代表列表的最后一个元素。

    ltrim 命令一个常见的用法是和rpush/lush命令使用,使用格式如下:

    lpush mylist someelement

    ltrim mylist 0 99

    这对命令对将一个新的元素添加到列表头部,并保证该列表不会增长到超过100个元素,如果创建一个有界队列时会用到它们。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> del key1
    (integer) 1
    127.0.0.1:6379> rpush key1 a b c d
    (integer) 4
    127.0.0.1:6379> ltrim key1 0 2
    OK
    127.0.0.1:6379> lrange key1 0 -1
    1) "a"
    2) "b"
    3) "c"
    127.0.0.1:6379>
  • linsert命令

    使用格式如下:

    linsert key BEFORE|AFTER pivot value

    该命令用于在列表中的pivot元素之前(BEFORE)或之后(AFTER)插入元素。

    测试案例

    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
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> rpush mylist a b c c c d
    (integer) 6
    127.0.0.1:6379> linsert mylist before c x
    (integer) 7
    127.0.0.1:6379> lrange mylist 0 -1
    1) "a"
    2) "b"
    3) "x"
    4) "c"
    5) "c"
    6) "c"
    7) "d"
    127.0.0.1:6379> linsert mylist after a AA
    (integer) 8
    127.0.0.1:6379> lrange mylist 0 -1
    1) "a"
    2) "AA"
    3) "b"
    4) "x"
    5) "c"
    6) "c"
    7) "c"
    8) "d"
    127.0.0.1:6379>
  • lindex命令

    使用格式如下:

    lindex key index

    该命令用于返回列表中指定index的元素

    测试案例

    1
    2
    3
    4
    5
    6
    7
    127.0.0.1:6379> rpush key1 a b c d
    (integer) 4
    127.0.0.1:6379> lindex key1 2
    "c"
    127.0.0.1:6379> lindex key1 -1
    "d"
    127.0.0.1:6379>
  • blpop命令

    如果使用列表来实现任务队列,那么当队列中没有任务时,客户端需要程序员使用轮询的方式来判断列表中有没有新元素,如果没有则继续轮询。这会造成空运行并且占用CPU资源,这种情况下可以使用阻塞BLOCK版本的pop命令解决。

    使用格式如下:

    blpop key [key …] timeout

    它是lpop命令的阻塞版本。

    使用阻塞版本的相关操作时,如果阻塞的时间过长,Linux会强制断开闲置的网络连接,释放网络资源。当被强制断开连接时会出现异常,所以要将catch和重试机制结合使用。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> lpush key2 a
    (integer) 1
    127.0.0.1:6379> blpop key2 5
    1) "key2"
    2) "a"
    127.0.0.1:6379> blpop key2 5
    (nil)
    (5.05s)
    127.0.0.1:6379>
  • brpop命令

    使用格式如下:

    brpop key [key …] timeout

    brpop命令是rpop命令的阻塞版本,删除并返回存于列表的最后一个元素。从功能上分析,brpop命令和blpop命令基本是一样的,只不过一个是从尾部弹出元素,而另外一个是从头部弹出元素。

  • brpoplpush命令

    使用格式如下:

    brpoplpush source destination timeout

    brpoplpush命令是rpoplpush命令的阻塞版本,把最后一个元素转移到其他列表的第一个位置。

    如果源列表和目标列表是同一个,则该命令可以实现循环链表。

  • 使用sort命令对列表进行排序

    使用格式如下:

    sort key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC|DESC] [ALPHA] [STORE destination]

    默认是按照数值类型排序的,并且按照两个元素的双精度浮点数进行比较。

    测试案例

    按数字大小进行正/倒排序

    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
    127.0.0.1:6379> rpush key1 13 234 345 324 23 345 4 67 345 8765 6
    (integer) 11
    127.0.0.1:6379> sort key1
    1) "4"
    2) "6"
    3) "13"
    4) "23"
    5) "67"
    6) "234"
    7) "324"
    8) "345"
    9) "345"
    10) "345"
    11) "8765"
    127.0.0.1:6379> sort key1 desc
    1) "8765"
    2) "345"
    3) "345"
    4) "345"
    5) "324"
    6) "234"
    7) "67"
    8) "23"
    9) "13"
    10) "6"
    11) "4"
    127.0.0.1:6379>

    按ASCII值进行正/到排序

    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
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> rpush key1 a a few r erth afd ad dfasd ga dgd egd
    (integer) 11
    127.0.0.1:6379> sort key1 alpha
    1) "a"
    2) "a"
    3) "ad"
    4) "afd"
    5) "dfasd"
    6) "dgd"
    7) "egd"
    8) "erth"
    9) "few"
    10) "ga"
    11) "r"
    127.0.0.1:6379> sort key1 alpha desc
    1) "r"
    2) "ga"
    3) "few"
    4) "erth"
    5) "egd"
    6) "dgd"
    7) "dfasd"
    8) "afd"
    9) "ad"
    10) "a"
    11) "a"
    127.0.0.1:6379>
  • List类型命令的常见使用模式

    List类型命令的常见使用模式如下:

    • 使用lpush+brpop实现阻塞队列。
    • 使用lpush+rpop实现非阻塞队列。
    • 使用lpush+lpop实现栈。
    • 使用lpush+ltrem实现有界队列。

Set类型命令

Set类型命令主要用于处理Set数据类型,Set数据类型的存储形式如图:

image-20220325152656228

和Java中的Set接口一样,Redis中的Set数据类型不允许存储重复的元素,存储元素具有无序性。Set数据类型的元素个数最多为$2^{32}$-1个。

  • sadd、smembers和scard命令

    sadd命令使用格式如下:

    sadd key member [member …]

    该命令的作用和Java中的HashSet.add(value)方法一致。

    smembers命令使用格式如下:

    smembers key

    该命令用于返回所有的value。

    scard命令使用格式如下:

    scard key

    该命令用于返回元素个数。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    127.0.0.1:6379> sadd ke1 a a b d x b d e f
    (integer) 6
    127.0.0.1:6379> smembers ke1
    1) "b"
    2) "d"
    3) "x"
    4) "a"
    5) "e"
    6) "f"
    127.0.0.1:6379> scard ke1
    (integer) 6
    127.0.0.1:6379>
  • sdiff和sdiffstore命令

    sdiff命令的使用格式如下:

    sdiff key [key …]

    该命令用于返回只有第一个key具有,而其他key不具有的元素。

    sdiffstore命令的使用格式如下:

    sdiffstore destination key [key …]

    该命令作用和sdiff命令基本一样,不同之处是将第一个key的独有元素放入目标key中。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    127.0.0.1:6379> sadd key1 a b c x y z
    (integer) 6
    127.0.0.1:6379> sadd key2 o p q
    (integer) 3
    127.0.0.1:6379> sadd key3 a b c x
    (integer) 4
    127.0.0.1:6379> sdiff key1 key2 key3
    1) "z"
    2) "y"
    127.0.0.1:6379> sdiffstore showkey1 key1 key2 key3
    (integer) 2
    127.0.0.1:6379> smembers showkey1
    1) "z"
    2) "y"
    127.0.0.1:6379>
  • sinter和sinterstore命令

    sinter命令的使用格式如下:

    sinter key [key …]

    该命令用于取得指定key共同交集的value

    sinterstore命令使用格式如下:

    sinterstore destination key [key …]

    该命令作用和sinter命令不同之处是将交集的value放入目标key中。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    127.0.0.1:6379> sadd key1 a b c x
    (integer) 4
    127.0.0.1:6379> sadd key2 o p q x
    (integer) 4
    127.0.0.1:6379> sadd key3 a u v x
    (integer) 4
    127.0.0.1:6379> sinter key1 key2 key3
    1) "x"
    127.0.0.1:6379> sinterstore key4 key1 key2 key3
    (integer) 1
    127.0.0.1:6379> smembers key4
    1) "x"
    127.0.0.1:6379>
  • sismember

    使用格式如下:

    sismember key member

    该命令用于判断元素是否在集合中。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> sadd key1 a b c
    (integer) 3
    127.0.0.1:6379> sismember key1 a
    (integer) 1
    127.0.0.1:6379> sismember key1 b
    (integer) 1
    127.0.0.1:6379> sismember key1 c
    (integer) 1
    127.0.0.1:6379> sismember key1 d
    (integer) 0
    127.0.0.1:6379>
  • smove命令

    使用格式如下:

    smove source destinarion member

    该命令用于将元素从source(源集合)移动到destination(目标集合)。

    如果source不存在或不包括指定的元素,则不执行任何操作,并返回0;否则,元素将从source中被删除并添加到destination中。

    当指定的元素已存在于destination中时,将从source中删除。如果source或destination不是集合类型,则返回错误。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    127.0.0.1:6379> del key1
    (integer) 1
    127.0.0.1:6379> sadd key1 a b c
    (integer) 3
    127.0.0.1:6379> sadd key2 1 2 3
    (integer) 3
    127.0.0.1:6379> smove key1 key2 b
    (integer) 1
    127.0.0.1:6379> smembers key1
    1) "c"
    2) "a"
    127.0.0.1:6379> smembers key2
    1) "3"
    2) "2"
    3) "1"
    4) "b"
    127.0.0.1:6379>
  • srandmember命令

    使用格式如下:

    srandmember key [count]

    只提供key参数时会随机获取key集合中的某一个元素,和spop命令作用类似。不同的是,spop命令会将获取的随机元素从集合中移除,而srandmember命令仅仅获取该随机元素,不做任何的操作,包括删除。

    count参数的作用如下:

    • 如果count是整数且小于元素的个数,则获取含有count个不同的元素的数组。
    • 如果count是整数且大于元素的个数,则获取整个集合的所有元素。
    • 如果count是负数,则获取包括count绝对值个数的元素的数组;如果count的绝对值大于元素的个数,则获取的数组里会出现元素重复的情况。

    测试案例

    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> sadd key1 1 2 3 4 5 6 7 8 9 10
    (integer) 10
    127.0.0.1:6379> srandmember key1
    "8"
    127.0.0.1:6379> srandmember key1
    "2"
    127.0.0.1:6379> srandmember key1
    "4"
    127.0.0.1:6379> srandmember key1 5
    1) "3"
    2) "4"
    3) "1"
    4) "6"
    5) "8"
    127.0.0.1:6379> srandmember key1 -4
    1) "3"
    2) "3"
    3) "1"
    4) "2"
    127.0.0.1:6379>
  • spop命令

    使用格式如下:

    spop key [count]

    该命令与srandmember命令功能相似,只不过spop命令要将随机获取的元素删除掉。

    参数count不允许为负数,不然会出现如下异常:

    ERR index out of range

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    127.0.0.1:6379> sadd key1 a b c x s g w
    (integer) 7
    127.0.0.1:6379> spop key1
    "a"
    127.0.0.1:6379> smembers key1
    1) "c"
    2) "x"
    3) "b"
    4) "w"
    5) "g"
    6) "s"
    127.0.0.1:6379>
  • srem命令

    使用格式如下:

    srem key member [member …]

    该命令用于从集合中删除指定的元素。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> sadd mySet 1 2 3 5 6 f  f sd
    (integer) 7
    127.0.0.1:6379> srem mySet f 5
    (integer) 2
    127.0.0.1:6379> smembers mySet
    1) "2"
    2) "1"
    3) "sd"
    4) "3"
    5) "6"
    127.0.0.1:6379>
  • sunion和sunionstore命令

    sunion命令使用格式如下:

    sunion key [key …]

    该命令用于合并所有key中的元素,并去掉重复的元素。

    sunionstore命令的使用格式如下:

    sunionstrore destination key [key …]

    该命令用于合并所有key中的元素,去掉重复的元素,并将结合后的元素存入目标key中。

    测试案例

    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
    127.0.0.1:6379> sadd key1 a b c
    (integer) 3
    127.0.0.1:6379> sadd key2 1 2 3
    (integer) 3
    127.0.0.1:6379> sunion key1 key2
    1) "a"
    2) "b"
    3) "2"
    4) "c"
    5) "1"
    6) "3"
    127.0.0.1:6379> smembers key1
    1) "c"
    2) "a"
    3) "b"
    127.0.0.1:6379> smembers key2
    1) "1"
    2) "2"
    3) "3"
    127.0.0.1:6379> sunionstore key3 key1 key2
    (integer) 6
    127.0.0.1:6379> smembers key3
    1) "a"
    2) "b"
    3) "2"
    4) "c"
    5) "1"
    6) "3"
    127.0.0.1:6379>
  • sscan命令

    使用格式如下:

    sscan key cursor [MATCH pattern] [COUNT count]

    该命令用于增量迭代。

    测试案例

    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> sadd setkey a b c d e g f h i j k l m n
    (integer) 14
    127.0.0.1:6379> sscan setkey 0
    1) "5"
    2) 1) "n"
    2) "d"
    3) "e"
    4) "k"
    5) "a"
    6) "f"
    7) "m"
    8) "l"
    9) "c"
    10) "h"
    127.0.0.1:6379> sscan setkey 3
    1) "0"
    2) 1) "b"
    2) "i"
    3) "g"
    127.0.0.1:6379>

Sorted Set数据类型

Sorted Set数据类型和Java中的LinkedHashSet类特性一致。

Sorted Set数据类型的存储形式如图:

Sorted Set数据类型中的元素根据分数(score)进行排序,并不像LinkedHashSet以添加的顺序作为排序依据,因此适合排行耪的场景。

Sorted Set数据类型中的元素以score的大小默认按升序的方式进行排序。因为存放在集合中,所以同一元素只存一次,不允许重复的元素存在。

可以用整数来表示score,因为Redis中的Sorted Set数据类型使用双精度64bit浮点数来表示score ,所以它能够精确地表示$-2^{53}$~$2^{53}$的整数。sorted set数据类型的元素最多为$2^{32}$-1个。

  • zadd、zrange和zrevrange命令

    zadd命令的使用格式如下:

    zadd key [NX|XX] [ch] [incr] score member [score member …]

    该命令用于将所有指定的元素添加到与key关联的有序集合里。添加时可以指定多个分数-元素(score-member)对。如果添加的元素已经是有序集合里面的元素,则会更新元素的score,并更到正确的排序位置。

    如果key不存在,那么将创建一个新的有序集合并将score-member对添加到有序集合中。如果key存在,但是存储的类型不是有序集合将会返回一个错误信息。

    score是一个双精度的浮点数字符串,正数最大值可以使用+inf作为代替,负数最小值可以使用-inf作为代替。

    zadd命令用于在key和score-member对之间加入NX、XX、ch、incr参数,参数解释如下:

    • NX:不存在时才更新。
    • XX:存在时才更新。
    • ch:ch是Changed的缩写。ch参数作用是返回新添加的新元素个数和已更新Score的已存在元素个数之和。命令行中指定的score和有序结合中拥有相同score的元素则不会计算在内。注意:zadd命令只返回新添加的新元素的个数。
    • incr:当zadd命令指定这个参数时,等同于zincrby命令,可以对元素的score进行递增操作。但同时只能对一个score-member进行自增操作。使用incr参数将返回元素的新score,用字符串来表示一个双精度浮点数。

    参数NX|XX、ch、incr之间可以联合使用。

    zrange命令的使用格式如下:

    zrange key start stop [withscores]

    该命令用于返回与key关联的有序集合中指定索引范围的元素,不是score范围。元素时按从低到高的score顺序进行排序的。

    当需要从高到低进行排序时,请参考zrevrange命令。

    start和stop都是从0开始的索引,其中0代表第一个元素,1代表下一个元素,依此类推。它们也可以是负数,如-1代表有序集合中的最后一个元素,-2代表倒数第二个元素,依此类推。

    可以使用withscores参数,以便将元素的score与元素值一起返回。

    zrevrange命令的使用格式如下:

    zrevrange key start stop [withscores]

    zrevrange命令是zrange命令的倒叙版本。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> zadd zset 1 a
    (integer) 1
    127.0.0.1:6379> zadd zset 2 b
    (integer) 1
    127.0.0.1:6379> zadd zset 100 z
    (integer) 1
    127.0.0.1:6379> zadd zset 3 c
    (integer) 1
    127.0.0.1:6379> zrange zset 0 -1
    1) "a"
    2) "b"
    3) "c"
    4) "z"
    127.0.0.1:6379>
  • zcard命令

    使用格式如下:

    zcard key

    该命令用于返回有序集合中元素的个数。

    测试案例

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> zadd key1 1 a 2 b 3 c 4 d 5 e
    (integer) 5
    127.0.0.1:6379> zcard key1
    (integer) 5
    127.0.0.1:6379> zrange key1

  • zcount命令

    使用格式如下:

    zcount key min max

    该命令用于返回score在min和max之间的元素个数。常量值-inf代表最小值,+inf代表最大值。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> zadd key1 1 a
    (integer) 1
    127.0.0.1:6379> zadd key1 2 b
    (integer) 1
    127.0.0.1:6379> zadd key1 3 c
    (integer) 1
    127.0.0.1:6379> zcount key1 1 3
    (integer) 3
    127.0.0.1:6379> zcount key1 1 2
    (integer) 2
    127.0.0.1:6379>
  • zunionstore命令

    使用格式如下:

    zunionstore destination numkeys key [key …] [weights weight [weight …]] [aggregate sum|min|max]

    该命令用于对多个key进行合并。

    参数weights和aggregate可以同时使用。

    numkeys指合并的key的数量。

    weights操作时对应key*weight。

    测试案例

    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> del key1
    (integer) 1
    127.0.0.1:6379> del key2
    (integer) 0
    127.0.0.1:6379> del key3
    (integer) 0
    127.0.0.1:6379> zadd key1 1 a 2 b 3 c
    (integer) 3
    127.0.0.1:6379> zadd key2 1 a 2 b 4 d
    (integer) 3
    127.0.0.1:6379> zunionstore key3 2 key1 key2
    (integer) 4
    127.0.0.1:6379> zrange key3 0 -1 withscores
    1) "a"
    2) "2"
    3) "c"
    4) "3"
    5) "b"
    6) "4"
    7) "d"
    8) "4"
    127.0.0.1:6379>
  • zinterstore命令

    使用格式如下:

    zinterstore destination numkeys key [key …] [weights weight [weight …]] [aggregate sum|min|max]

    该命令用于对多个key进行计算而获得交集。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    127.0.0.1:6379> zadd key1 1 a 2 b 3 c 4 d
    (integer) 4
    127.0.0.1:6379> zadd key2 1 a 2 b 3 c 5 e
    (integer) 4
    127.0.0.1:6379> zinterstore key3 2 key1 key2
    (integer) 3
    127.0.0.1:6379> zrange key3 0 -1
    1) "a"
    2) "b"
    3) "c"
    127.0.0.1:6379> zrange key3 0 -1 withscores
    1) "a"
    2) "2"
    3) "b"
    4) "4"
    5) "c"
    6) "6"
    127.0.0.1:6379>
  • zrangebylex、zrevrangebylex和zremrangebylex命令

    zrange命令按索引的范围查询出元素和score,命令实例如下。

    zrange key1 0 -1 withscores

    如果想按元素的字典顺序进行查询,则需要使用zrangebylex命令,命令示例如下。

    zrangebylex key1 a x

    上面命令的作用是查询出a~x的数据(包括a和x)

    zrangebylex命令的使用格式如下:

    zrangebylex key min max [limit offset count]

    当插入有序集合中的所有元素具有相同的score时,该命令按字典顺序从有序集合中返回最小值和最大值之间的元素。注意:此命令在使用时一定要确保score相同,否则达不到预期效果。

    如果在字符串开头有部分字符串相同,则较长的字符串被认为大于较短的字符串。

    有效的start和stop必须以符号“(”或“[”开始。符号“(”代表排除,符号“[”代表包括。而特殊参数“+”和“-”代表正无限和负无限,命令实例如下:

    zrangebylex myzset - +

    如果所有元素都具有相同的score,则返回有序集合中的所有元素。

    zrevrangebylex命令的使用格式如下:

    zrevrangebylex key max min [limit offset count]

    zrevrangebylex命令是zrangebylex命令的倒序版本。

    zremrangebylex命令的使用格式如下:

    zremrangebylex key min max

    zremrangebylex命令是zrangebylex命令的删除版本。

  • zlexcount命令

    使用格式如下:

    zlexcount key min max

    该命令与zrangebylex命令类似,只不过zrangebylex命令查询的是元素,而zlexcount命令查询的是元素的个数。

    测试案例

    1
    2
    3
    4
    5
    127.0.0.1:6379> zrangebylex key3 [a [c
    1) "a"
    2) "b"
    3) "c"
    127.0.0.1:6379>
  • zrangebyscore、zrevrangebyscore和zremrangebyscore命令

    zrangebyscore命令的使用格式如下:

    zrangebyscore key min max [withscores] [limit offset count]

    按score范围从有序集合中取得元素,范围包括min和max的值。如果不想具有包括功能,则要使用“(”。参数min和max可以使用-inf和+inf代替,代表最小值和最大值。

    zrevrangebyscore命令的使用格式如下:

    zrevrangebyscore key max min [withscores] [limit offset count]

    zrevrangebyscore命令是zrangebyscore命令的倒序版本。

    zremrangebyscore命令的使用格式如下:

    zremrangebyscore key min max

    zremrangebyscore命令是zrangebyscore命令的删除版本。

    测试案例

    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
    127.0.0.1:6379> zadd key1 1 a 11 b 111 c 111 d 2 e 3 f 33 g
    (integer) 7
    127.0.0.1:6379> zrange key1 0 -1 withscores
    1) "a"
    2) "1"
    3) "e"
    4) "2"
    5) "f"
    6) "3"
    7) "b"
    8) "11"
    9) "g"
    10) "33"
    11) "c"
    12) "111"
    13) "d"
    14) "111"
    127.0.0.1:6379> zrangebyscore key1 1 5 withscores
    1) "a"
    2) "1"
    3) "e"
    4) "2"
    5) "f"
    6) "3"
    127.0.0.1:6379> zrangebyscore key1 -inf +inf withscores
    1) "a"
    2) "1"
    3) "e"
    4) "2"
    5) "f"
    6) "3"
    7) "b"
    8) "11"
    9) "g"
    10) "33"
    11) "c"
    12) "111"
    13) "d"
    14) "111"
  • zpopmax和zpopmin命令

    zpopmax命令的使用格式如下:

    zpopmax key [count]

    该命令用于删除并且返回最多count个score值最大的元素。

    zpopmin key [count]

    该命令用于删除并且返回最多count个score值最小的元素。

    测试案例

    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
    57
    58
    59
    60
    61
    127.0.0.1:6379> zadd key1 1 a 2 b 3 c 4 d 5 e 6 f 7 g 8 h 9 i
    (integer) 9
    127.0.0.1:6379> zrange key1 0 -1 withscores
    1) "a"
    2) "1"
    3) "b"
    4) "2"
    5) "c"
    6) "3"
    7) "d"
    8) "4"
    9) "e"
    10) "5"
    11) "f"
    12) "6"
    13) "g"
    14) "7"
    15) "h"
    16) "8"
    17) "i"
    18) "9"
    127.0.0.1:6379> zpopmax key1
    1) "i"
    2) "9"
    127.0.0.1:6379> zrange key1 0 -1 withscores
    1) "a"
    2) "1"
    3) "b"
    4) "2"
    5) "c"
    6) "3"
    7) "d"
    8) "4"
    9) "e"
    10) "5"
    11) "f"
    12) "6"
    13) "g"
    14) "7"
    15) "h"
    16) "8"
    127.0.0.1:6379> zpopmax key1 2
    1) "h"
    2) "8"
    3) "g"
    4) "7"
    127.0.0.1:6379> zpopmin key1 2
    1) "a"
    2) "1"
    3) "b"
    4) "2"
    127.0.0.1:6379> zrange key1 0 -1 withscores
    1) "c"
    2) "3"
    3) "d"
    4) "4"
    5) "e"
    6) "5"
    7) "f"
    8) "6"
    127.0.0.1:6379>
  • bzpopmax和bzpopmin命令

    bzpopmax命令的使用格式如下:

    bzpopmax key [key …] timeout

    该命令是zpopmax命令的阻塞版本。

    bzpopmin命令的使用格式如下:

    bzpopmin key [key …] timeout

    该命令是zpopmin命令的阻塞版本。

    timeout参数为0,表示永远等待。

  • zrank、zrevrank和zremrangebyrank命令

    zrak命令的使用格式如下:

    zrank key member

    该命令用于取得元素在有序集合中的排名。排名以0为开始,相当于索引。

    zrevrank命令的使用格式如下:

    zrevrank key member

    该命令是zrank命令的倒序版本。

    zremrangebyrank命令的使用格式如下:

    zremrangebyrank key start stop

    该命令用于删除指定范围中的元素。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> zadd key1 11 a 22 b 33 c 44 d 55 e
    (integer) 5
    127.0.0.1:6379> zrank key1 b
    (integer) 1
    127.0.0.1:6379> zrank key1 c
    (integer) 2
    127.0.0.1:6379> zrevrank key1 b
    (integer) 3
    127.0.0.1:6379> zremrangebyrank key1 0 2
    (integer) 3
    127.0.0.1:6379>
  • zrem命令

    使用格式如下:

    zrem key member [member …]

    该命令用于删除指定的元素。

  • zscore命令

    使用格式如下:

    zscore key member

    该命令用于返回元素的score。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> flushdb 
    OK
    127.0.0.1:6379> zadd key1 1 a 2 b 3 c 40 d 50 e
    (integer) 5
    127.0.0.1:6379> zscore key1 b
    "2"
    127.0.0.1:6379> zscore key1 e
    "50"
    127.0.0.1:6379>
  • zscan命令

    使用格式如下:

    zscan key cursor [MATCH pattern] [COUNT count]

    该命令用于增量迭代。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    127.0.0.1:6379> zadd key1 1 a 2 b 3 c 40 d 50 e
    (integer) 5
    127.0.0.1:6379> zscan key1 0
    1) "0"
    2) 1) "a"
    2) "1"
    3) "b"
    4) "2"
    5) "c"
    6) "3"
    7) "d"
    8) "40"
    9) "e"
    10) "50"
    127.0.0.1:6379>
  • sort命令

    使用sort命令对有序集合排序时,只针对value进行排序,而不针对score。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> zadd key1 1 a 2 b 3 c 40 d 50 e
    (integer) 5
    127.0.0.1:6379> sort key1 alpha
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    5) "e"
    127.0.0.1:6379>

Key类型命令

Key类型命令主要用于处理key。

  • del和exists命令

    del命令的使用格式如下:

    del key [key …]

    该命令用用户删除给定的一个或多个key。不存在的key会被忽略。

    返回值是被删除key的数量。

    exists命令的使用格式如下:

    exists key

    该命令用于判断给定的key是否存在。

    如果key存在,则返回1;否则返回0。

  • unlink命令

    使用格式如下:

    unlink key [key …]

    此命令与del命令非常相似,功能也是删除指定的key。

    如果key不存在,就会忽略。但是,该命令在不同的线程中执行并实现内存回收,因此 它不会阻塞,而del命令会阻塞。

    unlink命令只断开key与value的关联,实际的删除操作是以异步的方式进行执行的。

    执行成功后返回为断开关联的key的数量。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    127.0.0.1:6379> clear
    127.0.0.1:6379> set key1 keyvalue
    OK
    127.0.0.1:6379> set key2 key2value
    OK
    127.0.0.1:6379> unlink key1 key2
    (integer) 2
    127.0.0.1:6379> get key1
    (nil)
    127.0.0.1:6379> exists key1
    (integer) 0
    127.0.0.1:6379>

  • rename指令

    使用格式如下:

    rename key newkey

    该命令用于对key进行重命名,当key不存在时返回错误。如果newkey已经存在,则最终使用key对应的值。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    127.0.0.1:6379> set key1 key1value
    OK
    127.0.0.1:6379> set key2 key2value
    OK
    127.0.0.1:6379> get key1
    "key1value"
    127.0.0.1:6379> get key2
    "key2value"
    127.0.0.1:6379> rename key1 key2
    OK
    127.0.0.1:6379> get key1
    (nil)
    127.0.0.1:6379> get key2
    "key1value"
    127.0.0.1:6379>
  • renamenx命令

    使用格式如下:

    renamenx key newkey

    如果newkey不存在,则将key重命名为newkey;如果newkey存在,则取消操作,当key不存在时它返回错误。

    返回1代表成功对key进行重命名。如果newkey已存在,则返回0。

  • keys命令

    使用格式如下:

    keys pattern

    该命令用于返回匹配的所有key列表。

    • 测试搜索模式:?

      h?llo可以匹配hello、hallo或hxllo,“?”代表一个字符

    • 测试搜索模式:*

      h*llo匹配hllo和heeeello,“*”代表任意个数的字符。

    • 测试搜索模式:[]

      h[ae]llo匹配hallo和hello,但不匹配hillo,“[]”中的内容之间有”或“关系,只匹配其中的一个字符。

    • 测试搜索模式:[^]

      h[^e]llo匹配hallo和hbllo等,但不匹配hello。

    • 测试搜索模式:[a-b]

      h[a-b]llo匹配hallo和hbllo。

  • type命令

    使用格式如下:

    type key

    该命令用于获取key的value的数据类型,常见的数据类型有String、List、Set、Sonted、Set和Hash。

  • randomkey命令

    使用格式如下:

    randomkey

    该命令用于随机返回key。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    127.0.0.1:6379> set key1 value1
    OK
    127.0.0.1:6379> set key2 value2
    OK
    127.0.0.1:6379> set key3 value3
    OK
    127.0.0.1:6379> set key4 value4
    OK
    127.0.0.1:6379> set key5 value5
    OK
    127.0.0.1:6379> randomkey
    "key1"
    127.0.0.1:6379> randomkey
    "key5"
    127.0.0.1:6379> randomkey
    "key3"
    127.0.0.1:6379> randomkey
    "key5"
    127.0.0.1:6379>
  • dump和restore命令

    dump命令使用格式如下:

    dump key

    该命令用于序列化指定key对应的值,通常将序列化值作为备份数据。

    序列化值有以下几个特点。

    • 它带有64位校验和,用于检测错误和验证数据有效性,在进行反序列化之前会先检查校验和的有效性。
    • 序列化值得编码格式和RDB文件保持一致。
    • RDB版本号会被编码在序列化值当中,如果由于Redis得版本不同造成RDB编码格式不兼容,那么Redis会拒绝对序列化值进行反序列化操作。
    • 序列化值不包括TTL信息。

    如果key不存在,则返回nil;否则,返回序列化值。

    restore命令的使用格式如下:

    restore key ttl serialized-value [replace]

    使用restore命令可以对序列化值进行反序列化,并将结果保存到当前Redis或其他Redis实例中,相当于还原数据。参数ttl以ms位单位,代表key的TTL,如果ttl为0,那么不设置TTL。

    restore命令在执行反序列化之前会先对序列化值得RDB版本号和校验和进行检查,如果RDB版本号不相同或者数据不完整得话,那么restore命令会拒绝进行反序列化,并返回一个错误。

    如果反序列化成功,则返回OK;否则,返回一个错误。

    dump和restore命令为非原子性命令。

    为什么不使用get和set命令实现数据得备份和还原呢?

    因为使用get命令获取的数据可能被恶意或非恶意地改动,造成欲还原地数据被破坏。可以使用dump和restore命令解决这个问题,因为restore命令在还原数据时要对校验和进行检查,不通过检查不执行还原操作。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    127.0.0.1:6379> set a aa
    OK
    127.0.0.1:6379> get a
    "aa"
    127.0.0.1:6379> dump a
    "\x00\x02aa\t\x00\x04\x92\xc5P\x1e\x7f\xeb\x93"
    127.0.0.1:6379> del a
    (integer) 1
    127.0.0.1:6379> keys *
    (empty array)
    127.0.0.1:6379> restore a 0 "\x00\x02aa\t\x00\x04\x92\xc5P\x1e\x7f\xeb\x93"
    OK
    127.0.0.1:6379> keys *
    1) "a"
    127.0.0.1:6379> get a
    "aa"
    127.0.0.1:6379>
    • 如果key已经存在,并且设置了replace参数,那么使用反序列化值来代替key原有地值。如果key已经存在,但是没有设置replace参数,那么该命令将返回一个错误。
  • expire和ttl命令

    expire命令的使用格式如下:

    expire key seconds

    注意:seconds参数是当前时间之后的秒数。

    expire命令用于在key上设置TTL,超时后key将自动删除。

    超时效果可以被删除,也可以被保持,具体如下。

    • 删除超时的效果可以使用del、set、getset和所有*store命令。使用RERSIST命令将key重新转换为永久key也可以删除超时效果。
    • 保持超时的效果可以使用”能改变“key中value的相关命令。如使用incr命令对旧value进行增加,使用lpush命令对旧value添加新的元素,或者使用hset命令改变一个散列的字段的旧value都会使超时效果保持不变。如果使用rename命令对key进行重命名,则原有的TTL将转移到新的key中。

    对一个已经拥有TTL的key再次执行expire命令时,会对该key重新设置新的TTL。

    ttl命令的使用格式如下:

    ttl key

    ttl命令的作用时返回具有ttl的key的剩余生存时间。

    如果key不存在,该命令将返回-2;如果key存在,但是没有设置TTL,则该命令返回-1.

    删除TTL请使用persist命令。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    127.0.0.1:6379> set b bb ex 20
    OK
    127.0.0.1:6379> ttl b
    (integer) 17
    127.0.0.1:6379> ttl b
    (integer) 15
    127.0.0.1:6379> ttl b
    (integer) 12
    127.0.0.1:6379> ttl b
    (integer) 9
    127.0.0.1:6379> ttl b
    (integer) 5
    127.0.0.1:6379> ttl b
    (integer) 1
    127.0.0.1:6379> ttl b
    (integer) -2
    127.0.0.1:6379>
  • pexpire和pttl命令

    pexpire命令使用格式如下:

    pexpire key miliseconds

    此命令的工作原理与expire命令完全相同,但key的TTL是以ms为单位的,而不是s。

    如果设置成功了TTL,则返回1;如果key不存在,则返回为0。

    pttl命令的使用格式如下:

    pttl key

    与ttl命令一样,pttl命令返回具有TTL的key的剩余生存时间,唯一的区别是ttl命令返回的剩余生存时间以s为单位,而pttl命令以ms为单位。

    如果key不存在,则命令将返回-2;如果key存在,但是没有关联的TTL,则该命令返回-1.

  • expireat命令

    使用格式如下:

    expireat key timestamp

    注意:timestamp参数是UNIX时间戳,时间单位是s。

    expireat命令具有与expire命令相同的效果和语义,但expireat命令不指定TTL的秒数,而是指定绝对的UNIX时间戳(自1970年1月1日起的秒数)。当前时间超过UNIX时间戳时,立即删除key。

    如果设置了TTL,则返回为1;如果key不存在,则返回为0。

  • pexpireat命令

    使用格式如下:

    pexpireat key milliseconds-timestamp

    pexpireat命令具有与expireat命令相同的效果和语义,但key超时的UNIX时间戳以ms而不是s为单位。

    如果成功设置了TTL,则返回1;如果key不存在,则返回0。

  • persist命令

    使用格式如下:

    persist key

    该命令用于删除key上的TTL,将key转换成没有TTL的key,永久保存key,不会过期时删除。

    如果TTL已删除,则返回1;如果key不存在或没有关联的TTL,则返回为0。

  • move命令

    使用格式如下:

    move key db

    该命令用于将key从当前选定的源数据库转移到指定的目标数据库,源数据库中的key会被删除。当key已存在于目标数据库中,或者当前选定的源数据库中不存在key时,不执行任何操作。

    如果成功移动了key,则返回1;如果未移动Key,则返回0。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> set a aa
    OK
    127.0.0.1:6379> move a 1
    (integer) 1
    127.0.0.1:6379> keys *
    (empty array)
    127.0.0.1:6379> select 1
    OK
    127.0.0.1:6379[1]> keys *
    1) "a"
    127.0.0.1:6379[1]> get a
    "aa"
    127.0.0.1:6379[1]>

  • object命令

    使用格式如下:

    object subcommand [arguments [arguments ….]]

    object命令可以获取key的元数据,检查与key关联的Redis Object的内部信息,内部信息可以理解成元数据,它对调试key使用指定编码以节省存储空间非常有用。另外,当使用Redis作为缓存时,应用程序还可以使用object命令得出的报告信息来实现应用程序级别的key删除策略,以释放缓存空间。

    object命令支持多个子命令,具体如下。

    • object refcount key:返回指定key关联的value的引用数。此命令主要用于调试。Redis新版本的refcount返回值并不是精确的数字。

    • object encoding key:返回key关联的value的内部表示形式。type命令取得key对应的存储数据类型,而object encoding 命令取得数据类型内部存储的具体形式。

    • object idletime key:返回未通过read或write操作的key的空闲时间。当内存淘汰策略设置为最少使用(Least Recently Used ,LRU)策略或不淘汰时,此子命令可用。

    • object freq key:返回指定key访问频率的对数。当内存淘汰策略设置为最近最不常用(Least Frequently Used ,LFU)策略时,此子命令可用。此命令的返回值是给Redis内部参考使用的,作用是在内存不够时决定将哪些数据清除。

    • object help:返回辅助的帮助文本。

      使用object encoding key命令可以获得编码格式,也就是使用哪种数据类型存储数据。

    • String 可以被编码为RAW字符串或Int(为了节约内存,Redis会将字符串表示的64bit有符号整数编码为整数来进行存储)。

    • List可以被编码为ziplist或linkedlist。

    • Set可以被编码为intset或者hashtable。

    • Hash可以编码为ziplist或者hashtable。

    • Sorted Set可以被编码为ziplist或者skiplist。

      数据最终使用哪种编码格式来存储取决于value的大小。

      Redis会随着value的大小来决定最终使用什么类型的内部编码,应用层程序员无法决定。

      将String编码成int数据类型可以节省内存。

    • Redis中的String内部编码有如下3种:

      • 8B的长整数。
      • embstr:小于等于39B的字符串。
      • 大于39B的字符串。
    • Redis内存淘汰策略主要有两种

      • LFU:删除访问频率最低的数据。
      • LRU:删除很久没有被范文的数据。

    测试案例

    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> clear
    127.0.0.1:6379> set a 0
    OK
    127.0.0.1:6379> set b 9999
    OK
    127.0.0.1:6379> set c 10000
    OK
    127.0.0.1:6379> get a
    "0"
    127.0.0.1:6379> get b
    "9999"
    127.0.0.1:6379> get c
    "10000"
    127.0.0.1:6379> object refcount a
    (integer) 2147483647
    127.0.0.1:6379> object encoding key1
    (nil)
    127.0.0.1:6379> object encoding a
    "int"
    127.0.0.1:6379> set key1 "123"
    OK
    127.0.0.1:6379> object encoding key1
    "int"
    127.0.0.1:6379> object idletime a
    (integer) 251
  • migrate命令

    使用格式如下:

    migrate host port key|”” desination-db timeout [COPY] [REPLACE] [KEYS key [key …]]

    该命令用于原子性的将key从源Redis实例传输到目标Redis实例中。成功时,key将从源Redis实例中删除,并保证存于目标Redis实例中。

    move命令在当前Redis实例中对数据进行移动,而migrate命令可以跨越不同Redis实例。

    该命令的内部实现是这样的:它的源Redis实例中给定key执行dump命令,将它进行序列化,然后传送到目标Redis实例中;目标实例Redis实例再执行restore命令对key进行反序列化,并将反序列化所得的key添加到数据库中。源Redis实例就像目标Redis实例的客户端,只要看到restore命令就返回OK,源Redis实例就会调用del命令删除其中的key。

    参数timeout以ms为单位,指定源Redis实例和目标Redis实例进行数据转移时的最大时间。操作耗时如果大于timeout就会出现异常。

    migrate命令需要再给定的timeout内完成数据转移操作,如果在转移数据时发生IO错误,或者到达了timeout,那么命令会停止执行,并返回一个-IOERR错误。当还错误出现时,有以下两种可能。

    • key可能存在于两个Redis实例中,源Redis实例中的key并没有被删除。如目的Redis实例成功添加了数据,返回给源Redis实例OK,但由于网络出现异常,源Redis实例并没有接收到OK,因此不会删除源Redis实例中的数据。
    • key可能只存在于源Redis实例中,目标Redis实例并没有key,也就是并没有转移成功。如在转移时网络出现异常。

    当返回任何以ERR开头的其他错误时,migrate命令保证key仍然存在于源Redis实例中,除非目标Redis实例中已存在同名的key。

    如果源Reids实例中没有要转移的key,则返回NOKEY。因为缺少key在正常情况下是可能的,如key超时了,所以NOKEY不是一个错误。

    可以在执行一次migrate命令时实现批量转移key。从Redis 3.0.6开始,migrate 命令支持一种新的大容量转移模式,该模式使用流水线,以便在Redis实例之间一起迁移key,减少了网络开销。想使用此模式,就要使用keys参数,并将正常key参数设置为空字符串””,实际的key名称将在keys参数之后提供,命令示例如下。

    migrate 192.168.31.45 8888 “” 0 5000 REPLACE auth accp KEYS a b c

    如果目标Redis实例有密码,则需要添加auth参数和密码值accp。

    参数的解释如下。

    • REPLACE:替换目标Redis实例中的现有key。
    • KEYS:如果key参数是空字符串,该命令将改为转移KEYS参数后面的所有key.

    Redis中的数据转移可以使用move、dump+restore和migrate命令,其中migrate命令功能最为完整和强大。

  • scan命令

    使用格式如下:

    scan cursor [MATCH pattern] [COUNT count]

    scan、sscan、hscan及zscan命令密切相关,它们都以增量的方式迭代元素集合。

    这四个命令的解释如下:

    • scan:迭代当前选定数据库中的key。
    • sscan:迭代Set中的元素。
    • hscan:迭代Hash中的field和value。
    • zscan:迭代Sorted Set中的元素和score

    这4个命令最明显的区别是sscan、hscan和zscan命令的第一个参数分别是Set、Hash和Sorted Set中key的名称。而scan命令不需要任何key名参数,因为它迭代当前数据库中所有key,因此迭代对象是数据库本身。

  • touch命令

    使用格式如下:

    touch key [key …]

    该命令用于修改指定key的最后访问时间。若key不存在,则不执行任何操作。此命令的作用是增加key的活跃度,避免其被内存淘汰策略所删除。

HyperLogLog、Bloom Filter类型命令及Redis-Cell模块

HyperLogLog类型命令

如果想统计一个页面被访问的次数可以使用incr命令,但如果想统计有多少个IP地址访问了它呢?借用Set数据类型的唯一特性,可以使用Set存储IP地址,再使用scard命令就能统计有多少个IP地址访问这个页面了,但这样做会占用大量内存空间。Set数据类型中已字符串存储IPv4格式的地址255.255.255.255,字符串长度为15B。如果有200000个IP地址访问,那么存储容量的大小为200000*15=3000000B,3000000B/1024=2929.6875MB,相当于要占用3G的内存空间,如果网站有1000个页面呢?并且还想统计每天每个页面被多少个IP地址访问了呢?这样数据存储容量的规模不可想象,购买内存的成本会非常高,这时可以考虑使用HyperLogLog数据类型。

HyperLogLog数据类型是一种概率数据类型,用于计算唯一事物的“近似数量”。由于是近似数量,因此其值并不精确,存在最大0.81%的误差,但HyperLogLog数据类型的优点是最多只占用12KB内存空间,以更低的精度换取更小的空间。

Redis再操作HyperLogLog数据类型时提供了如下3个命令。

  • pfadd:向key添加元素。

  • pfcount:返回key中存储元素的个数。

  • pfmerge:合并两个HyperLogLog数据类型中的元素。

  • pfadd和pfcount命令

    pfadd命令的使用格式如下:

    pfadd key element [element …]

    该命令用于向key中添加元素。

    如果HyperLogLog数据类型的近似数量(元素个数)在执行该命令时发生变化,则返回1,否则返回0。

    pfcount命令的使用格式如下:

    pfcount key [key …]

    当参数为一个key时,该命令返回存储在HyperLogLog数据类型的元素个数近似值。

    当参数为多个key时,该命令返回这些key并集的近似数量,近似数量是将指定多个key的HyperLogLog数据 类型合并到一个临时的HyperLogLog数据类型中计算而得到的。

    测试案例

    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> pfadd key1 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
    (integer) 1
    127.0.0.1:6379> pfcount key1
    (integer) 10
    127.0.0.1:6379> pfadd key2 a b c d e
    (integer) 1
    127.0.0.1:6379> pfcount key2
    (integer) 5
    127.0.0.1:6379> pfcount key1 key2
    (integer) 15
    127.0.0.1:6379> pfadd key3 a b c d e x y z
    (integer) 1
    127.0.0.1:6379> pfcount key3
    (integer) 8
    127.0.0.1:6379> pfcount key1 key3
    (integer) 18
    127.0.0.1:6379> pfcount key2 key3
    (integer) 8
    127.0.0.1:6379>
    #在使用pfcount命令指定多个key时,统计出来的近似数量是去重之后的。
  • pfmerge命令

    使用格式如下:

    pfmerge destkey sourcekey [sourcekey …]

    该命令用于合并HyperLogLog。

    该命令将多个sourcekey合并为一个destkey,合并后的destkey接近所有合并sourcekey的可见集合的并集。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> pfadd key1 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
    (integer) 1
    127.0.0.1:6379> pfadd key2 a b c d e
    (integer) 1
    127.0.0.1:6379> pfadd key3 a b c d e x y z
    (integer) 1
    127.0.0.1:6379> pfmerge key4 key1 key2
    OK
    127.0.0.1:6379> pfcount key4
    (integer) 15
    127.0.0.1:6379> pfmerge key5 key2 key3
    OK
    127.0.0.1:6379> pfcount key5
    (integer) 8
    127.0.0.1:6379>

Bloom Filter类型命令

HyperLogLog数据类型以近似数量的形式统计出海量元素的个数,但却不能确定某一个元素是否被添加过,因此HyperLogLog数据类型并没有提供PFCONTAINS方法,这时可以使用Bloom Filter数据类型来实现这样的需求。

举一个例子,某些App要求用户每次给客户端推荐的内容都是不重复的,如果用传统的RDBMS存储统计信息,那么无论如何优化也达不到理想的效果。RDBMS不管在硬盘还是内存占用等方面,都是极其浪费资源的,这种场景正是使用Bloom Filter数据类型的好时机。

Bloom Filter数据类型就像一个“不太精确的Set数据类型”,它使用contains()方法判断是否误判一个元素存在。Bloom Filter数据类型判断某个元素存在时,这个元素可能不存在,而判断某个元素不存在时,此元素肯定不存在 。根据这个特性,使用Bloom Filter数据类型实现推送系统时,可以绝对精确地向用户推荐没有看过地内容。

  • 在Redis中安装RedisBloom模块

    在Redis中需要以模块地方式安装RedisBloom。下载Redis的RedisBloom的压缩包。

    1
    2
    3
    4
    5
    git clone --recursive https://github.com/RedisBloom/RedisBloom.git
    cd redisbloom
    make
    # Assuming you have a redis build from the unstable branch:
    /path/to/redis-server --loadmodule ./redisbloom.so
  • bf.reserve、bf.add和bf.info命令

    bf.reserve命令的使用格式如下:

    bf.reserve(key) {error_rate} {capacity} [EXPANSION expansion] [NONSCALING]

    根据指定的误判率和初始容量创建一个空的布隆过滤器。命令执行成功返回OK,否则返回异常。

    可以通过创建子判过滤器的方式来扩展布隆过滤器容量(扩容),但与创建布隆过滤器时指定合适的容量比,子布隆过滤器将消耗更多的内存和CPU资源。

    参数解释如下:

    • key:key的名称。
    • error_rate:期望的误判率,取0~1的十进制数。如期望的误判率为0.1%(1000个中有1个),error_rate应该设置为0.001.其值越接近于0,则内存消耗越大,CPU使用率越高。
    • capacity:计划添加到布隆过滤器中的元素个数,添加超过此数量的元素后,布隆过滤器的性能开始下降。
    • EXPANSION expansion:如果创建了一个新的子布隆过滤器,则其内容将是当前布隆过滤器的容量*expansion,expansion默认为2。这意味着新的子布隆过滤器的布隆过滤器将是前一个布隆过滤器容量的两倍。
    • NONSCALING:如果达到初始的容量,则阻止布隆过滤器创建其他子布隆过滤器。不可扩容的布隆过滤器所需要的内存容量比可扩容的布隆过滤器要少。

    在使用布隆过滤器时,需要着重考虑两点。

    • 预估数据量n,也就是capacity。
    • 期望的误判率p,也就是error_rate。

    这两点关乎布隆过滤器的内存占用量,内存占用量的计算比较复杂,使用如下公式。

    ​ m=$-(n*lnp)/(lnk)^2$

    bf.add命令的使用格式如下:

    bf.add {key} {item}

    该命令用于将元素添加到布隆过滤器中,如果该布隆过滤器不存在,则创建布隆过滤器。如果添加了新的元素,则返回1;返回0代表添加的元素可能已经存在。

    bf.info命令的使用格式如下:

    bf.info {key}

    该命令用于返回key的相关信息。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> bf.reserve mykey 0.001 1000000
    OK
    127.0.0.1:6379> type mykey
    MBbloom--
    127.0.0.1:6379> keys *
    1) "mykey"
    127.0.0.1:6379>

    继续测试自动扩容的效果,测试案例如下

    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
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> bf.reserve mykey 0.001 3
    OK
    127.0.0.1:6379> bf.add mykey a
    (integer) 1
    127.0.0.1:6379> bf.add mykey b
    (integer) 1
    127.0.0.1:6379> bf.add mykey c
    (integer) 1
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 3
    3) Size
    4) (integer) 160
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted
    8) (integer) 3
    9) Expansion rate
    10) (integer) 2
    127.0.0.1:6379> bf.add mykey d
    (integer) 1
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 9
    3) Size
    4) (integer) 296
    5) Number of filters
    6) (integer) 2
    7) Number of items inserted
    8) (integer) 4
    9) Expansion rate
    10) (integer) 2
    127.0.0.1:6379>

    如果使用NONSCALING参数,则容量不够时出现异常,不再扩容,测试案例如下。

    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
    127.0.0.1:6379> bf.reserve mykey 0.001 3 NONSCALING
    OK
    127.0.0.1:6379> bf.add mykey a
    (integer) 1
    127.0.0.1:6379> bf.add mykey b
    (integer) 1
    127.0.0.1:6379> bf.add mykey c
    (integer) 1
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 3
    3) Size
    4) (integer) 160
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted
    8) (integer) 3
    9) Expansion rate
    10) (nil)
    127.0.0.1:6379> bf.add mykey c
    (integer) 0
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 3
    3) Size
    4) (integer) 160
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted
    8) (integer) 3
    9) Expansion rate
    10) (nil)
    127.0.0.1:6379>

    使用EXPANSION参数可以定义扩展的容量的大小,默认值是2,测试案例如下:

    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
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 3
    3) Size
    4) (integer) 160
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted
    8) (integer) 0
    9) Expansion rate
    10) (integer) 4
    127.0.0.1:6379> bf.add mykey a
    (integer) 1
    127.0.0.1:6379> bf.add mykey b
    (integer) 1
    127.0.0.1:6379> bf.add mykey c
    (integer) 1
    127.0.0.1:6379> bf.add mykey d
    (integer) 1
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 15
    3) Size
    4) (integer) 312
    5) Number of filters
    6) (integer) 2
    7) Number of items inserted
    8) (integer) 4
    9) Expansion rate
    10) (integer) 4
    127.0.0.1:6379>
  • bf.madd命令

    使用格式如下:

    bf.madd {key} {item} [item …]

    该命令用于向布隆过滤器中添加多个元素,返回值是boolean[]类型即布尔数组。如果成功添加新的元素,则返回true;如果元素已经存在,则返回false。

    测试案例

    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
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> bf.madd mykey a b c d
    1) (integer) 1
    2) (integer) 1
    3) (integer) 1
    4) (integer) 1
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 100
    3) Size
    4) (integer) 296
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted
    8) (integer) 4
    9) Expansion rate
    10) (integer) 2
    127.0.0.1:6379> bf.madd mykey a b c d e
    1) (integer) 0
    2) (integer) 0
    3) (integer) 0
    4) (integer) 0
    5) (integer) 1
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 100
    3) Size
    4) (integer) 296
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted
    8) (integer) 5
    9) Expansion rate
    10) (integer) 2
    127.0.0.1:6379>
  • bf.insert命令

    使用格式如下:

    bf.insert {key} [CAPACITY {cap}] [ERROR {error}] [EXPANSION expansion] [NOCREATE] [NONSCALING] ITMES {items ….}

    该命令用于创建布隆过滤器的同时向布隆过滤器中添加一个或多个元素。返回值是boolean[]类型,如果成功添加新的元素,返回true;如果元素已经存在,返回false。

    参数解释如下:

    • key:key的名称。
    • CAPACITY:如果指定此参数,则对创建的布隆过滤器设置cap。如果布隆过滤器已存在,则忽略此参数。如果创建布隆过滤器时并未指定此参数,则使用默认cap。
    • ERROR:如果指定此参数,则对创建的布隆过滤器设置error。如果布隆过滤器已经存在,则忽略此参数。如果创建布隆过滤器时并没有指定此参数,则使用默认error。
    • EXPANSION:如果创建了一个新的子布隆过滤器,则其容量将是当前布隆过滤器的容量*expansion,expansion默认值是2,这意味着新的子布隆过滤器的容量将是当前布隆过滤器容量的两倍。
    • NOCREATE:指定此参数,代表如果布隆过滤器不存在,则不创建布隆过滤器,并且会返回错误信息。参数NOCRETE与CAPCITY或ERROR一起使用会发生错误。
    • NONSCALING:如果达到初始的容量,则阻止布隆过滤器创建子布隆过滤器。不可扩容的过滤器所需要的内存容量比可扩容的过滤器少。
    • ITEMS:添加一个或多个元素。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> bf.insert mykey items a b c
    1) (integer) 1
    2) (integer) 1
    3) (integer) 1
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 100
    3) Size
    4) (integer) 296
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted
    8) (integer) 3
    9) Expansion rate
    10) (integer) 2
    127.0.0.1:6379>
  • bf.exists命令

    使用格式如下:

    bf.exists {key} {item}

    该命令用于判断元素是否在集合中。

    如果元素肯定不存在,返回0;如果元素可能存在,返回1.

    测试案例

    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> flushdb
    OK
    127.0.0.1:6379> bf.insert mykey items a b c
    1) (integer) 1
    2) (integer) 1
    3) (integer) 1
    127.0.0.1:6379> bf.info mykey
    1) Capacity
    2) (integer) 100
    3) Size
    4) (integer) 296
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted
    8) (integer) 3
    9) Expansion rate
    10) (integer) 2
    127.0.0.1:6379> bf.exists mykey a
    (integer) 1
    127.0.0.1:6379> bf.exists mykey g
    (integer) 0
    127.0.0.1:6379>
  • bf.mexists命令

    使用格式如下:

    bf.mexists {key} {item} [item …]

    该命令用于判断多个元素是否在集合中。

使用Redis-Cell模块实现限流

限流是互联网行业中应用得比较多的功能之一,指在有限的时间内允许多少次操作,如60s之内允许最多5次回帖,使用Redis-Cell模块能非常容易实现这类功能。

  • 在Redis中安装Redis-Cell模块

    进入Redis-Cell模块网站,下载二进制文件。

    Redis-Cell

    1
    2
    3
    4
    5
    6
    CL.THROTTLE user123 15 30 60 1
    ▲ ▲ ▲ ▲ ▲
    | | | | └───── 每操作1次作为1个令牌,默认值是0
    | | └──┴─────── 60秒内最多允许获取30个令牌
    | └─────────────令牌桶中令牌的总数16个,因为从0开始
    └─────────────────── key 的名称

    详细教程见官网。

GEO类型命令

Redis提供了地理位置数据类型的支持。使用GEO类型命令可以实现“附近的人”“附近约车”“就近干活”等与地理位置和距离有关的业务功能。

  • geoadd和agepos命令

    geoadd命令的使用格式如下:

    geoadd key longitude latitude member [longitude latitude member …]

    该命令用于将指定的地理空间项(包括经度、纬度、名称)添加到指定key中。数据以有序集合的方式存储在key中。

    geoadd 命令对可使用的坐标值有限制:非常靠近两级区域的坐标不可使用。经、维度的有效值在EPSG:900913、EPSG:3785的范围内。OSGEO:41001标准中有如下两个限制。

    • 有效经度为-180°~180°。
    • 有效纬度为-85.05112878~85.05112878

    如果使用非指定范围内的坐标值,就会出现异常。

    geopos命令的使用格式如下:

    geopos key member [member …]

    该命令用于获取指定key中member的地理位置,值是近似值。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    127.0.0.1:6379> geoadd mykey 50 50 china
    (integer) 1
    127.0.0.1:6379> geoadd mykey 51 51 america 52 52 shanghai
    (integer) 2
    127.0.0.1:6379> keys *
    1) "mykey"
    127.0.0.1:6379> geopos mykey china
    1) 1) "49.99999970197677612"
    2) "49.99999957172130394"
    127.0.0.1:6379> geopos mykey america
    1) 1) "51.00000232458114624"
    2) "51.00000029822487591"
    127.0.0.1:6379> geopos mykey shanghai china
    1) 1) "51.99999958276748657"
    2) "52.00000102472843366"
    2) 1) "49.99999970197677612"
    2) "49.99999957172130394"
    127.0.0.1:6379>
  • geodist命令

    使用格式如下:

    geodist key member1 member2 [unit]

    该命令用于返回两个元素之间的距离。

    距离单位可以选择如下单位,默认是m。

    • m:米。
    • km:公里。
    • mi:英里。
    • ft:英尺。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    127.0.0.1:6379> geoadd mykey 50 50 china
    (integer) 1
    127.0.0.1:6379> geoadd mykey 51 51 america 52 52 shanghai
    (integer) 2
    127.0.0.1:6379> keys *
    1) "mykey"
    127.0.0.1:6379> geodist mykey china shanghai m
    "262814.0788"
    127.0.0.1:6379> geodist mykey china shanghai km
    "262.8141"
    127.0.0.1:6379>

  • geohash命令

    使用格式如下:

    geohash key member [member …]

    该命令用于返回地理位置的散列字符串,字符串长度为11个字符。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    127.0.0.1:6379> geoadd key 30 30 A 
    (integer) 1
    127.0.0.1:6379> geoadd key 45 65 B
    (integer) 1
    127.0.0.1:6379> geohash key A
    1) "stm6dtm6dt0"
    127.0.0.1:6379> geohash key B
    1) "v581b0bh2n0"
    127.0.0.1:6379> geohash key B A
    1) "v581b0bh2n0"
    2) "stm6dtm6dt0"
    127.0.0.1:6379>
  • georadius命令

    使用格式如下:

    georadius key longitude laritude radius m|km|ft|mi [withdist] [withhash] [count count] [asc|desc] [store key] [storedist key1]

    该命令用于根据指定坐标(中心点),返回围绕坐标在指定半径之内的地理位置。

    结合如下3个参数可以返回其他附加的结果。

    • withdist:返回查找的结果与中心点的距离。

    • withcoord:返回经纬度坐标。

    • withhash:返回散列字符串。

      默认情况下,georadius命令将返回的结果是未排序的,可以使用如下两个参数进行排序。

      • asc:相对中心点,将返回的结果从近到远进行排序。
      • desc:相对中心点,将返回值的结果从远到近进行排序。

    结合参数count可以对返回结果的数量进行限制。

    参数store和storedist可以实现将返回的结果或距离另存到其他的key中。

    测试案例

    执行如下命令初始化测试环境。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> geoadd key 30 31 A 30 32 B 30 33 C 30 34 D 30 35 E
    (integer) 5
    127.0.0.1:6379> geodist key A B m
    "111226.0989"
    127.0.0.1:6379> geodist key A C m
    "222452.4797"
    127.0.0.1:6379> geodist key A D m
    "333678.8605"
    127.0.0.1:6379> geodist key A E m
    "444904.9594"
    127.0.0.1:6379>

    测试距离单位m、km、ft、mi

    1
    2
    3
    127.0.0.1:6379> georadius key 30 31 30000 m
    1) "A"
    127.0.0.1:6379>
  • gepradiusbymember命令

    使用格式如下:

    georadiusbymember key member radius m|km|ft|mi [withdist] [withhash] [count count] [asc|desc] [store key] [storedist key1]

    该命令用于根据元素返回附近的地理位置。

    测试案例

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> georadiusbymember key  B 300000 m
    1) "B"
    2) "C"
    3) "D"
    4) "A"
    127.0.0.1:6379>
  • 删除GEO数据类型中的元素

    GEO数据类型内部使用Sorted Set数据类型,删除元素可以使用zrem命令。

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    127.0.0.1:6379> geoadd key 30 31 A 30 32 B 30 33 C 30 34 D 30 35 E
    (integer) 5
    127.0.0.1:6379> type key
    zset
    127.0.0.1:6379> zcard key
    (integer) 5
    127.0.0.1:6379> zrem key A B
    (integer) 2
    127.0.0.1:6379> zcard key
    (integer) 3
    127.0.0.1:6379> zrange key 0 -1
    1) "C"
    2) "D"
    3) "E"
    127.0.0.1:6379>

Pub/Sub类型命令

Pub/Sub(发布/订阅)模式在生活中随处可见。如某些网站上的“话题广场”就是典型的Pub/Sub模式。

在话题广场中,用户选择自己感兴趣的话题进行订阅,当发布了与订阅话题有关的帖子时,网站会自动把帖子传送给订阅的用户。

Pub/Sub模式有3个角色。

  • 主题(Topic):发布者与订阅者沟通的桥梁。
  • 订阅者(Subscriber):订阅主题,并从主题获取消息。
  • 发布者(Publisher):向主题发布消息。

3个角色的结构关系如图:

image-20220326163807846

订阅者首先订阅指定的主题,发布者把消息传送给主题,主题会把消息传送给订阅主题的订阅者。

在Redis中,主题换了一个新的名称,即“频道”,频道其实和原来主题的功能作用是一样的。Redis中的Pub/Sub模式的结构如图:

image-20220326164101014

订阅者首先订阅指定的频道,发布者把消息传送给频道,频道会把消息传送给订阅频道的订阅者。

Redis中的Pub/Sub模式可以总结出如下3个特性。

  • 以频道未中介,实现了发布者与订阅者之间的解耦,发布者不知道订阅者在哪里,而订阅者也不知道发布者在哪里。
  • 订阅者要先订阅频道,然后频道会将发布者发布的消息传送给订阅者。如果发布者在订阅频道前发布消息,则订阅者接收不到消息。
  • Pub/Sub模式就是Java中的观察者模式。

具体命令

  • publish和subscribe命令

    publish和subscribe命令的作用是发布与订阅。

    publish使用格式如下:

    publish channel message

    该命令用于向指定的频道发布消息。

    subscribe使用格式如下:

    subscribe channel [channel …]

    该命令用于订阅指定的频道。

    一旦客户端(订阅者)进入订阅状态,就不能执行除subscribe、psubcribe、unsubscribe和punsubscribe以外的命令。

    测试案例

    订阅者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    127.0.0.1:6379> subscribe chnnelA channelB
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "chnnelA"
    3) (integer) 1
    1) "subscribe"
    2) "channelB"
    3) (integer) 2
    1) "message"
    2) "channelB"
    3) "bmessage"

    发布者

    1
    2
    3
    127.0.0.1:6379> publish channelB  bmessage
    (integer) 1
    127.0.0.1:6379>
  • unsubscribe命令

    使用格式如下:

    unsubscribe [channel [channel …]]

    该命令用于取消订阅指定频道,如果频道未指定,则取消订阅所有频道。

    redis-cli中一旦进入订阅状态,就无法执行unsubscribe命令实现取消订阅的效果,因此需要在Redis环境中进行。

  • psubscribe命令

    使用格式如下:

    psubscribe pattern [pattern]

    该命令用于订阅与指定模式pattern匹配的频道,支持glob风格的匹配模式。

  • punsubscribe命令

    使用格式如下:

    punsubscribe [pattern [pattern ,,,]]

    该命令用于按模式pattern批量取消订阅,如果pattern未指定,则取消订阅所有频道。

  • pubsub命令

    使用格式如下:

    pubsub subcommand [argument [argument …]]

    该命令用于获取订阅发布的状态,使用形式如下。

    pubsub … args ….

Stream类型命令

Stream数据类型是Redis5.0新添加的数据类型,是Redis数据类型中最复杂的,尽管其数据结构本身非常简单。其最大的特点就是有序存储field-value。

先来看一看其他数据类型的缺点。

  • String:想要存储field-value对,必须存储JSON格式,JSON格式里包括field-value对。另外需要在JSON格式的内容中自行处理元素的有序性,如使用数组;缺点是不支持直接存储field-value对。
  • Hash:无序,支持存储field-value对;缺点是无序。
  • List:有序,想要存储field-value对必须存储JSON格式;缺点是无序和不支持直接存储field-value对。
  • Sorted Set:有序,想要存储field-value对必须存储JSON格式;缺点是不支持直接存储field-value对。

以上5大数据类型都或多或少有缺点,但Stream数据类型的出现却改正了这些缺点。Stream数据类型不仅支持有序性。还支持直接存储field-value对。另外,Stream数据类型也允许消费者以阻塞的方式等待生产者向Stream数据类型中发送新消息,此外还有“消费者组”的实现。作用是允许多个消费者相互配合来消费同一个Stream数据类型中不同部分的消息。上面介绍的这些知识点都在本章以案例的形式实现。

Stream数据类型的存储形式如图:

image-20220326190031097

Stream数据类型中的key对应的value由一个ID和若干field-value对组成,和Hash数据类型非常相似。但Hash数据类型存储的元素是无序的,而Stream数据类型借助于ID的大小可以使存储的元素是有序的,和Sorted Set数据类型借助于score存储的元素是有序的效果一样。和Sorted Set 数据类型相比,Stream数据类型的优势是可以直接存储field-value对。

  • xadd命令

    使用格式如下:

    xadd key ID field string [field string …]

    该命令用于添加元素。

    参数field string整体代表流条目(Streams Entry),一个流条目由多个field-string对组成。流条目整体可以理解成一个普通的元素,为了便于理解,我们将“流条目”和“元素”作为同一事物。

    ID由time和sequence两部分组成,使用格式为”time-sequence”。

    生成元素ID可以有两种方式。

    • 自动生成:在自动生成ID的情况下,time单位是ms,是当前Redis实例的服务器时间。当time一样时,为了标识元素的唯一性,需要使用sequence进行自增。用time作为ID的优势是可以根据时间范围查询元素。使用“*”自动生成ID,有Redis根据当前时间自动生成一个唯一的ID,每次自动生成的ID都会比上一个ID更大,因为时间一直在前进,就像数据库的主键自增一样。
    • 自定义:在手动生成ID的情况下,time和sequence的值是可以自定义的。一般使用自定义ID的情况少。

    测试案例

    1 使用“*”自动生成ID。

    1
    2
    3
    4
    5
    127.0.0.1:6379> xadd key1 * a aa b bb c cc
    "1648293296729-0"
    127.0.0.1:6379> keys *
    1) "key1"
    127.0.0.1:6379>

    使用maxlen限制流的绝对长度,这样会保留最新的元素,也就是ID值小的元素会被删除。

    使用maxlen~限制流的近似长度,会保留最新的元素。

  • xlen命令

    使用格式 如下:

    xlen key

    该命令用于获取key中存储的元素个数。

  • xdel命令

    使用格式如下:

    xdel key ID [ID …]

    该命令用于 根据ID删除对应的元素。

  • xrange命令

    使用格式如下:

    xrange key start end [COUNT count]

    该命令用于按ID范围正序返回元素(包括start和end值,属于start<=ID<=end的关系)。

    xrange命令可以在如下场景中使用。

    • 按时间范围返回元素,因为ID可以以时间为值进行排序。
    • 如果流中存储的元素数量比较多,可以采用COUNT参数实现增量迭代,类似scan命令。
    • 返回单个元素。

    与xrange命令对应有一个倒序命令xrevrange,以相反的顺序返回元素,除了返回顺序相反以外,它们在功能上是完全相同的。

  • xrevrange命令

    使用格式 如下:

    xrevrange key end start [COUNT count]

    该命令用于按ID范围倒序返回元素(包括end和start值,属于end<=ID<=start的关系)。

  • xread命令

    使用格式如下:

    xread [COUNT count] [BLOCK milleseconds] STREAMS key [key …] id [id …]

    该命令用于读取流中比指定ID值大的元素(属于大于关系)。

    xrange命令和xrevrange只能从一个流中读取元素,而xread命令支持从多个流中读取元素,还支持阻塞与非阻塞的操作。

    xread命令还具有阻塞功能,和Pub/Sub模式或者阻塞队列动能非常相似,但却有本质上的不同。

    • xread可以有多个消费者以阻塞的方式一同监听新的元素。如果有新的元素到达,xread则把新的元素分发到不同的消费者,每个消费者接收的元素是相同的,和Pub/Sub模式的效果是一样的;但和阻塞队列不同,使用阻塞队列的消费者接收的元素是不相同的。
    • Pub/Sub模式和阻塞队列中的元素是瞬时的,元素被消费完毕后立即删除,并不保存,而流中的元素会一直保存,除非手动删除。不同的消费者通过接收的最后一个ID与服务器进行对比,从而知道哪些元素是最新的。
    • Stream数据类型提供消费者组的概念,更细化地控制流中元素地处理。

    符号“$”表示使用流已经存储地最大值ID作为最后一个ID值,让消费者仅接收从开始监听地那个时间以后地消息。示例如下:

    xread block 0 stream key1 $

    测试案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> xread streams key1 0-0
    1) 1) "key1"
    2) 1) 1) "1648293296729-0"
    2) 1) "a"
    2) "aa"
    3) "b"
    4) "bb"
    5) "c"
    6) "cc"
    127.0.0.1:6379>
  • xtrim命令

    使用格式如下:

    xtrim key maxlen [~] count

    该命令限制地长度为count。

    参数maxlen [~] count 支持绝对count长度和近似count长度,与xadd命令地maxlen参数一模一样,会保留最新地元素。

  • 消费者组的使用

    前文使用xread命令实现了对流中消息的阻塞监听功能。如图:

    image-20220326204331197

消费者13同时在监听流中是否有最新的消息,当生产者15对流中的消息执行添加操作时,消费者13就会收到生产者15添加的消息。

除了流中的消息可以持久保存外,使用xread命令结合阻塞功能所实现的效果和Pub/Sub模式别无两样,基本相同。

xread命令和Pub/Sub模式都有消息的生产者/发布者(简称生产者)和消费者/订阅者(简称消费者),同组消费者处理的是一样的。但是在某些情况下,想要实现的不是向多个消费者提供相同的消息,而是向不同的消费者传递不同的消息,通过将不同的消息传递到不同的消费者来模拟负载均衡的效果,将计算机资源更高效地利用。如图:

image-20220326205016144

消费者组就像一个”伪消费者“,从流中为多个消费者获取消息,消费者组提供如下5点保证。

  • 将一条消息提供给唯一的消费者,消费者和消息之间是一对一的关系,不同的消费者接收的消息不同,不会将同一份消息传递给多个消费者。
  • 消费者在消费者组中通过唯一名称来识别。
  • 当消费者请求新消息时,消费者组提供消费者以前从未收到的消息。
  • 消费消息后需要使用xack命令进行显示确认,表示这条消息已经被正确处理了,可以从消费者组中删除,之所以要有ACK确认机制,是因为万一在消费消息时出现宕机、掉电等情况造成消息丢失,只有消息消费完毕才会对其进行确认,将其从消费者组中删除。
  • 消费者组监控当前所有”待确认“的消息,也就是消息已经被传递到消费者,但消费者还没有对消息进行消费确认,消费者组中这样的消息就是待确认消息。每个消费者只能看到传递给它的消息。

使用消费者组实现消息消费需要3个命令。

  • xgroup:用于创建、删除或者管理消费者组。
  • xreadgroup:用于通过消费者组从一个流中读取消息。
  • xack:允许消费者将待确认消息标记为已确认。

于消费者组有关的命令

  • xgroup命令的使用格式如下:

    xgroup [create key groupname id-or-$] [setid key groupname id-or-$] [destroy key groupname] [delconsumer key groupname consumername]

    xgroup命令主要有如下4个参数。

    • 参数setid key groupname id-or-$:将消费者组中最后的ID值设置为其他值。
    • 参数create key groupname id-or-$:创建消费者组。
    • 参数destroy key groupname:删除消费者组。
    • 参数delconsumer key groupname consumername:从消费者组中删除指定的消费者。

    xreadgroup命令的使用格式如下:

    xreadgroup group group consumer [count count] [block milliseconds] [noack] streams key [key …] ID [ID …]

    xreadgroup命令的作用是从消费者组中读取消息,将消息存储在”待处理条目列表(Pending Entry List,PEL)“中,并且支持阻塞模式。

    • 参数group:监听消费者组的名称。
    • 参数consumer:设置消费者的唯一名称。
    • 参数noack:不需要ACK机制。

    xinfo命令的使用格式如下:

    xinfo [consumers key groupname] [groups key] [stream key] [help]

    xinfo命令主要有4个参数。

    • 参数consumers key groupname:查看消费者相关的信息。
    • 参数groups key:查看key对应消费者组相关的信息。
    • 参数stream key:查看流相关的信息。
    • 参数help:查看命令文档说明。

    xack命令的使用格式如下:

    xack key group ID [ID …]

    xack命令的作用是对消费者组中的待确认的消息进行确认。待确认的消息存储在待处理条目列表中。

    xpending命令的使用分时如下:

    xpending key group [start end count] [consumer]

    xpending命令的作用是查看待处理条目列表的中的消息。

    xclaim命令的使用格式如下:

    xclaim key group consumer min-idle-time ID [ID …] [idle ms] [time ms-unix-time] [retrycount count] [force] [justid]

    xlaim命令的作用是改变处理消息的消费者。

    当使用xgroup create 命令创建一个消费者之后,消费者组会维护至少3个主要的数据。

    • 该消费者组下有哪些消费者。

    • 创建一个队列,用于存储处于待确认状态的消息。

    • 当前消费者组最后发送消息的ID。

      当使用xreadgroup命令成功读取消息时,会把当前消费者关联到消费者组中;把读取到的消息存储在待确认队列中;将最后发送的消息ID设置为消费者组最后发送消息的ID。

  • 在xreadgroup命令中使用>或指定ID值。

    • >:代表从消费者组中读取从未被其他消费者消费的消息,会更新消费者组最后发送消息的ID值。
    • 指定ID值:从待处理消息队列中读取消息。
  • xgroup create mykey1 mygroup1 ID mkstream 命令中的”ID“代表消费者组从Stream中读取消息的起始ID。

  • xreadgroup group mygroup1 myconsumer1 block 0 streams mykey1 >命令中的”>“代表从消费者组中读取从未被其他消费者消息的消息,会更新消费者组最后发送消息的ID。

  • xreadgroup group mygroup1 myconsumer block 0 streams mykey1 ID 命令中的”ID“代表从待处理消息队列中读取消息。

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
57
58
127.0.0.1:6379> xgroup create mykey1 mygroup1 $ mkstream
OK
127.0.0.1:6379> xreadgroup group mygroup1 myconsumer1 block 0 streams mykey1 >
1) 1) "mykey1"
2) 1) 1) "1-0"
2) 1) "a"
2) "aa"
3) "b"
4) "bb"
5) "c"
6) "cc"
(22.49s)
127.0.0.1:6379> xreadgroup group mygroup1 myconsumer1 block 0 streams mykey1 >
1) 1) "mykey1"
2) 1) 1) "2-0"
2) 1) "a"
2) "aa"
3) "b"
4) "bb"
5) "c"
6) "cc"
(17.67s)
127.0.0.1:6379> xinfo groups mykey1
1) 1) "name"
2) "mygroup1"
3) "consumers"
4) (integer) 1
5) "pending"
6) (integer) 2
7) "last-delivered-id"
8) "2-0"
127.0.0.1:6379> xpending mykey1 mygroup1
1) (integer) 2
2) "1-0"
3) "2-0"
4) 1) 1) "myconsumer1"
2) "2"
127.0.0.1:6379> xpending mykey1 mygroup1 - + 100
1) 1) "1-0"
2) "myconsumer1"
3) (integer) 170386
4) (integer) 1
2) 1) "2-0"
2) "myconsumer1"
3) (integer) 106914
4) (integer) 1
127.0.0.1:6379> xack mykey1 mygroup1 1
(integer) 1
127.0.0.1:6379> xpending mykey1 mygroup1 - + 100 myconsumer1
1) 1) "2-0"
2) "myconsumer1"
3) (integer) 149483
4) (integer) 1
127.0.0.1:6379> xack mykey1 mygroup1 2
(integer) 1
127.0.0.1:6379> xpending mykey1 mygroup1 - + 100 myconsumer1
(empty array)
127.0.0.1:6379>
  • xclaim命令

    该命令用于实现消息认领。

    消息为什么要被认领呢?假设有一个消费者组,其中有两个消费者A和B,当消费者A从消费者组中获取消息后进行处理时,由于意外断电,导致消费者A处理消息的过程被中断。并且消费者A的服务器不能恢复,因此在待确认队列中会保存消费者A未被确认的消息,这些消息将占用服务器内存资源。可以将待确认队列中的消息有消费者B进行处理,xlcaim命令就是实现这个功能,也就是消息认领。

  • Stream数据类型与其他Redis数据类型有一个不同的地方在于:当其他数据类型中没有元素的时候,在内部会自动调用删除命令把key删除。如当调用ZREM命令时,会将Sorted Set数据类型中的最后一个元素删除,这个Sorted Set 数据类型也就彻底删除。但是Stream数据类型允许在内部没有元素的情况下key仍然存在,这样设计的原因是Stream数据类型可能和消费者进行关联,在实际场景中不希望由于Stream数据类型中没有元素而自动删除,导致消费者组的信息丢失。

Pipelining和Transaction类型命令

流水线(Pipelining)可以实现批量发送多个命令到服务器,提高程序运行效率。

事务(Transaction)可以保证服务器批量执行多个命令,这些命令是一体的,具有原子性,但Redis并没有完整的ACID特性。

流水线

流水线可以实现批量发送多个命令到服务器,提高程序运行效率。流水线是在客户端实现的,和服务器无关。

Redis中提供的流水线类似JDBC中的Bath技术。如果不用流水线或Batch技术,向服务器发送10条命令,会有10次请求和响应的过程,每一次的请求和响应的用时被称为往返时间(Round Trip Time,RTT)。由于Redis是单线程的,因此每一次的请求和响应都是按顺序执行的,会产生10次RTT,执行速度慢。当使用Pipelining或Batch技术时,一次打包发送10个命令到服务器,只需要一次请求和响应过程,提高了程序的运行效率。

事务

RDBMS中的事务是指一系列操作步骤完全地执行或完全不执行,具有回滚地特性。

但Redis中地事务是指一组Command命令(至少是两个或两个以上的命令)。Redis事务保证这些命令被执行时不会被任何其他操作打断,这和RDBMS中的事务完全不同。

Redis没有回滚。

Redis中的事务既然不能回滚,那么它的主要作用是什么呢?它的主要作用是保证 multi 命令和 exec命令之间的命令是原子性的、不可拆分的。其他命令必须在事务执行完毕之后才可以执行,所以其他命令看到的是事务提交之后最终的运行结果,而不是一个“半成品”。因为事务中存在n条命令,所以只有这n条命令执行完毕之后才可以执行其他命令。

事务开启的时候创建命令队列,把执行的命令放入队列中,事务接收到exec命令后就将命令队列中的命令一次性执行,中途不能被打断,具有原子性。

Redis通过multi、exec、watch等命令来实现事务功能。事务提供了一种将多条命令进行打包,然后一次性、按顺序执行多条命令的机制,并且在事务执行期间,服务器不会中断事务而去执行其他客户端的命令,会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令。一个事务从开始到结束通常会经历如下3个阶段。

  • 事务开始
  • 命令入队
  • 事务执行
multi和exec命令

multi命令的使用格式如下:

multi

该命令用于标记一个事务块的开始,事务内的多条命令会按照先后顺序放入一个事务命令队列当中,未来在执行exec命令时作为一个原子性命令被整体执行。

exec命令的使用格式如下:

exec

该命令用于执行事务中所有在事务命令队列中等待的命令。

测试案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set a aa
QUEUED
127.0.0.1:6379(TX)> set b bb
QUEUED
127.0.0.1:6379(TX)> set c cc
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) OK
127.0.0.1:6379> keys *
1) "b"
2) "a"
3) "c"
127.0.0.1:6379>
discard命令

discard命令的使用格式如下:

discard

该命令用于取消一个事务中所有在事务命令队列中等待的命令,也就是取消事务,放弃执行事务块内的所有命令。

测试案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set a aa
QUEUED
127.0.0.1:6379(TX)> set b bb
QUEUED
127.0.0.1:6379(TX)> set c cc
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>

执行了discard命令取消事务之后再执行exec命令出现了异常,因为没有事务环境了。

watch命令

使用格式如下:

watch key [key ..]

该命令用于监视指定的key来实现乐观锁。

什么是乐观锁?乐观锁是一种并发控制的方法,在提交数据更新之前,事务会先检查在该事务读取数据后,是否有其他事务修改了该数据,如果其他事务修改了数据的话,正在提交的事务会被取消。如果两个人同时要对第3个人实现转账操作,为了实现金额的累加,可以使用乐观锁。

Redis中watch的机制原理:使用watch命令监视一个或多个key,跟踪key的value修改情况,如果某个key的value在事务执行之前被修改了,那么整个事务被取消,返回提示信息,内容是事务已经失败。watch机制使事务执行变得有条件,事务只有在key没有被修改的前提下才能成功执行提交操作,如果不满足条件,事务被取消。乐观锁能够很好地解决数据冲突地问题。一句话总结:只要value被修改了,就取消事务的执行。

使用watch命令监视一个带TTL的key,那么即使这个key超时了,事务仍然可以正常执行。

测试案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set a aa
OK
127.0.0.1:6379> watch a
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set b bb
QUEUED
127.0.0.1:6379(TX)> set a AA
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
127.0.0.1:6379> keys *
1) "b"
2) "a"
127.0.0.1:6379>

这里value如果被修改了,更新事务就取消了。

何时取消key的监视?

  • watch命令可以被调用多次,对key的监视从watch命令执行之后开始生效,直到调用exec命令为止。不管事务是否执行成功,对所有key的监视都会被取消。
  • 当客户端断开连接时,该客户端对key的监视也会被取消。
  • unwatch命令可以手动取消对所有key的监视。
  • 调用discard命令时,如果已使用watch命令,则discard命令将释放所有被监视的key。
unwatch命令

使用格式如下:

unwatch

该命令用于取消watch命令所有key的监视。在执行watch命令之后,如果exec命令或discard命令先被执行了的话,就不需要手动执行unwatch命令了。

事务之内执行unwatch命令是无效的。

数据持久化

Redis中的数据默认存储在内存中的,因此断电或死机等不可抗的原因造成数据丢失是非常严重的后果。Redis支持将内存中的数据持久化到硬盘中,实现数据的持久化(Persistence)。

Redis实现数据持久化有3种方式,便于发生故障后能迅速恢复数据。

  • Redis数据库(Redis DataBase,RDB):RDB持久化数据其实就是持久化内存的快照,将内存种的数据整体持久化到硬盘上的二进制RDB文件中,相当于全量持久化。RDB方式持久化数据非常的占用内存,如果内存中待持久化的数据大小为4GB,则至少要有4GB的空闲内存作为数据持久化的交换空间,所以需要的总内存大小就是8GB。Redis默认启用RDB。

  • 扩展文件(Append-Only File,AOF):相当于增量持久化,把对Redis操作的命令保存进AOF文件中,重启Reids服务器时再从AOF文件中执行相应命令,实现还原数据的效果。当RDB文件和AOF文件同时存在时,优先加载AOF文件。

    RDB和AOF区别如下:

    AOF会把每一次写数据库的命令都同步到AOF文件中,AOF文件中的命令与内存中的数据一 一对应。

    RDB只把当前内存中的数据存放到RDB文件中,当对Redis文件中的数据再次修改时,只将内存中的数据进行修改,变成新数据,而RDB文件中的数据依然是旧的。

  • RDB和AOF混合:Redis4.0之后支持此种方式,也就是现在Redis版本默认启用的。

使用RDB实现数据持久化

使用RDB实现数据持久化可以有3种方式。

  • save配置选项:达到某一条件时执行数据持久化,自动方式。
  • SAVE命令:同步执行数据持久化,手动方式。
  • BGSAVE命令:异步执行数据持久化,手动方式。
自动方式:save配置选项

image-20220327134831375

上面配置save<seconds><changes>的作用是指在指定的seconds时间内,如果对各个数据库总共发生了changes次更改,就调用bgsave命令把当前内存中的数据以rdbcompression yes压缩的方式存储到路径 dir ./、文件名为dbfilename dump.rdb的文件中进行持久化。

  • ./:代表当前路径。
  • save 3600 1 代表3600s时间内有一次更改就开始RDB持久化。
  • save 300 100 代表300s时间内有100次更改就开始RDB持久化。
  • save 60 10000 代表60s时间内有1000次更改就开始RDB持久化。

设置save配置选项时,seconds、changes这两个参数需要根据业务需求来确定,设置时间太短虽然可以减少数据丢失的量,但浪费CPU资源,而设置时间太长虽然节省了CPU资源。但会出现数据大量丢失的情况。

手动方式:使用save命令

save命令具有同步性,当命令执行后Redis呈阻塞状态,会把内存中全部数据库的全部数据保存到新的RDB文件中。

持久化数据期间Redis呈现阻塞状态,不在执行客户端命令,知道生成RDB文件为止。持久化结束后删除旧的RDB文件,使用新的RDB文件。

使用save命令

测试案例

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> set 1 11
OK
127.0.0.1:6379> set 2 22
OK
127.0.0.1:6379> set 3 33
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379>
#在执行save命令之前没有dump.rdb文件,执行之后就创建它。
手动方式:使用bgsave命令

save命令具有同步性,在数据持久化期间,Redis不能执行其他客户端的命令,这降低了系统吞吐量,而bgsave命令是save命令的异步版本。

当bgsave命令执行后会创建子进程,子进程执行save命令把内存中全部数据库的全部数据保存到新的RDB文件中。持久化数据期间,Redis不呈现阻塞状态,可以接收新的命令。持久化结束后删除旧的RDB文件,使用新的RDB文件。

bgsave命令和save命令一样,也存在丢失数据的可能性,也就是在最后一次成功完成RDB持久化后的数据将会丢失。

小结

RDB的优点:使用RDB文件直接存储二进制数据,所以恢复数据比AOF速度快。

RDB的缺点如下:

  • 可能会丢失数据。丢失最后一次持久化以后更改的数据。如果应用能容忍一定程序的数据丢失,那么使用RDB是不错的选择;如果不能容忍一定程序的数据丢失,那么使用RDB就不是一个很好的选择。
  • 使用bgsave命令持久化数据时会创建一个新的子进程,如果Redis的数据量很大,那么子进程会占用比较多的CPU资源,并且在获取内存快照时会将Redis服务器停一段时间(毫秒级别)。如果数据量非常大而且硬件配置较差,可能出现暂停数秒的情况。
使用AOF实现数据持久化

使用AOF持久化数据时,Redis每次接收一条更改数据的命令时,都将把该命令写到一个AOF文件中(只记录写操作,读操作不记录)。当Redis重启时,它通过执行AOF文件中所有的命令来恢复数据。AOF的有点是比RDB丢失的数据少一些。另外,由于AOF文件存储Redis的命令,而不像RDB文件存储数据二进制,因此使用AOF还原数据时比RDB要慢很多。

测试案例

更改redis.conf配置文件,禁止RDB,只用AOF实现数据持久化。

在redis.cong配置文件中的“APPEND ONLY MODE”节点下有AOF的相关配置

image-20220327141815684

image-20220327141847208

image-20220327141922120

将配置appendonly no 改为appendonly yes,因为默认情况下AOF方式是不启用的。

配置appendfilename “apendonly.aof”指定使用哪个 AOF文件来存储命令。将配置aof-use-rdb-preamble yes改成 aof-use-rdb-preamble no,因为默认情况下采用RDB和AOF混合方式,使用no值后只使用AOF方式。

如果存在dump,rdb文件或aooendonly,aof文件则删除。重启Redis服务器。

存在数据丢失的可能性

向AOF文件同步命令是对AOF文件的写操作,现代操作系统为了提高写操作的效率,会将多次写操作最终转化成一次写操作。原理就是将多次写入的数据放入缓存区中,达到某个写入的条件时一次性将数据写入硬盘中,提高程序运行的效率,而选项appendfsync的作用就是配置向AOF文件执行写入命令的方式,Redis提供3中方式。

  • no:不主动进行同步操作,而是完全交由操作系用来做。比较快但不是很安全,会丢失最后一次写操作之后所有的写入命令。
  • always:每次执行写入命令都会同步到AOF文件中。此种方式比较慢,但是比较安全,在生产环境下不建议使用。
  • everysec:每秒执行一次同步操作。此种方式比较平衡,由于兼顾性能和安全,因此是默认选项,也是推荐使用的选项。它会丢失最后一秒未持久化的数据。
重写机制

前面的AOF文件中保存着大量操作Redis的命令,其中就包括del命令。其实在还原数据时只需要set a aa和set b bb命令,从原始数据效率上的考虑出发,set c cc和del c命令可以从AOF文件中删除,那么就有必要让Redis服务器重启时读取最精简版的AOF文件,没有其他多余的命令,也就是要把多余的命令过滤删除。这时就要创建最精简版的AOF文件,此过程在Redis中被称为“AOF”文件重写机制。

重写机制可以将多个命令缩写成一个,还可以对超时的数据不再恢复。重写机制的原理是开启新的进程,新的进程不读取旧版的AOF文件,而是直接把内存中的数据转化成最新版的AOF文件,完成后再对旧版AOF文件按进行覆盖,达到了AOF文件内容的精简。

手动方式实现重写机制案例

1
2
3
127.0.0.1:6379> bgrewriteAOF
Background append only file rewriting started
127.0.0.1:6379>

自动方式实现重写机制

redis.conf配置文件中的选项auto-AOF-rewrite-min-size的作用是设置重写的最小AOF文件的大小,默认是64MB。当AOF文件大小大于64MB时,开始重写AOF。目标是缩小AOF文件的大小。

redis.conf配置文件中的选项auto-AOF-rewrite-oercentage 100的作用是设置文件大小增大多少比例触发重写。该选项表示当前AOF文件的大小比最后一次执行AOF重写后增加了一倍(100%),则触发重写。

小结

AOF和RDB同时开启,并且存在AOF文件时,优先加载AOF文件。

AOF关闭或者没有AOF文件时,加载RDB文件。

AOF是另一个数据持久化的方案。AOF文件会在操作过程中变得越来越大,因为有很多命令是无用的,如查询命令,但Redis支持在不影响服务的前提下在后台重写AOF文件,让AOF文件得以变小。

AOF的优点是丢失的数据在理论上比RDB少,允许丢失最后1s内的数据。

AOF的缺点如下:

  • 由于AOF文件存储的是写命令,因此文件大小较大。
  • 由于RDB文件存储的是二进制的数据,因此恢复数据比AOF要快。AOF恢复数据慢。

RDB会在满足某个save配置条件时自动持久化,而AOF是根据appendfsync配置自动持久化。

RDB和AOF都有优缺点,可以两者结合使用,互相弥补。

使用RDB和AOF混合实现数据持久化

使用RDB和AOF混合实现数据持久化时,会在AOF文件的开头保存RDB格式的数据,然后保存AOF格式的命令。

使用shutdown命令正确停止Redis服务

正确停止Redis服务要使用shutdown命令,该命令执行后将执行接收新的请求,并且开始执行持久化操作,完成后销毁Redis进程。

不要暴力地销毁Redis进程,这样做会丢失数据。

复制

新版本地Redis已经将复制(Replication) 架构地名称由原来的主-从(Master-Slave),改未主-副本(Master-Replica),所以Slave和Replica在Redis中是一样的。

Redis可以对相同的数据创建多个副本,这些副本数据存放在其他服务器中,这样在数据恢复、负载均衡、读写分离等场景中非常有利。

一个Master服务器作为主节点可以有多个Replica服务器作为副本节点,但每个副本节点只能有一个主节点,Master架构类似于树形结构。如图:

image-20220327151024210

在一主多副本的架构中,默认情况下可以对Master服务器执行读写操作,而对Replica服务器执行查询、读取的操作,这就是经典的“读写分离”方案。Replica服务器越多,读的性能就越好,因为相同的数据分散在不同Replica服务器上,减轻了每台Peplica服务器的读的压力。

Master服务器对Replica服务器进行数据传输时是非阻塞的,代表Master服务器可以一边传输数据给Replica服务器,一边执行客户端发送过来的读写命令。Replica服务器在接收数据期间也是非阻塞的,可以一边接收数据一边执行其他客户端发送过来的读的请求。

默认情况下,Replica服务器只可以执行读操作,但可以对Replica服务器开放写权限。但建议不要这样做,如果Master服务器和Replica服务器恰好有相同的Key,则Master服务器的数据会把Replica服务器中的数据覆盖。如果对key的命名有好的规划,那么可以这样做。

有些情况下必须对Replica服务器开放写权限,如Replica服务器中执行类似ZINTERSTORE统计命令时,建议对key设置TTL,超时后自动删除。对Replica服务器开放写权限需要在redis.conf配置文件中使用配置replica-read-only=no。

当对主节点和副本节点进行关联时,Redis会将副本节点全部的数据进行清空,再对主节点执行写操作,主节点会将数据的变化同步到副本节点上。由于网络慢等原因,主节点和副本节点在某一时间会出现数据不一样的情况,如果要求数据强制一致性,客户端可以直接读取主节点,但在最后主节点和副本节点的数据会完全相同,实现最终一致性。

一主多副本架构的弊端比较明显,就是主节点需要承担更多的任务,如一个主节点在处理业务的同时还需要将数据发送给多个副本节点实现数据的复制,如果传输的数据量较大,很容易造成主节点发给副本节点数据时CPU占用率过高,还会产生网络拥堵,造成主节点性能下降,所以可以对主节点采用“多级级联”架构来疏散网络拥堵。所谓的多级级联就是副本节点再关联一个Replica副本节点,如图:

image-20220327153221319

副本节点Slave1关联了连个副本节点Slave2和Slave3,副本节点Slave1相当于副本节点Slave2和Slave3的主节点,副本节点Slave2和Slave3的数据由Slave1进行传输,Slavel的数据由主节点进行传输,主节点只负责一个节点的数据传输,而不是3个,所以多级级联架构减小了主节点的任务量。根据业务需求,多级级联架构可以继续级联。

复制数据时是由主节点向副本节点进行复制,反之则不支持。针对主节点的任何操作都会同步到副本节点。

实现复制

有3中方式可以实现Master-Replica的数据复制。

  • 在redis.conf配置文件中加入replicaof {masterHost} {masterPort}配置。
  • 对redis-server命令传入–replicaof {masterHost} {masterPort}参数。
  • 在副本节点中使用replicaof {masterHost} {masterPort} ,此种方式的优势是可以动态地创建复制连接。

参数masterHost是主节点地IP地址,参数masterPort是主节点地端口号。

当主节点和副本节点连接上时,主节点会把全部地数据传输到副本节点中。

取消复制

取消复制就是将Replica服务器和Master服务器断开Master-Replica关联。

取消复制地方式就是在Replica服务器使用如下命令:

replicaof no one

手动操作实现故障转移

当Master服务器发生故障时,需要手动对其中一台Replica服务器使用命令replicaof no one将这个Replicca服务器与Master服务器断开关联,目的是将此Replica服务器提升为Master服务器,其他的Replica服务器需要手动执行命令replicaof ip port指向这个新的Master服务器,建立新的关联后开始同步数据。

Master-Replica复制架构的特点如下:

  • 一个Master服务器可以有多个Replica服务器。
  • Replica服务器下线,读请求的处理性能下降,因为Master服务器要同时处理读和写请求。
  • Master服务器下线,写请求无法执行。
  • 在Master-Replica复制架构下,Master服务器A宕机,Replica服务器B还是Replica服务器,Master服务器A恢复运行时,Master-Replica复制架构恢复,又开始工作了。
  • 当Replica服务器B宕机后,重启Replica服务器B时不使用任何的replicaof配置会将Replica服务器B变成另外一个独立的Master服务器C,这时可以在Master服务器C中使用replicaof命令设置Master服务器,将Master服务器C重新变成Replica服务器。
  • Master-Replica复制架构出现故障时需要手动操作,比较繁琐,也不利于运行稳定性,不保证能高可用,但可以使用Redis提供的哨兵功能在出现故障时自动化处理。

哨兵

如果有3台计算机A、B和C,在Master-Replica架构下,A是Master节点,B和C分别是Slave1和Slave2节点,Master-Replica架构如图:

image-20220327163241822

如果Master节点A由于故障不能提供服务,则需要下面4个步骤进行处理。

  • 在Slave1节点B处手动输入replicaof no one命令将Slave1节点B与Master节点A断开,目的是把Slave1节点B作为新的主节点。
  • 更改Java代码,使用新的主节点B的IP地址。
  • 在Slave2节点C处手动输入命令replicaof no one将Slave2节点C与Master节点A断开,并且执行命令replicaof ip port将C的主节点改成B。
  • 重启A计算机后还需要使用replicaof ip port将A的主节点改成B。

这一系列的手动操作大大减弱了软件的高可用性,极大地增加了运维成本,这种情况下可以使用Redis提供的哨兵(Sentinel)以自动化的方式来解决,哨兵是Redis实现高可用性的方案之一。

将不可用的服务器替换成可用的服务器,这种机制被称为故障转移。Redis中的哨兵可以实现自动故障转移。

哨兵是Redis官方提供的保障高可用性的方案,它使用心跳检测的方法来监控多个Redis实例的运行情况。当主节点出现故障时,哨兵能自动完成故障发现和故障转移,这些步骤都是自动化的,不需要手动处理,真正实现了高可用性。哨兵的系统架构如图

image-20220327165118684

每个哨兵服务器(以下简称哨兵)一同监视Master节点、Slave1和Slave2节点,哨兵之间也是互相监视。

如上图中,若干个哨兵一直在监控一个主节点和两个副本节点,哨兵在一起“协商投票”后,确认主节点因为网络等原因出现了故障,并选举一个副本节点作为新的主节点,这个过程都是哨兵自动处理的,不需要人为的干预。

选举算法分为如下3步:

  • 优先级最高的Replica服务器获胜。优先级使用replica-priority 选项进行配置,默认是100,值越小优先级越高。
  • 如果有两个Replica服务器的replica-priority 值一样,则复制数据的偏移量最大的的Replica服务器获胜。
  • 如果复制数据的偏移量一致,则Redis服务器启动时被分配了一个小运行ID的Replica服务器获胜。

建议将哨兵安装到不同的物理计算机上,这样如果有一个哨兵出现了故障,至少还有其他的哨兵在工作,另外使用多个哨兵可以防止误判。

哨兵具有如下的几个功能。

  • 监视:哨兵会监视Master服务器和Replica服务器以及其他的哨兵是否可达。
  • 通知:哨兵会将故障转移的结果通知未Java客户端。
  • 故障转移:实现Replica服务器升级为Master服务器,并且维护后续的Master-Replica关联。
  • 入口提供者:Java客户端连接的是哨兵,通过哨兵来访问Master服务器。
  • 不提供数据保存:哨兵只负责监视,不提供数据保存的服务。

想要实现故障转移,至少需要有3个哨兵。

搭建哨兵环境

可以使用两种方式启动哨兵。

  • redis-sentinel sentinel.conf。
  • redis-server sentinel.conf –sentinel ,参数–sentinel代表启动的Redis服务器是具有监视功能的哨兵,不是普通的Redis服务。

系统中只有一个哨兵可能会出现单点故障,唯一的哨兵出现故障后不能进行故障发现和故障转移,造成整体的Master-Replica环境失效。

监视多个Master服务器

前面的哨兵只监视一个Master服务器,哨兵还可以监视多个Master服务器,监视多个Master服务器的架构如图:

image-20220327171023428

哨兵常用命令

测试哨兵常用命令:

sentinel masters

该命令的作用是显示所有主节点的信息。

sentinel master <master name>

该命令的作用是显示指定别名的主节点信息。

sentinel slaves <master name>

该命令的作用是显示指定别名节点下的所有副本节点相关信息。

sentinel sentinels <master name>

该命令的作用是显示指定别名主节点的哨兵节点集合,不包括当前的哨兵节点。

sentinel get-master-addr-by-name <master name>

该命令的作用是显示指定别名主节点的IP地址和端口号。

sentinel reset <pattern>

该命令的作用是对关联当前哨兵的主节点的配置和环境进行重置,包括清除主节点的相关信息,重新发现副本节点和哨兵节点。

sentinel ckquorum <master name>

该命令的作用是检查当前可达的哨兵节点总数是否到达quorum。如果quorom值是3,而当前可达的哨兵节点个数是为2,那么将无法进行故障转移。

集群

Redis集群(Cluster)分为以下两种。

  • 高可用集群:使用Redis的哨兵和Master-Replica架构实现,一个Master节点可以有个Replica节点,每个Redis实例中的数据保持一致。如图:

    image-20220327173102407

  • 分布式集群:使用Redis Cluster实现,同时有多个Master节点,数据被分片存储在各个Master节点中 =,平衡各个Master节点的压力,另外每个Master节点可以拥有多个Replica。如果使用Redis Cluster存储1~13个数字,则存储结构如下图:

    image-20220327191110229

Redis集群是Redis分布式存储的解决方案,在3.0版本被推出,解决了前面版本只能以单机模式运行的缺点。Redis集群可以有效解决单机内存不够、并发量大、流量大等系统运行的瓶颈。

集群是为了将大量数据分散在不同的计算机中进行存储、每台计算机分别存储一部分数据,每台计算机参与处理请求,将高并发、大流量的场景进行分散,多台计算机的内存支持量会比单机的更大。

使用虚拟槽实现数据分片

Redis集群中的数据采用虚拟槽(Sloat)技术来对数据进行分片,实现分布存储,Redis中一共有16384个槽(0~16383,以下简称槽)。每个虚拟槽(简称槽)代表集群内数据管理和迁移的基本单位。

如存在5台运行Redis实例的服务器,每台服务器都拥有一个槽集合,则存在5个集合。而Redis一共有16384个槽,所以按业务的需要将这16384个槽按需分配给5台服务器,这样每台服务器就可以存储指定槽范围的数据了,槽集合与服务器对应关系如图:

image-20220328191038880

自动搭建本地Redis集群环境

搭建Redis集群环境和搭建Master-Replica或哨兵环境一样,需要准备大量配置文件,但有些时候我们只是想在测试环境中快速把Redis集群环境搭建起来进行使用和测试,如果需要大量配置文件则会影响测试的效率,Redis提供了create-cluster命令来实现这样的需求,该命令在redis/utils/create-cluster路劲中,执行如下命令:

./create-cluster

执行成功有7个参数。参数作用如下:

  • create-cluster start:启动Redis集群实例。
  • create-cluster create:创建Redis集群。
  • create-cluster stop:停止Redis集群实例。
  • create-cluster watch:显示第一个服务器的输出(前30行)。
  • create-cluster tail 1:查看日志信息(1代表第一个服务器)。
  • create-cluster clean:删除所有实例数据,日志和配置文件。
  • create-cluster clean-logs:只删除实例日志文件。

内存淘汰策略

内存淘汰策略有两种算法。

  • LRU过时:删除很久没有访问的数据。
  • LFU过时:删除访问频率最低的数据。

Redis支持的内存淘汰策略有如下8种。

  • noeviction:不淘汰策略,存放的数据大于最大内存限制时返回异常。
  • volatile-lru:对超时的key使用LRU算法。
  • volatile-lfu:对超时的key使用LFU算法。
  • volatile-random:对超时的key使用随机删除策略。
  • volatile-ttl:对超时的key使用TTL最小值删除策略。
  • allkeys-lru:对所有key使用LRU算法。
  • allkeys-lfu:对所有key使用LFU算法。
  • allkeys-random:对所有key使用随机删除。

改变内存淘汰策略可以对redis.conf配置文件的maxmemory-policy属性进行更改。

本章种的案例使用最大内存为1MB,在redis.conf配置文件种使用maxmemory 1mb 进行配置。

当需要设置大小时,单位不区分大小写,1GB、1Gb和1gB的作用一样。


Redis
http://example.com/2022/09/07/NoSQL/
作者
liziyuan
发布于
2022年9月7日
许可协议