《Redis实战》读书笔记

《Redis实战》

《Redis实战》豆瓣链接

总结

本书面向 Redis 初学者,全书非常贯彻”实战“二字,基本是以应用案例驱动的一本书,非常适合想跟着案例讲解一步步编码学习的读者,但也是因为这个特色导致书本欠缺深度和广度,不少地方更是显得啰嗦无聊。

收获

  1. Redis 基本命令和概念
  2. Redis 各类数据结构的实际应用案例,有些比较有启发性,如倒排索引的实现

不足

  1. 局限于 Redis 单机技术,欠缺 Redis 集群环境上的技术讲解
  2. 应用案例的代码注释虽多但欠缺图解不易理解,不少浅显的代码会用一段文本去解释,着实有些啰嗦并降低了整体可读性,需要跟着作者的思路细看,效率低(故我没细看代码)

一个补充

Redis 创始人 Antirez 博客 Programming and Writing 写到编程和写作之间的异同之处,如下是文章摘抄:

The most obvious parallel between the two activities is that in both of them you write something. Code is not prose written in a natural language, yet it has a set of fixed rules (a grammar), certain forms that most programmers will understand as natural and others that, while formally correct, will sound hard to grasp.
There is, however, a much deeper connection between the two activities: a good program and a good novel are both the sum of local and global elements that work well. Good code must be composed of well written and readable single statements, but overall the different parts of the program must be orthogonal, designed in a coherent way, and have clean interactions. A good novel must also succeed in the same two scales of the micro and the macro. Sentences must be well written, but the overall structure and relationship between the parts is also crucial.
A less structural link between programming and writing is in the drive you need when approaching one or the other: to succeed you need to make progresses, and to make progresses you have to be consistent. There is extensive agreement on the fact that programs and novels don’t write themselves, yet. Twenty years of writing code helped me immensely with this aspect; I knew that things happen only if you sit every day and write: one day one hundred words, the other day two thousands, but rare is the day I don’t put words on the page. And if you have written code that is not just a “filler” for a bigger system, but a creation of your own, you know that writer block also happens in programming. The only difference is that for most people you are an engineer, hence, if you don’t work, you are lazy. The same laziness, in the case of an artist, will assume the shape of a fascinating part of the creative process.


知识点

持久化

  • 快照(snapshotting)
    • BGSAVE 命令,fork 子进程讲快照写入硬盘,父进程继续处理命令请求
    • SAVE 命令,阻塞命令执行直至快照创建完毕,一般在没有足够内存执行 BGSAVE 的情况下使用
  • AOF(append-only file)
    • 被执行的写命令写到 AOF 文件末尾,以此来记录数据发生的变化,Redis 只要从头到尾重新执行一次 AOF 文件的所有命令,就可以恢复 AOF 文件所记录的数据集
    • 随着 Redis 运行,AOF 文件的体积会不断增大,从而消耗存储空间,BGREWRITEAOF 命令通过子进程移除 AOF 文件中的冗余命令来重写 AOF 文件降低体积
    • appendfsync 选项
      • always:每个 Redis 写命令都同步写入硬盘,会严重降低 Redis 速度
      • everysec:每秒执行一次同步,显式地将多个写命令同步到硬盘
      • no:让操作系统来决定应该何时进行同步
  • 复制(replication)【引入外部资料补充】
    • 主从链:随着负载上升,主服务器无法快速地更新所有从服务器,或者因为重新连接和重新同步从服务器导致系统超载(主服务器网络带宽打满),可以创建一个由 Redis 主从节点组成的中间层来分担主服务器的复制工作(中间层的从服务器作为更下层节点的主服务器)
    • Redis 不支持“主主复制”
    • 主从复制启动过程如下【表格1】
    • 主从复制存在数据延迟或数据过期,应用层需要将此因素考虑进去,如果不能容忍延迟或过期,需要应用层自行添加外围监控
    • 规避全量复制,造成全量复制原因有
      • 节点RunID不匹配:master 节点重启(RunID发生变化),slave 保存 master 节点老的 RunID,与新的 RunID 不一致,就会采取一次全量复制
      • 复制积压缓冲区不足
    • 规避复制风暴
      • 单主节点复制风暴:主节点重启,多从节点全量复制 —— 引入树状主从链来降低顶层主节点的网络开销,但是会带来运维复杂性,增加手动或自动处理故障转移的难度
      • 单机器复制风暴:单机器上部署较多主节点,本机器宕机重启,机器上运行的主节点实例发生大量全量复制 —— 主节点分散部署到不同机器上

