目录
概念
Redis是什么
Redis 和 MySQL 的区别?
Redis单线程有什么极端场景的瓶颈
Redis为什么快?
为什么Redis是单线程?
Redis是单线程还是多线程
Redis为什么选择单线程做核心处理
Redis6.0之后引入了多线程,你知道为什么吗? 瓶颈是内存和I/O 不是 cpu 应对高并发场景
Redis6.0的多线程是默认开启的吗?
Redis6.0的多线程主要负责命令执行的哪一块 I/O的解包和回包,读写客户端socket的I/O
为什么用Redis作为MySQL的缓存?
Redis和Memcached(多线程、基于I/O多路复用、仅支持String,value大小限制在1MB,过期时间不超过30天、整存整取、有内存上限只有LRU、无法持久化、无法主从复制(集群))的联系和区别?
数据序列化与反序列化
如何理解Redis原子性操作原理?
说说 Redis 常用命令(补充)
sadd 命令的时间复杂度是多少?
单线程 Redis 的 QPS 是多少? 十万左右
概念
Redis是什么
Redis 和 MySQL 的区别?
- Redis:数据存储在内存中的 NoSQL 数据库,读写性能非常好,是互联网技术领域中使用最广泛的缓存中间件。
- MySQL:数据存储在硬盘中的关系型数据库,适用于需要事务支持和复杂查询的场景。
Redis单线程有什么极端场景的瓶颈
大key,复杂计算,阻塞操作
Redis为什么快?
- 基于内存操作: Redis的绝大部分操作在内存里就可以实现,数据也存在内存中,与传统的磁盘文件操作相比减少了IO,提高了操作的速度。
- 高效的数据结构: Redis有专门设计了STRING、LIST、HASH等高效的数据结构,依赖各种数据结构提升了读写的效率
- 采用单线程: 单线程操作省去了上下文切换带来的开销和CPU的消耗,同时不存在资源竞争,避免了死锁现象的发生
- I/O多路复用: 采用I/O多路复用机制,一个线程同时监听多个Socket,根据Socket上的事件来选择对应的事件处理器进行处理。
-
- 基于 Linux 的 select/epoll 机制。该机制允许内核中同时存在多个监听套接字和已连接套接字,内核会一直监听这些套接字上的连接请求或者数据请求,一旦有请求到达,就会交给 Redis 处理,就实现了所谓的 Redis 单个线程处理多个 IO 读写的请求。
为什么Redis是单线程?
Redis单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程来完成的,这也是我们常说Redis是单线程的原因。
官方答案是:因为CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
---------------------------------------------------------------------------------------------------------------------------
后续采用多线程的模块和原因:
Redis在启动的时候,会启动后台线程(BIO):
- Redis的早期版本会启动2个后台线程,来处理关闭文件、AOF刷盘这两个任务;
- Redis的后续版本4.0,新增了一个新的后台线程,用来异步释放 Redis内存。执行unlink key / flushdb async / flushall async等命令,会把这些删除操作交给后台线程来执行。
之所以Redis为关闭文件、AOF刷盘、释放内存这些任务创建单独的线程来处理,是因为这些任务的操作都很耗时,把这些任务都放在主线程来处理会导致主线程阻塞,导致无法处理后续的请求。后台线程相当于一个消费者,生产者把耗时任务丢到任务队列中,消费者不停轮询这个队列,拿出任务去执行对应的方法即可。
Redis是单线程还是多线程
Redis核心处理是单线程的,在6.0中使用了多线程进行I/O解包、回包。使用多线程进行删除数据等异步任务,4.0就引入了。
Redis为什么选择单线程做核心处理
我们从投入产出来看。首先如果引入多线程,主要是希望充分利用多核的性能,但Redis的定位,是内存 k-v存储,是做短平快的热点数据处理,一般来说执行会很快,执行本身不应该成为瓶颈,而瓶颈通常在n内存和网络I/O,处理逻辑上多线程并不会有太大收益。
同时,支持多线程的话,我们需要付出更大的复杂度、以及多线程上下文切换、同步机制的开销等成本。这样综合来看,成本高且收益不大,所以最终选择了不做,事实也证明,单线程的Redis也确实足够高效
Redis6.0之后引入了多线程,你知道为什么吗? 瓶颈是内存和I/O 不是 cpu 应对高并发场景
Redis主要瓶颈是I/O而不是CPU,但随着互联网的高速发展,在部分高并发场景,单核CPU也不见得处理得过来了,所以针对核心处理流程中的解包、发包这两个CPU耗时操作,进行了多线程优化,充分发挥多核优势
Redis6.0的多线程是默认开启的吗?
默认是关闭的,如果想要开启需要用户在 redis.conf 配置文件中修改。
第一是为了兼容以前的,毕竟很多用户的认知中, Redis是单线程的。
第二可能也是认为多线程并不是必要的,在大多数场景不开启也是完全够用的。
Redis6.0的多线程主要负责命令执行的哪一块 I/O的解包和回包,读写客户端socket的I/O
原来核心流程中的I/O处理,包括解包和回包,也就是读写客户端socket的I/O,这两部分都消耗CPU时
间,多线程的引入主要也是为了解决单核CPU在大数据下还是不够用的问题。
为什么用Redis作为MySQL的缓存?
MySQL是数据库系统,对于数据的操作需要访问磁盘,而将数据放在Redis中,需要访问就可以直接从内存获取,避免磁盘I/O,提高操作的速度。使用Redis+MySQL结合的方式可以有效提高系统QPS。
系统的 QPS(Queries Per Second)是指系统每秒处理的请求数量,用于衡量系统的性能和吞吐量。
Redis和Memcached(多线程、基于I/O多路复用、仅支持String,value大小限制在1MB,过期时间不超过30天、整存整取、有内存上限只有LRU、无法持久化、无法主从复制(集群))的联系和区别?
共同点:
- 都是内存数据库
- 性能都非常高
- 都有过期策略
区别:
线程模型:
- Memcached采用多线程模型,并且基于I/O多路复用技术,主线程接收到请求后分发给子线程处理,这样做好的好处是:当某个请求处理比较耗时,不会影响到其他请求的处理。
-
- 缺点是CPU的多线程切换存在性能损耗,同时,多线程在访问共享资源时要加锁,也会在一定程度上降低性能;
- Redis也采用I/O多路复用技术,但它处理请求采用是单线程模型,从接收请求到处理数据都在一个线程中完成。这意味着使用Redis一旦某个请求处理耗时比较长,那么整个Redis就会阻塞住,直到这个请求处理完成后返回,才能处理下一个请求,使用Redis时一定要避免复杂的耗时操作,
-
- 单线程的好处是,少了CPU的上下文切换损耗,没有了多线程访问资源的锁竞争,但缺点是无法利用CPU多核的性能
数据结构:
- Memcached支持的数据结构很单一,仅支持string类型的操作。并且对于value的大小限制必须在1MB以下,过期时间不能超过30天;
- Redis支持的数据结构非常丰富,除了常用的数据类型string、list、 hash、set、zset之外,还可以使用geo、hyperLogLog数据类型;
- 使用Memcached时,我们只能把数据序列化后写入到Memcached中。然后再从Memcached中读取数据,再反序列化为我们需要的格式,只能”整存整取";
- Redis提供的数据结构提升了操作的便利性。
淘汰策略:
- Memcached必须设置整个实例的内存上限,数据达到上限后触发LRU淘汰机制,优先淘汰不常用使用的数据。它的数据淘汰机制存在一些问题: 刚写入的数据可能会被优先淘汰掉,这个问题主要是它本身内存管理设计机制导致的;
- Redis没有限制必须设置内存上限,如果内存足够使用,Redis可以使用足够大的内存,同时Redis提供了多种内存淘汰策略。
持久化:
- Memcached不支持数据的持久化,如果Memcached服务宕机,那么这个节点的数据将全部丢失。Redis支持AOF和RDB两种持久化方式。
集群:
- Memcached没有主从复制架构,只能单节点部署,如果节点宕机,那么该节点数据全部丢失,业务需要对这种情况做兼容处理,当某个节点不可用时,把数据写入到其他节点以降低对业务的影响;
- Redis拥有主从复制架构,从节点可以实时同步主节点的数据,提高整个Redis服务的可用性。
数据序列化与反序列化
数据序列化是将数据对象转换为可以在网络传输或存储中使用的字节流的过程。序列化后的数据可以被传输到远程系统或存储到磁盘等介质中,并在需要时进行反序列化还原为原始的数据对象。
数据序列化的主要目的是实现数据的持久化、跨平台通信和远程过程调用(RPC)。在不同的编程语言和系统之间,使用统一的序列化格式可以方便地传递数据,并保持数据的一致性和完整性。
常见的数据序列化格式包括以下几种:
- JSON(JavaScript Object Notation):JSON 是一种轻量级的数据交换格式,易于阅读和编写。它使用键值对的方式表示数据,并支持多种数据类型,如字符串、数字、布尔值、数组和对象。JSON 在 Web 应用程序中广泛使用,且多数编程语言都提供了 JSON 的序列化和反序列化库。
- XML(eXtensible Markup Language):XML 是一种标记语言,用于描述和传输结构化数据。它使用标签和属性来定义数据的结构,并支持层次关系。XML 通常用于支持跨平台的数据交换,并且具有一定的扩展性和灵活性。
- Protocol Buffers:Protocol Buffers(简称 Protobuf)是一种由 Google 开发的二进制序列化格式。它使用结构化的消息定义来描述数据的结构,并生成相应的代码用于序列化和反序列化。Protobuf 具有高效的编码和解码性能,适用于高性能和分布式系统。
- MessagePack:MessagePack 是一种轻量级的二进制序列化格式,具有高性能和紧凑的数据表示。它支持多种数据类型,并提供了多个编程语言的实现。MessagePack 在数据传输和存储方面具有较低的开销和较高的效率。
- BSON(Binary JSON):BSON 是一种二进制形式的 JSON 扩展,用于存储和传输 MongoDB 数据库中的文档。BSON 支持更多的数据类型和功能,如日期、二进制数据和引用。
如何理解Redis原子性操作原理?
- API: Redis提供的API都是单线程串行处理的。
- 网络模型:采用单线程的epoll的网络模型,用来处理多个Socket请求。 IO多路复用
- 请求处理: Redis会fork子进程来出来类似于RDB和AOF的操作,不影响主进程工作。
epoll 是 Linux 内核提供的一种事件驱动 I/O 接口,用于高效地处理大量的文件描述符(sockets、文件等)的并发事件。
socket 请求是一种基于网络的通信方式,用于在客户端和服务器之间进行数据传输和交互。通过 Socket 请求,客户端可以向服务器发送请求,并接收服务器的响应。
说说 Redis 常用命令(补充)
①、操作字符串的命令有:
SET key value
:设置键 key 的值为 value。GET key
:获取键 key 的值。DEL key
:删除键 key。INCR key
:将键 key 存储的数值增一。DECR key
:将键 key 存储的数值减一。
②、操作列表的命令有:
LPUSH key value
:将一个值插入到列表 key 的头部。RPUSH key value
:将一个值插入到列表 key 的尾部。LPOP key
:移除并返回列表 key 的头元素。RPOP key
:移除并返回列表 key 的尾元素。LRANGE key start stop
:获取列表 key 中指定范围内的元素。
③、操作集合的命令有:
SADD key member
:向集合 key 添加一个元素。SREM key member
:从集合 key 中移除一个元素。SMEMBERS key
:返回集合 key 中的所有元素。
④、操作有序集合的命令有:
ZADD key score member
:向有序集合 key 添加一个成员,或更新其分数。ZRANGE key start stop [WITHSCORES]
:按照索引区间返回有序集合 key 中的成员,可选 WITHSCORES 参数返回分数。ZREVRANGE key start stop [WITHSCORES]
:返回有序集合 key 中,指定区间内的成员,按分数递减。ZREM key member
:移除有序集合 key 中的一个或多个成员。
⑤、操作哈希的命令有:
HSET key field value
:向键为 key 的哈希表中设置字段 field 的值为 value。HGET key field
:获取键为 key 的哈希表中字段 field 的值。HGETALL key
:获取键为 key 的哈希表中所有的字段和值。HDEL key field
:删除键为 key 的哈希表中的一个或多个字段。
sadd 命令的时间复杂度是多少?
向指定 Set 中添加 1 个或多个 member,如果指定 Set 不存在,会自动创建一个。时间复杂度 O(N) ,N 为添加的 member 个数。
单线程 Redis 的 QPS 是多少? 十万左右
Redis 的 QPS(Queries Per Second,每秒查询率)受多种因素影响,包括硬件配置(如 CPU、内存、网络带宽)、数据模型、命令类型、网络延迟等。
根据官方的基准测试,一个普通服务器的 Redis 实例通常可以达到每秒数万到几十万的 QPS。
可以通过 redis-benchmark
命令进行基准测试:
redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000
-h
:指定 Redis 服务器的地址,默认是 127.0.0.1。-p
:指定 Redis 服务器的端口,默认是 6379。-c
:并发连接数,即同时有多少个客户端在进行测试。-n
:请求总数,即测试过程中总共要执行多少个请求。
我本机是一台 macOS,4 GHz 四核 Intel Core i7,32 GB 1867 MHz DDR3,测试结果如下:
可以看得出,每秒能处理超过 10 万次请求。
自己整理,借鉴很多博主 感谢他们