Redis作为一种KV缓存服务器,有着极高的性能,相对于memcache,Redis支持更多中数据类型,因此在业界广泛应用。

Redis为什么快:

  1. 数据是存储在内存中的。
  2. Redis是单线程的。

将数据存储在内存中,读取的时候后不需要进行磁盘的IO,单线程也保证了系统没有线程的上下文切换。

从数据存储层面上分析Redis性能为何如此高。

Redis性能如此高的原因,有如下几点

  1. 纯内存操作
  2. 单线程
  3. 高效的数据结构
  4. 合理的数据编码
  5. 其他方面的优化

在Redis中,常用的5中数据结构和应用场景如下:

  1. String:缓存、计数器、分布式锁等
  2. List:链表、队列、微博关注人世间轴列表等
  3. Hash:用户信息、Hash表等
  4. 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性能的更多相关文章

  1. Redis性能问题排查解决手册(七)

     阅读目录: 性能相关的数据指标 内存使用率used_memory 命令处理总数total_commands_processed 延迟时间 内存碎片率 回收key 总结 性能相关的数据指标 通过Red ...

  2. Redis性能问题排查解决手册

    转自:http://www.cnblogs.com/mushroom/p/4738170.html 阅读目录: 性能相关的数据指标 内存使用率used_memory 命令处理总数total_comma ...

  3. Redis(二十一):Redis性能问题排查解决手册(转)

    性能相关的数据指标 通过Redis-cli命令行界面访问到Redis服务器,然后使用info命令获取所有与Redis服务相关的信息.通过这些信息来分析文章后面提到的一些性能指标. info命令输出的数 ...

  4. Redis为什么变慢了?透彻解读如何排查Redis性能问题

    Redis 作为优秀的内存数据库,其拥有非常高的性能,单个实例的 OPS 能够达到 10W 左右.但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大的情况,就会与我们的预期不符. 你也 ...

  5. redis性能调优笔记(can not get Resource from jedis pool和jedis connect time out)

    对这段时间redis性能调优做一个记录. 1.单进程单线程 redis是单进程单线程实现的,如果你没有特殊的配置,redis内部默认是FIFO排队,即你对redis的访问都是要在redis进行排队,先 ...

  6. 『性能』ServiceStack.Redis 和 StackExchange.Redis 性能比较

    背景 近来,需要用到 Redis 这类缓存技术 —— MongoDB 和 Redis 没有进行过比较. 我也懒得在这些细节上 纠结那么多 —— 按照网友给出的文章,听从网友建议,选择 Redis. R ...

  7. 关于redis性能问题分析和优化

    一.如何查看Redis性能 info命令输出的数据可以分为10个分类,分别是: server,clients,memory,persistence,stats,replication,cpu,comm ...

  8. Redis性能调优

    Redis性能调优 尽管Redis是一个非常快速的内存数据存储媒介,也并不代表Redis不会产生性能问题.前文中提到过,Redis采用单线程模型,所有的命令都是由一个线程串行执行的,所以当某个命令执行 ...

  9. Redis性能调优建议

    一. Redis部署结构优化建议 1. Master不做AOF或RDB持久化,Slave做AOF持久化,建议同时做RDB持久化 2. 所有Master全部增加Slave 3. Master挂载Slav ...

随机推荐

  1. MSIX 打包 DotNetCore 3.0

    使用 MSIX 打包 DotNetCore 3.0 客户端程序 如何你希望你的 WPF 程序能够以 Windows 的保护机制保护起来,不被轻易反编译的话,那么这篇文章应该能帮到你. 介绍# MSIX ...

  2. 用docker 跑 logstash节点 - kibana界面汉化

    0.docker中logstash的配置文件 [root@VM_0_6_centos pipeline]# cat logstash.yml #http.host: "0.0.0.0&quo ...

  3. 【0.1】mysql版本升级(5.6升级到5.7)

    目录 [1].升级操作 [2].mysql 5.6安装(二进制) [3].mysql 5.7安装(二进制) [1].升级操作 核心步骤 [1.1]停止mysql 5.6 [1.2]把环境变量引用到My ...

  4. (public丶private丶protected) 的理解

    public(公有):公有的类成员可以在任何地方被访问. protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问. private(私有):私有的类成员则只能被其定义所在的类访 ...

  5. 云数据库 Redis 版

    首先观看视频简介 云数据库 Redis 版是一项易于部署和管理的按需数据库服务,与 Redis 协议兼容.云数据库 Redis 版通过从内存缓存中检索数据而提供高速数据读写功能,并通过同时使用内存和硬 ...

  6. Intellij IDEA神器好用到飞起来的配置

    ​   IDEA 全称 IntelliJ IDEA,是java编程语言开发的集成环境. IntelliJ在业界被公认为最好的java开发工具之一, 尤其在智能代码助手.代码自动提示.重构.J2EE支持 ...

  7. Navicat 连接数据库避免中文显示乱码问题解决

    在使用Navicat Premium连接数据库进行操作时,为避免出现中文乱码的问题解决: 1.连接SQL Server 在新建数据库时,常规 设置 排序规则 为 Chinese_PRC_CS_AS_W ...

  8. # Tallest Cows(差分)

    Tallest Cows(差分) n头牛,给出最高牛的位置和身高,其他牛身高未知,给出m对相对关系,表示可以相互看见当且仅当他们中间的牛都比他们矮.求每头牛身高最大值是多少. 差分数组的性质:前缀和为 ...

  9. C++实用模板 | 党办大活动计分

    #include <iostream> #include <cmath> #include <cstring> #include <string> #i ...

  10. jquery的ajax方法使用application/json出现400错误码的解决方案

    400说明是客户端错误,将contentType默认的application/x-www-form-urlencoded改成application/json就出现错误,说明传输的数据不是JSON. 解 ...