Redis数据结构:快速的Redis有哪些慢操作?

  • 引言
  • 键和值用什么结构组织
  • 为什么哈希表操作变慢了
  • 集合数据操作效率
  • 有哪些底层数据结构
  • 不同操作的复杂度

引言

它接收到一个键值对操作后,能以微秒级别的速度找到数据,并快速完成操作。

  • 一方面,它是内存数据库,所有操作都在内存上完成,内存的访问速度本身就很快。
  • 另一方面,这要归功于它的数据结构。键值对是按一定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操作,所以高效的数据结构是 Redis 快速处理数据的基础。


String 类型的底层实现只有一种数据结构,也就是简单动态字符串。而 List、Hash、Set 和 Sorted Set 这四种数据类型,都有两种底层实现结构。

键和值用什么结构组织

为了实现从键到值的快速访问,Redis 使用了一个哈希表来保存所有键值对一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。所以,我们常说,一个哈希表是由多个哈希桶组成的,每个哈希桶中保存了键值对数据

其实,哈希桶中的元素保存的并不是值本身,而是指向具体值的指针。这也就是说,不管值是 String,还是集合类型,哈希桶中的元素都是指向它们的指针。


哈希表的最大好处很明显,就是让我们可以用 O(1) 的时间复杂度来快速查找到键值对
当你往 Redis 中写入大量数据后,就可能发现操作有时候会突然变慢了。这其实是因为你忽略了一个潜在的风险点,那就是哈希表的冲突问题和 rehash 可能带来的操作阻塞

为什么哈希表操作变慢了

哈希冲突是指,两个 key 的哈希值和哈希桶计算对应关系时,正好落在了同一个哈希桶中。毕竟,哈希桶的个数通常要少于 key 的数量,这也就是说,难免会有一些 key 的哈希值对应到了同一个哈希桶中

Redis 解决哈希冲突的方式,就是链式哈希。链式哈希也很容易理解,就是指同一个哈希桶中的多个元素用一个链表来保存,它们之间依次用指针连接

哈希冲突链上的元素只能通过指针逐一查找再操作。如果哈希表里写入的数据越来越多,哈希冲突可能也会越来越多,这就会导致某些哈希冲突链过长,进而导致这个链上的元素查找耗时长,效率降低

Redis 会对哈希表做 rehash 操作。rehash 也就是增加现有的哈希桶数量,让逐渐增多的entry 元素能在更多的桶之间分散保存,减少单个桶中的元素数量,从而减少单个桶中的冲突

为了使 rehash 操作更高效,Redis 默认使用了两个全局哈希表:哈希表 1 和哈希表 2。rehash步骤如下:

  1. 给哈希表 2 分配更大的空间,例如是当前哈希表 1 大小的两倍;
  2. 把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中;
  3. 释放哈希表 1 的空间。

可以从哈希表 1 切换到哈希表 2,用增大的哈希表 2 保存更多数据,而原来的哈希表 1 留作下一次 rehash 扩容备用。但是第二步涉及大量的数据拷贝,如果一次性把哈希表 1 中的数据都迁移完,会造成 Redis 线程阻塞,无法服务其他请求

Redis 采用了渐进式 rehash:
简单来说就是在第二步拷贝数据时,Redis 仍然正常处理客户端请求,每处理一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝到哈希表 2中;等处理下一个请求时,再顺带拷贝哈希表 1 中的下一个索引位置的entries


巧妙地把一次性大量拷贝的开销,分摊到了多次处理请求的过程中,避免了耗时操作,保证了数据的快速访问。

集合数据操作效率

和 String 类型不同,一个集合类型的值,第一步是通过全局哈希表找到对应的哈希桶位置,第二步是在集合中再增删改查

集合的操作效率和哪些因素相关呢:
首先,与集合的底层数据结构有关。例如,使用哈希表实现的集合,要比使用链表实现的集合访问效率更高。
其次,操作效率和这些操作本身的执行特点有关,比如读写一个元素的操作要比读写所有元素的效率高。

有哪些底层数据结构

集合类型的底层数据结构主要有 5 种:整数数组、双向链表、哈希表、压缩列表和跳表。

整数数组和双向链表也很常见,它们的操作特征都是顺序读写,也就是通过数组下标或者链表的指针逐个元素访问,操作复杂度基本是 O(N),操作效率比较低。

压缩列表实际上类似于一个数组,数组中的每一个元素都对应保存一个数据。和数组不同
的是,压缩列表在表头有三个字段 zlbytes(压缩列表内存字节数)、zltail(压缩链表尾部元素节点距离起始地址字节数,不包含zlend) 和 zllen(压缩列表节点数量,仅限元素个数)压缩列表在表尾还有一个 zlend(压缩链表末端),表示列表结束。

在压缩列表中,如果我们要查找定位第一个元素和最后一个元素,可以通过表头三个字段的长度直接定位,复杂度是 O(1)。而查找其他元素时,就没有这么高效了,只能逐个查找,此时的复杂度就是 O(N) 了。

跳表在链表的基础上,增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位

这个查找过程就是在多级索引上跳来跳去,最后定位到元素。这也正好符合“跳”表的叫法。当数据量很大时,跳表的查找复杂度就是 O(logN)

不同操作的复杂度

  • 第一,单元素操作,是指每一种集合类型对单个数据实现的增删改查操作。例如,Hash 类型的 HGET、HSET 和 HDEL,Set 类型的 SADD、SREM、SRANDMEMBER 等。这些操作的复杂度由集合采用的数据结构决定,例如,HGET、HSET 和 HDEL 是对哈希表做操作,所以它们的复杂度都是 O(1);Set 类型用哈希表作为底层数据结构时,它的 SADD、SREM、SRANDMEMBER 复杂度也是 O(1)。
    集合类型支持同时对多个元素进行增删改查,例如 Hash类型的 HMGET 和 HMSET,Set 类型的 SADD 也支持同时增加多个元素。此时,这些操作的复杂度,就是由单个元素操作复杂度和元素个数决定的。例如,HMSET 增加 M 个元素时,复杂度就从 O(1) 变成 O(M) 了。

  • 第二,范围操作,是指集合类型中的遍历操作,可以返回集合中的所有数据,比如 Hash类型的 HGETALL 和 Set 类型的 SMEMBERS,或者返回一个范围内的部分数据,比如 List类型的 LRANGE 和 ZSet 类型的 ZRANGE。这类操作的复杂度一般是 O(N),比较耗时,我们应该尽量避免
    Redis 从 2.8 版本开始提供了 SCAN 系列操作(包括 HSCAN,SSCAN 和ZSCAN),这类操作实现了渐进式遍历,每次只返回有限数量的数据。这样一来,相比于HGETALL、SMEMBERS 这类操作来说,就避免了一次性返回所有元素而导致的 Redis 阻塞。

  • 第三,统计操作,是指集合类型对集合中所有元素个数的记录,例如 LLEN 和 SCARD。这类操作复杂度只有 O(1),这是因为当集合类型采用压缩列表、双向链表、整数数组这些数据结构时,这些结构中专门记录了元素的个数统计,因此可以高效地完成相关操作。

  • 第四,例外情况,是指某些数据结构的特殊记录,例如压缩列表和双向链表都会记录表头
    和表尾的偏移量。这样一来,对于 List 类型的 LPOP、RPOP、LPUSH、RPUSH 这四个操作来说,它们是在列表的头尾增删元素,这就可以通过偏移量直接定位,所以它们的复杂度也只有 O(1),可以实现快速操作。

总结:
觉得有用的客官可以点赞、关注下!感谢支持

