【Redis】浅谈数据结构和内部编码和单线程架构

🐼Redis 的 5 种数据类型

type 命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)、list(列表)、hash(哈希)、set(集合)、zset(有序集合),但这些只是 Redis 对外的数据结构,当然Redis可不只这5种数据结构,大概有10种。我们介绍最常用的,如图所示:

实际上 Redis 针对每种数据结构都有自已的底层内部编码实现,而且是多种实现,这样 Redis 会

在合适的场景选择合适的内部编码,目的就是为了达到节省时间/空间的效果!举个例子:对于哈希表来说,Reids给我们承诺:进行增加,删除,查找,保证时间复杂度为O(1)。但是在这个背后,在特定场景下,使用别的数据结构来实现,不保证是真的用哈希表实现的,但是仍要保持时间复杂度为O(1)的承诺!

所以数据结构/类型是Redis承诺给我们的,但是其底层的实现,也就是内部编码方式,Redis对于不同的类型会采取不同的内部编码,会自适应,我们用户感知不到,对于我们用户是透明的

不过要想查看内部编码方式可以使用OBJECT encoding key来查看

下面我们来一一看看Redis的数据结构和数据编码方式,数据结构可以看做为数据类型,数据编码方式,就是内部的底层实现:如图:

✅string

类比于std::string.其编码方式

  • 如果采用的是raw,表示的是最基本的字符串,它的底层就是一个char的数组
  • 而如果采用的是int的编码方式,则对应的场景可能是要使用一些类似于计数的功能,那么此时作为Value值,其实使用传统意义的字符是没有意义的,直接使用整数int来保存是一个更好的解决方案
  • 如果采用的是embstr,则表示的是针对于短字符串进行的特殊优化

✅hash

  1. hash这种数据类型,第一种内部编码是hashtable,这种实现方式就是最基本的实现方式,Redis内部也是用这种最基本的方式来实现的,虽然这里的实现方式和前面的不太一样,但是也大体思维差不多
  2. 而对于一些元素比较少的时候,如果还使用哈希表其实是没有意义的,因此就会把对应的编码方式更换为ziplist,它的主要目的可以进行列表的压缩,这样可以节省空间

可是为什么要压缩?

redis中可能会有很多key,key的value也可能对应很多hash,如果key特别多,hash也特别多,但是每个hash又不是很大,Redis就会自适应,内部编码就为ziplist,帮助我们去压缩,压缩后整体的空间就会变小了很多。

✅list

linklist可以看做链表,ziplist就是压缩链表,不过现在有了quicklist,兼顾了linklist和ziplist的优点

✅set

类比于我们学的集合

✅zset

有序集合,其中skiplist是根据跳表实现的

为啥redis要这么设计嘞?

可以改进内部编码,⽽对外的数据结构和命令没有任何影响,这样⼀旦开发出更优秀的内部编码,⽆需改动外部数据结构和命令,例如 Redis 3.2 提供了 quicklist,结合了 ziplist 和 linkedlist 两者的优势,为列表类型提供了⼀种更为优秀的内部编码实现,而对用户来说基本⽆感知
多种内部编码实现可以在不同场景下发挥各⾃的优势,例如 ziplist ⽐较节省内存,但是在列表元素⽐较多的情况下,性能会下降,这时候 Redis 会根据配置选项将列表类型的内部实现转换为 linkedlist,整个过程用户同样无感知

最后总结一下,redis会根据实际情况来选择内部的编码方式,我们只需要理解编码方式的思想即可


redis只有一个线程,来处理所有的命令请求!不是说redis服务器内部只有一个线程,还是有多个线程去处理网络IO

🐼redis单线程还能这么快?

难不成这个线程有魔法?其实不是这样的。redis快,是比MySQL要快。为什么redis能够使用单线程处理任务?

✅redis能够使用单线程的原因,是因为它是在内存的,并且任务仅仅就是处理kv键值对,这样的简单操作,短平快的操作。Redis 将所有数据放在内存中,内存的响应时⻓⼤约为 100 纳秒,这是 Redis 达到每秒万级别访问的重要基础。

✅redis的核心业务逻辑,不太消耗CPU和内存,都是很简单的

✅没有了多线程,就不会进行线程间切换,并且避免了锁带来的竞争

✅redis采用多用复用的非阻塞IO,使⽤ epoll 作为 I/O 多路复⽤技术的实现,再加上 Redis ⾃⾝的事件处理模型将 epoll 中的连接、读写、关闭都转换为事件,不在⽹络 I/O 上浪费过多的时间

不过这样单线程带来的缺点就是redis特别怕哪个线程发来一个请求特别长,最阻塞其他命令的执行。

我们这里再来思考一个问题,如果多个并发的客户端同时请求redis服务器,修改一个变量,会导致线程安全问题吗?不会!多个请求会在redis事件处理队列中排队,所以这些命令即使在发起时是并发的,但是到达redis,就变的有序了,需要排队,redis在执行这些命令时,是串行执行的。(现实生活打饭的例子~)

相关推荐
倔强的石头_18 小时前
kingbase备份与恢复实战(二)—— sys_dump库级逻辑备份与恢复(Windows详细步骤)
数据库
jiayou642 天前
KingbaseES 实战:深度解析数据库对象访问权限管理
数据库
李广坤3 天前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
爱可生开源社区4 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1774 天前
《从零搭建NestJS项目》
数据库·typescript
加号35 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏5 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐5 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
琢磨先生David5 天前
Day1:基础入门·两数之和(LeetCode 1)
数据结构·算法·leetcode
百锦再5 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip