目录
目录
目录
5. Redis 为什么是单线程?真的是单线程的吗?单线程的Redis为什么快?Redis 6.0为何引入多线程?
8. 为啥redis zset使用跳跃链表而不用红黑树实现?
11. Redis中Sorted Set的实际应用场景有哪些?
18. 为什么使用Redis跳跃表(Skip List)实现有序集合(sorted set)?
2. Redis支持的 java 客户端都有哪些?有什么区别?官网推荐使用哪一个?
3. Redis和Redisson有什么关系?对比有什么优缺点?
(第一炮)一、Redis?常用数据结构?
1. 项目里面到了Redis,为什么选用Redis?
因为传统的关系型数据库如Mysql已经不能适用所有的场景了,比如秒杀的库存扣减,APP首页的访问流量高峰等
等,都很容易把数据库打崩,所以引用了缓存中间件,目前市面上比较常用的缓存中间件有Redis 和
Memcached 不过综合和考虑了他们的优缺点,最后选择了Redis。
2. Redis 是什么?
Redis 全称 Remote Dictionary Server。
它是一个 Key-Value 类型的内存数据库/存储系统,它支持存储的value类型相对更多,包括string(字符串)、
list(列表)、set、、(集)、zset(有序集)、hash(哈希)等。
这些数据结构都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性
的。
在此基础上,Redis支持各种不同方式的排序。
为了保证效率,数据都是缓存在内存中,Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录
文件,并且在此基础上实现了master-slave(主从)同步。
它也很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘
上进行保存。
因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快的Key-Value
DB。
与此同时,Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限
制是1GB,不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能。
比方说用他的List来做FIFO双向链表,实现一个轻量级的高性能消息队列服务,用他的Set可以做高性能的tag系统
等等。
另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主
要局限在较小数据量的高性能操作和运算上。
3. Redis和关系型数据库的本质区别有哪些?
Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库,由C语言编写,
官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。
完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
它的,数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用
去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
使用多路I/O复用模型,非阻塞IO;
使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了 VM
机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
4. Redis 的线程模型了解吗?
Redis 内部使用文件事件处理器 file event handler ,这个文件事件处理器是单线程的,所以 Redis才叫做单线程
的模型。
它采用 IO 多路复用机制同时监听多个 Socket,根据 Socket 上的事件来选择对应的事件处理器进行处理。
文件事件处理器的结构包含 4 个部分:
- 多个 Socket
- IO 多路复用程序
- 文件事件分派器
- 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
多个 Socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个
Socket,会将 Socket 产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的
事件处理器进行处理
再次介绍:Redis线程模型
Redis的线程模型包括Redis 6.0之前和Redis 6.0。 下面介绍的是Redis 6.0之前。
Redis 是基于 Reactor 模式开发了网络事件处理器,这个处理器叫做文件事件处理器(file event handler)。由于
这个文件事件处理器是单线程的,所以 Redis 才叫做单线程的模型。采用 IO 多路复用机制同时监听多个
Socket,根据 socket 上的事件来选择对应的事件处理器来处理这个事件。
IO多路复用是 IO 模型的一种,有时也称为异步阻塞 IO,是基于经典的 Reactor 设计模式设计 的。多路指的是多
个 Socket 连接,复用指的是复用一个线程。多路复用主要有三种技术: Select,Poll,Epoll。
Epoll 是最新的也是目前最好的多路复用技术。
模型如下图:
文件事件处理器的结构包含了四个部分
- 多个 Socket
Socket 会产生 AE_READABLE 和 AE_WRITABLE 事件:
当 socket 变得可读时或者有新的可以应答的 socket 出现时,socket 就会产生一个AE_READABLE 事件,当
socket 变得可写时,socket 就会产生一个 AE_WRITABLE 事件。
- IO 多路复用程序
- 文件事件分派器
- 事件处理器
包括:连接应答处理器、命令请求处理器、命令回复处理器,每个处理器 对应不同的 socket 事件:
如果是客户端要连接 Redis,那么会为 socket 关联连接应答处理器,如果是客户端要写数据到 Redis(读、写请求
命令),那么会为 socket 关联命令请求处理器,如果是客户端要从 Redis 读数据,那么会为 socket 关联命令回复
处理器。
多个 socket 会产生不同的事件,不同的事件对应着不同的操作,IO 多路复用程序监听着这些 Socket, 当这些
Socket 产生了事件,IO 多路复用程序会将这些事件放到一个队列中,通过这个队列,以有序、 同步、每次一个
事件的方式向文件时间分派器中传送。当事件处理器处理完一个事件后,IO 多路复用程 序才会继续向文件分派器
传送下一个事件。
下图是客户端与 Redis 通信的一次完整的流程:
(1)Redis 启动初始化的时候,Redis 会将连接应答处理器与 AE_READABLE 事件关联起来。
(2)如果一个客户端跟 Redis 发起连接,此时 Redis 会产生一个 AE_READABLE 事件,由于开始之初
AE_READABLE 是与连接应答处理器关联,所以由连接应答处理器来处理该事件,这时连接应答处 理器会与客户
端建立连接,创建客户端响应的 socket,同时将这个 socket 的 AE_READABLE 事件 与命令请求处理器关联起
来。
(3)如果这个时间客户端向 Redis 发送一个命令(set k1 v1),这时 socket 会产生一个 AE_READABLE 事件,
IO 多路复用程序会将该事件压入队列中,此时事件分派器从队列中取得该事 件,由于该 socket 的
AE_READABLE 事件已经和命令请求处理器关联了,因此事件分派器会将该 事件交给命令请求处理器处理,命令
请求处理器读取事件中的命令并完成。操作完成后,Redis 会 将该 socket 的 AE_WRITABLE 事件与命令回复处理
器关联。
(4)如果客户端已经准备好接受数据后,Redis 中的该 socket 会产生一个 AE_WRITABLE 事件,同样会 压入队
列然后被事件派发器取出交给相对应的命令回复处理器,由该命令回复处理器将准备好的响 应数据写入 socket
中,供客户端读取。
(5)命令回复处理器写完后,就会删除该 socket 的 AE_WRITABLE 事件与命令回复处理器的关联关系。
5. Redis 为什么是单线程?真的是单线程的吗?单线程的Redis为什么快?Redis 6.0为何引入多线程?
5.1. 为什么是单线程?
- 代码更清晰,处理逻辑更简单;
- 不用考虑各种锁的问题,不存在加锁和释放锁的操作,没有因为可能出现死锁而导致的性能问题;
- 不存在多线程切换而消耗CPU;
- 无法发挥多核CPU的优势,但可以采用多开几个Redis实例来完善;
- 缺点:无法发挥多核CPU的优势,但可以采用多开几个Redis实例来完善
5.2. 真的是单线程的吗?
Redis6.0之前是单线程的,Redis6.0之后开始支持多线程;
redis内部使用了基于epoll的多路复用,也可以多部署几个redis 服务器解决单线程的问题;
redis主要的性能瓶颈是内存 & 网络;
内存好说,加内存条就行了,而网络才是大麻烦,所以redis6内存好说,加内存条就行了;
而网络才是大麻烦,所以redis6.0引入了多线程的概念,
redis6.0在网络IO处理方面引入了多线程,如网络数据的读写和协议解析等,需要注意的是,执行命令的核心模块还是单线程的。
5.3. 单线程的Redis为什么快?
Redis是单线程的,但它采用了异步非阻塞的I/O模型和内存管理机制,因此具有很高的性能和响应速度。
具体来说,Redis采用了以下几种技术来提高性能:
- 基于内存操作Redis数据存储在内存中,内存的读写速度比磁盘快很多,因此Redis可以快速地处理数据读写
请求。
- 异步非阻塞的I/O模型Redis采用事件驱动模型,通过单线程的事件循环机制,可以高效地处理大量的并发请
求。
- 单线程避免了线程切换和锁竞争的开销,同时可以避免多线程编程中的数据同步和死锁等问题,从而减少了
系统的复杂度和出错的可能性。
- Redis采用了多种数据结构和算法来优化数据操作,如基数过滤器、跳跃表、压缩列表等,可以在保证数据结
构正确性的前提下提高读写性能。
综合以上几点,单线程的Redis可以充分利用内存和CPU资源,同时通过异步非阻塞的I/O模型和内存管理机制,
可以实现非常高效的数据读写和处理。
换种说法:Redis单线程为什么快?
Redis之所以能够快速地处理请求,主要是因为它采用了单线程的方式处理请求。单线程的优势在于避免了线程切
换的开销和锁竞争的问题,从而提高了Redis的性能。
具体来说,Redis的单线程模型有以下几个优点:
- 单线程操作,避免了线程切换的开销线程切换是非常耗费CPU资源的,因为需要保存和恢复线程的上下文信
息。Redis采用单线程的方式处理请求,避免了线程切换的开销,从而提高了处理请求的效率。
- 避免了锁竞争的问题在多线程环境下,如果多个线程同时访问同一个数据结构,就会产生锁竞争的问题。为
了避免锁竞争,需要使用锁机制来保护共享数据结构,但是锁机制会降低程序的并发性。Redis采用单线程的
方式处理请求,避免了锁竞争的问题,从而提高了程序的并发性。
- 纯内存操作,优化了CPU缓存在多线程环境下,多个线程访问同一个数据结构时,会导致CPU缓存的失效,
从而降低程序的性能。Redis采用单线程的方式处理请求,可以优化CPU缓存,提高程序的执行效率。
- 避免了上下文切换的开销在多线程环境下,线程之间的切换需要保存和恢复线程的上下文信息,这个过程会
消耗大量的CPU资源。Redis采用单线程的方式处理请求,避免了上下文切换的开销,提高了程序的执行效
率。
- 采⽤了⾮阻塞I/O多路复用机制
总的来说,Redis采用单线程的方式处理请求,可以避免线程切换、锁竞争和CPU缓存失效等问题,从而提高了程
序的执行效率。
但是,在某些高并发的场景下,单线程模型可能会成为Redis的瓶颈,这时可以通过利用多核CPU和分布式集群等
方式来提高Redis的性能。
5.4. Redis 6.0为何引入多线程?
很简单,就是 Redis的网络 I/O 瓶颈已经越来越明显了。 随着互联网的飞速发展,互联网业务系统所要处理的线
上流量越来越大,Redis的单线程模式会导致系统消耗很多时间在网络 IO 上,从而降低吞吐量。
要提升 Redis的性能有两个方向:
- 优化网络 I/O 模块
- 提高机器内存读写的速度
后者依赖于硬件的发展,暂时无解。
所以只能从前者下手,网络 I/O 的优化又可以分为两个方向: 零拷贝技术或者 DPDK 技术利用多核优势。
零拷贝技术有其局限性,无法完全适配 Redis这一类复杂的网络 I/O 场景,更多网络 I/O 对 CPU 时间的消耗和
Linux 零拷贝技术。
而 DPDK 技术通过旁路网卡 I/O 绕过内核协议栈的方式又太过于复杂以及需要内核甚至是硬件的支持。
总结起来,Redis支持多线程主要就是两个原因:
- 可以充分利用服务器 CPU 资源,目前主线程只能利用一个核
- 多线程任务可以分摊 Redis 同步 IO 读写负荷
Redis作者Antirez在RedisConf 2019分享时曾提到:Redis 6 引入的多线程IO特性对性能提升至少是一倍以上。
6. 支持哪几种数据类型?
Redis支持五种数据类型
String、hash、list、set、zset
1. String字符串
字符串类型是 Redis 最基础的数据结构,首先键都是字符串类型,⽽且 其他几种数据结构都是在字符串类型基础
上构建的,我们常使⽤的 set key value 命令就是字符串。常⽤在缓存、计数、共享Session、限速等。
2. Hash哈希
在Redis中,哈希类型是指键值本⾝⼜是⼀个键值对结构,哈希可以⽤来存放用户信息,比如实现购物⻋。
3. List列表(双向链表)
列表(list)类型是⽤来存储多个有序的字符串。可以做简单的消息队列的功能。
4. Set集合
集合(set)类型也是⽤来保存多个的字符串元素,但和列表类型不⼀ 样的是ÿ