当前位置: 首页 > article >正文

Redis:常见数据类型和单线程模型

目录

常见数据类型

不同数据类型的编码方式

单线程模型


常见数据类型

在前面学习的 type 命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)、list(列表)、hash(哈希)、set(集合)、zset(有序集合),但这些只是Redis对外的数据结构,如下图所示:

前四种:

  • string类似于C++的 std::string
  • list 类似于C++的 std::deque
  • hash 类似于C++的 std::unordered_map
  • set 类似于C++的 std::set

zset 有序集合,C++ 并没有类似的概念
相当于是除了存储 member 之外还需要存储一个 score (权重,分数)

Redis 底层在实现上述数据结构的时候,会在源码层面,针对上述实现进行特定的优化,来达到 节省时间/节省空间 效果

内部的具体实现的数据结构,还会有变数
redis 承诺,现在我这有个 hash 表,你进行 查询、插入、删除 操作, 都保证 O(1)
但是这个背后的实现,不一定就是一个标准的 hash 表,可能再特定场景下,使用别的数据结构实现,但是仍然保证时间复杂度符合承诺


不同数据类型的编码方式

同一个数据类型,背后可能的编码实现方式是不同的,会根据特定场景优化

redis 会自动适应,程序猿在使用 redis 的时候一般感知不到

对于 list 数据类型来说,从 redis 3.2 开始,引入了新的实现方式 quicklist,它同时兼顾了 linkedlist 和 ziplist 的优点
quicklist 就是一个链表, 每个元素又是一个 ziplist,把空间和效率都折衷的兼顾到
quicklist 比较类似于 C++ 中的 std::deque

相关编码方式的解释:

raw:最基本的字符串(底层就是持有一个 char 数组(C++))

int:redis 通常也可以用来实现一些"计数"这样的功能,当 value 就是一个整数的时候此时可能 redis 会直接使用 int 来保存

embstr:针对短字符串进行的特殊优化

hashtable:最基本的哈希表

ziplist:在哈希表里面元素比较少的时候,可能就优化成 ziplist 了,压缩列表能够节省空间
redis 上有很多很多 key,可能是某些 key 的 value 是 hash,此时如果 key 特别多,对应的 hash 也特别多,但是每个 hash 又不大的情况下,就尽量去压缩,压缩之后就可以让整体占用的内存更小了

linkedlist:链表

intset:集合中存的都是整数,就选择 intset

skiplist:跳表,也是链表,不同于普通的链表,每个节点上有多个指针域,巧妙的搭配这些指针域的指向,就可以做到,从跳表上查询元素的时间复杂度是 O(logN)


命令查看编码方式

可以通过 object encoding 命令查询内部编码

redis 会自动根据当前的实际情况选择内部的编码方式,自动适应的

网上的说法:如果字符串长度小于39 字节,使用 embstr,超过 39 字节,使用raw
是否要记住,啥时候使用啥编码方式呢
注意:只记思想,不记数字

相比于知道数字,更重要的是知道数字是怎么得到的,就可以根据所处的实际场景,重新得到这样的数字


单线程模型

单线程模型的工作过程

redis 只使用一个线程,处理所有的命令请求,不是说一个 redis 服务器进程内部真的就只有一个线程其实也有多个线程,多个线程是在处理 网络 IO

如果两个客户端同时向 Redis 发送 incr(自增请求),此时就意味着是否服务器这边也会存在类似的线程安全问题呢
当前这两个客户端,也相当于"并发"的发起了上述的请求,但幸运的是并不会有线程安全问题,redis 服务器实际上是单线程模型,保证了当前收到的这多个请求是串行执行的
多个请求同时到达 redis 服务器,也是要先在队列中排队,再等待 redis 服务器一个一个的取出里面的命令,再执行
微观上讲,redis 服务器是 串行/顺序 执行这多个命令的

redis 能够使用 单线程模型 很好的工作
原因主要在于 redis 的核心业务逻辑,都是短平快的,不太消耗 cpu 资源也就不太吃多核了

弊端:
redis 必须要特别小心,某个操作占用时间长,就会阻塞其他命令的执行


单线程为什么效率高、速度快

效率高、速度快的参照物,是数据库(mysql、oracle、sql server)

1. redis 访问内存,而数据库则是访问硬盘

2. redis 核心功能,比数据库的核心功能更简单
数据库对于数据的插入删除査询,都有更复杂的功能支持,这样的功能势必要花费更多的开销
比如,针对插入删除,数据库中的各种约束,都会使数据库做额外的工作
redis 干的活少,提供的功能相比于 mysql 也是少了不少

3.单线程模型,避免了一些不必要的线程竞争开销
redis 每个基本操作,都是短平快的,就是简单操作一下内存数据,不是什么特别消耗 cpu 的操作就算搞多个线程,也提升不大

4. 处理网络 IO 的时候,使用了 epoll 这样的 IO 多路复用机制
一个线程,就可以管理多个 socket
针对 TCP 来说,服务器这边每次要服务一个客户端,都需要给这个客户端安排一个 socket
一个服务器服务多个客户端,同时就有很多个 socket,这些 socket 上并不是无时无刻的在传输数据,很多情况下,每个客户端和服务器之间的通信也没那么频繁,此时这么多 socket 大部分时间都是 静默 的,上面是没有数据需要传输的
也就是同一时刻,只有少数 socket 是活跃的

基于上述原因,我们就使用了 IO 多路复用,一个线程来处理多个 socket
IO 多路复用(操作系统给程序猿提供的机制. 提供了一套 API,内部的功能都是操作系统内核实现的)

Linux 上提供的 IO 多路复用, 主要是三套 API:

  • select
  • poll
  • epoll (运行效率最高的机制)

关于 IO 多路复用,在 Linux 章节中有对应的博客


Redis:常见数据类型和单线程模型,学习到此结束


http://www.kler.cn/a/583680.html

相关文章:

  • 反射、 Class类、JVM的类加载机制、Class的常用方法
  • 【pyenv+venv组合来使用Python虚拟环境的方法】
  • 大脑宏观结构中的富集俱乐部:图论分析视角
  • 【Vue】el-dialog的2种封装方法(父子组件双向通信),$emit触发父事件/.sync修饰符双向绑定
  • 家电制造行业内检实验室LIMS系统推荐 如何提升家电制造行业品质
  • Linux网络编程之——网络初认识
  • 家政服务平台(源码+文档+讲解+演示)
  • 探讨如何在AS上构建webrtc(3)分享WebRTC-CMakeBuild轻量化工程
  • 深度剖析Redis:双写一致性问题及解决方案全景解析
  • Insar结合ISCE2,某一个文件进行并行-stackSentinel.py
  • 二、Jenkins部署你的第一个自动化流程(Vuepress项目)
  • Redis 主从复制机制深度解析与实践指南
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_core_module
  • 面试之《commonjs,requirejs和es6 Module的区别》
  • 高并发下订单库存防止超卖策略
  • 物理服务器抵御网络攻击的方法都有哪些?
  • 力扣练习之确定两个字符串是否接近
  • Redis 哨兵模式详解:实现高可用与自动故障转移
  • MySQL数据实时同步至Elasticsearch的高效方案:Java实现+源码解析,一文搞定!
  • 【嵌入式linux】网口和USB热插拔检测