Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Shellbye opened this issue Nov 13, 2019 · 0 comments
Open

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

Shellbye opened this issue Nov 13, 2019 · 0 comments

Comments

@Shellbye
Copy link
Owner


开篇:授人以鱼不若授人以渔—— 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:极度深寒 —— 探索「基数树」内部
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant