Shellbye.github.io icon indicating copy to clipboard operation
Shellbye.github.io copied to clipboard

《redis深度历险:核心原理和应用实践》读书笔记

Open Shellbye opened this issue 5 years ago • 0 comments


开篇:授人以鱼不若授人以渔—— Redis 可以用来做什么?
    由 Redis 面试想到的
    小册的内容范围
    Redis 可以做什么?
    小结

基础:万丈高楼平地起 ——Redis 基础数据结构
    Redis 安装
    Redis 基础数据结构
        string (字符串)
        list (列表)
            Redis 的列表结构常用来做异步队列使用
        hash (字典)
            Redis 为了高性能,不能堵塞服务,所以采用了渐进式 rehash 策略
        set (集合)
            zset (有序列表)
                它的内部实现用的是一种叫着「跳跃列表」的数据结构
            跳跃列表
    容器型数据结构的通用规则
        list/set/hash/zset 这四种数据结构是容器型数据结构,它们共享下面两条通用规则
            1、create if not exists
            2、drop if no elements
        过期时间
            如果一个字符串已经设置了过期时间,然后你调用了 set 方法修改了它,它的过期时间会消失

应用 1:千帆竞发 —— 分布式锁
    分布式锁
        setnx(set if not exists)
    超时问题
        Redis 的分布式锁不能解决超时问题,如果在加锁和释放锁之间的逻辑执行的太长,以至 于超出了锁的超时限制,就会出现问题。因为这时候锁过期了,第二个线程重新持有了这把锁, 但是紧接着第一个线程执行完了业务逻辑,就把锁给释放了,第三个线程就会在第二个线程逻 辑执行完之间拿到了锁
    可重入性

应用 2:缓兵之计 —— 延时队列
    异步消息队列
        Redis 的 list(列表) 数据结构常用来作为异步消息队列使用,使用rpush/lpush操作入队列, 使用 lpop 和 rpop 来出队列
    队列延迟
        blpop/brpop
    空闲连接自动断开
    锁冲突处理
        1、直接抛出异常,通知用户稍后重试; 
        2、sleep 一会再重试; 
        3、将请求转移至延时队列,过一会再试;
    延时队列的实现

应用 3:节衣缩食 —— 位图
    基本使用
        零存零取
            setbit
            getbit
        整存零取
    统计和查找
        bitcount
        bitpos
    魔术指令 bitfield
        饱和截断 SAT
        失败不执行 FAIL

应用 4:四两拨千斤 —— HyperLogLog
    UV 粗略统计
    使用方法
        pfadd
        pfcount
        pfmerge
    HyperLogLog 实现原理

应用 5:层峦叠嶂 —— 布隆过滤器
    布隆过滤器是什么?
        当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在
    Redis 中的布隆过滤器
    布隆过滤器基本使用
        bf.add
        bf.exists
        bf.madd
        bf.mexists
    布隆过滤器的原理
    布隆过滤器的其它应用
        爬虫系统
        NoSQL 数据库
            通过内存中的布隆过滤器过滤掉大量不存在的 row 请求
        垃圾邮件过滤

应用 6:断尾求生 —— 简单限流
    如何使用 Redis 来实现简单限流策略?

应用 7:一毛不拔 —— 漏斗限流
    Redis-Cell

应用 8:近水楼台 —— GeoHash
    GeoHash 算法
    Redis 的 Geo 指令基本使用

应用 9:大海捞针 —— Scan
    Scan 的基础使用
        keys 的两个缺点
            1、没有 offset、limit 参数,一次性吐出所有满足条件的 key
            2、keys 算法是遍历算法,复杂度是 O(n)
        scan 相比 keys 具备有以下特点:
            1、复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程;
            2、提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是一个 hint,返回的 结果可多可少;
            3、同 keys 一样,它也提供模式匹配功能;
            4、服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;
            5、返回的结果可能会有重复,需要客户端去重复,这点非常重要;
            6、遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;
            7、单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零;
    字典的结构
    scan 遍历顺序
        普通加法和高位进位加法的区别
    字典扩容
    对比扩容缩容前后的遍历顺序
    渐进式 rehash
    更多的 scan 指令
        zscan
        hscan
        sscan
    大 key 扫描
        在平时的业务开发中,要尽量避免大 key 的产生
        那如何定位大 key 呢

