Redis 中间件

本文试图让读者对 Redis 有一个整体的认识。如果想深入学习,可以先阅读掘金小册上的 “老钱 Redis”,然后再找一本 Redis 实体书啃啃。

1. 是什么

Redis「Remote Dictionary Service」是基于内存的存储中间件,通常用于数据库、缓存、消息队列。

2. 基础 - 数据结构

Redis 有 5 中数据结构:string、list、hash、set 和 zset(有序集合),分别对应 Java 中的 String、LinkedList、HashMap、Set、SortedSet。

string 字符串

string 是 Redis 中最常见的数据结构,也称 SDS「Simple Dynamic String」,通常作为 key 或 value(最大长度 512 M)。图中粉色部分为 Redis 对象的通用头部,ptr 指向 SDS。string 按长短分以 embstr(len <= 44) 和 raw 的形式存储。

字符串在长度小于 1 M 时采用加倍扩容;大于 1 M 时采用 +1 M 扩容。

list 列表

list 对应的是链表而不是数组,插入删除快而索引慢,可以用于消息队列。list 的底层不是简单的 LinkedList,而是 ziplist(压缩列表)或 quicklist(快速列表)。

quicklist

压缩列表使用一块连续的内存,元素间紧挨着存储没有空隙。

ziplist

每个元素中包括前一个元素的长度、当前元素的编码和内容,在编码中标识当前元素的长度。在没有前后指针时,能够快速的前后索引。

struct entry {
    int<var> prevlen; // 前一个 entry 的字节长度
    int<var> encoding; // 元素类型编码
    optional byte[] content; // 元素内容
}

list 在增加元素时会根据内存分配算法和当前内存大小,决定是在原有地址上扩展还是重新分配内存

当某个元素的长度超过 254 个字节时,后面元素的 prevlen 需要从 1 字节改成 5 字节用于表示上一元素的长度。这个操作可能会造成后面的元素雪崩式的更改 prevlen,即 联级更新。因此,list 中存储的元素不应该太多、太大。

hash 字典

hash 是 Redis 中的字典结构,内部实现与 Java 中的 HashMap 一致(数组 + 链表)。字典内部有两个 HashTable,Rehash(扩容或缩容)时用于存储新旧两份数据。

扩容时机:元素个数等于数组长度(bgsave 时 5 倍数组长度时扩容)
扩容方式:2 倍数组长度
缩容时机:元素个数少于数组长度 10%

set 集合

set 是没有排序的字符串集合,不允许出现重复元素,内部结构与 hash 字典一致,只是 value 为 null。

zset 有序集合

zset 是有序集合,内部实现为跳跃链表,用于支持随机地插入和删除。

skiplist

跳跃链表其实是基于 LinkedList + 多级索引 的实现。链表用于前后索引,多层级索引通过类似 2 分的思路帮助快速定位,复杂度 O(lgn)。跳跃链表相比二叉树实现更加简单,通过随机层数来解决平衡的问题。

节点更新的过程是先删除再添加。

3. 应用

分布式锁

Redis 底层是单线程、事件循环

// 先争抢再设置过期时间,容易死锁
setnx lock_abc true
expire lock_abc 5

// 原子命令
set lock_abc true ex 5 nx

延时队列

异步消息队列

// 阻塞读 notify-queue 队列
blpop notify-queue

// 使用 redis zset 来实现延时队列,按 score 获取需要执行的任务
redis.zrangebyscore("delay-queue", 0, time.time(), start=0, num=1)

HypeLogLog

HypeLogLog 是 Redis 的高级数据结构,用于获取唯一元素总数。基于数学概率公式,通过计算某一事件连续发生的最大次数估算实验的次数。Redis 通过对元素进行哈希,记录从某位开始连续出现 0 的最大次数,来估算元素样本的大小。

布隆过滤器

布隆过滤器是一个不精确的 set 结构,用于判断某个元素是否存在过,存在一定误判。内部实现是一个大型的位数组,通过多次哈希进行散落。效果与空间向量类似,同样的元素散落结果一定相同。

GeoHash

在 Redis 中基于 zset 实现,score 是 GeoHash 的值。原理是将经纬度经过哈希算法映射到一条直线上的多个值,哈希值约接近表示两个点约接近。

4. 原理

IO 模型

Redis 是单线程程序。

使用非阻塞 IO:能读时读,能写时写,无线程阻塞。

非阻塞 IO 基于操作系统底层的事件轮询 API,注册监听基于回调。

持久化

Redis 提供两种持久化方式:

  1. RDB - 一段时间内生成指定时间点生成数据集快照 (snapshot)
  2. AOF - 增量日志

Redis 提供了 SAVE 和 BGSAVE 两个命令来生成 RDB 文件,前者是阻塞的,后者是后台 fork 子进程。

AOF 持久化实现可以分为命令追加 (append)、文件写入 (write)、文件同步 (fsync) 三个步骤。Append 追加命令到 AOF 缓冲区,Write 将缓冲区的内容写入到程序缓冲区,Fsync 将程序缓冲区的内容写入到文件。

由于 AOF 比 RDB 文件更加完整,Server 启动时优先采用 AOF 文件进行恢复。

主从复制

主从复制是指一个 Server 复制另一个 Server 的数据。

主从复制的对象:mater 和 多 Slaver,和集群的概念区分开。

主从复制的三个阶段:复制初始化(建立连接)、数据同步(同步现有数据)和命令传播(同步增量数据)。

主从复制的策略:从未同步的 Slaver 全量同步,同步过的 Slaver 部分同步

5. 集群

大规模数据存储系统都会面临水平扩展的问题。水平扩展可以通过数据分片来解决,通过一致性哈希来避免大量的 rehash 数据迁移。但是数据分片如何屏蔽业务,在 Redis 3.0 支持集群以前,业界有两种的解决方案。

一是使用中间代理层来屏蔽集群分布,优点是数据迁移和运维方便,缺点是转发有一定的性能损失,代表有 Twitter 的 Twemproxy 和豌豆荚的 Codis。

二是将数据路由和故障转移封装到 client,优点是去中心化、可扩展性高,缺点是运维困难。

Redis 3.0 版本开始官方正式支持分集群,集群通过分片进行数据共享,分片内采用一主多从的形式进行副本复制,并提供复制和故障恢复功能。

哈希槽

Redis Cluster 的数据分片依赖哈希槽(slot)实现,集群预先划分 16384 个 slot,每个分片负责一部分 slot。请求时先通过 key 计算哈希值,然后映射到唯一的 slot,最后直连对应的数据分片。

分片

Redis Cluster 的数据分片通常有多台服务组成,一主多从(master and slavers)。master 负责写(或读),slaver 负责读(或转发写请求)。主从服务使用 Raft 协议完成 Leader 选举(故障转移),使用 Epoch(纪元)的概念来给事件增加版本号。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,360评论 5 461
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,195评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,543评论 0 323
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,420评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,242评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,224评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,626评论 3 384
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,301评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,589评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,660评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,423评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,286评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,662评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,951评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,237评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,613评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,804评论 2 335