haizdl
作者haizdl2020-12-11 17:11
技术经理, 大连

NOSQL DB:Redis-KV 七问解惑

字数 5695阅读 2774评论 0赞 3

1. Redis 是什么?

Redis ( Remote Dictionary Server ) ,是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的 Key-Value 数据库,并提供多种语言的 API 。从 2013 年 5 月开始, Redis 的开发由 Pivotal 赞助。

键值数据库可会列举出很多,例如 Dynamo 、 LevelDB 、 RocketDB 等等。那么相对于这些键值数据库来讲, Redis 的最大特点又是什么呢?个人觉得有两点:首先是 Redis 的性能,因为它主要是基于内存运行的数据库,虽然具备持久化的功能,但是大多数场景下相对于持久化来讲,内存运行还是最有价值的点。其次是 Redis 丰富的数据结构,它的值可以是 String , List , Set , sorted Set , hash 等各种数据结构,所以这一点是它能够应用到更多场景当中的必要条件。

2. Redis 数据模型是什么样的?

Redis 支持数据类型:字符串、哈希、列表、集合、有序集合等 5 种。

1 、 String (字符串): String 是 Redis 最基本的数据类型。一个键对应一个值。 Redis 的 String 可以包含任何数据。比如图片或者序列化的对象。 String 类型是 Redis 最基本的数据类型,最大 512 MB 。

2 、 Hash (哈希): Redis hash 是一个键值对( Key - Value )集合。 Redis hash 是一个 String 类型的 Key 和 Value 的映射表, hash 特别适合用于存储对象。并且可以像数据库中一样只对某一项属性值进行存储、读取、修改等操作。

3 、 List (列表): Redis 列表是简单的字符串列表,按照插入顺序排序。我们可以网列表的左边或者右边添加元素。 List 就是一个简单的字符串集合,元素是可重复的。

4 、 Set (集合): Redis 集合是通过哈希表实现的,添加、删除、查找的复杂度都是 O ( 1 )。一个 Key 对应着多个字符串类型的 Value ,也是一个字符串类型的集合,和 Redis 的 List 不同的是 Set 中的字符串集合元素不能重复。

5 、 ZSet (有序集合): Redis 有序集合与集合的区别在于 ZSet 每个元素都会关联一个 double 类型的分数。 Redis 通过分数来为集合中的成员进行排序。 ZSet 的元素是唯一的,但是分数却可以重复。

如果用一张图来表示 Redis 支持的数据模型,那么可以参照以下的图示:

图 2.1 Redis 数据模型总览

3. Redis 的特性?

Redis 不仅仅在互联网业务场景当中使用的越来越广法,而且在传统的业务场景下也越来越被大家所推崇,那么究其原因总结为如下几个特性:

1.性能

按照 Redis 官方的测试结果来看,它的 QPS 指标能达到 10W 。当然这也要看硬件的性能,但是总体来讲我们认为 Redis 的性能一定是它最大的优势。从原理上讲,有两点非常重要:首先, Redis 的所有数据都是存放在内存中的,所以把数据放在内存中是 Redis 速度快的最主要原因;其次, Redis 是用 C 语言实现,并且采用单线程架构,预防了多线程可能产生的竞争问题。

但是对于 Redis 的性能问题,我们需要正确客观评价并且利用,如图 3.1 所示:

图 3.1 Redis 性能分析图

图中是官方从三个不同角度对 Redis 进行的性能测试结果,左边的图展示给我们的是不同的 CPU 架构对 Redis 的影响,显然从结果可以看出,对于多核的 CPU 架构是 Redis 最理想的架构;中间的图展示给我们的是不同的 IO 大小对 Redis 性能的影响,显然数据大小在 100-10000K 之间是 Redis 可以发挥性能优势的区间;右边的图显示的是客户连接数对于 Redis 处理能力的影响,当客户端连接数在 30000-60000 的时候, Redis 的处理能力相对达到一个平衡稳定的区间。

2.事务支持

Redis 的事务当中的操作都是原子操作,它要么全部执行对数据的更改,要么全部不执行。但是 Redis 的事务并非具备 ACID 特性,事务当中任意命令执行失败,其余命令依然被执行,也不支持事务回滚。事务中的多条命令被一次性发送给服务器,服务器在执行命令期间,不会去执行其他客户端的命令请求。

3.丰富的数据淘汰机制