原理 1:鞭辟入里 —— 线程 IO 模型
    非阻塞 IO
    事件轮询 (多路复用)
    指令队列
    响应队列
    定时任务

原理 2:交头接耳 —— 通信协议
    RESP(Redis Serialization Protocol)
    客户端 -> 服务器
    服务器 -> 客户端

原理 3:未雨绸缪 —— 持久化
    快照原理
        copyOnWrite
    fork(多进程)
    AOF 原理
    AOF 重写
    fsync
        glibc 提供的 fsync(int fd)函数可以将指定文件的内容强制从内核缓存刷到磁盘
    运维
    Redis 4.0 混合持久化

原理 4:雷厉风行 —— 管道
    Redis 的消息交互
        多条合并一条
    管道压力测试
    深入理解管道本质

原理 5:同舟共济 —— 事务
    Redis 事务的基本使用
        multi/exec/discard
    原子性
        不保证原子性
    discard(丢弃)
    优化
    Watch

原理 6:小道消息 —— PubSub
    消息多播
    PubSub
    模式订阅
    消息结构
    PubSub 缺点

原理 7:开源节流 —— 小对象压缩
    小对象压缩存储 (ziplist)
    内存回收机制
        操作系统回收内存是以页为单位,如果这个页上只要有一个 key 还在使用,那么它就不能被回收
    内存分配算法

原理 8:有备无患 —— 主从同步
    CAP 原理
        C - Consistent ,一致性
        A - Availability ,可用性
        P - Partition tolerance ,分区容忍性
        
        网络断开的场景的专业词汇叫着「网络分区」
        一句话概括 CAP 原理就是——网络分区发生时,一致性和可用性两难全
    最终一致
    主从同步
    增量同步
        Redis 同步的是指令流,主节点会将那些对自己的状态产生修改性影响的指令记录在本 地的内存 buffer 中,然后异步将 buffer 中的指令同步到从节点,从节点一边执行同步的指 令流来达到和主节点一样的状态,一遍向主节点反馈自己同步到哪里了
    快照同步
    增加从节点
    无盘复制

集群 1:李代桃僵 —— Sentinel
    消息丢失
    Sentinel 基本使用

集群 2:分而治之 —— Codis
    略
集群 3:众志成城 —— Cluster
    槽位定位算法
    跳转
    迁移

拓展 1:耳听八方 —— Stream
    消息 ID
        timestampInMillis-sequence
    消息内容
    增删改查
        1、xadd 追加消息
        2、xdel 删除消息,这里的删除仅仅是设置了标志位,不影响消息总长度
        3、xrange 获取消息列表,会自动过滤已经删除的消息
        4、xlen 消息长度
        5、del 删除 Stream
    独立消费
        xread
    创建消费组
        略

拓展 2:无所不知 —— Info 指令
    Info
        1、Server 服务器运行的环境参数 2、Clients 客户端相关信息 3、Memory 服务器运行内存统计数据 4、Persistence 持久化信息
        5、Stats 通用统计数据
        6、Replication 主从复制相关信息
        7、CPU CPU 使用情况
        8、Cluster 集群信息
        9、KeySpace 键值对统计数量信息
    Redis 每秒执行多少次指令?
        > redis-cli info stats | grep ops 
        instantaneous_ops_per_sec:789
    Redis 连接了多少客户端?
        > redis-cli info clients
        connected_clients:124
    Redis 内存占用多大 ?
        > redis-cli info memory | grep used | grep human
        used_memory_human:827.46K # 内存分配器 (jemalloc) 从操作系统分配的内存总量 
        used_memory_rss_human:3.61M # 操作系统看到的内存占用 ,top 命令看到的内存 
        used_memory_peak_human:829.41K # Redis 内存消耗的峰值 
        used_memory_lua_human:37.00K # lua 脚本引擎占用的内存大小
    复制积压缓冲区多大?
        > redis-cli info replication |grep backlog 
        repl_backlog_active:0
        repl_backlog_size:1048576 # 这个就是积压缓冲区大小
        复制积压缓冲区大小非常重要,它严重影响到主从复制的效率。当从库因为网络原因临 时断开了主库的复制,然后网络恢复了,又重新连上的时候,这段断开的时间内发生在 master 上的修改操作指令都会放在积压缓冲区中,这样从库可以通过积压缓冲区恢复中断的 主从同步过程。
        积压缓冲区是环形的,后来的指令会覆盖掉前面的内容。如果从库断开的时间过长,或 者缓冲区的大小设置的太小,都会导致从库无法快速恢复中断的主从同步过程,因为中间的 修改指令被覆盖掉了。这时候从库就会进行全量同步模式,非常耗费 CPU 和网络资源。

拓展 3:拾遗漏补 —— 再谈分布式锁
    略

拓展 4:朝生暮死 —— 过期策略
    过期的 key 集合
    定时扫描策略
    从库的过期策略
        从库对过期的处理是被动的。主库在 key 到期时,会在 AOF 文件里增加一条 del 指令

拓展 5:优胜劣汰 —— LRU
    当实际内存超出 maxmemory 时,Redis 提供了几种可选策略 (maxmemory-policy) 来让 用户自己决定该如何腾出新的空间以继续提供读写服务
    LRU 算法
    近似 LRU 算法
        Redis 为实现近似 LRU 算法,它给每个 key 增加了一个额外的小字 段,这个字段的长度是 24 个 bit,也就是最后一次被访问的时间戳

拓展 6:平波缓进 —— 懒惰删除
    Redis 为什么要懒惰删除(lazy free)?
        del
            删除大对象会导致卡顿
        unlink
            源自 4.0 
    flush
    异步队列
    AOF Sync 也很慢
    更多异步删除点

拓展 7:妙手仁心 —— 优雅地使用 Jedis
    略 
拓展 8:居安思危 —— 保护 Redis
    指令安全
        rename-command
    端口安全
    Lua 脚本安全
    SSL 代理

拓展 9:隔墙有耳 —— Redis 安全通信
    spiped

源码 1:极度深寒 —— 探索「字符串」内部结构
    Redis 的字符串叫着「SDS」,也就是 Simple Dynamic String。它的结构是一个带长度信 息的字节数组。
        struct SDS<T> {
            T capacity; // 数组容量
            T len; // 数组长度
            byte flags; // 特殊标识位,不理睬它 
            byte[] content; // 数组内容
        }
    embstr vs raw
        Redis 的字符串有两种存储方式,在长度特别短时,使用 emb 形式存储 (embeded),当 长度超过 44 时,使用 raw 形式存储

源码 2:极度深寒 —— 探索「字典」内部
    dict 内部结构
        dict 结构内部包含两个 hashtable,扩容缩容时,进行渐进式搬迁
    渐进式 rehash
    查找过程
    hash 函数
    hash 攻击
        元素都集中到个别链表中,直接导致查找效率急剧下降,从 O(1)退化到 O(n)
    缩容条件
    set 的结构
    Redis 里面 set 的结构底层实现也是字典,只不过所有的 value 都是 NULL,其它的特性和字典一模一样。

源码 3:极度深寒 —— 探索「压缩列表」内部
    增加元素
    级联更新
    IntSet 小整数集合

源码 4:极度深寒 —— 探索「快速列表」内部
    略

源码 5:极度深寒 —— 探索「跳跃列表」内部结构
源码 6:极度深寒 —— 探索「紧凑列表」内部
源码 7:极度深寒 —— 探索「基数树」内部

Shellbye avatar Nov 13 '19 07:11 Shellbye