redis-为什么redis速度快?
为什么redis速度快?
1:redis的核心瓶颈从不是cpu,而是网络IO
redis的单线程模型指的是网络IO与键值对读写是用单线程完成的。之所以使用单线程支撑如此高的并发,关键在于核心事件处理模型“IO多路复用”;
IO多路复用:redis利用操作系统提供的机制,让单个线程可以同时监听多个客户端连接的”读写事件“,有事件发生时就去处理,没有事件就阻塞等待或去处理其他任务。
非阻塞IO:一种不等待的IO模型,当线程调用 read / write / accept 等系统调用时,如果数据尚未准备好(或缓冲区满),内核会立即返回一个错误,而不是将线程挂起等待,仍可执行其他任务。
非阻塞 I/O 不让单线程卡在某个慢连接上,多路复用告诉它哪个连接准备好了,两者结合实现极致性能。使得redis可以高效处理成千上万的并发连接,而不创建大量线程。redis性能瓶颈绝大部分情况下是网络IO,而不是CPU计算。单线程是解决网络并发高效方式之一,避免多线程在共享数据时复杂的加锁机制。
2:单线程与redis6.0的多线程
随着网络硬件的发展,网络IO延迟大大降低,同时redis存储的数据也越来越复杂,某些情况cpu消耗开始显现,单线程面对这些慢操作时会阻塞所有请求。
redis6.0的多线程:多线程不是将读写命令的执行变成多线程,多线程只用于处理网络IO的读写,核心命令执行依旧是单线程。目的是为了利用多核CPU加速网络包的解析,又保持了核心数据结构的安全性,避免并发竞争。
3:总结
所以真正拖垮redis的不是高QPS,而是慢查询,关键是命令的复杂度,如:keys * ,大集合交集运算等,这些命令会让redis的单线程陷入长时间阻塞。
虽然cpu不是性能瓶颈,但内存是。如:内存不足导致的内存碎片,写时复制的内存抖动,频繁的淘汰策略,才是生产环境更常见性能问题。
RDB与AOF的持久化机制,尤其AOF的刷盘策略,会引入磁盘IO,这才是可能成为瓶颈的地方
高 QPS(如 10 万次 GET)单次耗时 约10 微秒,一次慢查询(如 KEYS *)可能数秒。
虽然 Redis 是内存数据库,但内存不足不会直接导致 Redis 变慢,而是引发一系列连锁反应,最终表现为高延迟、高 CPU、甚至宕机。
性能问题
内存碎片:
成因:频繁的增删改、key 过期等
解决:
# 查看碎片率redis-cli INFO memory|grepmem_fragmentation_ratio# 主动整理(Redis 4.0+)redis-cli MEMORY PURGE# 配置自动碎片整理activedefragyesactive-defrag-ignore-bytes 100mb写时复制(Copy-on-Write,COW)内存抖动
场景:BGSAVE(RDB)或 BGREWRITEAOF 时,fork 子进程
问题:主进程写数据时,操作系统会复制被修改的内存页,导致内存瞬间翻倍
影响:内存不足时触发 swap 或 OOM Killer
解决:
避免在高峰期间执行持久化操作
调优 repl-diskless-sync 等参数
增加物理内存或使用从库持久化
频繁淘汰(eviction)
触发:maxmemory 达到上限,且 maxmemory-policy 为 allkeys-lru / volatile-lru 等
行为:Redis 在每次写入命令时,尝试淘汰数据,消耗 CPU
表现:写入延迟突增,QPS 骤降
解决:
提高 maxmemory
优化淘汰策略:缓存场景用 allkeys-lru,数据不可丢用 noeviction
监控 evicted_keys 指标
案例一:KEYS * 导致生产瘫痪
现象:Redis CPU 飙升到 100%,所有超时
原因:新入职 DBA 在 Master 上执行 KEYS *(实例有 2 亿 Key)
后果:阻塞 30 秒,触发哨兵误判主库下线,切换后数据丢失
改进:禁用 KEYS,提供 SCAN 接口给运维脚本
案例二:内存碎片率 300% 引发 swap
现象:used_memory 8GB,但 used_memory_rss 24GB,服务器频繁 swap
原因:业务频繁更新 Hash 字段,jemalloc 产生大量外部碎片
解决:执行 MEMORY PURGE,开启 activedefrag,碎片率降至 1.1
案例三:大 Key 删除引发阻塞
现象:业务低峰期执行 DEL 一个 5000 万成员的 Set,阻塞 8 秒,导致从库复制中断
解决:改用 UNLINK(异步删除),或分批 SPOP
核心结论:
真正拖垮 Redis 的是O(N) 命令和内存使用不当。高 QPS 并不可怕,可怕的是一个 KEYS * 或一个未拆分的大 Hash。
生产中应该设置:
禁用危险命令
拆分 BigKey
监控内存碎片和淘汰
持久化操作移至从库
使用 UNLINK 替代 DEL
bigkey优化
1:拆分bigkey,按业务维度拆分多个小key,按需查询字段,不要一次拉取全量数据,
2:数据压缩,缩减value存储空间,部分文本,超长json,业务数据无法拆分时,开启压缩,使用snappy、gzip等压缩算法,压缩后value占用空间能砍半或者更多,缓解内存负载。但压缩解压缩会影响cpu算力,适用低频率访问的历史数据,归档日志。高频数据不建议压缩。
3:更换适配数据结构,弃用String,像商品多规格多属性数据,改用hash结构更优,主key固定product:1001,name、price单独作为file字段,单点取值不用解析整个json,规避bigkey问题。list、set也可以按需替换string场景
4:过期控制+定时巡检、提前预防冗余bigKey,很多bigkey源于过期数据遗留,注销用户缓存,临时业务缓存到期未回收。日常开发给缓存配置合理TTL,依托redis过期淘汰机制自动失效,搭配定时脚本巡检,定时扫描清理无用冗余key,从·源头杜绝大key持续堆积。
持久化方式RDB与AOF是如何工作的
1:RDB快照工作方式与写时复制
什么是RDB :按一定时间间隔,将某一时刻的数据生成快照,以二进制压缩格式写入磁盘文件(dump.rdb)。
如何触发RDB:
1、到达配置⽂件中默认的快照配置时
2、主从复制
3、save命令 :主线程直接执行快照生成,过程中阻塞所有客户端请求,直至快照完成。通常用于停机维护场景。
4、bgsave命令:不会阻塞主进程,异步生成快照,主进程fork一个子进程负责将内存数据写入rdb文件。主进程继续处理客户端请求,不受影响。
写时复制技术:fork子线程时操作系统使用写时复制技术。
1、子进程创建时,复制主进程页表,不复制内存数据,与主进程共享同一块物理内存页。父子进程所有内存页全部标为只读。
2、当主进程需要修改某个内存页时,操作系统会将该页复制给主进程(延时复制),子进程依然指向原内存页。这意味着子进程持久话的是fork那一刻的内存快照,主进程修改不会影响快照的一致性。
3、子进程遍历所有数据,将键值对序列化为rdb格式,写入临时文件,完成后主线程将临时文件重命名为目标RDB文件
写时复制问题写时复制但如果在生成快照期间有大量写操作,操作系统会频繁复制内存页,增加内存开销与cpu消耗。但内存不一定翻倍,只复制修改的内存页。
页表(Page Table):每个进程拥有独立的页表。告诉cpu页表中虚拟地址对应哪块物理地址
优点:
1:文件体积小,适合全量备份,异地容灾
2:与AOF相⽐,⼤数据量恢复速度快,直接加载到内存
3:备份时性能快,不影响主线程(写时复制)
缺点:
1:定时备份策略导致数据丢失风险(默认配置:900秒内至少1次修改,300秒内至少10次修改,60秒内至少10000次修改)
2:宕机时丢失最后一次快照后所有数据
3:写时复制问题
2:AOF日志工作机制与重写原理
什么是AOF :通过记录写命令方式持久化,当redis执行一条写命令,如set,hset等,就将该命令追加至AOF文件末尾。
AOF工作过程:
1:执行完写命令后,将写命令先写入缓冲区,根据同步策略决定何时刷盘。
2:文件刷盘 appendfsync同步策略:
- always:每个命令都刷一次盘(最多丢一个命令,性能最差)
- everysec:每秒刷一次盘(默认策略,最多丢失一秒数据,性能与安全的平衡选择)
- no:由操作系统决定何时刷盘(性能最好,丢失数据最多)
3:AOF文件记录所有命令,随着时间增加文件会不断增长,需要定期重写压缩体积。
重写核心思想:不读取就AOF文件,直接读取当前内存数据,用最少量命令记录数据。
如:一个key被反复修改100次,重写时只记录最终值一条set命令。
重写过程
1:主进程fork一个子进程,子进程根据当前内存数据生成新的AOF文件。
2:重写期间主进程收到写命令,会同时记录到旧的AOF缓冲区追加至旧AOF文件与重写缓冲区(rewrite_buffer)
3:子进程完成后,主进程将重写缓冲区所有命令追加到新的AOF文件,然后新的AOF文件原子替换旧的AOF文件,保证数据不丢失。
追加旧 AOF 文件:保证重写期间的写命令实时持久化,防止宕机丢数据。
重写缓冲区:记录增量命令,用于重写完成后追加到新 AOF 文件,保证新文件完整
优点:
1:支持秒级持久化,
2:数据丢失极低
3:文件可读性强
4:单个AOF⽂件太⼤时,Redis会⾃动切换新的⽇志⽂件
缺点
1:文件体积大,写操作频繁,AOF备份性能通常⽐RDB更慢。
2:数据恢复时重放命令较慢
3:持久化策略选型与混合持久化
生产环境通常 RDB 与AOF同时开启;
redis重启时优先使用AOF文件恢复,因为AOF文件丢失数据更少。如果AOF未开启使用RDB文件恢复。
如果AOF文件毁坏,使用redis-check-AOF 工具对AOF文件修复,移除损坏的尾部命令。如果文件损坏严重可弃用AOF,改用RDB。
RDB用于定期全量备份,应对大规模快速恢复场景,AOF保证增量数据极致安全,减少意外丢失。
redis4.0引入了混合持久化策略:AOF与RDB的结合
AOF重写时不是生成纯AOF格式的命令,而是将当前内存数据先以RDB格式写入AOF文件的开头,在追加重写缓冲区增量命令。
恢复时,先加载RDB部分快速恢复大部分数据,在重放后面的增量命令。
兼顾了RDB恢复快,AOF丢失数据少的特点。
4:生产环境下如何选择
1:数据不重要可以不开启持久化,只当缓存使用
2:可以容忍几分钟数据丢失,可以之开启RDB,配置快照间隔5分钟/十分钟
3:不能容忍数据丢失,必须开启AOF,配和everysec最多丢失1秒数据
4:重要数据,同时开启AOF与RDB,RDB用于快速备份和恢复演练,AOF使用宕机恢复。
5:对数据安全极高场景,开启混合持久化,同时配置主从复制和定期备份到远程存储。
性能与安全的权衡方面,AOF的everysec适合绝大数场景。
always策略会严重降低写性能,单机写QPS可能从10万降至1万,非必要不适用
RDB的bgsave虽然不堵塞主线程,但fork子进程在内存较大时耗时较长,如30G内存,fork可能需要几百毫秒,甚至几秒。
建议业务低峰期执行RDB快照 。
几个问题
- RDB快照生成时redis还能处理请求吗?
- bgsave与save核心区别?
- bgsave如何做到不堵塞线程?
- 写时复制的作用?
- 大量写操作会导致内存翻倍吗?
- AOF文件膨胀怎末办?
- AOF重写是新请求如何罗盘?
- AOF重写底层逻辑?
- appdendfsync同步策略?
- 双持久化恢复优先级?
- AOF文件损坏的修复?
- 混合持久化的价值?
