乐趣区

数据库Redis进阶篇

欢迎关注公众号:【爱编码
如果有需要后台回复 2019 赠送 1T 的学习资料 哦!!

继续接着上一篇【数据库】Redis 基础篇

事务

为了保证多条命令组合的原子性,Redis 提供了简单的事务功能以及集成 Lua 脚本来解决这个问题。简单介绍 Redis 中事务的使用方法以及它的局限性。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd user:a:follow user:b
QUEUED
127.0.0.1:6379> zadd user:b:fans 1 user:a
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> sismember user:a:follow user:b
(integer) 1

Redis 提供了简单的事务功能,** 将一组需要一起执行的命令放到 multi 和
exec 两个命令之间 **。

multi命令代表事务 开始
exec 命令代表事务 结束
它们之间的命令是原子顺序执行的。
不支持事务中的回滚特性

Lua 脚本

基本语法教程可参考下面这个网址:
https://www.runoob.com/lua/lu…

在 Redis 中执行 Lua 脚本有两种方法:eval 和 evalsha。

发布订阅

Redis 提供了基于“发布 / 订阅”模式的消息机制,此种模式下,消息发布
者和订阅者不进行直接通信,发布者客户端向指定的频道(channel)发布消息,订阅该频道的每个客户端都可以收到该消息。

  1. 发布消息
publish channel:sports "Tim won the championship"
  1. 订阅消息
subscribe channel:sports

有关订阅命令有两点需要 注意

  • 客户端在执行订阅命令之后进入了订阅状态,只能接收 subscribe、

psubscribe、unsubscribe、punsubscribe 的四个命令。

  • 新开启的订阅客户端,无法收到该频道之前的消息,因为 Redis 不会对

发布的消息进行持久化。

Redis 发布订阅与成熟 MQ 的比较

(1)MQ 支持 多种消息协议 ,包括 AMQP,MQTT,Stomp 等,并且支持 JMS 规范,但 Redis 没有提供对这些协议的支持;
(2)MQ 提供 持久化功能 ,但 Redis 无法对消息持久化存储,一旦消息被发送,如果没有订阅者接收,那么消息就会丢失;
(3)MQ 提供了消息传输保障,当客户端连接超时或事务回滚等情况发生时, 消息会被重新发送给客户端,Redis 没有提供消息传输保障。

总之,MQ 所提供的功能远比 Redis 发布订阅要复杂,毕竟 Redis 不是专门做发布订阅的,但是如果系统中已经有了 Redis,并且需要基本的发布订阅功能,就没有必要再安装 MQ 了,因为可能 MQ 提供的功能大部分都用不到,而且你可以容忍 redis 发布订阅的缺点的话,可以考虑用它。

持久化

Redis 支持RDB 和 AOF 两种持久化机制,持久化功能有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。

RDB 持久化

RDB 持久化是把当前进程数据生成快照保存到硬盘的过程,触发 RDB 持
久化过程分为手动触发和自动触发。

手动触发和自动触发操作方法参考这位大神的文章:
https://www.cnblogs.com/ysoce…

RDB 的优缺点
优点:

  1. RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每 6 小时执行 bgsave 备份,并把 RDB 文件拷贝到远程机器或者文件系统中(如 hdfs),用于灾难恢复。
  2. Redis 加载 RDB 恢复数据远远快于 AOF 的方式。

缺点:

  1. RDB 方式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运

行都要执行 fork 操作创建子进程,属于重量级操作,频繁执行成本过高。

  1. RDB 文件使用特定二进制格式保存,Redis 版本演进过程中有多个格式的 RDB 版本,存在老版本 Redis 服务无法兼容新版 RDB 格式的问题。

针对 RDB 不适合实时持久化的问题,Redis 提供了 AOF 持久化方式来解决。

AOF 持久化

AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。
AOF 的主要作用是解决了数据持久化的实时性,目前已经是 Redis 持久化的主流方式。

开启 AOF,通过修改 redis.conf 配置文件

appendonly yes    ## 默认不开启。

AOF 文件名通过 appendfilename 配置设置,默认文件名 appendonly.aof。保存路径同 RDB 持久化方式一致,通过 dir 配置指定。

AOF 的工作流程如下图:

1)所有的写入命令会追加到 aof_buf(缓冲区)中。
2)AOF 缓冲区根据对应的策略向硬盘做同步操作。
3)随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
4)当 Redis 服务器重启时,可以加载 AOF 文件进行数据恢复。

更详细的工作原理可以参考书籍【Redis 开发与运维 (付磊)】
或者参考这篇文章:
https://redisbook.readthedocs…

温馨提示
场景:AOF 文件可能存在结尾不完整的情况,比如机器突然掉电导致 AOF 尾部文件命令写入不全。

解决方法:

  1. 对于错误格式的 AOF 文件,先进行 备份,然后采用 redis-check-aof –fix 命令进行修复,修复后使用 diff- u 对比数据的差异,找出丢失的数据,有些可以人工修改补全。
  2. Redis 为我们提供了 aof-load-truncated 配置来兼容这种情况,默认开启。加载 AOF 时,当遇到此问题时会忽略并继续启动,同时打印如下警告日志:
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 397856725 !!!
# AOF loaded anyway because aof-load-truncated is enabled

持久化的优化

Redis 持久化功能一直是影响 Redis 性能的高发地。主要有以下方面

1. fork 操作
起因:对于高流量的 Redis 实例 OPS 可达 5 万以上,如果 fork 操作耗时在秒级别将拖慢 Redis 几万条命令执行,对线上应用延迟影响非常明显。正常情况下 fork 耗时应该是每 GB 消耗 20 毫秒左右。可以在 info stats 统计中查 latest_fork_usec 指标获取最近一次 fork 操作耗时,单位微秒。

优化
1)优先使用物理机或者高效支持 fork 操作的虚拟化技术,避免使用 Xen。
2)控制 Redis 实例最大可用内存,fork 耗时跟内存量成正比,线上建议每个 Redis 实例内存控制在 10GB 以内。
3)合理配置 Linux 内存分配策略,避免物理内存不足导致 fork 失败。
4)降低 fork 操作的频率,如适度放宽 AOF 自动触发时机,避免不必要的全量复制等。

2. CPU
CPU 开销分析。子进程负责把进程内的数据分批写入文件,这个过程属于 CPU 密集操作,通常子进程对单核 CPU 利用率接近 90%。
CPU 消耗优化。Redis 是 CPU 密集型服务,不要做绑定单核 CPU 操作。由于子进程非常消耗 CPU,会和父进程产生单核资源竞争。
不要和其他 CPU 密集型服务部署在一起,造成 CPU 过度竞争。如果部署多个 Redis 实例,尽量保证同一时刻只有一个子进程执行重写工作。

3. 内存
内存消耗优化:
1)同 CPU 优化一样,如果部署多个 Redis 实例,尽量保证同一时刻只有一个子进程在工作。
2)避免在大量写入时做子进程重写操作,这样将导致父进程维护大量页副本,造成内存消耗。

4. 硬盘
优化方法如下:
a)不要和其他高硬盘负载的服务部署在一起。如:存储服务、消息队列服务等。
b)AOF 重写时会消耗大量硬盘 IO,可以开启配置 no-appendfsync-on-rewrite,默认关闭。表示在 AOF 重写期间不做 fsync 操作。
c)当开启 AOF 功能的 Redis 用于高流量写入场景时,如果使用普通机械磁盘,写入吞吐一般在 100MB/ s 左右,这时 Redis 实例的瓶颈主要在 AOF 同步硬盘上。
d)对于单机配置多个 Redis 实例的情况,可以配置不同实例分盘存储 AOF 文件,分摊硬盘写入压力。

注:配置 no-appendfsync-on-rewrite=yes 时,在极端情况下可能丢失整个 AOF 重写期间的数据,需要根据数据安全性决定是否配置。

5.AOF 追加阻塞
当开启 AOF 持久化时,常用的同步硬盘的策略是everysec,用于平衡性能和数据安全性。对于这种方式,Redis 使用另一条线程每秒执行 fsync 同步硬盘。当系统硬盘资源繁忙时,会造成 Redis 主线程阻塞,

阻塞流程分析:
1)主线程负责写入 AOF 缓冲区。
2)AOF 线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时间。
3)主线程负责对比上次 AOF 同步时间:如果距上次同步成功时间在 2 秒内,主线程直接返回。如果距上次同步成功时间超过 2 秒,主线程将会阻塞,直到同步操作完成。

优化 AOF 追加阻塞问题主要是优化系统硬盘负载,优化方法参考第 4 点

总结

本文主要学习 Redis 的事务、发布订阅、以及持久化。
后续会继续学习 Redis 集群等方面的知识。

最后

如果对 Java、大数据感兴趣请长按二维码关注一波,我会努力带给你们价值。觉得对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。
关注公众号 【爱编码】,回复2019 有相关资料哦。

退出移动版