ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Redis八股高频面试总结个人开源笔记

Redis八股高频面试总结个人开源笔记 redis底层数据结构Redis对外暴露了5种基本数据类型但底层为了兼顾内存效率和操作性能实际使用了多种物理数据结构。同一种上层类型在不同数据量下底层类型还会自动切换。1.String底层数据结构是SDS简单动态字符串可以在O(1)时间复杂度内获取长度支持自动扩容避免缓冲区溢出增长时支持空间预分配截短时将多出的空间放到free里之后无需重新分配不以\0判断字符串结束可以安全存储图片序列号对象[图片]2.List早期是ZipListLinkedList到3.2的QuickList再到7.0用ListPack替代ZipList小数据量用ZipList它是一块连续内存所有元素紧凑排列非常省内存大数据量用双向链表。但两者各有缺陷——ZipList修改效率低且有连锁更新风险LinkedList小数据时每个节点都要存两个指针一个数据指针共24字节开销指针比数据还大内存浪费严重。QuickList统一了这两种结构。本质是双向链表ZipList的混合体外层用双向链表连接多个节点每个节点内部是一个ZipList保留了链表的灵活修改能力又保持了节点内部的内存紧凑。用ListPack替代了ZipList。ZipList的核心缺陷是连锁更新——每个元素记录前驱长度一次插入可能触发后续所有元素的级联扩容。ListPack的解决方案是去掉前驱长度改为在末尾记录自身长度使每个元素的大小变化完全独立从根本上消除了连锁更新。3.Redis Hash的底层采用自适应编码策略小数据用ZipList/ListPack数组节省内存大数据用哈希表保证性能两者之间通过阈值自动切换且扩容时使用渐进式rehash避免阻塞主线程。当HashTable需要扩容时传统做法是一次性将所有数据迁移到新表Redis是单线程模型任何阻塞都会导致所有请求停滞dict结构内部维护了两个哈希表ht[0]和ht[1]。当需要扩容时先分配ht[1]。之后每次操作都会迁移ht[0]中的一个桶到ht[1]。rehash期间新增操作只写入ht[1]查找先查ht[1]再查ht[0]。这样就把原本一次性迁移分摊到了每次微秒级的操作中。4.Redis Set的底层采用内容感知编码当所有元素都是整数且数量较小时用IntSet一旦混入字符串或数量超512自动升级为HashTable。Set的HashTable只用key不用valuevalue字段始终为NULL。对于纯整数的小集合IntSet能节省90%以上的内存。5.ZSet小数据用ZipList7.0后为ListPack,大数据量时使用跳表SkipList 哈希表HashTable跳表负责按score排序和范围查询哈希表负责O(1)通过member查score。跳表的核心设计是多层索引最底层包含所有节点高层是低层的快速通道。查找时从最高层开始能前进就前进不能前进就下降.为什么选跳表而不是红黑树主要有三个原因一是范围查询友好找到起点后沿最底层顺序遍历即可红黑树需要复杂的中序遍历二是实现简单不需要旋转和变色三是并发友好局部修改易于加锁。[图片][图片][图片][图片]持久化Redis是内存数据库数据全在内存中。一旦进程崩溃或机器断电数据全部丢失。持久化就是将内存数据定期或实时地保存到磁盘重启后恢复。Redis提供RDB和AOF两种持久化机制RDB是时间点快照适合备份和灾难恢复AOF是命令日志追加数据安全性更高。Redis 4.0引入的混合持久化结合了两者的优点是当前生产环境的推荐方案。RDB核心定位全量快照在指定的时间点手动或自动把内存里所有的数据打包成一个紧凑的二进制文件dump.rdb。把内存中的所有数据都记录到磁盘RDB文件中当Redis实例故障重启后读取磁盘快照文件恢复数据速度极快手动save会阻塞主线程用bgsave去fork子进程异步执行主线程继续服务copy-on-writeCOW主进程如果在写数据触发COW单独拷贝一份数据让主进程在上面写而子进程fork的是那一瞬间的数据如果写入量大COW会导致额外内存消耗极端情况下可能翻倍。生产环境要预留足够内存。AOF核心定位增量日志记录每一个写命令在AOF文件刷盘策略何时将缓冲区数据刷到磁盘always同步刷盘写入内存数据后直接把命令写入AOF文件优点是几乎零数据丢失缺点是磁盘 IO 压力极大严重拖慢 Redis 性能。everysec每秒刷盘写内存后先把命令放入Aof缓冲区后台线性每隔一秒写入AOF文件优点是性能极高且最多只会丢失 1 秒钟的数据适合 99% 的生产环境。no操作系统控制写内存后把命令放Aof缓冲区由操作系统决定何时将缓冲区内容写入AOF文件Redis 性能最好但一旦断电可能会丢失几秒甚至几分钟的数据。bg re write aof命令让AOF文件执行重写功能 最少的命令达到相同效果开启混合持久化解法在触发 AOF 重写时子进程先把当前内存的全量数据以 RDB 格式写到文件前半段然后把重写期间新产生的增量命令以 AOF 格式追加到后半段。效果重启时先秒级加载前半段的 RDB解决恢复慢的问题再快速重放后半段的 AOF解决数据丢失的问题。既保证了极速恢复又保证了极高的数据安全性。分布式锁redis命令实现set unique_key 机器码线程ID NX EX TTLNX (互斥)只有钥匙不在门上Key不存在时才能插进去。保证了同一时刻只有一个人能进。EX (防死锁)钥匙上绑了一个倒计时炸弹TTL。如果进去的人突发心脏病服务宕机/网络断开没出来倒计时结束后炸弹爆炸锁自动销毁后面的人还能继续进。机器码线程ID (防误删)这是最关键的。假设你进去后业务卡住了倒计时结束锁自动销毁了。这时别人拿到了新钥匙进去了。等你回过神来想把锁删掉如果不校验身份你就会把别人的锁给删了 所以删锁前必须判断“这锁是我刚才加的吗”通常配合 Lua 脚本保证判断和删除的原子性Redisson实现可重入利用Hash结构记录获取锁的线程和获取锁的次数避免死锁问题大 Key密室的门牌号锁名。小 Key (Field)你的身份证号UUID 线程ID。Value你开门的次数重入计数器。加锁逻辑你第一次进门Hash 里写入 你的ID: 1你再去开保险箱Redisson 发现 Hash 里已经有你的 ID 了于是把次数变成 你的ID: 2。解锁逻辑每退出一扇门次数减 1。只有当次数减到 0 时才真正把这把锁从 Redis 中删掉。这就完美避免了“自己把自己锁死”的问题。看门狗机制当没有指定锁的TTL时默认会使用默认 TTL 为 30 秒后台定时 每隔 10 秒 检查业务是否完成。若未完成 → 重置锁 TTL续期至 30 秒保证业务完成再释放锁内存淘汰策略8种4针对设置了ttl的key-lru最近最久未使用的看最后访问时间,lfu(最近访问频率最低的),random,ttl剩余时间最短的3针对所有key-lru适合“热点数据明显”的场景比如新闻首页缓存经常看的新闻留下几年没人看的旧新闻扔掉。,lfu,random1noeviction啥也不淘汰适用场景对数据安全性要求极高宁可拒绝服务也不能丢数据的场景Redsi的key过期了怎么处理是直接删除吗[图片]Redis内存满了怎么处理[图片][图片][图片][图片][图片]Redis为什么快[图片][图片]Redis是单线程吗[图片][图片][图片][图片]大key[图片][图片]热key[图片]redis和mysql数据一致性先更新数据库再删除缓存在极端并发下它产生脏数据的概率远低于“先删缓存再更新数据库”。如果先删缓存因为写DB会慢很多在缓存已经被删除DB未更新成功之前这个间隙内读请求就会把旧值 回填到缓存造成脏数据而先更DB再删缓存脏数据只在微秒级窗口内可能发生。[图片]缓存击穿雪崩穿透[图片][图片][图片]主从复制[图片][图片][图片]
返回列表