0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一文理解 Redis 的核心原理与技术

数据分析与开发 来源:何永康 作者:何永康 2021-05-28 10:49 次阅读

一、Redis 基础数据结构

1. String

Redis 里的字符串是动态字符串,会根据实际情况动态调整。类似于 Go 里面的切片-slice,如果长度不够则自动扩容。至于如何扩容,方法大致如下:当 length 小于 1M 的时候,扩容规则将目前的字符串翻倍;如果 length 大于 1M 的话,则每次只会扩容 1M,直到达到 512M。

e9ea5f1e-bf1a-11eb-9e57-12bb97331649.png

2. List

Redis 里的 List 是一个链表,由于链表本身插入和删除比较块,但是查询的效率比较低,所以常常被用做异步队列。Redis 里的 List 设计非常牛,当数据量比较小的时候,数据结构是压缩链表,而当数据量比较多的时候就成为了快速链表。 可运用的场景:在业务中异步队列使用 rpush/lpush 操作队列,使用 lpop 和 rpop 出队列,具体结构如下图所示:

e9f5ba08-bf1a-11eb-9e57-12bb97331649.png

3. Set Redis 中的 set 是一个无序 Map,由于 Go 中没有 set 结构,所以这里只能类比 Java 中的 HashSet 概念。Redis 的 set 底层也是一个 Map 结构,不同于 Java 的是:alue 是一个 NULL。由于 set 的特性,它可以用于去重逻辑,这一点在 Java 中也经常使用。 可运用场景:活动抽奖去重。

4. Hash Redis 中的字典类型大家不陌生,也许其他语言都有这种结构(python,Java,Go), hash 的扩容 rehash 过程和 Go 里面的设计颇有类似,也就是维护了两个 hash 结构,如果需要扩容的时候,就把新的数据写入新字典中,然后后端起一个线程来逐步迁移,总体上来说就是采用了空间换时间的思想。 可运用场景:记录业务中的不同用户/不同商品/不同场景的信息:如某个用户的名称,或者用户的历史行为。

ea135022-bf1a-11eb-9e57-12bb97331649.png

5. Zset

Redis 中的 zset 是一个比较特殊的数据结构(跳跃列表),也就是我们了解到的跳表,底层由于 set 的特性保证了 value 唯一,同时也给了 value 一个得分,所谓的有序其实就是根据这个得分来排序。至于跳跃表如何插入,其实内部采用了一个随机策略:L0:100%-L2:50%-L3:25%-。。。.Ln:(n-1)value/2%。 可运用场景:榜单,总榜,热榜。

ea236d22-bf1a-11eb-9e57-12bb97331649.png

二、Redis 进阶使用

1. 布隆过滤器

Redis 在 4.0 以后支持布隆过滤(准确的来说是支持了布隆过滤器的插件),给 Redis 提供了强大的去重功能。在业务中,我们可能需要查询数据库判断历史数据是否存在,如果数据库的并发能力有限,这个时候我们可以采用 Redis 的 set 做去重。

如果缓存的数据过大,这个时候就需要遍历所有缓存数据,另外如果我们的历史数据缓存写不下了,终究要去查询数据库,这个时候就可以使用布隆过滤器。 当然布隆过滤器精确度不是 100% 准确(如果对数据准确度要求很高的话,这里不建议使用),因为对于存在的数据也许这个值不一定存在,当然如果不存在,那肯定 100% 不存在了。

(1)命令使用

bf.add #添加元素bf.exists #判断元素是否存在bf.madd #批量添加bf.mexists #批量判断是否存在

(2)原理

ea358660-bf1a-11eb-9e57-12bb97331649.png

布隆过滤的组成可以当作一个位数组和几个计算结果比较均匀的 hash 函数,每次添加 key 的时候,会把 key 通过多次 hash 来计算所得到的位置,如果当前位置不是 0 则表示存在。可以看到,这样的计算存在一定误差,这也正是它的不准确性问题的由来。

2. 分布式锁

大家对分布式锁也许也不会陌生,现在市面上主流的实现分布锁的技术有 ZK 和 Redis;下文为大家简单介绍一下 Redis 如何实现分布式锁。

命令

setnx lock:mutex ture #加锁del lock:mutex #删除锁 实现分布式锁的核心就是:请求的时候 Set 这个 key,如果其他请求设置失败的时候,即拿不到锁。但是存在一个问题:如果业务 panic 或者忘记调用 del 的话,就会产生死锁,这个时候大家很容易能想到:我们可以 expire 一个过期时间,这样就可以保证请求不会一直独占锁且无法释放锁的逻辑了。

