Redis超详细入门教程


Redis的持久化机制:https://zhuanlan.zhihu.com/p/77646963

Redis

介绍redis

  1. 概念:
    redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value的NoSQL数据库。

  2. 特征
    (1) 支持数据的持久化,可以将数据保存在磁盘中,重启之后可以再次加载到内存中使用
    (2) 支持多种数据类型,除了KV类型的数据,还支持list、set、hash等数据结构
    (3) 支持master-slave模式的数据备份

  3. 应用场景
    (1) 热点数据加速查询(主要场景),如热点商品、热点信息等访问量较高的数据
    (2) 即时信息查询,如公交到站信息、在线人数信息等
    (3) 时效性信息控制,如验证码控制、投票控制等
    (4) 分布式数据共享,如分布式集群架构中的session分离消息队列
    会话缓存、消息队列(支付)、商品列表、评论列表、发布订阅

redis的发布与订阅(发布/订阅)是它的一种消息通信模式,一方发送信息,一方接收信息。
下图是三个客户端同时订阅同一个频道
在这里插入图片描述
下图是有新信息发送给频道1时,就会将消息发送给订阅它的三个客户端
在这里插入图片描述

【redis数据结构 – 简介】
redis是一种高级的key:value存储系统,其中value支持五种数据类型:

  1. 字符串(strings)
  2. 字符串列表(lists)
  3. 字符串集合(sets)
  4. 有序字符串集合(sorted sets)
  5. 哈希(hashes)

key注意的点:

  1. key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率;
  2. key也不要太短,太短的话,key的可读性会降低;
  3. 在一个项目中,key最好使用统一的命名模式

【redis基础知识】

  • Redis采用单线程机制进行工作
  • Redis默认拥有16个数据库,数据库编号从0开始,默认使用0号数据库
  • 使用select 数据库编号 可以切换使用的数据库
  • dbsize 命令查看当前数据库key的数量
  • keys * 命令查看当前数据库所有的key
  • flushdb 命令清空当前数据库
  • flushall 命令清空所有数据库
  • Redis中所有数据库使用同一个密码,默认没有密码,Redis认为安全层面应该由Linux来保证
  • Redis中所有索引都是从0开始
  • Redis默认端口是6379

安装redis

【学会安装redis】

  1. 去官网下载redis-3.0.4.tar.gz安装包,并放入Linux中的/opt目录
  2. 在/opt目录下,执行解压命令tar -zxvf redis-3.0.4.tar.gz
  3. 解压完成后出现文件夹redis-3.0.4
  4. 进入文件夹redis-3.0.4,在此目录下执行make && make install命令
  5. 进入默认安装目录cd /usr/local/bin,此目录中有如下文件
$ find . -type f -executable
./redis-benchmark //用于进行redis性能测试的工具
./redis-check-dump //用于修复出问题的dump.rdb文件
./redis-cli //redis的客户端,命令行工具
./redis-server //redis的服务端启动命令
./redis-check-aof //用于修复出问题的AOF文件
./redis-sentinel //用于redis集群管理

【学会启动redis】
启动redis非常简单,直接./redis-server就可以启动服务端了,还可以用下面的方法指定要加载的配置文件:

./redis-server ../redis.conf

默认情况下,redis-server会以非daemon的方式来运行,且默认服务端口为6379。

  1. 修改redis配置文件,vim /opt/redis-3.0.4/redis.conf
    在这里插入图片描述
    在这里插入图片描述

  2. 启动redis服务,cd /usr/local/bin,执行redis-server /opt/redis-3.0.4/redis.conf

  3. 查看服务是否启动,ps aux | grep redis-server ,查看到6379

  4. 使用redis命令行工具进行测试redis-cli -p 6379

【使用redis客户端】
我们直接看一个例子:

# redis-cli -p 6379  //这样来启动redis客户端了
127.0.0.1:6379> ping //测试是否连通
PONG

//set指令来设置key、value
127.0.0.1:6379> set name "roc" 
OK
//来获取name的值
127.0.0.1:6379> get name 
"roc"
//通过客户端来关闭redis服务端
127.0.0.1:6379> shutdown 
127.0.0.1:6379>

redis常用查询指令

查询key

key pattern

查询pattern规则:
* 匹配任意数量的任意符号
? 配合一个任意符号
[] 配合一个指定符号

