热门八股-Redis

发布时间:2026/8/31 6:20:13
热门八股-Redis 基础概念1.Redis是什么为什么叫高性能Redis是一个基于内存的高性能Key-Value数据库也常被用作缓存、分布式锁、消息队列、排行榜、计数器等它之所以高性能主要因为数据主要存在内存里读写速度远高于磁盘核心命令执行是单线程避免了多线程锁竞争和上下文切换使用IO多路复用一个线程可以同时处理大量连接数据结构设计得很精巧不同场景有不同的底层编码使用操作系统零拷贝减少数据拷贝开销面试可以说Redis快不只是因为内存还因为单线程模型、多路IO复用和高效数据结构一起发挥作用2.Redis为什么快单线程模型、多路IO复用、内存操作、数据结构优化纯内存操作。大部分请求直接在内存里完成不需要频繁访问磁盘单线程执行命令。Redis的核心命令处理是单线程避免了加锁、线程切换等开销IO多路复用。Redis用一个线程监听多个socket连接谁有事件就处理不需要一个线程一个连接底层数据结构优化。比如List用quicklistHash小数据用listpack大数据再转hashtable既省内存又保证性能3.Redis单线程为什么还能抗高并发Redis单线程指的是命令执行主流程单线程不是整个Redis只有一个线程它能抗高并发原因是请求大多数是内存操作执行非常快单线程避免了锁竞争IO多路复用可以同时管理大量连接Redis命令通常很短不会长时间占用CPU但也要注意如果执行大key删除、复杂Lua、大范围查询等慢命令单线程会被阻塞影响所有请求。4.Redis支持的数据类型有哪些五大基础3大特殊高级类型Redis常见5大基础数据类型String字符串、数字、二进制List列表Hash哈希Set无序集合ZSet有序集合3大特殊数据类型Bitmap位图HyperLogLog基数统计Geo地理位置高级或扩展类型Stream消息队列Bitfield位域操作Bloom Filter布隆过滤器通常来自RedisBloom模块5.Redis常用应用场景有哪些缓存缓存热点数据减轻数据库压力分布式锁用 set nx ex 实现互斥计数器文章阅读数、点赞数、库存扣减排行榜ZSet实现积分榜、热度榜Session共享分布式系统保存登录态消息队列List、Stream实现异步消息限流计数器、滑动窗口、令牌桶去重统计Bitmap、HyperLogLog、布隆过滤器Redis适合高频、低延迟、数据结构友好的场景基础数据结构1.String底层结构、最大容量、使用场景Redis String底层使用SDS也就是Simple Dynamic String。SDS相比C字符有几个优势记录字符串长度获取长度为O1二进制安全可以存图片、序列化对象等预分配空间减少频繁扩容防止缓冲区溢出String最大容量是512MB但实际开发不建议存这么大否则会造成网络、内存和阻塞问题常见场景缓存JSON字符串计数器incr分布式锁验证码、token、session2.List底层quicklist原理Redis早期List底层用ziplist和linkedlist后来统一改成quicklist。quicklist可以理解为“链表压缩列表”的组合。整体是一个双向链表每个链表节点里存一段连续内存结构这样做的好处比纯链表更省内存因为减少大量指针开销比纯链表数组更适合两端插入删除兼顾空间和性能List常用于队列、栈、简单消息队列等场景但现在更推荐Stream做可靠消息队列3.Hash底层结构为什么适合hashHash是field-value结构很适合存对象Hash底层在数据少、字段短时使用listpack节省内存数据变多后会转为hashtable提高查询效率它适合存对象的原因可以单独修改某个字段不用整体反序列化相比多个String key更省key的空间结构清晰适合用户信息、商品信息、配置对象等但如果对象字段很多嵌套复杂还是要注意序列化和维护成本4.Set底层、特点、应用场景Set是无序不重复集合。底层根据数据情况选择整体且数量少时用intset省内存其他情况或数量变大时用hashtable特点元素唯一支持交集、并集、差集查询元素是否存在很快常见场景标签系统好友共同关注抽奖去重黑名单、白名单用户去重集合5.ZSet底层跳表skiplist原理为什么用跳表不用红黑树ZSet是有序集合每个元素有一个score用于排序。Redis ZSet底层通常由dictskiplidt组成dict用来按member快速查scoreskiplist用来按score排序和范围查询跳表可以理解为多层有序链表底层链表保存全部数据上层链表保存部分索引。查找时从高层往低层走平均复杂度O(logN)为什么不用红黑树跳表实现比红黑树简单范围查询很方便只有找到起点后沿链表向后遍历插入删除也比较容易维护性能接近平衡树工程实现更友好ZSet常用于排行榜、延迟队列、权重排序、热度榜特殊数据结构1.HyperLogLog作用、误差范围、适用场景HyperLogLog用于基数统计也就是统计不重复元素个数典型场景是统计UV一天有多少独立用户访问它的优点是非常省内存。Redis中一个HyperLogLog大约只需要12KB就能统计海量数据基数缺点是有误差标准误差大约是0.81%。所以它适合允许少量误差的统计场景不适合精确计数常用命令PFADD key user1 user2 PFCOUNT key PFMERGE dest source1 source22.Bitmap位图原理、签到、统计活跃用户Bitmap本质上还是String只是按bit位操作每一位只有0或1非常适合表示是否存在、是否签到、是否活跃比如用户签到key:sign:1001:202608第一天对应offset 0setbit key offset value签到设置为1未签到设置为0常用场景用户签到活跃用户统计‘布尔状态记录连续打卡天数Bitmap很省内存但offset如果特别大中间空洞也会占用空间所以要设计好偏移量3.Geo地理位置实现原理Redis Geo用来存储经纬度并计算地理位置距离底层实际是ZSet。Redis会把经纬度编码为geohash在作为score存到ZSet里常见命令GEOADD city 116.40 39.90 beijing GEODIST city beijing shanghai km GEOSEARCH city FROMLONLAT 116.40 39.90 BYRADIUS 10 KM适用场景附近的人附近门店网约车附近司机地理围栏粗筛注意Geo适合快速筛选不适合替代专业GIS系统4.Stream消息队列原理、消费组、偏移量Stream是redis 5.0引入的消息队列结构比List更适合做可靠消息。每条消息都有一个递增ID格式类似时间戳-序号Stream支持消息队列持久保存消费组Consumer Group多消费者分担消费ACK确认机制Pending List记录已投递但未确认的消息消费组里每个消费者读取消息后需要XACK确认如果消费者挂了消息会留在Pending List可被其他消费者认领它适合轻量级消息队列但如果对高吞吐、复杂路由、跨机房可靠性要求很高还是更推荐Kafka RocketMQ更专业MQ5.Bitmap和HyperLogLog区别Bitmap适合精确记录某个用户是否存在比如“用户今天是否签到”。它可以精确统计但需要能把用户映射到合理的offsetHyperLogLog适合统计不重复数量比如“今天UV是多少”它非常省内存但有误差不能知道具体有哪些用户Bitmap精确可判断某个元素是否存在占用空间和最大offset有关HyperLogLog近似只统计数量不能反查元素占用空间固定且很小6.Redis布隆过滤器原理、优缺点、误判问题、使用场景布隆过滤器用于判断一个元素“可能存在”或“一定不存在”它由一个位数组和多个哈希函数组成。添加元素时用多个哈希函数算出多个位置把这些位置设为1.查询元素时如果这些位置都为1就认为可能存在只有有一个位置为0就一定不存在优点非常省内存查询速度快适合海量数据判重缺点有误判不存在的数据可能被判断为存在一般不支持直接删除除非使用计数布隆过滤器使用场景缓存穿透防护黑名单过滤爬虫URL去重推荐系统去重缓存经典问题1.缓存穿透是什么产生原因四种解决方案缓存穿透指查询一个缓存和数据库都不存在的数据。请求每次都打到数据库缓存不起作用产生原因恶意请求大量不存在的key业务查询了非法ID数据本身确实不存在解决方案缓存空值。数据库查不到也把空结果缓存一小段时间布隆过滤器请求先经过布隆过滤器不存在是key直接拦截参数校验明显非法id、格式直接拒绝接口限流和风控防止恶意高频请求实际开发常用“参数校验缓存空值布隆过滤器”2.缓存击穿是什么产生原因解决方案缓存击穿指某个热点key过期瞬间大量请求同时访问这个key全部打到数据库它和缓存穿透不同击穿访问的是热点存在数据只是缓存刚好失效解决方案热点key不设置过期时间后台异步刷新互斥锁只允许一个线程查数据库并重建缓存逻辑过期缓存里存过期时间过期后先返回旧值再异步刷新热点key提前续期3.缓存雪崩是什么产生原因事前/事中/事后解决方案缓存雪崩指大量key同一时间失效或者Redis整体不可用导致请求集中打到数据库产生原因大量key设置相同过期时间Redis宕机缓存集群故障事前防御key过期时间加随机值热点数据永不过期或逻辑过期Redis高可用部署做限流、降级、熔断事中处理限流保护数据库降级返回旧数据或默认值快速扩容和恢复Redis事后优化排查过期时间设计完善监控告警做缓存预热4.Redis大key问题本质就是做拆分大key指value很大或者集合元素特别多的key常见例子一个String存几MB的JSON一个Hash有几十万个field一个List、Set、ZSet有上百万元素危害网络传输慢删除阻塞迁移和扩容慢单线程处理时间长影响其他请求解决思路拆key比如按用户、时间、分片拆分大对象压缩或只存必要字段集合分页读取避免一次取全量删除用UNLINK异步删除定期扫描识别大key5.Redis热key问题如何发现和解决热key指某个key被大量访问导致单节点压力过高发现方式Redis hotkeys采样探测业务埋点统计访问频次代理层或网关统计监控Redis单节点CPU、QPS、带宽异常解决方案本地缓存减少Redis访问热key拆分成多个副本key随机读读写分离让从节点分担读请求热点数据提前预热对极端热点做限流和降级京东hotkey探测机制的思路也是通过客户端、代理或服务端采样识别热点key在推送到本地缓存减少集中访问过期/淘汰策略1.Redis键过期删除三种策略定时删除、惰性删除、定期删除定时删除给每个key设置定时器到期立刻删。优点是内存释放及时缺点是定时器太多CPU压力大惰性删除访问key时才判断是否过期过期就删除。优点是省CPU缺点是过期key如果没有访问会一直占内存定期删除Redis定期抽样检查一批key删除过期数据。它是CPU和内存之间的折中Redis实际采用“惰性删除定期删除”2.Redis内存满后八大淘汰策略分别是什么Redis内存达到maxmemory后会根据淘汰策略处理noeviction不淘汰新写入报错allkeys-lru所有key中淘汰最近最少使用volatile-lru设置过期时间的key中淘汰最近最少使用allkeys-random所有key中随机淘汰volatile-random设置过期时间的key中随机淘汰volatile-ttl设置过期时间的key中优先淘汰快过期的allkeys-lfu所有key中淘汰最近最不常用volatile-lfu设置过期时间的key中淘汰最近最不常用缓存场景常用allkeys-lru或allkeys-lfu3.LRU底层实现原理Redis近似LRU怎么做LRU:Least Recently Used最近最少使用标准LRU通常用“哈希表双向链表”实现哈希表key映射链表节点快速找到节点双向链表维护访问顺序访问某个key时把它移动到链表头淘汰时删除链表尾但Redis没有严格维护全局LRU链表因为成本太高Redis使用近似LRU随机采样一批key从中淘汰最久未访问的key。采样数量由配置控制近似LRU的好处是实现简单开销低效果接近真实LRU4.过期键会不会主动占用内存主从间过期怎么同步过期key如果没有被访问可能会在内存里停留一段时间直到定期删除扫描到它。所以过期key可能短暂占用内存。主从同步方面为了保持一致性一般由主节点负责过期删除。主节点删除过期key后会向从节点发出删除命令从节点执行删除。从节点通常不会独立判断并删除过期key以避免主从数据不一致缓存与数据库一致性1.缓存和数据库双写一致性几种策略常见策略有先更新数据库再删除缓存先删除缓存再更新数据库先更新数据库再更新缓存延时双删订阅binlog异步更新缓存生产中最常见的是“先更新数据库再删除缓存”因为直接更新缓存容易受并非写影响导致旧数据覆盖新数据2.先更新数据库再删缓存、先删缓存再更新数据库优缺点先更新数据库再删缓存优点是更符合数据源以数据库为准的思想。删除缓存后下次查询会从数据库加载新数据缺点是删除缓存失败可能留下旧缓存所以需要重试、消息队列或订阅binlog兜底先删除缓存再更新数据库缺点更明显删除缓存后如果有查询请求进来可能读到旧数据库值并写回缓存。随后数据库才更新缓存就变成旧值所以一般不推荐简单使用“先删缓存再更新数据库”。3.延时双删策略原理延时双删的流程先删除缓存更新数据库等待一小段时间再删除一次缓存第二次删除是为了清理并发读请求可能写入的旧缓存但延时时间不好控制太短可能无效太长影响一致性窗口。因此它不是银弹适合对一致性要求不是极端高的场景4.如何保证强一致性Canal订阅binlog方案严格强一致性很难靠缓存做到因为缓存和数据库是两个系统。如果业务必须强一致建议不使用缓存直接查数据库或者更新时加锁串性化处理或者使用事务消息、可靠队列保证最终一致Canal方案属于最终一致性它模拟MySQL从库订阅binlog监听数据库变更再异步删除或更新Redis缓存流程业务写数据MySQL产生binlogCanal监听binlogCanal投递变更事件消费者删除或更新缓存它的优点是解耦业务代码缺点是链路变长需要处理消息延迟、重复消费和失败重试。持久化RDBAOF超级高频1.RDB是什么原理、触发方式、优缺点RDB是Redis的快照持久化。它会在某个时间点把内存数据生成一个快照文件通常叫dump.rdb原理执行RDB时Redis调用fork()创建子进程fork采用写时复制COW子进程把内存全量数据写入临时rdb文件写入完成替换旧的dump.rdb文件AOF触发方式配置规则自动触发手动执行save手动执行bgsave主从复制是可能触发save会阻塞主线程不推荐线上使用。bgsave会fork子进程生成RDB主进程继续处理请求优点文件紧凑恢复速度快适合全量备份对运行时影响相对较小缺点两次快照之间的数据可能丢失fork子进程会有开销大内存实例可能有抖动2.AOF是什么日志刷写策略三种AOF是Append Only File追加写日志。它会把写命令追加到AOF文件里重启时通过重放命令恢复数据。AOF刷盘策略有三种always每次写命令都刷盘最安全性能最低everysec每秒刷盘一次最多丢一秒数据默认常用no由操作系统决定刷盘性能高但丢数据风险大生产中通常使用everysec在性能和可靠性之间折中3.AOF重写机制原理为什么要重写AOF会不断追加写命令时间久了会越来越大很多历史命令可以合并比如1 set count 12 set count 23 set count 3最终只需要保存set count 3AOF重写不是简单压缩旧文件而是根据当前内存数据生成一份新的最小化AOF文件重写期间Redis会fork子进程生成新的AOF同时主进程继续处理写请求并把新增写入记录到缓冲区。重写完成后把缓冲区内容追加到新AOF再替换旧文件4.RDB和AOF对比区别、各自适用场景RDB是快照AOF是命令日志RDB文件小恢复快适合备份但可能丢失最近一段数据AOF数据更完整最多通常丢一秒但文件大恢复可能更慢。适用场景只做缓存适合丢数据可以不开持久化或只开RDB希望恢复较快RDB更合适希望数据尽量少丢AOF更合适既要备份又要可靠RDBAOF混合持久化5.混合持久化原理RDBAOF混合混合持久化是Redis 4.0后支持的能力开启后AOF重写生成的新文件前半部分是RDB格式的全量快照后半部分是AOF格式的增量命令。这样既能利用RDB恢复快的优点也能利用AOF丢数据少的优点简单说先用RDB快速恢复大部分数据再用AOF回放后续增量6.Redis宕机后的恢复流程Redis重启恢复时大致按优先级加载持久化文件。如果开启AOF。通常优先加载AOF因为AOF数据更完整如果没开启AOF则加载RDB如果使用混合持久化加载AOF文件会先读取其中的RDB部分再回放后面的AOF增量命令如果文件损坏可以使用redis-check-aof或redis-check-rdb工具尝试修复并发分布式锁1.Redis分布式锁基础实现原理SET NX EXSET NX EXnx只有key不存在时才设置成功key存在时获取锁失败用来保证互斥同一时间只能一个客户端拿到锁ex seconds:给key设置过期时间防止死锁不能直接del lock:order。value存唯一标识释放是判断value是不是自己的才删除。释放锁需要Lua脚本保证原子性存在的问题锁过期业务还没执行完。解决方案看门狗续期Redission主从切换锁丢失。解决方案Redlock业务很少用2.分布式锁超时释放问题怎么解决业务执行时间超过锁过期时间锁提前释放锁失效解决看门狗 Redission实现获取锁不手动设置过期时间后台线程每10秒检测锁还被当前线程持有就把过期时间重置回30秒业务执行完释放锁看门狗停止客户端宕机则不再续期锁自动过期注意手动指定leaseTime看门狗失效3.锁续约/续命怎么做Redission看门狗原理看门狗是Redission后台守护线程实现锁自动续约默认锁超时30秒每10秒执行Lua脚本锁还被当前线程持有重置过期时间为30秒底层锁使用Hash结构同时支持可重入4.主从架构下分布式锁锁失效问题主从失效原因主拿到锁锁还未异步同步到从主宕机从升主锁消失其他客户端获取锁锁失效。解决RedlockN个独立Redis半数以上节点获取锁才算成功但是成本高性能差生产很少使用业务兜底Redis锁做前置控制数据库乐观锁做最终防护5.Redission可重入锁原理Redission可重入锁底层是Hash结构key锁名field线程唯一标识value可重入计数器锁不存在则创建同一线程再次加锁计数器加1实现重入释放锁计数器减1计数器大于0不删key计数器为0真正释放锁全部逻辑放在Lua脚本保证Redis端原子执行主从复制集群1.Redis主从复制原理全量同步/增量同步主从复制就是把主节点数据同步到从节点实现读扩展和高可用基础第一次同步通常是全量同步从节点发送同步请求主节点生成RDB快照主节点把RDB发给从节点从节点加载RDB主节点把同步期间的新写命令继续发给从节点断线重连时如果复制积压缓冲区还保留这缺失数据可以做增量同步只补发断开期间的命令2.主从复制缓冲区和积压缓冲区作用复制缓冲区是每个从节点对应的输出缓冲区用来暂存主节点要发给从节点的数据。如果从节点太慢缓冲区可能变大甚至导致连接断开复制积压缓冲区是主节点维护的一块环形缓冲区保存最近一段写命令。它用于从节点短暂断线后的增量同步区别辅助缓冲区面向单个从节点积压缓冲区主节点全局共享用于断线续传3.主从同步断线后怎么增量同步Redis通过复制偏移量和主节点runid判断能不能增量同步从节点断线重连后会告诉主节点自己的复制偏移量。如果主节点runid没变并且缺失的数据还在复制积压缓冲区里主节点就只发送缺失部分如果条件不满足比如主节点重启了或者积压缓冲区被覆盖了就只能全量同步4.哨兵Sentinel作用、原理、故障转移流程Sentinel用来监控Redis主从集群并在主节点故障时自动故障转移。它主要做三件事监控检查主从节点是否存活通知发现异常后通知管理员或系统自动故障转移主节点挂了选一个从节点升级为新主故障转移流程Sentinel主观判断主节点下线多个Sentinel达成客观下线Sentinel之间选举leaderleader选择一个合适从节点作为新主其他从节点改为复制新主客户端更新主节点地址5.哨兵怎么选主、投票机制Sentinel选主时会综合考虑从节点是否在线slave-priority优先级复制偏移量数据越新越优先runid字典序作为兜底比较投票机制上Sentinel需要达到quorum判断客观下线故障转移时还需要Sentinel集群选出一个leader来执行切换。6.Redis Cluster集群原理、哈希槽16384Redis Cluster是Redis官方分片集群方案。它把整个key空间划分为16384个哈希槽每个key通过CRC16计算后对16384取模决定落在哪个槽每个主节点负责一部分槽客户端访问key时如果请求发错节点Redis会返回MOVED或ASK让客户端重定向到正确节点Cluster同时支持主从复制每个主节点可以有从节点用于故障转移7.集群槽位分配、分配规则Redis Cluster的分片单位是槽不是单个key比如节点A负责0到5000节点B负责5001到10000节点C负责10001到16383key根据哈希结果落到某个槽再由负责该槽的节点处理如果key使用{}哈希标签比如user:{100}:name和order:{100}:listRedis只对{100}计算哈希这样可以让多个key落到同一个槽方便多key操作8.集群扩容、缩容原理扩容时新增节点加入集群然后从已有节点迁移一部分槽到新节点缩容时先把要下线节点负责的槽迁移到其他节点再把节点移出集群槽迁移过程中客户端可能收到ASK重定向表示这个key正在迁移需要临时去另一个节点访问。整个过程本质是槽位重新分配而不是直接按节点搬整个数据库9.为什么是16384个哈希槽不是更大16384是一个工程折中槽太少分片不够细槽太多集群节点之间需要交换的槽位状态信息会变大Redis Cluster节点之间通过gossip协议传播集群信息每个节点都要维护槽位分配状态。16384个槽既能满足绝大多数分片需求也能控制元数据开销10.集群高可用、脑裂问题怎么解决Redis Cluster通过主从复制和故障转移实现高可用。主节点挂了从节点可以被提升为新主脑裂指网络分区时旧主和新主可能同时对外提供写服务导致数据冲突缓解方案设置合适的min-replicas-to-write和min-replicas-max-lag主节点在从节点不足或复制延迟过高时拒绝写入部署奇数个主节点和合理副本保证Sentinel或Cluster节点分布在不同机器客户端正确处理故障转移和重定向Redis无法像强一致数据库那样完全避免所有脑裂数据风险所以核心账务类强一致场景要谨慎使用。网络模型底层原理1. Redis多路复用IO模型epoll原理Redis使用IO多路复用处理大量客户端连接在Linux上常用epoll它可以让一个线程同时监听多个socket当某个socket可读或可写时内核通知Redis处理相比一个连接一个线程epoll的优势是线程数量少上下文切换少可以支撑大量连接事件来了再处理效率高Redis的事件循环会不断处理文件事件和时间事件从而完成网络读写、命令执行、定时任务等2.Redis 6.0多线程做了什么网络多线程、命令还是单线程Redis 6.0引入多线程主要用于网络IO读写和协议解析不是把命令执行改成多线程也就是说读取请求可以多线程写回响应可以多线程命令执行仍然主要是单线程这样设计是为了提升网络IO性能同时保留单线程命令执行的简单性避免复杂锁竞争面试说清楚Redis 6.0多线程不是所有操作都多线程核心命令执行仍然保持单线程模型3.Pipeline管道作用、原理、适用场景Pipeline可以把多个命令一次性发给Redis不要每条命令都等待响应后再发下一条它优化的是网络往返时间RTT比如原来执行100条命令需要100次请求使用Pipeline后可以一次发送100条命令再批量读取响应适用场景批量写入缓冲批量查询多个key初始化数据网络延迟较高时提升吞吐注意Pipeline不是事务中间命令失败不会自动回滚。批量太大也会占用Redis和客户端内存4.Lua脚本作用、原子性、批量操作Redis支持执行Lua脚本常用来把多个命令打包成一个原子操作Redis执行Lua脚本时脚本会作为一个整体执行中间不会被其他命令插入所以具备原子性常见场景分布式锁释放判断value后再删除秒杀库存扣减限流脚本批量判断和更新注意Lua脚本不要写太复杂不要执行太久因为Redis单线程执行命令长脚本会阻塞其他请求总结Redis快是因为内存操作、单线程命令执行、IO多路复用和高效数据结构Redis单线程指核心命令执行单线程不代表没有后台线程或网络辅助线程String底层是SDSList底层是quicklistZSet常用dictskiplistHyperLogLog用于近似UV统计Bitmap用于精确状态标记缓存穿透差不存在的数据缓存击穿是热点key失效缓存雪崩是大量key失效或Redis故障Redis过期删除采用惰性删除定期删除内存淘汰常见有LRU、LFU、随机、TTL、noeviction缓存一致性常用先更新数据库再删除缓存失败要有重试或binlog兜底RDB是快照AOF是命令日志混合持久化兼顾恢复速度和数据完整性Redis分布式锁STE NX EXRedission看门狗解决分布式锁超时问题自动续期Redission可重入锁底层Hashvalue为0时才删除锁Cluster用16384个哈希槽做分片哨兵负责主从自动故障转移主线基础概念理解Redis为什么快适合做什么数据结构掌握基础数据结构及特殊结构的场景缓存问题穿透、击穿、雪崩、大key、热key是必问过期淘汰理解删除策略和内存淘汰策略一致性知道缓存和数据库双写为什么难以及常见方案持久化RDB、AOF、混合持久化要能对比