Redis ⑤-单线程模型 | 常用数据结构
常用数据结构
当前版本的 Redis 支持 10 多种数据结构,但主要使用的就只有 5 种。
- String(字符串),相当于 Java 的 String 类型。
- Hash(哈希),相当于 Java 的 HashMap 集合。
- List(列表),相当于 Java 的 List 集合。
- Set(集合),相当于 Java 的 Set 集合。
- zset(有序集合),相当于存储了 member 之外,还存储了一个 score(分数,权重)。
实际上,Redis 在实现这些数据结构时,会在源码层面,针对某些情况进行特殊的优化,以节省空间或者时间。
也就是说,Redis 承诺你这是个 Hash,但实际的底层可能并不是一个真的 Hash,但起增删查改的时间复杂度确实都是 O(1)。
数据结构
Redis 承诺给你的,也可以理解为数据类型。
编码方式
Redis 内部底层的实现。
常用数据结构
数据结构 | 编码方式 |
---|---|
String(字符串) | raw |
int | |
embstr | |
Hash(哈希) | hashtable |
ziplist | |
List(列表) | linkedlist |
ziplist | |
Set(集合) | hashtable |
intset | |
zset(有序集合) | skiplist |
ziplist |
在Redis 5.0 版本后,ziplist 被 listpack 替代,但本质还是一种压缩的链表,其目的还是为了优化
- String(字符串)
- raw:最基本的字符串,底层就是一个char数组(C++),byte数组(Java)。
- int:整数,但值为整数时,会采用自动整数编码。
- embstr:短字符串,针对短字符串进行的特殊优化。
- Hash(哈希)
- hashtable:哈希表,底层是一个哈希表,但还是和 Java 的 HashTable 不太一样。
- ziplist:压缩列表,针对小规模数据进行的特殊优化,当哈希表里元素较少时,可能就会有化成压缩列表。
- List(列表)
- linkedlist:链表。
- ziplist:压缩列表,针对小规模数据进行的特殊优化。
- 从 Redis 3.2 开始,引入了新的实现方式:quicklist,同时兼顾了 linkedlist 和 ziplist 的优点。
- quicklist 就是一个链表,但每一个元素又是一个 ziplist,这样就能达到压缩链表的效果。
- Set(集合)
- hashtable:哈希表。
- intset:整数集合,存储的都是整数。
- zset(有序集合)
- skiplist:跳表,跳表也是链表,不用与普通的链表,每个节点上有多个指针域,巧妙的搭配这些指针域的指向,就可以做到从跳表上查询元素的时间复杂度为 O(logN)。
- ziplist:压缩列表,针对小规模数据进行的特殊优化。
查看某个键对应的值的类型
object encoding key
Redis 线程模型
Redis 是一个单线程模型的程序,但这并不意味着其服务器进程内部就真的只有一个线程。
在处理 网络IO 时,最开始也是只采用一个线程来处理,通过 IO多路复用 的方式进行处理。通过使用 IO多路复用 的方式,Redis 便可以通过一个线程同时处理多个 socket。
Redis 对于读写命令,采用的是单线程模型,当有多个客户端发起请求处理时,Redis 会将请求放入一个队列中,然后逐个处理。
使用单线程模型,就使得 Redis 中的线程天然安全,所有请求都是串行执行的,不存在线程安全问题。
为什么单线程?
Redis 的核心业务逻辑都是 “短平快” 的,不太消耗 CPU 资源,也就不太吃多核性能,故使用单线程模型也能很好的工作。
使用单线程,使得其编程变得更加容易且易维护。
Redis 的操作都是基于内存的,因此其速度非常快,性能瓶颈不在 CPU 上,主要还是在 内存 和 网络上。
使用多线程就会有线程安全问题,存在死锁、上下文切换等问题,甚至会影响性能。
V4.0
However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For the next releases, the plan is to make Redis more and more threaded.
然而,在 Redis 4.0 中,我们开始使 Redis 更加线程化。目前,这仅限于在后台删除对象,以及阻止通过 Redis 模块实现的命令。对于下一个版本,计划使 Redis 越来越线程化。
在 4.0 版本中,Redis 引入了多线程,但主要针对的是一些大键值对的删除命令,使用这些命令就会使用主线程之外的其他线程来异步处理,从而减少对主线程的影响。
在 4.0 版本中,Redis 增加了一些命令:UNLIKE
、FLUSHALL ASYNC
、FLUSHDB ASYNC
等非阻塞的删除命令。
尽管在 4.0 版本中引入了多线程,但这并不违背 Redis 单线程模型的设计理念,不过是在部分命令上引入的,对于整体架构而言,其还是单线程模型。
V6.0
Redis 在 网络IO 方面,引入了对多线程的支持,由于 Redis 的主要性能瓶颈出现在内存和网络上,引入多线程,可以提高 网络IO 的效率。
尽管引入了多线程,依然不需要担心线程安全问题,这是因为 Redis 的多线程只是在网络数据读写时使用多线程,执行命令的操作,依然是单线程的。
Redis 的多线程是默认禁用的,可以通过修改配置文件的操作开启多线程。
Redis 虽然是单线程模型,为什么效率这么高,速度这么快?(面试题)
Redis 的快,是相比于 MySQL 这些关系型数据库的而言的,并不是和谁比都快。
Redis 是基于内存的,数据读写速度非常快,内存的读写速度是非常快的。而像 MySQL 这样的关系型数据库则是访问硬盘的,速度则要慢很多。
Redis 的核心功能比其他数据库的要简单很多。在其他数据库上,我们通过编写 SQL 语句可以实现一些复杂功能的操作,但是 Redis 是 NoSQL 的,所以,Redis 干的活少,其功能相比于其他数据库也是更少的。
Redis 采用单线程模型,避免了不必要的线程竞争和上下文切换问题,这就保证了其高效。
在处理网络 IO 时,Redis 采用了 IO 多路复用机制,可以同时处理多个 socket 请求,这就使得 Redis 能处理更多的请求,提高了处理能力。