笔记大纲
1.Redis的慢查询日志
和MySQL一样,Redis也有相应的慢查询统计,Redis的慢查询统计的是步骤3的耗时。Redis提供了一个配置参数slowlog-log-slower-than
来设置慢查询的阈值,默认是10毫秒。
2.Pipeline流水线管道
其中1+4称为 Round Trip Time (RTT,往返时间),也就是数据在网络上传输的 时间。 Redis提供了批量操作命令(例如 mget、mset等),有效地节约 RTT。但大部分命令是不支持批量操作的,需要消耗n次RTT。
Pipeline(流水线)机制能改善上面这类问题,它能将一组Redis命令进行组装,通过一次RTT
传输给Redis,再将这组Redis命令的执行结果按顺序返回给客户端,整个过程只需要1次RTT。 Pipeline并不是什么新的技术或机制,很多技术上都使用过。而且RTT在不同网络环境下会有不同,例如同机房和同机器会比较快,跨机房跨地区会比较慢。Redis 命令真正执行的时间通常在微秒级别,所以才会有Redis性能瓶颈是网络这样的说法。 Pipeline 虽然好用,但是每次Pipeline组装的命令个数不能没有节制,否则一 次组装 Pipeline数据量过大,一方面会增加客户端的等待时间,另一方面会造成 一定的网络阻塞,可以将一次包含大量命令的Pipeline拆分成多次较小的Pipeline 来完成,比如可以将Pipeline的总发送大小控制在内核输入输出缓冲区大小之内或者控制在TCP包的大小1460字节之内。 同时Pipeline只能操作一个Redis实例,但是即使在分布式Redis场景中,也可以作为批量操作的重要优化手段。
3.Redis的事务
Redis提供了简单的事务功能,将一组需要一起执行的命令放到multi和exec两个命令之间。multi命令代表事务开始,exec命令代表事务结束(开始提交),如果要停止事务的执行,可以使用discard命令代替exec命令即可。
MULTI :开启事务,redis将后续的客户端命令放入事务缓冲队列中
EXEC :真正执行事务缓冲队列中的命令
DISCARD :取消事务,丢弃缓冲队列中的命令
WATCH :监视N个key,如果在事务执行前,key的值被修改,则事务中断
UNWATCH :取消WATCH监视
但是Redis的事务是一个伪事务,它并不能保证一致性。因为他没有回滚的功能。如果事务中存在受检异常则能够保证事务性,但是如在事务执行过程中抛出运行时异常,Redis不会发生回滚。
4.Lua脚本的支持
Lua语言是在1993年由巴西一个大学研究小组发明,其设计目标是作为嵌入式程序移植到其他应用程序,它是由C语言实现的,虽然简单小巧但是功能强大,所以许多应用都选用它作为脚本语言,尤其是在游戏领域,暴雪公司的“魔兽世界”,“愤怒的小鸟”,Nginx将 Lua语言作为扩展。Redis将Lua作为脚本语言可帮助开发者定制自己的Redis命令。Redis2.6版本通过内嵌支持Lua环境。也就是说一般的运用,是不需要单独安装Lua的。
在Redis使用Lua脚本的好处包括:
1、减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行;
2、原子操作,Redis会将整个脚本作为一个整体执行,中间不会被其它 命令插入,编写脚本的过程中无 需担心会出现竞态条件;
3、复用性,客户端发送的脚本会存储在Redis中,这意味着其他客户端可以复用这一脚本来完成同样的 逻辑
但是eval命令要求你在每次执行脚本的时候都发送一次脚本,所以 Redis有一个内部的缓存机制,因此它不会每次都重新编译脚本
,为了减少带宽的消耗,Redis提供了evalsha命令,它的作用和EVAL一样,都用于对脚本求值,但它接受的第一个参数不是脚本,而是脚本的SHA1摘要。
5.Redis的发布订阅(鸡肋)
6.Stream消息队列
Redis提供了基于“发布/订阅”模式的消息机制,此种模式下,消息发布者和订阅者不进行直接通信,发布者客户端向指定的频道(channel)发布消息,订阅该频道的每个客户端都可以收到该消息。对应命令publish cnannel msg和subscribe channel [channel …],但是Redis原生的发布订阅机制太过简陋,不适用于业务领域开发,随后诞生了Stream。 Redis5.0最大的新特性就是多出了一个数据结构Stream,它是一个新的强大的 支持多播的可持久化的消息队列,作者声明Stream借鉴了Kafka的设计。 Stream的消费模型借鉴了Kafka的消费分组的概念,它弥补了Redis的Pub/Sub不能持久化消息的缺陷。但是它又不同于kafka,Kafka的消息可以分partition,而 Stream不行。如果非要分parition的话,得在客户端做,提供不 同的Stream名称,对消息进行hash取模来选择往哪个Stream里放数据。
7.Redis的持久化
Redis 虽然是个内存数据库,但是Redis支持RDB和AOF两种持久化机制,将数据写往磁盘,可以有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。
7.1 RDB
RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发。
7.1.1 手动触发
save命令:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。
bgsave命令:Redis进程执行fork()函数创建子进程,RDB持久化过程由子进程 负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。显然bgsave命令是针对save阻塞问题做的优化。因此Redis内部所有的涉及RDB 的操作都采用bgsave的方式。
7.1.2 自动触发
1、使用save相关配置,如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave。
2、如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点。
3、执行debug reload命令重新加载Redis时,也会自动触发save操作。
4、默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动执行 bgsave。
7.1.3 RDB文件的压缩
Redis默认采用LZF算法
对生成的RDB 文件做压缩处理,压缩后的文件远远小于内存大小,默认开启。虽然压缩RDB会消耗 CPU,但可大幅降低文件的体积
,方便保存到硬盘或通过网络络发送给从节点,因此线上建议开启。如果 Redis 加载损坏的RDB文件时拒绝启动。
7.1.4 RDB的优点
RDB 是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。 非常适用于备份
,全量复制
等场景。 比如每隔几小时执行bgsave备份,并把RDB 文件拷贝到远程机器或者文件系统中,用于灾难恢复
。Redis加载RDB恢复数据远远快于AOF的方式
。
7.1.5 RDB的缺点
RDB方式数据没办法做到实时持久化/秒级持久化
。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作
,频繁执行成本过高。
RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB 版本存在老版本Redis服务无法兼容新版RDB格式的问题。针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决。
7.2 AOF
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。
AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。开启AOF功能需要设置配置:appendonly yes,默认不开启。
Redis使用单线程响应命令
,如果每次写AOF文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载。先写入缓冲区aof_buf
中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。Redis提供了多种AOF缓冲区同步文件策略,由参数appendfsync控制。
AOF的覆写:通过对日志文件的整理和指令合并用来减少AOF文件的大小的一种策略。
7.2.1 aof_buffer刷盘策略
1、always:命令写人aof_buf后调用系统fsync()操作同步到AOF文件, fsync完成后线程返回命令fsync同步文件。
2、everysec:写人aof_buf后调用系统write()操作,write完成后线程返回。刷盘操作由专门线程每秒调用一次fsync命令。(建议配置)
3、no:写入aof_buf后调用系统write()操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责。write操作会触发延迟写(delayed write)机制。Linux在内核提供页缓冲区用来提高硬盘IO性能。write操作在写入系统缓冲区后直接返回。同步硬盘操作依赖 于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。fsync 针对单个文件操作,做强制刷盘,fsync将阻塞直到写入硬盘完成后返回,保证了数据持久化。
8.键扫描和渐进式扫描
8.1 keys
用来全量遍历键,用法很简单keys pattern遍历所有的键, pattern直接使用星号即可,pattern 使用的是 glob 风格的通配符:代表匹配任意字符。
?代表匹配一个字符。
[]代表匹配部分字符,例如[1,3]代表匹配 1,3。
\用来做转义,例如要匹配星号、问号需要进行转义。
例如,查询key名称是uis开头的键集合:keys uis
但是如果考虑到Redis的单线程架构就不那么美妙了,如果Redis包含了大量的键,执行 keys命令很可能会造成Redis阻塞,所以一般建议不要在生产环境下使用keys命令。但有时候确实有遍历键的需求该怎么办,可以在以下三种情况使用: 1、在一个不对外提供服务的Redis从节点上执行,这样不会阻塞到客户端的请 求,但是会影响到主从复制。 2、如果确认键值总数确实比较少,可以执行该命令。 3、使用scan命令渐进式的遍历所有键,可以有效防止阻塞。
8.2 scan
Redis从2.8版本后,提供了一个新的命令scan,它能有效的解决keys命令存在的阻塞问题。和keys命令执行时会遍历所有键不同,scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,但是要真正实现keys的功能,需要执行多次scan。可以想象成只扫描一个字典中的一部分键,直到将字典中的所有键遍历完毕。
渐进式遍历可以有效的解决keys命令可能产生的阻塞问题,但是 scan 并非 完美无瑕,如果在 scan 的过程中如果有键的变化(增加、删除、修改),那么遍 历效果可能会碰到如下问题:新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键,这些是我们在开发时需要考虑的。