Redis知识点整理

本篇文章是JavaGuide大佬关于Redis部分内容的整理,内容包含基本类型、跳表、过期字典、删除策略、事务、持久化、穿透/击穿/雪崩问题、读写策略、RDB/AOF持久化、主从、单/多哨兵、Cluster集群、性能优化等多个方面。原文连接:https://javaguide.cn/database/redis/redis-questions-02.html

Redis 是基于 C 语言编写的。
官方在线测试网站:https://try.redis.io/

单线程模型

对于读写命令来说,Redis 一直是单线程模型。不过,在 Redis 4.0 版本之后引入了多线程来执行一些大键值对的异步删除操作, Redis 6.0 版本之后引入了多线程来处理网络请求(提高网络 IO 读写性能)
Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型 (Netty 的线程模型也基于 Reactor 模式),文件事件处理器以单线程方式运行,但通过使用 I/O 多路复用程序来监听多个套接字(与NIO的selector类似),文件事件处理器既实现了高性能的网络通信模型,又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接,这保持了 Redis 内部单线程设计的简单性。
文件事件处理器(file event handler)主要是包含 4 个部分:

  • 多个 socket(客户端连接)
  • IO 多路复用程序(支持多个客户端连接的关键)
  • 文件事件分派器(将 socket 关联到相应的事件处理器)
  • 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)

image.png

Redis为什么那么快?

  1. Redis 基于内存,内存的访问速度是磁盘的上千倍;
  2. Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用(Redis 线程模式后面会详细介绍到);
  3. Redis 内置了多种优化过后的数据类型/结构实现,性能非常高。

image.png

Redis为什么使用单线程?

单线程编程容易并且更容易维护;
Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。

Redis为什么引入多线程?

Redis在4.0就引入了多线程,用于删除操作。Redis6.0正式引入多线程,提高网络 IO 读写性能,Redis的性能瓶颈受限与内存和网络。但执行命令依然是单线程,所以不用担心线程安全问题。
多线程默认是关闭的,通过修改配置文件redis.conf,io-threads大于1开启多线程(推荐CPU核数的3/4,例如4核填3)。

Redis的后台线程有什么?

为了处理耗时的操作,会开启后台线程单独去处理。

  • 通过 bio_close_file 后台线程来释放 AOF / RDB 等过程中产生的临时文件资源。
  • 通过 bio_aof_fsync 后台线程调用 fsync 函数将系统内核缓冲区还未同步到到磁盘的数据强制刷到磁盘( AOF 文件)。
  • 通过 bio_lazy_free后台线程释放大对象(已删除)占用的内存空间。

Redis 除了做缓存,还能做什么?

  • 分布式锁:通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。
  • 限流:一般是通过 Redis + Lua 脚本的方式来实现限流。相关阅读:我司用了 6 年的 Redis 分布式限流器,可以说是非常厉害了!
  • 消息队列:Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。(很多欠缺的地方比如消息丢失和堆积问题不好解决)
  • 延时队列:Redisson 内置了延时队列(基于 Sorted Set 实现的)。
  • 分布式 Session :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
  • 复杂业务场景:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。
  • 搜索引擎:小项目可以,因为Redis在内存上处理数据,性能不错(大项目不推荐)

基本类型

image.png
底层实现主要依赖这 8 种数据结构:简单动态字符串(SDS)、LinkedList(双向链表)、Dict(哈希表/字典)、SkipList(跳跃表)、Intset(整数集合)、ZipList(压缩列表)、QuickList(快速列表)

StringListHashSetZset
SDSLinkedList/ZipList/QuickListDict、ZipListDict、IntsetZipList、SkipList
  • String(字符串):底层使用SDS(简单动态字符串),SDS API 是安全的,不会造成缓冲区溢出。
  • List(列表):底层使用双向链表。

image.png

  • Hash(散列):String 类型的键值对映射表,类似数组+链表的结构(JDK1.7的HashMap类似),不过做了很多优化。适合存储对象,可以直接修改存储对象某些字段的值。
  • Set(集合):无序唯一的集合(与HashSet类似)。取差集、并集、合集很方便。
  • Zset(有序集合):比 Set 多个一个权重参数 score,根据score进行正反排序。取差集、并集、合集很方便。

跳表