【表格1】

步骤 主服务器操作 从服务器操作
1 等待命令进入 连接(或重新连接)主服务器,发送 SYNC 命令
2 开始执行 BGSAVE,使用缓冲区记录 BGSAVE 之后执行的所有写命令 根据配置选项来决定是继续使用现有的数据(如果有)来处理客户端的命令请求,还是向发送请求的客户端返回错误
3 BGSAVE 执行结束,向从服务器发送快照文件,并在发送期间继续使用缓冲记录被执行的写命令 丢弃所有旧数据(如果有),开始载入主服务器发过来的快照
4 块找文件发送完毕,开始向从服务器发送存储在缓冲器里的写命令 完成对快照文件的解释操作,像往常一样开始接受命令请求
5 缓冲区存储的写命令发送完毕;从现在开始,每执行一个写命令,就向从服务器发送相同的写命令 执行主服务器发来的所有存储在缓冲区里的写命令;从现在开始,接收并执行主服务器传来的每个写命令

Redis事务

  • Redis事务不支持回滚,但具备原子性(要么都执行,要么都不执行,但并不代表执行不会出错,出错也没有 rollback)
  • 使用 multi & exec 实现多个命令的一次性、按顺序地执行(集群下效果不明);流程是 开始事务->命令如入队->执行事务;意义似乎只是减少网络通信从而提升性能
  • pipeline 和事务是两个完全不同的概念,pipeline是客户端的行为,实现单次批量传输命令给服务端从而减少网络开销,对于服务端是透明的,pipeline 没有原子性(而事务是服务端实现的,保证原子性)
  • Redis事务的一份好资料,简述 Redis 事务源码实现,以及 Redis 事务与 MySQL 事务的作用对比
  • Lua 脚本具备原子性,当脚本运行时间超过 lua-time-limit 选项指定的时间之后,执行 SCRIPT KILL 命令可以杀死正在运行的脚本,但如果脚本存在写操作会使得数据不一致

  • 单机锁
    • watch 命令监视数据是否被修改,可以实现乐观锁
    • setnx + 超时释放 + uuid 持锁标记 + Lua 脚本释放锁
  • 集群锁
    • RedLock算法(书中没讲解,只知道这个概念但没去理解)

信号量

  • 计数信号量(集群下表现未阐述)
    • 基于本机时间戳的 zset 排名(缺点:机器时之间间差异导致锁的不合理分配,不公平)
    • 客户端对计数器进行自增操作,谁先自增成功谁获得信号量(解决机器之间时间差异的问题)

任务队列

  • 多级的任务队列
  • 延迟任务
    • zset 存储任务预期运行的时间戳(以及任务相关的其他要素),外部轮询第一个元素的运行时间是否满足
  • 消息拉取
    • publish & subscribe 命令,要求命令接收方一直在线,否则会丢消息
    • 群组聊天的实现,需要三类表:一类是群组包含的成员名单(群id对应的成员id)、一类是群组发送的所有消息(所有消息id和消息体)、一类是成员在不同群组的消息消费进度(消费到什么id的消息)

应用

  • 实现倒排索引
    • key 为分词结果(比如一个单词或一个短语),value 为 key 所出现的位置列表
    • 通过 redis 交集、并集、差集操作,实现搜索语义
    • 通过 redis sort 对搜索结果进行排序,或通过有序集合实现排序

线程问题(补充)

  • Redis 单线程
    • 执行 Redis 命令的核心模块是单线程(所以命令之间是串行的确实没错),但是整个 Redis 实例并不只有一个线程,Redis 其他模块有各自的线程
    • Redis 瓶颈一般不在 CPU,而是在内存和网络。如果要尽可能提高 CPU 利用率,可以通过部署多 Redis 实例
    • 网络 I/O 读写使用多线程,更多拓展知识见他人网络博文
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2017-2022 Zingphoy Han
  • 访问人数: | 浏览次数:

一块钱一个俯卧撑 O_O

微信