阅读之Redis性能
Redis作为一种KV缓存服务器,有着极高的性能,相对于memcache,Redis支持更多中数据类型,因此在业界广泛应用。
Redis为什么快:
- 数据是存储在内存中的。
- Redis是单线程的。
将数据存储在内存中,读取的时候后不需要进行磁盘的IO,单线程也保证了系统没有线程的上下文切换。
从数据存储层面上分析Redis性能为何如此高。
Redis性能如此高的原因,有如下几点
- 纯内存操作
- 单线程
- 高效的数据结构
- 合理的数据编码
- 其他方面的优化
在Redis中,常用的5中数据结构和应用场景如下:
- String:缓存、计数器、分布式锁等
- List:链表、队列、微博关注人世间轴列表等
- Hash:用户信息、Hash表等
- Set:去重、赞、踩、共同好友等
Zset:访问量排行榜、点击量排行榜等
SDS
Redis是用C语言开发完成的,但在Redis字符串中,并没有使用C语言中的字符串,而是用一种称为SDS(simple dynamic string)的结构体来保存字符串,SDS的结构如下
struct sdshdr {
int len;
int free;
char buf[];
}
len:用于记录buf中已使用空间的长度
free:buf中空闲空间的长度
buf[]:存储实际内容
例如:执行命令set key value,key和value都是一个SDS类型的结构存储在内存中
二、SDS与C字符串的区别
1、常数时间内获得字符串长度
- C字符串本身不记录长度信息,每次获取长度信息都需要遍历整个字符串,复杂度为O(n)
- C字符串遍历时遇到'\0'时结束
SDS中len字段保存着字符串的长度,所以总能在常数时间内获取字符串长度,复杂度是O(1)
2、避免缓冲区溢出
假设在内存中有两个紧挨着的两个字符串,s1=“xxxxx”和s2=“yyyyy”,由于在内存上紧紧相连,当我们对s1进行扩充的时候,将s1=“xxxxxzzzzz”后,由于没有进行相应的内存重新分配,导致s1把s2覆盖掉,导致s2被莫名其妙的修改。
但SDS的API对zfc修改时首先会检查空间是否足够,若不充足则会分分配新空间,避免了缓冲区溢出问题。
3、减少字符串修改时带来的内存重新分配的次数
在C中,当我们频繁的对一个字符串进行修改(append或trim)操作的时候,需要频繁的进行内存重新分配的操作,十分影响性能。如果不小心忘记,有可能会导致内存溢出或内存泄漏,对于Redis来说,本身就会很频繁的修改字符串,所以使用C字符串并不合适。而SDS实现了空间预分配和惰性空间释放两种优化策略:
- 空间预分配
当SDS的API对一个SDS修改后,并且对SDS空间扩充时,程序不仅会为SDS分配所需要的必须空间,还会分配额外的未使用空间,分配规则如下:
如果对SDS修改后,len的长度小于1M,那么程序将分配和len相同长度的未使用空间。举个例子,如果len=10,重新分配后,buf的实际长度会变为10(已使用空间)+10(额外空间)+1(空字符)=21。
如果对SDS修改后len长度大于1M,那么程序将分配1M的未使用空间
- 惰性空间释放
当对SDS进行缩短操作时,程序并不会回收多余的内存空间,而是使用free字段将这些字节数量记录下来不释放,后面如果需要append操作,则直接使用free中未使用的空间,减少了内存的分配。
4、二进制安全
在Redis中不仅可以存储String类型的数据,也可能存储一些二进制数据。二进制数据并不是规则的字符串格式,其中会包含一些特殊的字符如'\0',在C中遇到'\0'则表示字符串的结束,但在SDS中,标志字符串结束的是len属性。
一、String对象的编码转化
String对象的编码可以是int或raw,对于String类型的键值,如果我们存储的是纯数字,Redis底层采用的是int类型的编码,如果其中包括非数字,则会立即转为raw编码
127.0.0.1:6379> set str 1
OK
127.0.0.1:6379> object encoding
str
"int"
127.0.0.1:6379> set str 1a
OK
127.0.0.1:6379> object encoding
str
"raw"
127.0.0.1:6379>
二、List对象的编码转化
List对象的编码可以是ziplist或linkedlist,对于List类型的键值,当列表对象同时满足一下两个条件时采用ziplist编码
- 列表对象保存的所有字符串元素的长度都小于64字节
- 列表对象保存的元素个数小于512个
如果不满足这两个条件的任意一个,就会转化为linkedlist编码
注意:这两个条件是可以修改的,在redis.conf中
list-max-ziplist-entries 512
list-max-ziplist-value
64
三、Set类型的编码转化
Set对象的编码可以是intset或hashtable,intset编码的结婚对象使用整数集合作为底层实现,把所有元素都保存在一个整数集合里面。
127.0.0.1:6379> sadd set 1 2 3
(integer) 3
127.0.0.1:6379> object encoding set
"intset"
127.0.0.1:6379>
如果set集合中保存了非整数类型的数据时,Redis会将intset转化为hashtable
127.0.0.1:6379> sadd set 1 2 3
(integer) 3
127.0.0.1:6379> object encoding set
"intset"
127.0.0.1:6379> sadd set a
(integer) 1
127.0.0.1:6379> object encoding set
"hashtable"
127.0.0.1:6379>
当Set对象同时满足一下两个条件时,对象采用intset编码
- 保存的所有元素都是整数值(小数不行)
- Set对象保存的所有元素个数小于512个
不能满足这两个条件的任意一个,Set都会采用hashtable存储
注意:第两个条件是可以修改的,在redis.conf中
set-max-intset-entries 512
四、Hash对象的编码转化
Hash对象的编码可以是ziplist或hashtable,当Hash以ziplist编码存储的时候,保存同一键值对的两个节点总是紧挨在一起,键节点在前,值节点在后
|
zlbytes |
zltail |
zllen |
name |
tom |
age |
25 |
career |
programmer |
zlend |
当Hash对象同时满足一下两个条件时,Hash对象采用ziplist编码
- Hash对象保存的所有键值对的键和值的字符串长度均小于64字节
- Hash对象保存的键值对数量小于512个
如果不满足以上条件的任意一个,ziplist就会转化为hashtable编码
注意:这两个条件是可以修改的,在redis.conf中
hash-max-ziplist-entries 512
hash-max-ziplist-value
64
五、Zset对象的编码转化
Zset对象的编码可以是ziplist或zkiplist,当采用ziplist编码存储时,每个集合元素使用两个紧挨在一起的压缩列表来存储。第一个节点存储元素的成员,第二个节点存储元素的分值,并且按分值大小从小到大有序排列。
|
zlbytes |
zltail |
zllen |
java |
5.0 |
python |
6.0 |
html |
7.0 |
zlend |
当Zset对象同时满足一下两个条件时采用ziplist编码
- Zset保存的元素个数小于128
- Zset元素的成员长度都小于64字节
如果不满足以上条件的任意一个,ziplist就会转化为zkiplist编码
注意:这两个条件是可以修改的,在redis.conf中
zset-max-ziplist-entries 128
zset-max-ziplist-value
64
思考:Zset如何做到O(1)复杂度内元素并且快速进行范围操作?
Zset采用skiplist编码时使用zset结构作为底层实现,该数据结构同时包含了一个跳跃表和一个字典,其结构如下:
typedef struct zset{
zskiplist *zsl;
dict *dict;
}
Zset中的dict字典为集合创建了一个从成员到分值之间的映射,字典中的键保存了成员,字典中的值保存了成员的分值,这样定位元素时时间复杂度是O(1)
Zset中的zsl跳跃表适合范围操作,比如ZRANK、ZRANGE等,程序使用zkiplist。
另外,虽然zset中使用了dict和skiplist存储数据,但这两种数据结构都会通过指针来共享相同的内存,所以没有必要担心内心的浪费。
总结
总而言之,Redis为了高性能,从各方各面都进行了优化,不只是单线程和内存存储了。
阅读之Redis性能的更多相关文章
- Redis性能问题排查解决手册(七)
阅读目录: 性能相关的数据指标 内存使用率used_memory 命令处理总数total_commands_processed 延迟时间 内存碎片率 回收key 总结 性能相关的数据指标 通过Red ...
- Redis性能问题排查解决手册
转自:http://www.cnblogs.com/mushroom/p/4738170.html 阅读目录: 性能相关的数据指标 内存使用率used_memory 命令处理总数total_comma ...
- Redis(二十一):Redis性能问题排查解决手册(转)
性能相关的数据指标 通过Redis-cli命令行界面访问到Redis服务器,然后使用info命令获取所有与Redis服务相关的信息.通过这些信息来分析文章后面提到的一些性能指标. info命令输出的数 ...
- Redis为什么变慢了?透彻解读如何排查Redis性能问题
Redis 作为优秀的内存数据库,其拥有非常高的性能,单个实例的 OPS 能够达到 10W 左右.但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大的情况,就会与我们的预期不符. 你也 ...
- redis性能调优笔记(can not get Resource from jedis pool和jedis connect time out)
对这段时间redis性能调优做一个记录. 1.单进程单线程 redis是单进程单线程实现的,如果你没有特殊的配置,redis内部默认是FIFO排队,即你对redis的访问都是要在redis进行排队,先 ...
- 『性能』ServiceStack.Redis 和 StackExchange.Redis 性能比较
背景 近来,需要用到 Redis 这类缓存技术 —— MongoDB 和 Redis 没有进行过比较. 我也懒得在这些细节上 纠结那么多 —— 按照网友给出的文章,听从网友建议,选择 Redis. R ...
- 关于redis性能问题分析和优化
一.如何查看Redis性能 info命令输出的数据可以分为10个分类,分别是: server,clients,memory,persistence,stats,replication,cpu,comm ...
- Redis性能调优
Redis性能调优 尽管Redis是一个非常快速的内存数据存储媒介,也并不代表Redis不会产生性能问题.前文中提到过,Redis采用单线程模型,所有的命令都是由一个线程串行执行的,所以当某个命令执行 ...
- Redis性能调优建议
一. Redis部署结构优化建议 1. Master不做AOF或RDB持久化,Slave做AOF持久化,建议同时做RDB持久化 2. 所有Master全部增加Slave 3. Master挂载Slav ...
随机推荐
- jQuery事件操作
bind绑定事件 bind(type,data,fn) [参数描述] type (String) : 事件类型 data (Object) : (可选) 作为event.data属性值传递给事件对象的 ...
- ArcEngine中打开各种数据源(WorkSpace)的连接 (SDE、personal/File、ShapeFile、CAD数据、影像图、影像数据集)
ArcEngine 可以接受多种数据源.在开发过程中我们使用了如下几种数据源 1.企业数据库(SDE) 企业数据库需要使用SDE来管理,所以需要使用SDE的Workspace来表示连接.在AE接口中, ...
- 实验----Java的二维数组的应用及杨辉三角的编写
(1) 编写一个程序,生成一个10*10的二维随机整数数组,并将该数组的每行最大值保存于一个一维数组中,将每列平均值保存于另外一个一维数组中并分别输出. (2) 编程输出杨辉三角的前10行. 找出一个 ...
- 【Python开发】Python 适合大数据量的处理吗?
Python 适合大数据量的处理吗? python 能处理数据库中百万行级的数据吗? 处理大规模数据时有那些常用的python库,他们有什么优缺点?适用范围如何? 需要澄清两点之后才可以比较全面的看这 ...
- hadoop的目录结构介绍
hadoop的目录结构介绍 解压缩hadoop 利用tar –zxvf把hadoop的jar包放到指定的目录下. tar -zxvf /home/software/aa.tar.gz -C /home ...
- 【C】命令行参数解析——getopt、getopt_long及getopt_long_only
前言 在linux下学习开源代码Webbench,遇到get_long等函数的用法,一时有点懵,故想深入了解这类命令行解析函数,并记此博文. 1.getopt getopt主要用来处理短命令行选项,例 ...
- MVC与MTV模型及Django请求的生命周期
MVC模型 MVC:Model View Controller M: 模型.是应用程序中用于处理应用程序数据逻辑的部分 V:视图.是应用程序汇总处理数据显示的部分 C:控制器.是应用程序中处理用户交互 ...
- 【STM32】串行通信原理
(1)通信接口背景知识 并行通信: --传输原理:数据各个位同时传输 --优点:速度快 --缺点:占用引脚资源多 串行通信: ...
- Linux下安装双JDK环境与双服务器
安装双JDK环境和双服务器,具体操作如下: (1)使用tar -xvf命令解压Tomcat: (2)在Tomcat服务器下的bin文件夹下的catalina.sh文件中的头部加入以下内容: (3)修改 ...
- Hive 教程(四)-分区表与分桶表
在 hive 中分区表是很常用的,分桶表可能没那么常用,本文主讲分区表. 概念 分区表 在 hive 中,表是可以分区的,hive 表的每个区其实是对应 hdfs 上的一个文件夹: 可以通过多层文件夹 ...