但是假设业务存在这样一种情况:A 请求在获取锁后处理逻辑,由于逻辑过长,这个时候锁到期释放了,A 这个时候刚刚处理完成,而 B 又去改了这个数据,这就存在一个锁失效的问题。解决这种问题参考 CAS 的方式,对锁设置一个随机数,可以理解为版本号,如果释放的时候版本号不一致,则表示数字已经在释放那一刻改掉了。

三、深入原理

1. IO模型

Redis 是单线程模型(这里的单线程指的是 IO 和键值对的读写是一个线程完成的),当然如果严谨的来说还是可以理解为是多线程,不过这样的多线程不过是在数据备份的时候会 fork 一个子进程对数据进行从磁盘读取数据并组装 RDB,然后同步给 slaver 节点的操作,当然包括备份和持久化也都是通过另外起线程完成的,所以我们可以把 Redis 认作为一个单线程模型。

那么问题来了,为什么单线程的模型能这么快?原因很简单,因为 Redis 本身就是在内存中运算,而对于上游的客户端请求,采用了多路复用的原理。Redis 会给每一个客户端套接字都关联一个指令队列,客户端的指令队列通过队列排队来进行顺序处理,同时 Reids 给每一个客户端的套件字关联一个响应队列,Redis 服务器通过响应队列来将指令的接口返回给客户端。

ea6729ea-bf1a-11eb-9e57-12bb97331649.png

Redis IO 处理模型

2. 通信协议

Redis 采用了 Gossip 协议作为通信协议。Gossip 是一种传播消息的方式,可以类比为瘟疫或者流感的传播方式,使用 Gossip 协议的有:Redis Cluster、Consul、Apache Cassandra 等。Gossip 协议类似病毒扩散的方式,将信息传播到其他的节点,这种协议效率很高,只需要广播到附近节点,然后被广播的节点继续做同样的操作即可。当然这种协议也有一个弊端就是:会存在浪费,哪怕一个节点之前被通知到了,下次被广播后仍然会重复转发。

3. 持久化

(1)RDB

RDB 是对当前 Redis 的存储数据进行一次快照(具体原理和如何做,限于篇幅这里不做过多复述了)。

(2)AOF

日志只记录 Redis 对内存修改的指令记录,Redis 提供了一个 bgrewriteaif 的指令对 AOF 进行压缩。原理就是:开辟一个子进程对内存进行遍历后,转换成一系列对 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。系列化完成后再将发送的增量 AOF 日志追加到这个新的 AOF 日志中,追加完成后用新的 AOF 日志代替旧的。

(3)混合持久化

由于单纯 RDB 的话,可能存在数据的丢失,而频繁的 AOF 又会影响了性能,在 Redis 4.0 之后,支持了混合持久化,也就是每次启动时候通过 RDB+增量的 AOF 文件来进行回复,由于增量的 AOF 仅记录了开始持久化到持久化结束期间发生的增量,这样日志不会太大,性能相对较高。

4. 主从同步

Redis 的同步方式有:主从同步、从从同步(由于全部都由 master 同步的话,会损耗性能,所以部分的 slave 会通过 slave 之间进行同步)。

同步过程:

建立连接,然后从库告诉主库:“我要同步啦,你给我准备好”,然后主库跟从库说:“收到”。

从库拿到数据后,要把数据保存到库里。这个时候就会在本地完成数据的加载,会用到 RDB 。

主库把新来的数据 AOF 同步给从库。

5. Sentinel

Redis 的主从切换是通过哨兵来解决的。这里哨兵主要解决的问题就是:当 master 挂了的情况下,如果在短时间内重新选举出一个新的 master 。

Sentinel 集群是一个由 3-5 个(可以更多)节点组成的,用来监听整个 Redis 的集群,如果发现 master 不可用的时候,会关闭和断开全部的与 master 相连的旧链接。这个时候 Sentinel 会完成选举和故障转移,新的请求则会转到新到 master 中。

6. Redis集群工作原理

Redis 集群通过槽指派机制来决定写命令应该被分配到那个节点。整个集群对应的槽是由 16384 大小的二进制数组组成,集群中每个主节点分配一部分槽,每条写命令落到二进制数组中的某个位置,该位置被分配给了哪个节点,则对应的命令就由该节点去执行。槽指派对应的二进制数组如下图所示:

eae9ca30-bf1a-11eb-9e57-12bb97331649.png

从上图可以看到:节点 1 只负责 执行 0 - 4999 的槽位,而节点 2 负责执行 5000 - 9999,节点 3 执行 9999- 16383 。当进行写的时候:

set key value 命令通过CRC16(key) & 16383 = 6789(假设结果),由于节点 2 负责 5000~9999 的槽位,则该命令的结果 6789 最终由节点 2 执行。当然如果在节点 2 执行一条命令时,假设通过 CRC 计算后得到的值为 567,则其应该由节点 1 执行,此时命令会进行转向操作,将要执行的命令流转到节点 1 上去执行。

eb9a420c-bf1a-11eb-9e57-12bb97331649.png

集群节点同步:集群中每个主节点都会定时发送信息到其他主节点进行同步,如果其他主节点在规定时间内响应了发送消息的主节点,则发送消息的主节点认为响应了消息的主节点正常,反之则认为响应消息的主节点疑似下线,则发送消息的主节点在其节点上将其标记“疑似下线”。

当集群中超过一半以上的节点认为某个主节点被标记为“疑似下线”,则其中某个主节点将疑似下线节点标记为下线状态,并向集群广播一条下线消息,当下线节点对应的从节点接收到该消息时,则从从节点中选举出一个节点作为主节点继续对外提供服务。

四、Redis为什么变慢了

业务场景中,不知道大家是否碰到过 Redis 变慢的情况:

执行 SET、DEL 命令耗时也很久;

偶现卡顿,之后又恢复正常了;

在某个时间点,突然开始变慢了。

原因分析

查看慢查询,由于笔者本身机器没有慢查询,所以这里看到是空(实在尴尬,这里没有可用的例子~~)

ebcccf92-bf1a-11eb-9e57-12bb97331649.png

由于 Redis 在 IO 操作和对键值对的操作是单线程的,所以直接在客户端 Redis-cli 上执行的 Redis 命令有可能会导致操作延迟变大;

使用复杂的命令会让 Redis的处理变慢,以及CPU过高,例如 SORT、SUNION、ZUNIONSTORE 聚合类命令(时间负责度O(N) );

查询的数据量过大,使得更多时间花费在数据协议的组装和网络传输过程中;

大 key 查询,比如对于一个很大的 hash、zset 等,这样的对象对 Redis 的集群数据迁移带来了很大的问题,因为在集群环境下,如果某个 key 太大,会导致数据迁移卡顿;

另外在内存分配上,如果一个 key 太大,那么当它需要扩容时,会一次性申请更大的一块内存,这也会导致卡顿。如果这个大 key 被删除,内存会一次性回收,卡顿现象会再一次产生。

集中过期,变慢的时间统一,所以业务中的 Key 过期时间尽量在统一的一个时间点加上一个随机数时间;

内存使用达到上限,当内存达到内存上限的时候,就不许淘汰一些数据,这个时候也可能导致 Redis 查询效率低;

碎片整理,Redis 在 4.0 版本后会自动整理碎片(由于内存回收过程中存在大量的碎片空间,不整理会导致 Redis 的空间少量浪费),而在整理碎片的过程中会消耗 CPU 的资源,从而影响了请求得到性能;

网络带宽,Redis 集群和业务混部,或者并发量过大以及每次返回的数据也很大,网卡带宽跑满的情况容易导致网络阻塞;

AOF 的频率过高,由于 AOF 需要将全部的写命令同步,如果同步的间隔比较短,也会影响到 Redis 的性能;

Redis 提供了 flushdb 和 flushall 指令,用来清空数据库,这也是导致 Redis 缓慢的操作。

五、Redis安全

默认会监听 6379 端口,最好在 Redis 的配置文件中指定监听的 IP 地址,更进一步还可以增加 Redis 的 ACL 访问控制,对客户指定群组,并限限制用户对数据的读写权限。 访问 Redis 尽量走公司代理,由于 Redis 本身不支持 SSL 的链接,所以走公司代理可以保证安全。客户端登陆 Redis 必须设置 Auth 秘密登陆。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10794

    浏览量

    210661
  • ACL
    ACL
    +关注

    关注

    0

    文章

    61

    浏览量

    11955
  • 网络带宽
    +关注

    关注

    0

    文章

    36

    浏览量

    8225
  • sort
    +关注

    关注

    0

    文章

    5

    浏览量

    2597

原文标题:一文理解 Redis 的核心原理与技术

文章出处:【微信号:DBDevs,微信公众号:数据分析与开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    家人工智能企业成功IPO,核心技术涉及感知、理解、执行

    流程。主要服务于城市管理及行政、汽车及交通、通信、金融以及教育、医疗健康、电商及零售等行业。   核心技术涉及感知、理解、执行                                   声通科技核心技术包括交互式人工智
    的头像 发表于 07-17 00:16 2976次阅读

    Redis开源版与Redis企业版,怎么选用?

    Redis开源版,二者有何不同?该如何选择?Redis企业版Redis企业版基于开源Redis构建,企业版将开发人员、架构师和DevO
    的头像 发表于 04-04 08:04 878次阅读
    <b class='flag-5'>Redis</b>开源版与<b class='flag-5'>Redis</b>企业版,怎么选用?

    Redis为LangChain定制AI代理——OpenGPTs

    于构建定制化的人工智能代理。因为Redis具有高速和稳定性的优点,所以LangChain选择了Redis来作为OpenGPTs的默认向量数据库。
    的头像 发表于 01-13 08:03 784次阅读
    用<b class='flag-5'>Redis</b>为LangChain定制AI代理——OpenGPTs

    文理解自举电路原理

    我们从最简单的电路开始点分析,先定义下输入阻抗的计算过程。我们可以粗略的把负载作为个黑盒子来对待,所谓的输入阻抗,就是计算输入到这个黑盒子的电压与电流的比值,比如下图,输入阻
    的头像 发表于 12-18 09:24 1558次阅读
    <b class='flag-5'>一</b><b class='flag-5'>文理解</b>自举电路原理

    redis容器内怎么查看redis日志

    redis款流行的开源内存数据库,常用于缓存、消息队列、任务管理等场景。在使用redis时,了解如何查看redis日志对于排查问题、监控性能和分析应用程序行为非常重要。在本文中,我
    的头像 发表于 12-05 10:10 3310次阅读

    redis的lru原理

    Redis种基于内存的键值数据库,它使用了LRU(Least Recently Used)算法来进行缓存的数据淘汰。LRU算法的核心思想是最近最少使用的数据将会在未来也不常用,因此应该优先
    的头像 发表于 12-05 09:56 584次阅读

    redis的原理和使用场景

    Redis(Remote Dictionary Server)是个开源的、高性能的非关系型(NoSQL)的键值对数据库管理系统。它以其快速读写能力和多种数据结构支持而闻名,并被广泛应用于缓存
    的头像 发表于 12-04 16:29 543次阅读

    redis hash底层实现原理

    Redis个开源的内存数据库,使用键值对存储数据。其中,Redis中的数据结构之就是哈希(Hash),它提供了种将多个字段(Fiel
    的头像 发表于 12-04 16:27 537次阅读

    redis的淘汰策略

    Redis种基于内存的键值存储系统,为了充分利用内存,Redis采用了些淘汰策略来管理内存空间。淘汰策略的作用是当内存空间不足时,选择合适的数据对象进行淘汰,释放出更多的内存空间
    的头像 发表于 12-04 16:23 507次阅读

    redis和rabbitMQ的区别

    Redis和RabbitMQ是两个流行的开源消息传递技术,用于构建高可靠、可扩展和可扩展的应用程序。虽然它们都用于实现消息传递机制,但它们在设计和运作方式上存在些不同之处。在本文中,我们将详细讨论
    的头像 发表于 12-04 14:48 1231次阅读

    redis锁超时了怎么处理

    问题,以确保系统的正常运行和数据的致性。 第部分:理解Redis锁的超时问题 1.1 Redis锁的基本原理: 在
    的头像 发表于 12-04 13:53 1087次阅读

    Java redis锁怎么实现

    在Java中实现Redis锁涉及到以下几个方面:Redis的安装配置、Redis连接池的使用、Redis数据结构的选择、实现分布式锁的几种方式等。
    的头像 发表于 12-04 10:47 1069次阅读

    redis集群中的hash致性算法的理解

    Redis集群是种为了增强Redis的可扩展性和高可用性而设计的集群方案。在Redis集群中,致性哈希算法被广泛地应用于数据分片和负载均
    的头像 发表于 12-04 10:45 689次阅读

    Redis Enterprise vs ElastiCache——如何选择缓存解决方案?

    使用Redis或AmazonElastiCache来作为缓存加速已经是业界主流的解决方案,二者各有什么优势?又有哪些区别呢?况速览:Redis是什么?RedisEnterprise
    的头像 发表于 11-26 08:06 433次阅读
    <b class='flag-5'>Redis</b> Enterprise vs ElastiCache——如何选择缓存解决方案?

    文理解示波器带宽

    当示波器用户选择示波器进行关键的测量时,示波器的主要参数指标往往是选择哪款示波器的唯标准。
    的头像 发表于 11-06 10:37 1692次阅读
    <b class='flag-5'>一</b><b class='flag-5'>文理解</b>示波器带宽