跳表的特征:

  1. 结合了链表和二分查找的思想,通过创建多条链表,以空间换时间,加快查询的速度。
  2. 底层为第0层的原始链表,查询时从上到下查找数据(判断当前链表的下个节点是否小于等于查询值,如果不是往下切换到另一条链表上),不断缩小查询范围

Zset的跳表

Redis为什么用跳表实现有序集合
通过object encoding keyname即可查看key的数据结构,数据量不多时,会使用zipList(压缩列表),当到达阈值后转为skipList(跳表)(实际是 dict+skiplist,还会借用字典来提高获取指定元素的效率)。
因为设计者考虑到 Redis 数据存放于内存,为了节约宝贵的内存空间在有序集合在元素小于 64 字节且个数小于 128 的时候,会使用 ziplist,而这个阈值的默认值的设置就来自下面这两个配置项。

zset-max-ziplist-value 64
zset-max-ziplist-entries 128

特殊类型

  • Bitmap (位图): 存储的是连续的二进制数字(0 和 1),可以将 Bitmap 看作是一个存储二进制数字(0 和 1)的数组,数组中每个元素的下标叫做 offset(偏移量)。
  • HyperLogLog(基数统计):一种有名的基数计数概率算法,占用空间非常非常小。基数计数概率算法为了节省内存并不会直接存储元数据,而是通过一定的概率统计方法预估基数值(集合中包含元素的个数)。因此, HyperLogLog 的计数结果并不是一个精确值,存在一定的误差(标准误差为 0.81% )。
    • 稀疏矩阵:计数较少的时候,占用空间很小。
    • 稠密矩阵:计数达到某个阈值的时候,占用 12k 的空间。
  • Geospatial(地理位置): 主要用于存储地理位置信息,基于 Sorted Set 实现。可以轻松实现两个位置距离的计算、获取指定位置附近的元素等功能。

Redis的删除与阻塞

过期字典

过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
image.png

过期数据的删除策略

  1. 惰性删除:只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
  2. 定期删除:每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。

定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 定期删除+惰性/懒汉式删除
但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。由内存淘汰机制解决。

内存淘汰机制

Redis 提供 6 种数据淘汰策略:

  1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。
  4. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰。
  6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

4.0 版本后增加以下两种:

  1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰。
  2. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key。

Redis阻塞的情况

Redis常见阻塞原因总结

  1. 执行O(n)复杂度的命令,如KEYS *SMEMBERS等。
  2. RDB使用save命令,而不是bgsave
  3. AOF子线程刷盘阻塞(第三步),导致主线程AOF写入(第二步)阻塞
  4. 操作bigkey
  5. 清除或释放所有key
  6. 集群扩容
  7. 内存交换:内存不足时使用部分硬盘空间(把硬盘当内存使用)
  8. CPU竞争:Redis和其他进程竞争CPU
  9. 网络延迟

事务

Redis 事务提供了一种将多个命令请求打包的功能。然后,再按顺序执行打包的所有命令,并且不会被中途打断。除了不满足原子性和持久性之外,事务中的每条命令都会与 Redis 服务器进行网络交互(浪费资源)。
MULTIEXECDISCARDWATCH 等命令来实现事务(Transaction)功能。

  • MULTI:开始事务,接下来的命令都存到队列里(开始事务后命令执行都返回QUEUED,直到事务提交或取消)。
  • EXEC:提交事务,依次执行队列里的命令。
  • DISCARD:取消事务,清空事务命令队列。
  • WATCH:监听key,在MULTI前执行,如果监听的key事务中被其他客户端修改,则事务失败(执行EXEC返回(nil)),如果是监听的key在事务里被修改则不会失败。

不支持原子性EXEC时,如果某条命令执行失败,前面执行的命令不会回滚。持久性无法保证,RDB和AOF。
通过Lua脚本编写多条命令,Redis会一致性执行完Lua脚本。Lua脚本也不完全满足原子性,如果Lua脚本中途执行失败,前面执行的也不会回滚。

生产问题

缓存穿透(大量恶意请求不存在的数据)

大量请求的 key 是不合理的,根本**不存在于缓存中,也不存在于数据库中 **。大量请求直接落在数据库上而导致宕机。
image.png

解决方法

  1. 对请求的参数做校验。
  2. 对无效的key进行缓存(设置过期时间)。
  3. 布隆过滤器:使用bit数组进行存储,把有可能的查询的key先进行hash取值后存到bit数组里,请求过来时,然后没在bit数组里找到,直接返回错误信息;有新数据时需要更新数组。(有可能误判)

image.png

  1. 接口限流:异常频繁的用户或IP直接加入黑名单限制访问。

缓存击穿(hotkey过期导致)

请求的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期) 。大量请求直接落在数据库上而导致宕机。
image.png

解决办法

  1. 设置热点数据永不过期或者过期时间比较长。
  2. 针对热点数据提前预热,将其存入缓存中并设置合理的过期时间。
  3. 请求数据库写数据到缓存之前,先获取互斥锁,保证只有一个请求会落到数据库上,减少数据库的压力。

缓存雪崩(缓存大面积失效)

**缓存(正常的)在同一时间大面积的失效,导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。**Redis宕机也会造成雪崩。
image.png

解决办法

  1. 设置不同的过期时间(随机)
  2. 采用Redis集群,避免单点故障
  3. 限流,避免同时处理大量请求
  4. 多级缓存,本地缓存+Redis,防止Redis出问题

缓存读写策略

旁路缓存模式(Cache Aside Pattern

先更新DB,再删除缓存,以DB结果为主。适合读多的场景。
缺陷:

  1. 首次请求数据不在缓存中,得到数据库中重新获取。解决办法:将热点数据放到缓存中。
  2. 写操作频繁会导致缓存频繁被删,影响缓存命中率。解决办法:(1) 更新DB的同时更新缓存,加分布式锁保证线程安全;(2) 允许短暂的数据不一致。

读写穿透(Read/Write Through Pattern)

比较少见。

  • 写:先查询缓存,存在则先更新缓存,然后在更新数据库;缓存不存在直接更新数据库,以缓存结果为主。
  • 读:查询缓存,数据存在直接返回,数据不存在查询DB保存到缓存后返回。

同样需要提前把热点数据放到缓存里。

异步缓存写入(Write Behind Pattern)

**先查询缓存,存在则先更新缓存,不存在查询DB后创建缓存;DB更新结果先保存,后续批量更新DB,以缓存结果为主。**比较少见。DB写性能很好,适合对数据一致性不高的场景,例如点赞数,阅读数。

持久化

用于重启或宕机后的数据恢复,或者集群间的数据同步(RDB)。Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)

RDB(快照)

创建快照获得某个时间点上Redis的所有数据副本。RDB是Redis默认的持久化方式。用于数据恢复、数据同步。
redis.conf中配置中可配置创建快照的条件:

save 60 1000	#在60秒(1分钟)内有1000个key发生变化,自动触发bgsave命令创建快照。

创建快照的命令:

  • save:同步保存,阻塞主线程
  • bgsave:子线程执行,不会阻塞主线程(默认)

优点:

  • 内容是经过压缩的二进制数据,文件小
  • 保存整个Redis数据集,恢复很快,无需一条条命令重新执行

缺点:

  • 实时性差
  • 生成过程会占用大量的CPU和内存资源
  • 不同Redis版本,RDB文件可能不兼容

AOF(只追加文件)

AOF比RDB实时性更好。默认没开启AOF(Redis6.0默认开启)。在redis.conf中通过appendonly yes进行开启。开启后每执行一条写命令都会写到AOF缓冲区(server.aof_buf)中,然后在写入AOF文件中(系统内核缓冲区,LINUX系统延迟写入还未同步到硬盘),最后根据持久化方式(fsync策略)的配置同步到硬盘中appendonly.aof文件里。只有同步到硬盘才完成持久化,否则都有可能丢失数据。

优点:

  • 实时性高,宕机丢失数据少
  • 对Redis误操作时,主要AOF没重写,就可以恢复数据
  • AOF文件兼容性高

缺点:

  • 通过AOF文件恢复数据只能一条条执行命令
  • 保存的文件大
  • 重写期间可能会使用大量CPU和内存,且写入命令会有两次磁盘I/O(Redis7.0的Multi Part AOF机制此问题进行了优化)

AOF持久化步骤

  1. 命令追加(append):执行的所有写命令都追加到AOF缓冲区中。
  2. 文件写入(write):将AOF缓冲区中数据同步到AOF文件中。系统调用write函数写到系统内存缓冲区后结束,延迟写,还未保存到硬盘中。
  3. 文件同步(fsync):Redis后台线程根据fsync策略定期强制硬盘对文件进行同步,完成持久化。
  4. 文件重写(rewrite):定期对AOF进行重写(新创建AOF文件)压缩文件大小,防止文件大小过大。
  5. 重启加载(load):Redis重启后,加载AOF文件进行数据恢复。

LINUX对文件写入进行了优化,按照一定频率对缓存区进行文件写入,提高了I/O效率。
LINUX提供系统调用函数(syscall)用于对文件和设备的访问和控制,Redis通过调用这些函数实现文件的实时写入。

AOF持久化方式

redis.conf中对appendfsync进行配置:

appendfsync always    #性能差,每次有数据修改发生时都会调用fsync函数同步AOF文件,fsync完成后线程返回,这样会严重降低Redis的速度
appendfsync everysec  #可能丢失数据,每秒钟调用fsync函数同步一次AOF文件
appendfsync no        #可能丢失数据,让操作系统决定何时进行同步,一般为30秒一次
  • always:主线程调用 write 执行写操作后,后台线程( aof_fsync 线程)立即会调用 fsync 函数同步 AOF 文件(刷盘),fsync 完成后线程返回,这样会严重降低 Redis 的性能(write + fsync)。
  • everysec:主线程调用 write 执行写操作后立即返回,由后台线程( aof_fsync 线程)每秒钟调用 fsync 函数(系统调用)同步一次 AOF 文件(write+fsync,fsync间隔为 1 秒)
  • no:主线程调用 write 执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(write但不fsync,fsync 的时机由操作系统决定)。

Redis7.0增加 Multi Part AOF 机制,将AOF文件拆成多个,详情请看这篇文章:https://zhuanlan.zhihu.com/p/467217082

AOF文件重写

当AOF文件过大时,Redis会通过子线程会读取当前Redis中所有的键值对,生成最新的写命令后保存到新AOF文件中,且子线程重写期间会维护AOF重写缓存区保存重写期间的写入操作,重写完毕后将AOF重写缓冲区的数据追加到新AOF文件中,然后用新AOF文件覆盖原AOF文件。

重写期间,执行的写命令会保存到AOF重写缓存区和旧的AOF文件里,最终一份文件产生两次磁盘I/O。Redis7.0后加入的Multi Part AOF机制解决了这个问题,去掉了AOF重写缓存区,重写区间新建一个INCR AOF文件保存重写期间的所有写操作,重写完成后追加到新AOF文件里。
image.png

开启 AOF 重写功能,可以调用 BGREWRITEAOF 命令手动执行,也可以设置下面两个配置项,让程序自动决定触发时机:

  • auto-aof-rewrite-min-size:如果 AOF 文件大小小于该值,则不会触发 AOF 重写。默认值为 64 MB;
  • auto-aof-rewrite-percentage:执行 AOF 重写时,当前 AOF 大小(aof_current_size)和上一次重写时 AOF 大小(aof_base_size)的比值。如果当前 AOF 文件大小增加了这个百分比值,将触发 AOF 重写。将此值设置为 0 将禁用自动 AOF 重写。默认值为 100。

集群

主从模式

主从集群配置可以看这篇文章:https://blog.csdn.net/CodingFire/article/details/130942851

在从节点Redis的配置文件里添加slaveof ip 端口,然后启动主节点和从节点redis-server,即实现主从配置,master负责读写,slave只负责读
slave节点第一次连接master是采用全量同步同步master节点所有数据,后续此slave节点就采用增量同步的方式同步master节点新的写操作,如果中途断了重连也只会采用部分同步,不会在进行全量同步。

主从数据同步的原理:

缺点:未配置哨兵时,一旦master宕机,Redis集群不可用,需要人工修改配置文件指定新master。

哨兵模式(sentinel)

哨兵配置可以看看这两篇文章:
单哨兵配置:https://blog.csdn.net/CodingFire/article/details/130984964
多哨兵配置:https://codingfire.blog.csdn.net/article/details/131109255

为了防止主从模式master节点宕机导致服务不可用,需要通过哨兵模式(独立进程)进行监控,一旦master节点宕机,哨兵就让节点内部重新竞选master。即使后续宕机的原master节点上线(或者网络问题断连后重新连接),也会变成slave节点,去同步新master的数据。
哨兵不存储数据,只用于集群监控。哨兵配置只需要配置监控master节点即可,一旦有新slave节点,能马上感知到并对其监控。只用一个哨兵也可能会有单点故障问题,所以一般配置多个哨兵,即多哨兵模式。

  • 同时监控所有节点(主从节点),多个哨兵相会相互监控(多哨兵模式)。
  • 一旦master节点宕机,哨兵会让从节点重新选出master节点,并修改从节点的配置文件使其跟随新master,也会修改新master的配置文件,删除slaveof配置。注意:如果有配置密码,重新竞选master,哨兵只会修改slaveof配置,并不会修改对应的密码,需要手动修改。

多哨兵判断master是否下线分两步:

  • 主观下线:哨兵进程会一直给所有节点发心跳检测,如果一定时间都没收到master节点的返回,则标记为主观下线。
  • 客观下线:哨兵会询问其他哨兵,当超过设定哨兵数量都标记master为主观下线时,当前节点就为客观下线,需要重新竞选master。

哨兵的配置文件sentinel.conf,哨兵启动命令redis-sentinel sentinel.conf

缺点:主从节点数据都一样,浪费内存,在线扩容困难,且需要再维护一套哨兵。

Cluster集群模式(多master)

关于Cluster集群的搭建可以看看这篇文章:https://blog.csdn.net/lzb348110175/article/details/99408551

Cluster集群解决了多节点数据重复存放导致浪费内存的问题。从单主多从切换成多主多从的模式,是无中心化集群配置,实现对Redis进行分布式存储,对数据进行分片存放(slots插槽,数量固定),不同的master存放不同的内容。一般一组6个节点,3个master,每个master对应都有1个slave节点(可能多个)。同样master负责读写,slave只负责读,当master宕机后,对应的slave会接替其位置作为新master。
在Cluster集群上,通过对key进行一致性hash计算对应的slot是落在哪个master上,然后到哪个master上进行读写操作。
**master节点间采用gossip协议进行信息同步,**下面几种情况就会通过gossip进行信息同步:

  • 新节点加入
  • slot迁移(再分配)
  • 节点宕机
  • slave选举master

性能优化

批量操作

一个命令步骤为 发送命令命令排队命令执行返回结果。第一步和第四步称为**Round Trip Time (RTT,往返时间) **,即网络传输的时间。通过批量操作可大幅减少RTT,并且发送一次命令的socket I/O成本也高(涉及上下文切换),批量操作也可以减少这部分成本。
原生批量操作命令:MGET、MSET、HMGET、HMSET、SADD。
pipeline(流水线):命令封装成一组提交,Redis一次性执行。注意控制操作的个数(发送和返回,5百内),避免网络传输数据量过大。(非原子)
Lua脚本:一段Lua脚本可以视作一条命令,可看成原子操作但无法回滚。

大量key集中过期

对于过期 key,Redis 采用的是 定期删除+惰性/懒汉式删除 策略。

  1. 给key设置随机过期时间。(尽量设置)
  2. 开启 lazy-free(惰性删除/延迟释放)(Redis 4.0引入)。交给子线程处理避免阻塞主线程。

bigkey(大key)

对应的 value 所占用的内存比较大(String为1MB,复合类型为5千个)。

bigkey出现原因

  1. 存储二进制数据(文件)
  2. 复合类型数据增长过快
  3. 未及时清理复合类型数据

bigkey的危害

  1. 客户端超时阻塞
  2. 网络阻塞
  3. 工作线程阻塞(主线程)

检查bigkey

  1. --bigkeys命令启动(例redis-cli -p 6379 --bigkeys),扫描所有key找出每种类型top1的bigkey。
  2. 每种类型查看长度的命令
  3. 借助开源工具分析RDB文件
  4. 公有云Redis的分析服务(例如阿里云)

解决bigkey

  1. 分割成多个小key
  2. 手动清理
  3. 采用合适的类型
  4. 开启 lazy-free(惰性删除/延迟释放)

hotkey(热点key)

访问次数比较多且明显多于其他 key 。

