目录
[查看 key 对应 value 的编码方式](#查看 key 对应 value 的编码方式)
[Reids 单线程模型](#Reids 单线程模型)
[IO 多路复用](#IO 多路复用)
Redis 常用数据结构
- Redis 中所有的 key 均为 String 类型,而不同的是 value 的数据类型却有很多种
- 以下介绍 5 种 value 常见的数据类型
注意:
- 上述的 有序集合 相当于是除了存储 member 之外,还需要存储一个 score
- 而此处的 socre(分数) 相当于有序集合的 权重
各数据结构具体编码方式
- Redis 是一种非关系型的数据库,它的底层实现了一些特定的优化,即 通过选择合适的 数据结构 和 编码方式,以便达到节省时间 和空间的效果
实例理解
- Redis 在实现 哈希表时 可能会根据特定的场景和需求 选择使用不同的数据结构 ,而不仅仅是标准的哈希表
- 但是无论使用哪种数据结构,Redis 都承诺查询、插入、删除操作的时间复杂度为 O(1)
数据结构:
- redis 承诺给你提供使用的,也可以理解成 数据类型
编码方式:
- redis 数据结构 内部底层的实现
- 同一个数据结构 可能背后的编码方式是不同的,会根据特定场景优化
注意:
- redis 会自动根据当前的实际情况来选择内部编码方式,即自适应
- 那么是否要记住 什么情况下 使用 什么编码方式 呢?
- 只记思想,不记数字,记数字没有任何意义
原因:
- 数字都是可自行配置的
- 数字是怎么来的,这点需要考证清楚
- 相比于知道数字,更重要的是知道数字是怎么得到的,就可以根据所处的实际场景,重新得到这样的数字
正确做法:
- 根据实际的测试结果,测试出一个更合适的数值
查看 key 对应 value 的编码方式
语法:
object encoding key
实例理解
Reids 单线程模型
- redis 只使用一个线程,处理所有的命令请求
- 不是说一个 redis 服务器进程内部真的就只有一个线程
- 其实也有多个线程,多个线程是在处理 网络 IO
实例理解
- 假设有多个客户端,同时操作一个 redis 服务器,且这两个客户端 并发 的发起请求
- 在多线程场景下 ,针对类似于这样的情形
- 即 两个线程尝试同时对同一个变量进行自增操作, 虽然表面上看是自增两次,但实际上可能只自增了一次
- 但是由图可知 我们的 redis 服务器并不会存在类似的线程安全问题
原因:
- Redis 服务器实际上是 单线程模型
- 即保证了 服务器收到多个请求时,并会 并发执行这多个请求,而是 串行/顺序执行
补充:
- Redis 之所以能够使用 单线程模型 也能很好的工作,其 原因主要在于 Redis 的核心业务逻辑都是短平快的
- 短 指的是 Redis 的每个操作都很简单 ,平 指的是 Redis 的操作都很稳定 ,快 指的是 Redis 的操作都很快
- 所以 Redis 不太需要消耗 CPU 资源,从而也就不太需要 利用多核来提高效率了
弊端:
- Redis 必须要特别小心 某个操作占用时间长,其可能 会阻塞其他命令的执行
经典面试题
- redis 虽然是单线程模型,但为啥效率这么高呢? 速度这么快呢?
注意:
- 此时我们的 参照物 是相对于 数据库 而言
1、redis 访问内存,而数据库访问的是硬盘
- 内存的访问速度 要远远大于 硬盘的访问速度
2、redis 核心功能 比 数据库的核心功能更简单
- 数据库 对于数据的插入删除查询 均支持更复杂的功能
- 这样的功能必然需要花费更多的开销 ,比如针对插入删除,加之数据库中的各种约束,这都会使数据库做更多额外的工作
- redis 干的活少,因此 redis 所提供的功能相较于 mysql 也是少了很多
3、单线程模型,避免了一些不必要的线程竞争开销
- redis 的每个操作基本都是短平快的, 即简单操作一下内存数据,并不需要消耗大量的 cpu 资源
- 所以就算搞个多线程,对效率的提升也是不大
4、处理网络 IO 的时候,使用了 epoll 这样的 IO 多路复用机制
- IO 多路复用本质上为一个线程可以管理多个 socket
实例理解
- 针对 TCP 来说,每当服务器要服务一个客户端时 均需要给该客户端安排一个 socket
- 一个服务器 服务多个客户端时,便会有多个 socket
问题:
- 这些 socket 上都是无时不刻的在传输数据吗?
具体理解:
- 很多情况下,每个客户端和服务器之间的通信 并不会特别频繁
- 所以多数 socket 大部分时间都是静默的,即没有数据需要进行传输
实际情况:
- 综上所述 对于 TCP 服务器来说,大部分 socket 都是静默的,仅只有少数 socket 是活跃的,即会进行数据传输
- 消耗大量系统资源的同时,大量的线程 占着茅坑不拉屎 ,由此可见效率十分低下
IO 多路复用
- 基于上述的场景,便有了 IO 多路复用机制,即一个线程来处理多个 socket
- 这是 操作系统给程序员提供的机制
- 同时操作系统也提供了一套 API ,其内部的功能均由操作系统内核实现的
- Linux 上提供的 IO 多路复用,主要是三套 API (select、poll、epoll),其中 epoll 的运行效率最高
实例理解:
- 此时我晚饭想吃三样美食,均在同一条街道上
方案一:
- 一个一个买
- 相当于单线程串行执行,效率最低
方案二:
- 我喊了两个好友,来帮我一起买
- 相当于多线程并行执行,效率大大提升,但是系统开销大了
方案三:
- 我一个人来买,但是我买的时候不再干等着了
- 此时相当于一个线程同时做三件事
- 能高效完成这三件事的前提是 这三件事的交互都不频繁,大部分时间都在等待!
- 此处的 ' 哪样美食好了 哪个老板就喊我一声 ' 相当于 epoll,即事件 通知/回调 机制
注意:
- 如果这三件事情都是交互特别频繁的,还是需多引入几个线程,否则一个线程就容易忙不过来
补充:
- Java 可以使用 NIO (标准库提供的一组类,底层就是封装了 epoll)