一、redis订阅发布模式
1.1 简介
redis 可以做消息中间件(MQ =message queue),通常通过订阅发布模式来实现(消息订阅发布模式),还可以基本数据类型Lists实现(点到点模式,可以使用lpush,lpop 实现消息先进先出)。
1.2 订阅发布模式示意图
1.3 订阅发布模式好处—— 解耦、异步、削峰
1.3.1 解耦
A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果C 系统现在不需要了呢?A 系统负责人几乎崩溃......A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦。
1.3.2 异步
A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 +200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求。如果使用MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms。
1.3.3 削峰
减少高峰时期对服务器压力。生产者服务器性能非常好,同一时刻能接受大量并发请求(假如并发3000请求),假如消费者服务器性能不好(并发300请求),突然生产者搞了秒杀活动,并发量突然增加,可能让消费者宕机。这时候有了消息中间,生产者直接把消息放入中间件,再由消费者根据自身性能慢慢消费。
1.4 redis订阅发布模式具体实现
发送者(发布者)不是计划发送消息给特定的接收者(订阅者)。而是发布的消息分到不同的频道,不需要知道什么样的订阅者订阅。订阅者对一个或多个频道感兴趣,只需接收感兴趣的消息,不需要知道什么样的发布者发布的。这种发布者和订阅者的解耦合可以带来更大的扩展性和更加动态的网络拓扑。
启动redis服务端:
/usr/redis/bin/redis-server /usr/redis/bin/redis.conf
启动redis客户端:
/usr/redis/bin/redis-cli -h 192.168.140.41
1、模拟订阅者,订阅某一个或者多个管道(订阅2个频道(管道)):
192.168.140.41:6379> subscribe aaa bbb
1)消息类型 2) 频道名称 3)代表我们现在订阅的频道的数量
双击新建会话,再启动redis一个客户端:
/usr/redis/bin/redis-cli -h 192.168.140.41
模拟发布者,发布消息到一个管道:
92.168.140.41:6379> publish aaa 111
观察消费者窗口,会发现出来订阅到消息:
1) 消息类型 2)来源频道的名称 3) 实际消息的内容
2、 发布者模拟多次消息发布:
订阅者会接受到多个频道多条消息
3、多个发布者和订阅者
启动多个会话 模拟多个订阅者:(此处启用两个)
/usr/redis/bin/redis-cli -h 192.168.140.41
192.168.140.41:6379> subscribe aaa ccc
/usr/redis/bin/redis-cli -h 192.168.140.41
192.168.140.41:6379> subscribe bbb ccc
启动多个会话 模拟多个发布者:(此处在模拟一个)
/usr/redis/bin/redis-cli -h 192.168.140.41
192.168.140.41:6379> publish ccc 4444
订阅者会接受到多个频道多条消息
4、数据库(默认分为16个库)和作用域:
发布/订阅与key所在空间没有关系,它不会受任何级别的干扰,包括不同数据库编码。
启动客户端:
/usr/redis/bin/redis-cli -h 192.168.140.41
切换数据库:
select 10
在第11库订阅频道:
192.168.140.41:6379[10]> subscribe aaaa
发布者使用第1库和第3库 发布消息:
192.168.140.41:6379> publish aaaa test1aaaa
(integer) 2
192.168.140.41:6379> select 3
OK
192.168.140.41:6379[2]> publish aaaa test2bbbb
(integer) 2
在第1库和第3库 发布的消息,在第1和10库订阅中都能看到
5、模糊匹配订阅
客户端可以订阅全风格的模式以便接收所有来自能匹配到给定模式的频道的消息
psubscribe p=pattern 模式
启动一个客户端,模拟订阅者:
/usr/redis/bin/redis-cli -h 192.168.140.41
使用psubscribe命令,订阅所有以a开头的频道(不具体订阅频道):
192.168.140.41:6379> psubscribe a*
启动一个客户端,模拟发布者:
/usr/redis/bin/redis-cli -h 192.168.140.41
使用publish命令,向a开头的频道名称上发送消息
二、持久化
2.1 概念
redis本身是基于内存存储,平时进行读写数据都是在内存中,直接和内存交互。但是redis支持持久化,把内存数据保存磁盘上,到达持久化的效果。
redis支持的持久化方式有两种:
1) rdb(redis database) 底层将内存中的数据根据条件定时保存到磁盘上。(a=1......);官网:持久化方式能够在指定的时间间隔能对你的数据进行快照存储.
2) aof(append of file) 底层将执行的命令追加日志尾部,当需要恢复数据时,需要重新执行这些命令,数据才会恢复。(set a=1......);官网:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
2.2 具体实现
2.2.1 RDB方式
redis server 主进程仍然进行缓存读写,分叉出一个子进程,在满足配置的策略时定时,保存到临时文件,保存完毕后,覆盖原来文件(15分钟,5分钟或者1分钟之前文件)
vim /usr/redis/bin/redis.conf
:196-220 行
Save the DB on disk:
save <seconds> <changes>
Will save the DB if both the given number of seconds and the given
number of write operations against the DB occurred.
根据给定的秒数和 key更新的数量(写操作的次数) 来保存数据
save 900 1 在900秒(15分钟)之内 内存中的所有数据有1个key发生变化 进行一次rdb
save 300 10 在300秒(5分钟)之内 内存中的所有数据有10个key发生变化 进行一次rdb
save 60 10000 在60秒(1分钟)之内 内存中的所有数据有10000个key发生变化 进行一次rdb (redis性能非常好,每秒写的速度可以达到81000次/秒 1分钟 内理论上支持81000*60 =4860000 key发生变化的 )
rdb保存到磁盘文件名称为:
:253 dump.rdb
2.2.2 AOF方式
客户端执行命令时,redis server 会把执行命令 按照配置策略(3钟),写入aof文件中(appendonly.aop)
:699 appendonly yes 开启aof方式 默认没有开始,默认使用 rdb方式
:693 RDB和AOF方式可以同时开启,同时开启时,redis优先使用AOF方式。
aof保存到磁盘文件名称为:
:703 appendonly.aop
:709
Redis supports three different modes:
# no: don't fsync, just let the OS flush the data when it wants. Faster.
从来不执行文件同步 只有当系统主动刷新数据才会。速度最快,最不安全的
# always: fsync after every write to the append only log. Slow, Safest.
每次写命令执行完毕都会追加命令到文件中。速度最慢,最安全的。
# everysec: fsync only one time every second. Compromise.
一秒钟执行一次文件同步。速度适中,安全性适中。
:728-730
# appendfsync always
appendfsync everysec
# appendfsync no
默认方式为 一秒钟同步一次
2.3 优缺点
2.3.1 RDB优缺点:
优点:
- RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
- RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复.
- RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做(备份),父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
- 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.(RDB是保存数据本身,恢复时,只需要加载数据到内存即可,如果是AOF,AOF保存是命令,如果想恢复数据,需要重新执行所有命令,肯定会慢)
缺点:
- 如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据. (默认使用系统配置3种策略 假如有9999 key发生改变,满足5分钟备份一次的条件 假如服务运行到4分钟59时宕机 丢失9999个数据,使用aof默认1秒一次,不会出现大规模丢失 )
- RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的(使用CPU比较多,占用内存比较多),可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒(1秒8.1W可能会丢失),AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.
2.3.2 AOF优缺点:
优点:
- 使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.(不容易造成等数据丢失)
- AOF文件是一个只进行追加的日志文件,所以不需要写入seek(不需要每次写时记录写的位置的,下次从该位置开始),即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写(防止文件过大,读写速度慢): 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
- AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
缺点:
- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)
三、redis 事务
3.1 事务概念
将一组命令放在同一个事务中进行处理。
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
3.2 具体命令及用法
3.2.1 命令简介
Exec (execute):
命令负责触发并执行事务中的所有命令(相当于数据库事务中的commit)
Multi:
开启一个事务(相当于数据库事务中的begin trasication),总是返回OK。 MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC命令被调用时, 所有队列中的命令才会被执行。
Discard:
回滚事务(相当于数据库事务中的rollback)。 当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出。
Watch:
redis使用关键字实现乐观锁(check-and-set)。被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回nil-reply来表示事务已经失败。
3.2.2 具体用法
启动redis服务端:
/usr/redis/bin/redis-server /usr/redis/bin/redis.conf
启动redis客户端:
/usr/redis/bin/redis-cli -h 192.168.140.41
multi和exec用法
开启一个事务(相当于数据库事务中的begin trasication),总是返回OK
multi和discard用法:
watch乐观锁用法——乐观锁:多线程/多请求时才会发生,当修改某一个数据时,乐观认为其他线程/请求,都不会修改这数据,执行修改业务过程中,不会对该数据加锁,至到最后修改数据的时刻,判断该数据有没有其他线程/请求进行修改,如果被修改,放弃修改,如果没有被修改,执行修改。
启动两个客户端(模拟多线程)
/usr/redis/bin/redis-cli -h 192.168.140.41 在两个会话启动客户端
事务的特殊情况演示(事务中出现错误操作:redis中的回滚效果):
按照传统的数据库事务,有一个操作实行失败,所有事务都会回滚,但redis对事务事务支持不符合我们理解的事务操作。把正确的提交了,错误的报错,并没有把所有操作滚回.
这种做法的优点:
- Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
- 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。