hotkey出现原因

  1. 热搜
  2. 秒杀商品

hotkey的危害

处理 hotkey 会占用大量的 CPU 和带宽,超过Redis处理能力后有可能宕机导致雪崩。

检查hotkey

  1. --hotkeys命令启动(例redis-cli -p 6379 --hotkeys),扫描所有key被访问的次数。(前提条件是 Redis Server 的 maxmemory-policy 参数设置为 LFU 算法,否则会报错;hotkey命令会增加CPU和内存的消耗)
  • Redis 中有两种 LFU 算法:在redis.conf配置文件里的maxmemory-policy参数配置。
    1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰。
    2. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key。
  1. MONITOR命令:实时查看Redis所有操作,对性能影响比较大,谨慎使用。
  2. 业务提前分析
  3. 业务代码分析
  4. 借助开源工具分析
  5. 借助公有云服务

解决hotkey

  1. 读写分离:集群master负责写,其他slave负责读
  2. 使用Redis Cluster:hotkey分散到多个节点上
  3. 二级缓存:本地JVM内存存一份热点数据

慢查询

redis.conf 文件中,我们可以使用 slowlog-log-slower-than 参数设置耗时命令的阈值,并使用 slowlog-max-len 参数设置耗时命令的最大记录条数。
当 Redis 服务器检测到执行时间超过 slowlog-log-slower-than 阈值的命令时,就会将该命令记录在慢查询日志(slow log) 中,这点和 MySQL 记录慢查询语句类似。当慢查询日志超过设定的最大记录条数之后,Redis 会把最早的执行命令依次舍弃。
⚠️注意:由于慢查询日志会占用一定内存空间,如果设置最大记录条数过大,可能会导致内存占用过高的问题。

  • SLOWLOG GET:查询慢查询日志(默认10条)。空格加数量可指定返回的数量。
  • SLOWLOG LEN:查看慢查询日志数量
  • SHOWLOG RESET:清空慢查询日志

慢查询日志中的每个条目都由以下六个值组成:

  1. 唯一渐进的日志标识符。
  2. 处理记录命令的 Unix 时间戳。
  3. 执行所需的时间量,以微秒为单位。
  4. 组成命令参数的数组。
  5. 客户端 IP 地址和端口。
  6. 客户端名称。

内存碎片

不可用的闲置空间,产生内存碎片的原因:

  1. 存储时向操作系统申请的内存空间比实际使用的大
  2. 频繁修改数据(Redis删除时不会轻易释放内存)

使用info memory命令即可查看Redis内存的相关信息。mem_fragmentation_ratio(内存碎片率)属性值越大,内存碎片越严重,大于1.5才需要清理。

// 快速查看内存碎片率
redis-cli -p 6379 info | grep mem_fragmentation_ratio

清理内存碎片

执行config set activedefrag yes即可开启自动清理内存碎片功能。

## 清理的时机
# 内存碎片占用空间达到 500mb 的时候开始清理
config set active-defrag-ignore-bytes 500mb
# 内存碎片率大于 1.5 的时候开始清理
config set active-defrag-threshold-lower 50

## 减少清理对性能的影响
# 内存碎片清理所占用 CPU 时间的比例不低于 20%
config set active-defrag-cycle-min 20
# 内存碎片清理所占用 CPU 时间的比例不高于 50%
config set active-defrag-cycle-max 50

Redis使用建议

  • 使用连接池:避免频繁创建关闭客户端连接。
  • 尽量不使用 O(n)指令,使用 O(n) 命令时要关注 n 的数量:像 KEYS *、HGETALL、LRANGE、SMEMBERS、SINTER/SUNION/SDIFF等 O(n) 命令并非不能使用,但是需要明确 n 的值。另外,有遍历的需求可以使用 HSCAN、SSCAN、ZSCAN 代替。
  • 使用批量操作减少网络传输:原生批量操作命令(比如 MGET、MSET等等)、pipeline、Lua 脚本。
  • 尽量不适用 Redis 事务:Redis 事务实现的功能比较鸡肋,可以使用 Lua 脚本代替。
  • 禁止长时间开启 monitor:对性能影响比较大。
  • 控制 key 的生命周期:避免 Redis 中存放了太多不经常被访问的数据。

阿里官方 Redis 开发规范-阿里云开发者社区

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值