keys *		查询所有
keys it*	查询所有以it开头
keys *heima	查询所有以heima结尾
keys ??heima	查询所有前面两个字符任意,后面以heima结尾
keys user:?		查询所有以user:开头,最后一个字符任意
keys u[st]er:1	查询所有以u开头,以er:1结尾,中间包含一个字母,s或t

redis数据结构

strings

它是redis的最基本的数据类型,一个键对应一个值,需要注意是一个键值最大存储512MB。

set mystr "hello world!" //设置字符串类型
get mystr //读取字符串类型

字符串类型的用法就是这么简单,因为是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储。

另外,我们还可以通过字符串类型进行数值操作:

127.0.0.1:6379> set mynum "2"
OK
127.0.0.1:6379> get mynum
"2"
127.0.0.1:6379> incr mynum
(integer) 3
127.0.0.1:6379> get mynum
"3"

看,在遇到数值操作时,redis会将字符串类型转换成数值。

由于INCR等指令本身就具有原子操作的特性,所以我们完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令来实现原子计数的效果,假如,在某种场景下有3个客户端同时读取了mynum的值(值为2),然后对其同时进行了加1的操作,那么,最后mynum的值一定是5。不少网站都利用redis的这个特性来实现业务上的统计计数需求。

lists

redis的另一个重要的数据结构叫做lists,翻译成中文叫做“列表”。

首先要明确一点,redis中的lists在底层实现上并不是数组,而是链表,也就是说对于一个具有上百万个元素的lists来说,在头部和尾部插入一个新元素,其时间复杂度是常数级别的,比如用LPUSH在10个元素的lists头部插入新元素,和在上千万元素的lists头部插入新元素的速度应该是相同的。

虽然lists有这样的优势,但同样有其弊端,那就是,链表型lists的元素定位会比较慢,而数组型lists的元素定位就会快得多。

lists的常用操作包括LPUSH、RPUSH、LRANGE等。我们可以用LPUSH在lists的左侧插入一个新元素,用RPUSH在lists的右侧插入一个新元素,用LRANGE命令从lists中指定一个范围来提取元素。我们来看几个例子:

//新建一个list叫做mylist,并在列表头部插入元素"1"
127.0.0.1:6379> lpush mylist "1" 
//返回当前mylist中的元素个数
(integer) 1 
//在mylist右侧插入元素"2"
127.0.0.1:6379> rpush mylist "2" 
(integer) 2
//在mylist左侧插入元素"0"
127.0.0.1:6379> lpush mylist "0" 
(integer) 3
//列出mylist中从编号0到编号1的元素
127.0.0.1:6379> lrange mylist 0 1 
1) "0"
2) "1"
//列出mylist中从编号0到倒数第一个元素
127.0.0.1:6379> lrange mylist 0 -1 
1) "0"
2) "1"
3) "2"

lists的应用相当广泛,随便举几个例子:

  1. 我们可以利用lists来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样还需要通过ORDER BY来进行排序。
  2. 利用LRANGE还可以很方便的实现分页的功能。
  3. 在博客系统中,每片博文的评论也可以存入一个单独的list中。

集合

redis的集合,是一种无序的集合,集合中的元素没有先后顺序。

集合相关的操作也很丰富,如添加新元素、删除已有元素、取交集、取并集、取差集等。我们来看例子:

//向集合myset中加入一个新元素"one"
127.0.0.1:6379> sadd myset "one" 
(integer) 1
127.0.0.1:6379> sadd myset "two"
(integer) 1
//列出集合myset中的所有元素
127.0.0.1:6379> smembers myset 
1) "one"
2) "two"
//判断元素1是否在集合myset中,返回1表示存在
127.0.0.1:6379> sismember myset "one" 
(integer) 1
//判断元素3是否在集合myset中,返回0表示不存在
127.0.0.1:6379> sismember myset "three" 
(integer) 0
//新建一个新的集合yourset
127.0.0.1:6379> sadd yourset "1" 
(integer) 1
127.0.0.1:6379> sadd yourset "2"
(integer) 1
127.0.0.1:6379> smembers yourset
1) "1"
2) "2"
//对两个集合求并集
127.0.0.1:6379> sunion myset yourset 
1) "1"
2) "one"
3) "2"
4) "two"

对于集合的使用,也有一些常见的方式,比如,QQ有一个社交功能叫做“好友标签”,大家可以给你的好友贴标签,比如“大美女”、“土豪”、“欧巴”等等,这时就可以使用redis的集合来实现,把每一个用户的标签都存储在一个集合之中。

