前言
最近在整理 Redis 的相关知识体系,书和博客都看了很多,知识点零零碎碎不成体系,最近想沉下心来,好好地梳理一番!既然要学习 Redis,就得先了解 Redis 都有哪些知识点,最便捷了解 Redis 知识点的渠道就是八股文啦,我从八股文中总结了一下 Redis 相关的知识点如图,后边我们就以知识点的形式为大家讲述 Redis 的核心知识,边解答问题,边总结知识点,后边有时间再带大家深入源码,探索 redis 设计的奥秘。有兴趣的小伙伴欢迎收藏、点赞、加关注哦!
1.什么是 Redis
Redis(REmote DIctionary Server)是一种基于 C 语言开发的、基于内存存储的开源数据库(BSD 许可)。Redis 拥有以下特点:
- Redis 的数据存储在内存中,因此具有非常快的读写速度。这使得 Redis 在处理大量数据和高并发请求时表现出色。
- 与传统的关系型数据库不同,Redis 使用类似于键值对的结构(哈希表)来存储数据;Redis 提供了多种数据类型来支持不同的业务场景,包括:String(字符串)、Hash(哈希)、 List (列表)、Set(集合)、Zset(有序集合)、Bitmaps(位图)、HyperLogLog(基数统计)、GEO(地理信息)、Stream(流)。
- Redis 执行命令是由单线程负责的,因此不存在并发竞争的问题,保证了操作的原子性。如果某个命令执行过长(大 key 问题),会造成其他命令的阻塞,对于 Redis 这种高性能的服务来说是致命的,所以 Redis 是面向快速执行场景的数据库。Redis 6.0 版本新增了多线程模型,默认是不打开多线程的,该多线程模型是为了解决网络 IO 性能瓶颈而设计的,而在多线程模型下 Redis 执行命令依然是单线程的,不会打破操作的原子性,多线程只是应用到了处理网络 IO的层面。
- Redis 提供了持久化功能,包括:AOF 、RDB 以及混合持久化,可以将内存中的数据定期或异步地写入硬盘,以实现数据的持久化和备份。这可以保证数据的安全性,即使在系统出现故障时也不会丢失数据。
- Redis 支持事务功能,提供了一种保证一系列命令按顺序、原子性、隔离性执行的机制,特别适用于需要多个命令按顺序执行并保证一致性的场景。但 Redis 事务并不支持回滚操作,如果在事务执行过程中某条命令执行失败,后续的命令仍然会继续执行,而不会回滚到事务开始前的状态。因此,这里的”原子性“指的是保证命令队列作为一个单独的原子操作被执行。
- 除此之外,Redis 还支持 Lua 脚本、多种集群方案(主从复制模式、哨兵模式、集群模式)、发布/订阅模式,内存淘汰机制、过期删除机制等等。
因此,Redis 是一个功能强大、性能卓越的数据库系统,它被广泛应用于缓存、数据库、消息代理等领域,是解决 高并发、高性能 等问题的优秀解决方案之一。
2.Redis 为什么快?
根据官方数据,Redis 的 QPS 可以达到约 100000(每秒请求数 10w/s),如下图:(横轴是连接数,纵轴是 QPS)
为什么 redis 可以这么快呢?比较重要的有下面几点:
本图来自: Why is Redis so fast?
- Redis 是基于内存实现的数据库,内存的访问速度是磁盘(ms 级别)的千倍万倍,请求执行时间是 ns 级别;(网上看到一张图很有意思,贴在这里,这张图不仅给出了不同介质的延迟时间,还形象的用我们能直观理解的时间做了说明,内存:磁盘 = 6分钟:1-12月,内存相对于磁盘的访问速度提升一目了然)
- Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型:单线程执行命令和 IO 多路复用:
- Redis 采用单线程模型可以避免多线程之间的竞争问题,省去了多线程切换带来的时间和性能上的开销,而且也不会导致死锁问题,同时保证了每个操作的原子性。(当然 Redis 6.0 增加了多线程处理网络 IO,但执行命令依然是单线程的,不会破坏操作的原子性 )
- Redis 采用了 I/O 多路复用机制处理大量的客户端 Socket 请求,IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个 Socket。内核会一直监听这些 Socket 上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果,而 epoll 是基于事件驱动的,Redis 的单线程模式下不会阻塞等待网络 IO,网络 IO 的读写和命令处理是交替执行的,没有浪费一点 CPU,因此单线程的 Redis 也能达到 10w QPS。具体可以参考:一文读懂网络 IO 模型原理 。随着网络硬件的发展,网络 IO 读写处理有时会出现性能瓶颈,针对这部分情况,可以使用多线程去处理网络 IO,借此进一步提升 Redis 的性能。
- Redis 数据库使用哈希表存储数据,并内置了多种优化过后的数据类型,性能非常高。
- Redis 数据库使用全局哈希表来保存所有键值对,哈希表的时间复杂度是O(1),它能够提供高效的查找和插入操作。Redis 为了防止哈希冲突导致链表过长,设计了渐进式 rehash 操作,由后台定时任务和每一次对 key 的访问为触发契机,巧妙地将一个繁重且可能阻塞主线程的内存搬运操作,分摊到一段时间内进行分批搬运(类似的削峰优化),避免了主线程的阻塞,进而提升 Redis 的运行速度。
- Redis 内置了很多高效的数据结构,包括:SDS(简单动态字符串)、链表、压缩列表、哈希表、整数集合、跳表、quicklist(3.2 以后版本 双向链表 + 压缩列表)、listpack(5.0 以后版本) 等等。
3.Redis 和 Memcached 有什么区别?
Redis 与 Memcached 共同点:
- 都是基于内存的数据库,一般都用来当做缓存使用;
- 都有过期策略;
- 两者的性能都非常高。
Redis 与 Memcached 区别:
- Redis 支持的数据类型更丰富(String、Hash、List、Set、ZSet),而 Memcached 只支持最简单的 key-value 数据类型;
- Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 没有持久化功能,数据全部存在内存之中,Memcached 重启或者挂掉后,数据就没了;
- Redis 原生支持集群模式,而 Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;
- Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持;
- Redis 删除策略有惰性删除与定期删除两种形式,而 Memcached 对过期数据的删除策略只用了惰性删除。
- 惰性删除:当一个 key 过期时,会被标记为过期状态(是由定时器或定时任务的机制来检测过期的 key,并标记状态),只有当这个 key 被访问时,才会执行删除操作。这种策略可以节省 CPU 资源,因为只有在需要时才会进行删除操作。然而,如果一个 key 过期后没有被访问,它将继续存在于内存中,直到被访问。
- 定期删除:Redis 定期扫描其内部的数据结构,检查是否有 key 到达了过期时间,并执行删除操作。这种策略可以确保过期的 key 被及时删除,但可能会浪费 CPU 资源,因为删除操作需要额外的 CPU 时间和检测成本。
4.为什么要用 Redis 作缓存?
主要是因为 Redis 具备 “高性能”和“高并发” 两种特性。
- Redis 具备高性能的数据存储和查询能力
假如用户第一次访问 Mysql 数据库中的某些数据的话,这个过程是比较慢,因为是从硬盘中读取的。如果将该用户访问的数据存在 Redis 缓存中。那用户下一次再访问这些数据的时候,就可以直接从 Redis 缓存中获取了。而操作 Redis 缓存就是直接操作内存,所以速度相当快。如果 MySQL 中对应数据发生改变,需要同步改变 Redis 缓存中相应的数据,不过这里会有 Redis 和 MySQL 双写一致性的问题,我们后续文章再详细讨论。
- Redis 具备高并发的数据处理能力
一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易突破 10w。
QPS(Query Per Second):服务器每秒可以执行的查询次数,查询次数具体是指发出请求到服务器处理完成返回结果的数量;
所以,直接访问 Redis 能够承受的请求数量是远远大于直接访问 MySQL 的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库,因此我们也就提高了系统整体的并发能力。
总结
本文内容比较简单,知识点却十分重要,在面试中也经常会被问到。本文以认识 Redis 为题,为大家建立起 Redis 的核心知识点网络,后续将逐步点亮知识网络上的每一个分支,帮助大家掌握 Redis。
本文从整体上讲述了 Redis 的核心知识体系,帮助大家重新认识了:
- Redis 是什么
- Redis 为什么这么快
- Redis 在分布式缓存常见技术选型上的对比
- 业务中最常使用 Redis 的方式:缓存。
以上就是本文的全部内容,如果觉得还不错的话欢迎点赞,转发和关注,感谢支持。