Redis 在内存当中的数据管理必然会涉及到内存当中数据换入换出的问题,它是有着非常丰富的数据淘汰机制来保障内存当中的数据命中率。这也是它大部分场景是用于缓存的原因。

[1].volatile-lru :从已经设置过期时间的数据集中,挑选最近最少使用的数据淘汰;

[2].volatile-ttl :从已经设置过期时间的数据集中,挑选即将要过期的数据淘汰;

[3].volatile-random :从已经设置过期时间的数据集中,随机挑选数据淘汰;

[4].allKeys-lru :从所有的数据集中,挑选最近最少使用的数据淘汰;

[5].allKeys-random :从所有的数据集中,随机挑选数据淘汰;

[6].no-enviction :禁止淘汰数据。

同时对于具体过期键的策略有:定时删除,惰性删除,定期删除等。

4.数据的持久化

与其他的 KV 数据库相比而言, Redis 最大的优势在于内存运行。那么与其他的缓存产品相比而言,它又有一个特性就是它支持数据的持久化。它可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。 Redis 提供了两种持久化的方式,分别是 RDB ( Redis DataBase )和 AOF ( Append Only File )。

[1]. RDB 简而言之,就是在不同的时间点,将 Redis 存储的数据生成快照并存储到磁盘等介质上,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本。如果系统发生故障,将会丢失最后一次创建快照之后的数据。如果数据量大,保存快照的时间会很长。

[2]. AOF 换了一个角度来实现持久化,那就是将 Redis 执行过的所有写指令记录下来,在下次 Redis 重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。将写命令添加到 AOF 文件( append only file )末尾。

RDB 和 AOF 两种方式也可以同时使用,在这种情况下,如果 Redis 重启的话,会优先采用 AOF 方式来进行数据恢复,这是因为 AOF 方式的数据恢复完整度更高。当然我们也完全可以关闭 RDB 和 AOF 方式,这样的话, Redis 将变成一个纯内存数据库。

4. Redis 如何实现数据复制?

数据在节点之间的主从复制机制是保障数据库架构高可用性的基本条件。无论是关系数据库的 MySQL 还是 NOSQL 当中 MongoDB ,他们都具备主备库复制功能,同样 Redis 也是具备这样的功能。如图 4.1 所示:

图 4.1 Redis 主备库复制机制

a. Slave 向 Master 发送 sync( 数据同步 ) 命令。

b. Master 接收同步命令后,会保存快照,创建一个 RDB 文件。

c. Master 执行完保持快照后会向 Slave 发送 RDBs ,而 Slave 会接收并载入 RDBs 。同时, Master 将缓冲区的所有写命令发给 Slave 执行。

d. 以上处理完之后, Master 每执行一个写命令,都会将被执行的写命令发送给 Slave 。可以同步发送也可以异步发送,同步发送可以不用每台都同步,可以配置一台 master ,一台 slave ,同时这台 salve 又作为其他 slave 的 master 。异步方式无法保证数据的完整性,比如在异步同步过程中主机突然宕机了,也称这种方式为数据弱一致性。在 Redis2.8 之后,主从断开重连后会根据断开之前最新的命令偏移量进行增量复制。

5. Redis 如何实现分布式?

Redis 如何实现自己的分布式架构?关于这个问题我们首先要明白 Redis 分布式集群的整体框架图,如图 5.1 所示:图中所示是一个数据分片的分布式架构,包括客户端、代理层、集群分组、监控控制层。客户端通过访问代理层提供的虚拟地址来访问 Redis 数据库资源,数据请求经过代理层之后可以通过哈希或者其他算法映射到具体集群分组的 Master 节点,然后完成数据请求。监控控制层主要监控集群分组内的节点健康状况,并且根据监控结果实时更新代理层的机群访问信息,保障客户端的请求能访问到正确的节点。


图 5.1 Redis 分布式集群架构图

哨兵 ( Sentinel ), 是 Redis 集群架构中非常重要的一个监控控制组件,哨兵的出现主要是解决了主从复制出现故障时需要人为干预的问题。 Redis 哨兵主要功能:

( 1 )集群监控:负责监控 Redis master 和 slave 进程是否正常工作;

( 2 )消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员;

( 3 )故障转移:如果 master node 挂掉了,会自动转移到 slave node 上;

( 4 )配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址;

