Redis数据结构:快速的Redis有哪些慢操作?
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步骤如下:
- 给哈希表 2 分配更大的空间,例如是当前哈希表 1 大小的两倍;
- 把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中;
- 释放哈希表 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有哪些慢操作?相关推荐
- zincrby redis python_【Redis数据结构 序】使用redis-py操作Redis数据库
想要看更加舒服的排版.更加准时的推送 关注公众号"不太灵光的程序员" 每日八点有干货推送 同时发布<[Redis数据结构 1序]1使用redis-py操作Redis数据库&g ...
- Redis数据结构-对象
目录 前言 1.对象的类型与编码 1.1.类型 2.编码和底层实现 2.1.字符串对象 2.2.列表对象 2.3.哈希对象 2.4.集合对象 2.5.有序集合对象 参考 最后 前言 Redis并没有直 ...
- Redis数据结构有哪些
Redis数据结构有哪些 一.Redis数据结构 一.Redis数据结构 Redis是一种基于内存的数据库,并且提供一定的持久化功能,它是一种键值(key-value)数据库,使用 key 作为 索引 ...
- 01丨数据结构:快速的Redis有哪些慢操作
Redis数据类型与底层数据类型关系 简单来说,底层数据结构一共有 6 种,分别是简单动态字符串.双向链表.压缩列表.哈希表.跳表和整数数组.它们和数据类型的对应关系如下图所示: 键和值的结构组织 为 ...
- 十五分钟介绍 Redis数据结构
2019独角兽企业重金招聘Python工程师标准>>> 参考:http://blog.nosqlfan.com/html/3202.html?ref=rediszt 下面是一个对Re ...
- 为了拿捏 Redis 数据结构,我画了 40 张图
Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. 因此,这次我们就来好 ...
- 高性能存储之--快速理解redis(简版)
为什么要用NoSql 随着流量的增加,链路性能会成为系统非常大的挑战,实现链路高性能有以下方案: 对于关系数据库,为解决高QPS带来的数据库压力,可以采用分库分表.读写分离.但还会存在一些挑战,比如I ...
- 【带你重拾Redis】Redis数据结构及使用场景
Redis数据结构 Redis有着非常丰富的数据结构,这些数据结构可以满足非常多的应用场景, 如果对这些数据结构有一个比较清晰的认知,使用Redis也会更加得心应手. Redis主要支持以下数据结构: ...
- redis 自减命令_Redis 实战 —— 04. Redis 数据结构常用命令简介
字符串 P39 Redis 的字符串是一个有字节组成的序列,可以存储以下 3 种类型的值:字节串(byte string).整数.浮点数. 在需要的时候, Redis 会将整数转换成浮点数.整数的取值 ...
最新文章
- Hystrix 超时配置重写
- HTML5一个浪漫的程序猿:3D旋转爱心表白神器思路源码视频
- SVG与UML图详解
- 怎么连接屏幕_触控一体机怎么实现无线投屏功能
- LuoguP5366 [SNOI2017]遗失的答案
- Hibernate基本概念 (2)
- 三级分类菜单的数据库设计
- 各个JSON技术的比较(Jackson,Gson,Fastjson)的对比
- CDM是什么?和CDP有什么区别?
- oracle 正则表达式2
- 3D渲染和动画制作KeyShot Pro for mac
- 拓扑次序(Topological Order)
- 代码审计——你是如何发现那些有缺陷的代码的
- 软件测试必备技能有哪些?
- 《信息安全技术》实验三 数字证书应用
- 如何保障企业业务流程的落地实施?
- 《这!就是街舞》,好综艺还是好生意?
- KiCad 泪滴插件 teardrop plugin
- The tomcat server configuration at /sever/tomcat v9.0 localhost-config is.......错误解析
- 单选题计算机科学的奠基人是,计算机科学的奠基人是( )。
热门文章
- php 输出等腰三角形,C语言输出等腰三角形
- 网红超火罗马桌面时钟效果
- 35个Photoshop最强己付费扩展面板+自动修图PS插件
- 行为识别阅读笔记(paper + parted code):Beyond Frame-level CNN Saliency-Aware 3-D CNN with LSTM for Video Acti
- Java学生成绩处理
- 我国开始研制电子计算机,我国从( )年开始研制电子计算机。
- python中定义函数,斐波纳契数列:1,1,2,3,5,8,13。。
- [牛客网中级项目]第四章用户注册登陆管理
- 关于Maxon上位机EPOS Studio的配置
- python手机充值代码_不用框架,原生使用python做注册接口/登陆接口/充值接口的测试,做的数据/代码分离...