Redis数据结构:快速的Redis有哪些慢操作?相关推荐

  1. zincrby redis python_【Redis数据结构 序】使用redis-py操作Redis数据库

    想要看更加舒服的排版.更加准时的推送 关注公众号"不太灵光的程序员" 每日八点有干货推送 同时发布<[Redis数据结构 1序]1使用redis-py操作Redis数据库&g ...

  2. Redis数据结构-对象

    目录 前言 1.对象的类型与编码 1.1.类型 2.编码和底层实现 2.1.字符串对象 2.2.列表对象 2.3.哈希对象 2.4.集合对象 2.5.有序集合对象 参考 最后 前言 Redis并没有直 ...

  3. Redis数据结构有哪些

    Redis数据结构有哪些 一.Redis数据结构 一.Redis数据结构 Redis是一种基于内存的数据库,并且提供一定的持久化功能,它是一种键值(key-value)数据库,使用 key 作为 索引 ...

  4. 01丨数据结构:快速的Redis有哪些慢操作

    Redis数据类型与底层数据类型关系 简单来说,底层数据结构一共有 6 种,分别是简单动态字符串.双向链表.压缩列表.哈希表.跳表和整数数组.它们和数据类型的对应关系如下图所示: 键和值的结构组织 为 ...

  5. 十五分钟介绍 Redis数据结构

    2019独角兽企业重金招聘Python工程师标准>>> 参考:http://blog.nosqlfan.com/html/3202.html?ref=rediszt 下面是一个对Re ...

  6. 为了拿捏 Redis 数据结构,我画了 40 张图

    Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. 因此,这次我们就来好 ...

  7. 高性能存储之--快速理解redis(简版)

    为什么要用NoSql 随着流量的增加,链路性能会成为系统非常大的挑战,实现链路高性能有以下方案: 对于关系数据库,为解决高QPS带来的数据库压力,可以采用分库分表.读写分离.但还会存在一些挑战,比如I ...

  8. 【带你重拾Redis】Redis数据结构及使用场景

    Redis数据结构 Redis有着非常丰富的数据结构,这些数据结构可以满足非常多的应用场景, 如果对这些数据结构有一个比较清晰的认知,使用Redis也会更加得心应手. Redis主要支持以下数据结构: ...

  9. redis 自减命令_Redis 实战 —— 04. Redis 数据结构常用命令简介

    字符串 P39 Redis 的字符串是一个有字节组成的序列,可以存储以下 3 种类型的值:字节串(byte string).整数.浮点数. 在需要的时候, Redis 会将整数转换成浮点数.整数的取值 ...

最新文章

  1. Hystrix 超时配置重写
  2. HTML5一个浪漫的程序猿:3D旋转爱心表白神器思路源码视频
  3. SVG与UML图详解
  4. 怎么连接屏幕_触控一体机怎么实现无线投屏功能
  5. LuoguP5366 [SNOI2017]遗失的答案
  6. Hibernate基本概念 (2)
  7. 三级分类菜单的数据库设计
  8. 各个JSON技术的比较(Jackson,Gson,Fastjson)的对比
  9. CDM是什么?和CDP有什么区别?
  10. oracle 正则表达式2
  11. 3D渲染和动画制作KeyShot Pro for mac
  12. 拓扑次序(Topological Order)
  13. 代码审计——你是如何发现那些有缺陷的代码的
  14. 软件测试必备技能有哪些?
  15. 《信息安全技术》实验三 数字证书应用
  16. 如何保障企业业务流程的落地实施?
  17. 《这!就是街舞》,好综艺还是好生意?
  18. KiCad 泪滴插件 teardrop plugin
  19. The tomcat server configuration at /sever/tomcat v9.0 localhost-config is.......错误解析
  20. 单选题计算机科学的奠基人是,计算机科学的奠基人是( )。

热门文章

  1. php 输出等腰三角形,C语言输出等腰三角形
  2. 网红超火罗马桌面时钟效果
  3. 35个Photoshop最强己付费扩展面板+自动修图PS插件
  4. 行为识别阅读笔记(paper + parted code):Beyond Frame-level CNN Saliency-Aware 3-D CNN with LSTM for Video Acti
  5. Java学生成绩处理
  6. 我国开始研制电子计算机,我国从( )年开始研制电子计算机。
  7. python中定义函数,斐波纳契数列:1,1,2,3,5,8,13。。
  8. [牛客网中级项目]第四章用户注册登陆管理
  9. 关于Maxon上位机EPOS Studio的配置
  10. python手机充值代码_不用框架,原生使用python做注册接口/登陆接口/充值接口的测试,做的数据/代码分离...