有序集合

redis不但提供了无需集合(sets),还很体贴的提供了有序集合(sorted sets)。有序集合中的每个元素都关联一个序号(score),这便是排序的依据。

很多时候,我们都将redis中的有序集合叫做zsets,这是因为在redis中,有序集合相关的操作指令都是以z开头的,比如zrange、zadd、zrevrange、zrangebyscore等等

老规矩,我们来看几个生动的例子:

//新增一个有序集合myzset,并加入一个元素baidu.com,给它赋予的序号是1127.0.0.1:6379> zadd myzset 1 baidu.com 
(integer) 1
//向myzset中新增一个元素360.com,赋予它的序号是3
127.0.0.1:6379> zadd myzset 3 360.com 
(integer) 1
//向myzset中新增一个元素google.com,赋予它的序号是2
127.0.0.1:6379> zadd myzset 2 google.com 
(integer) 1
//列出myzset的所有元素,同时列出其序号,可以看出myzset已经是有序的了。
127.0.0.1:6379> zrange myzset 0 -1 with scores 
1) "baidu.com"
2) "1"
3) "google.com"
4) "2"
5) "360.com"
6) "3"
//只列出myzset的元素
127.0.0.1:6379> zrange myzset 0 -1 
1) "baidu.com"
2) "google.com"
3) "360.com"

哈希

哈希是从redis-2.0.0版本之后才有的数据结构。

hashes存的是字符串和字符串值之间的映射,适合用于存储对象,比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。

我们来看一个例子:

//建立哈希,并赋值
127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34 
OK
//列出哈希的内容
127.0.0.1:6379> HGETALL user:001 
1) "username"
2) "antirez"
3) "password"
4) "P1pp0"
5) "age"
6) "34"
//更改哈希中的某一个值
127.0.0.1:6379> HSET user:001 password 12345 
(integer) 0
//再次列出哈希的内容
127.0.0.1:6379> HGETALL user:001 
1) "username"
2) "antirez"
3) "password"
4) "12345"
5) "age"
6) "34"

有关hashes的操作,同样很丰富,需要时,大家可以从这里查询。
https://redis.io/commands#hash

Redis实践案例

  1. 业务场景
    在微信聊天过程中,当接收到信息时,会将最新的消息显示在窗口顶端,当收到不同用户的消 息时,会因为是否置顶以及消息到达的先后顺序产生不同的显示顺序

  2. 业务分析
    假设100这位用户将400与500两位好友消息置顶,当收到多个好友的消息时,它的微信聊天 窗口会产生怎样的显示顺序呢?

  3. 解决思路
    (1) 将400与500两位用户放入set中(因为不重复),表示置顶好友
    (2) 创建两个list列表(因为有顺序),分别表示置顶用户的消息列表与非置顶用户的消息列表
    (3) 将上述两个list列表当作栈来使用
    (4) 当300用户给100发送消息时,检测到300用户并不属于set集合,将其压入普通list栈中
    在这里插入图片描述

(5) 当200用户和400用户给100发送消息时,同理得图
在这里插入图片描述

(6) 当300用户再次给100发送消息时,判断得到300的消息需要压入普通list栈中,首先将栈 中原来存在的300用户删除,此时200用户到达原来300用户的位置,然后将新的300用 户的消息压入栈中
在这里插入图片描述

Redis持久化

持久化

  1. 意外断电或重启之后,内存中的数据将会丢失,故应当将内存中的数据保存在磁盘中
  2. 概念:
    利用磁盘等将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化

持久化的两种方式
(1) 快照
将某个时间点的工作状态保存下来,恢复时可直接恢复指定时间点的工作状态
Redis中这种方式称为RDB

(2) 日志
将对数据的所有操作过程记录下来,恢复数据时重新执行这些操作
Redis中这种方式称为AOF

【聊聊redis持久化 – 两种方式】

redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。

  • RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;
  • AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。

其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。

如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。

【聊聊redis持久化 – RDB】

RDB方式,是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法。

redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的。

对于RDB方式,redis会单独创建(fork)一个子进程来进行持久化,而主进程是不会进行任何IO操作的,这样就确保了redis极高的性能。

如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。

【RDB设置】
1、对redis.conf配置文件进行修改(修改配置文件后需要重启Redis)
(1) 修改内存中数据保存的文件的名称,默认值为dump.rdb
在这里插入图片描述
(2) 修改rdb文件保存的目录
在这里插入图片描述
2、执行save指令即可将内存中的数据保存到/opt/redis-3.0.4/目录的dump.rdb文件中
3、再次启动redis服务即可自动读取rdb文件中的数据并加载到内存
4、save指令工作原理
Redis是单线程的,故执行save指令会阻塞其之后的命令的执行(可能多人操作同一个Redis 服务器),如果要保存的数据较多时,会导致之后的命令长时间阻塞,故一般不使用save指令

5、bgsave指令可以让保存操作在后台执行,让redis服务可以继续执行其之后的指令,使用较多
6、bgsave指令工作原理
在这里插入图片描述
7、配置自动保存 (修改配置文件后需要重启Redis)
在这里插入图片描述
8、自动保存方式的注意点
(1) get操作没有导致key发生变化
(2) 对存在的key修改才算发生变化
(3) set k1 v1,set k1 v1认为key的值发生变化
(4) 配置方式执行的是bgsave指令

9、RDB两种指令的对比
在这里插入图片描述
 
RDB缺点
(1) 基于快照思想,每次读写都是全部数据,当数据量较大时,效率非常低
(2) 基于fork创建子进程,内存产生额外的消耗
(3) 宕机带来数据丢失风险(可能某个时间点的数据未保存)

虽然RDB有不少优点,但它的缺点也是不容忽视的。如果你对数据的完整性非常敏感,那么RDB方式就不太适合你,因为即使你每5分钟都持久化一次,当redis故障时,仍然会有近5分钟的数据丢失。所以,redis还提供了另一种持久化方式,那就是AOF。
 


 
【聊聊redis持久化 – AOF】

AOF,英文是Append Only File,即只允许追加不允许改写的文件。

如前面介绍的,AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍。
(以日志的方式记录每次操作的命令,重启之后执行AOF中保存的命令恢复数据)
在这里插入图片描述
【AOF设置】
我们通过配置redis.conf中的appendonly yes就可以打开AOF功能。如果有写操作(如SET等),redis就会被追加到AOF文件的末尾。
在这里插入图片描述

【AOF执行策略】
(1) everysec (每秒)
每秒将缓冲区的指令写入aof文件中,宕机会丢失0-1秒的数据,性能高,建议使用

(2) always (每次)
每次执行指令都将其写入aof文件中,数据零失误,性能较低,不建议使用

(3) no (系统控制)
由操作系统控制写入aof文件的时间,不建议使用

注意:
i. 只有使得key变化的指令才记录
ii. 重启之后自动从aof文件中读取指令并执行
iii. select指令虽然没有对key进行修改,但仍需记录,以知道数据存储的位置

  • 默认的AOF持久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),因为在这种情况下,redis仍然可以保持很好的处理性能,即使redis故障,也只会丢失最近1秒钟的数据。

  • 如果在追加日志时,恰好遇到磁盘空间满、inode满或断电等情况导致日志写入不完整,也没有关系,redis提供了redis-check-aof工具,可以用来进行日志修复。

【AOF重写】

  1. 概念
    AOF文件中已经记录的对同一数据的若干条操作的记录转换为数据最终结果对应指令的记录
    在这里插入图片描述

  2. AOF重写作用
    (1)降低磁盘占用量,提高磁盘利用率
    (2)提高持久化效率,降低持久化写时间,提高IO性能
    (3)降低数据恢复用时,提高数据恢复效率

  3. 为防止数据流过大导致缓冲区溢出,合并后的每条指令最多写入64个元素

  4. AOF重写方式
    (1) 手动重写,执行bgrewriteaof指令
    在这里插入图片描述
    ①在重写即将开始之际,redis会创建(fork)一个“重写子进程”,这个子进程会首先读取现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
    ②与此同时,主工作进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。
    ③当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中。
    ④当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指令,就都会追加到新的AOF文件中了。
     
    (2) 自动重写,修改配置文件
    在这里插入图片描述

因为采用了追加方式,如果不做任何处理的话,AOF文件会变得越来越大,为此,redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。举个例子或许更形象,假如我们调用了100次INCR指令,在AOF文件中就要存储100条指令,但这明显是很低效的,完全可以把这100条指令合并成一条SET指令,这就是重写机制的原理。