哨兵集群机制建立多个哨兵节点进程,共同监控数据节点的运行状况,以及哨兵节点之间互相通信交换对主从节点的监控状况。每隔 1 秒每个哨兵会向整个集群: Master + Slave + Sentinel ,发送一次 ping 命令做一次心跳检测。这个就是哨兵用来判断节点是否正常的重要依据。一个哨兵节点判定主节点 down 掉是主观下线,只有半数哨兵节点都主观判定主节点 down 掉,此时多个哨兵节点交换主观判定结果,才会判定主节点客观下线。基本上哪个哨兵节点最先判断出这个主节点客观下线,就会在各个哨兵节点中发起投票机制 Raft 算法(选举算法),最终被投为领导者的哨兵节点完成 Master&Slave 自动化切换的过程。

6. Redis 如何解决高并发问题?

我们需要明确的一点是 Redis 本身所谓的高并发问题并不是锁的机制带来的,因为 Redis 本身是单线程模式,所以不存在分布式锁的概念。它的高并发问题完全是因为短时间内的巨量连接请求集中访问某些少数键值带来的访问过于集中击穿节点硬件承受能力的问题。

首先,我们可以通过业务经验判断、客户端收集、代理层统计等手段确定这些热点 Key 。

接下来,如何解决呢?

(1). 多级缓存:与其他缓存优化的方法一样,通过多级缓存降低请求直接命中 Redis 集群的可能性。我们可以通过设置二级缓存将热点 Key 的数据加载到系统的 JVM 中,针对这种热 Key 的请求,就会直接从 JVM 中取,而不会走到 Redis 层。

(2). 发散热 Key :所谓发散热 Key 就是不要让 Key 走到同一台 Redis 上,而是转移到多台 Redis 上。我们把这个 Key 进行哈希或者其他算法处理之后,使得其在多个 Redis 上都存一份。比如可以用 HOTKEY 加上一个随机数( N ,集群组标识)组成一个新 Key 。其本质想法就是把少数的热点数据通过哈希组合的方式变成多数数据,从而增大这部分业务热点数据过于集中访问的受力面。

7. Redis 最适合的应用场景?

a. 缓存( Session Cache )

缓存的要求就是能够帮助应用快速读写经常被用到的数据。可见快速读写是缓存的最大要求,但是一般来讲我们的高速缓存设备都会比较昂贵,所以在一般服务器当中,缓存设备所占的空间会非常非常有限。那么 Redis 作为内存数据库可以将其所有的数据运行于内存当中并且以其丰富的 KV 数据模型结构可以保障大多数应用场景缓存数据结构的契合度,因此它是最佳的缓存设备代替者。

对于一些特殊的应用缓存,例如会话缓存、页面缓存等,不仅仅需要快速读取,而且需要一定的数据持久化,不会因为设备断电重启而导致购物车数据丢失等类似场景发生。这个时候就是 Redis 持久化功能可以发挥的最佳时机。

b. 消息队列 ( Message Queue )

作为消息队列来讲,有其特有的要求,生产者、消费者、消息事务性等。 Redis 它有几个阻塞式的 API 可以使用,正是这些阻塞式的 API 让他有做消息队列的能力。 另外做消息队列的其他特性,例如 FIFO 也很容易实现,只需要一个 list 对象从头取数据,从尾部塞数据即可实现。 Redis 能做消息队列得益于它的 list 对象 blpop brpop 接口以及 Pub/Sub( 发布 / 订阅 ) 的某些接口。他们都是阻塞版的,所以可以用来做消息队列。

Redis 并非专业的 MQ 软件,与 RabbitMQ 、 ActiveMQ 等专业的消息中间件相比来讲,其专业特性方面自然是不能比拟,但是在某些高并发并且轻量级的消息队列场景下,并且消息的数据结构又极其与 Redis 的 List 、 Set 相吻合的情况下, Redis 就会显示出其特有的优势了。

c. 计数器 ( Counter )

Redis 之所有非常胜任计数器、排行榜这样的场景,主要取决于以下几点:

首先、 Redis 是内存数据库,数据的递增递减排序操作在内存当中是效率最高的;

其次、 Redis 提供的数据模型 set 、 Zset ,其不允许元素重复、 Zset 本身自带 double 数字元素的特点使得 Redis 能够设计出非常适合计数器和排行榜类似的业务数据结构。

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

3

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广