在进行AOF重写时,仍然是采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性,这点大家可以放心。

AOF方式的另一个好处,我们通过一个“场景再现”来说明。某同学在操作redis时,不小心执行了FLUSHALL,导致redis内存中的数据全部被清空了,这是很悲剧的事情。不过这也不是世界末日,只要redis配置了AOF持久化方式,且AOF文件还没有被重写(rewrite),我们就可以用最快的速度暂停redis并编辑AOF文件,将最后一行的FLUSHALL命令删除,然后重启redis,就可以恢复redis的所有数据到FLUSHALL之前的状态了。是不是很神奇,这就是AOF持久化方式的好处之一。但是如果AOF文件已经被重写了,那就无法通过这种方法来恢复数据了。

虽然优点多多,但AOF方式也同样存在缺陷,比如在同样数据规模的情况下,AOF文件要比RDB文件的体积大。而且,AOF方式的恢复速度也要慢于RDB方式。

如果你直接执行BGREWRITEAOF命令,那么redis会生成一个全新的AOF文件,其中便包括了可以恢复现有数据的最少的命令集。

如果运气比较差,AOF文件出现了被写坏的情况,也不必过分担忧,redis并不会贸然加载这个有问题的AOF文件,而是报错退出。这时可以通过以下步骤来修复出错的文件:

1.备份被写坏的AOF文件
2.运行redis-check-aof –fix进行修复
3.用diff -u来看下两个文件的差异,确认问题点
4.重启redis,加载修复后的AOF文件


RDB与AOF的选择

  1. 对比
    在这里插入图片描述
  2. 选择策略
    在这里插入图片描述

Redis性能

【Redis性能测试】
在这里插入图片描述
实际测试同时执行100万的请求
在这里插入图片描述

【Redis性能监控】
https://blog.csdn.net/qq_44015579/article/details/112009805
 

Redis主从

【主从 — 介绍】
像MySQL一样,redis是支持主从同步的,而且也支持一主多从以及多级从结构。

主从结构,一是为了纯粹的冗余备份,二是为了提升读性能,比如很消耗性能的SORT就可以由从服务器来承担。

redis的主从同步是异步进行的,这意味着主从同步不会影响主逻辑,也不会降低redis的处理性能。

主从架构中,可以考虑关闭主服务器的数据持久化功能,只让从服务器进行持久化,这样可以提高主服务器的处理性能。

在主从架构中,从服务器通常被设置为只读模式,这样可以避免从服务器的数据被误修改。但是从服务器仍然可以接受CONFIG等指令,所以还是不应该将从服务器直接暴露到不安全的网络环境中。如果必须如此,那可以考虑给重要指令进行重命名,来避免命令被外人误执行。

【主从 — 同步原理】
从服务器会向主服务器发出SYNC指令,当主服务器接到此命令后,就会调用BGSAVE指令来创建一个子进程专门进行数据持久化工作,也就是将主服务器的数据写入RDB文件中。在数据持久化期间,主服务器将执行的写指令都缓存在内存中。
在BGSAVE指令执行完成后,主服务器会将持久化好的RDB文件发送给从服务器,从服务器接到此文件后会将其存储到磁盘上,然后再将其读取到内存中。这个动作完成后,主服务器会将这段时间缓存的写指令再以redis协议的格式发送给从服务器。

另外,要说的一点是,即使有多个从服务器同时发来SYNC指令,主服务器也只会执行一次BGSAVE,然后把持久化好的RDB文件发给多个下游。在redis2.8版本之前,如果从服务器与主服务器因某些原因断开连接的话,都会进行一次主从之间的全量的数据同步;而在2.8版本之后,redis支持了效率更高的增量同步策略,这大大降低了连接断开的恢复成本。

主服务器会在内存中维护一个缓冲区,缓冲区中存储着将要发给从服务器的内容。从服务器在与主服务器出现网络瞬断之后,从服务器会尝试再次与主服务器连接,一旦连接成功,从服务器就会把“希望同步的主服务器ID”和“希望请求的数据的偏移位置(replication offset)”发送出去。主服务器接收到这样的同步请求后,首先会验证主服务器ID是否和自己的ID匹配,其次会检查“请求的偏移位置”是否存在于自己的缓冲区中,如果两者都满足的话,主服务器就会向从服务器发送增量内容。

增量同步功能,需要服务器端支持全新的PSYNC指令。这个指令,只有在redis-2.8之后才具有。

Redis事务

众所周知,事务是指“一个完整的动作,要么全部执行,要么什么也没有做”。

在聊redis事务处理之前,要先和大家介绍四个redis指令,即MULTI、EXEC、DISCARD、WATCH。这四个指令构成了redis事务处理的基础。

  1. MULTI用来组装一个事务;
  2. EXEC用来执行一个事务;
  3. DISCARD用来取消一个事务;
  4. WATCH用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行。

纸上得来终觉浅,我们来看一个MULTI和EXEC的例子:

redis> MULTI //标记事务开始
OK
redis> INCR user_id //多条命令按顺序入队
QUEUED
redis> INCR user_id
QUEUED
redis> INCR user_id
QUEUED
redis> PING
QUEUED
redis> EXEC //执行
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG

在上面的例子中,我们看到了QUEUED的字样,这表示我们在用MULTI组装事务时,每一个命令都会进入到内存队列中缓存起来,如果出现QUEUED则表示我们这个命令成功插入了缓存队列,在将来执行EXEC时,这些被QUEUED的命令都会被组装成一个事务来执行。

对于事务的执行来说,如果redis开启了AOF持久化的话,那么一旦事务被成功执行,事务中的命令就会通过write命令一次性写到磁盘中去,如果在向磁盘中写的过程中恰好出现断电、硬件故障等问题,那么就可能出现只有部分命令进行了AOF持久化,这时AOF文件就会出现不完整的情况,这时,我们可以使用redis-check-aof工具来修复这一问题,这个工具会将AOF文件中不完整的信息移除,确保AOF文件完整可用。

有关事务,大家经常会遇到的是两类错误:
1.调用EXEC之前的错误
2.调用EXEC之后的错误

“调用EXEC之前的错误”,有可能是由于语法有误导致的,也可能时由于内存不足导致的。只要出现某个命令无法成功写入缓冲队列的情况,redis都会进行记录,在客户端调用EXEC时,redis会拒绝执行这一事务。(这时2.6.5版本之后的策略。在2.6.5之前的版本中,redis会忽略那些入队失败的命令,只执行那些入队成功的命令)。我们来看一个这样的例子:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> haha //一个明显错误的指令
(error) ERR unknown command 'haha'
127.0.0.1:6379> ping
QUEUED
127.0.0.1:6379> exec
//redis无情的拒绝了事务的执行,原因是“之前出现了错误”
(error) EXECABORT Transaction discarded because of previous errors.

而对于“调用EXEC之后的错误”,redis则采取了完全不同的策略,即redis不会理睬这些错误,而是继续向下执行事务中的其他命令。这是因为,对于应用层面的错误,并不是redis自身需要考虑和处理的问题,所以一个事务中如果某一条命令执行失败,并不会影响接下来的其他命令的执行。我们也来看一个例子:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 23
QUEUED
//age不是集合,所以如下是一条明显错误的指令
127.0.0.1:6379> sadd age 15 
QUEUED
127.0.0.1:6379> set age 29
QUEUED
127.0.0.1:6379> exec //执行事务时,redis不会理睬第2条指令执行错误
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379> get age
"29" //可以看出第3条指令被成功执行了

好了,我们来说说最后一个指令“WATCH”,这是一个很好用的指令,它可以帮我们实现类似于“乐观锁”的效果,即CAS(check and set)。

WATCH本身的作用是“监视key是否被改动过”,而且支持同时监视多个key,只要还没真正触发事务,WATCH都会尽职尽责的监视,一旦发现某个key被修改了,在执行EXEC时就会返回nil,表示事务无法触发。

127.0.0.1:6379> set age 23
OK
127.0.0.1:6379> watch age //开始监视age
OK
127.0.0.1:6379> set age 24 //在EXEC之前,age的值被修改了
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 25
QUEUED
127.0.0.1:6379> get age
QUEUED
127.0.0.1:6379> exec //触发EXEC
(nil) //事务无法被执行

 

-------分界线-------

Redis配置

https://blog.csdn.net/liqingtx/article/details/60330555

Redis集群

https://blog.csdn.net/qq_42815754/article/details/82912130

Redis面试题

https://blog.csdn.net/ThinkWon/article/details/103522351
https://blog.csdn.net/Design407/article/details/103242874

Guess you like

Origin blog.csdn.net/qq_39578545/article/details/115099458