Redis高效率原因及数据结构分析

目录
  • 1、什么是redis?它主要用来干什么的?
  • 2、redis为什么这么快?
    • 基于内存存储实现
    • 高效的数据结构
      • 1、SDS简单动态字符串
      • 2、字典
      • 3、跳表
    • 合理的数据编码
    • 合理的线程模型
      • 1、I/O多路复用
      • 2、什么是I/O多路复用?
      • 3、单线程模型
  • 虚拟内存机制
    • Redis的虚拟内存机制是啥呢?

1、什么是redis?它主要用来干什么的?

Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redis被广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。

知道redis是什么后,接下来我们来说一说redis为什么这么快。

2、redis为什么这么快?

我们来一个一个说明!

基于内存存储实现

计算机专业的同学我们都知道内存读写是要比磁盘快很多的,Redis是基于内存实现的数据库,相对于数据存在磁盘的mysql等数据库,省去了磁盘I/O的消耗。

高效的数据结构

我们都知道,mysql索引为了提高效率,选择了B+树的数据结构,对于一个应用场景来说合理的数据结构可以让你的应用或者程序更快。我们来看看Redis的数据结构–内部编码图:

String : 动态字符串SDS
List: 双端链表LinkedList+压缩链表ziplist
Hash: 压缩链表ziplist+字典哈希表hashtable
Set: hashtable(+inset)
Zset: 压缩链表ziplist+跳表skiplist

我们来说一说这几种内部编码:

1、SDS简单动态字符串


我们来和C语言中的char[ ]对比下

字符串长度处理: Redis获取字符串长度,时间复杂度为O(1),而C语言中,需要从头遍历,复杂度为O(N)。

空间预分配: 字符串修改越频繁的话,内存分配就越频繁,就会很消费性能,而SDS修改和空间扩充,会额外分配未使用的空间,减少性能损耗。

惰性空间释放: SDS缩短时,不是回收多余的内存空间,而是free记录下多余的空间,后续有变更,直接使用free中记录的空间,减少分配。

二进制安全: Redis可以存储一些二进制数据,在C语言中字符串遇到'/0'会结束,而SDS中标志字符串结束的是len属性。

2、字典

Redis 作为 K-V 型内存数据库,所有的键值就是用字典来存储。字典就是哈希表,比如HashMap,通过key就可以直接获取到对应的value。而哈希表的特性,在O(1)时间复杂度就可以获得对应的值。

3、跳表

跳表是Redis特有的数据结构,就是在链表的基础上,增加多级索引提升查找效率。
跳表支持平均O(logN),最坏O(N)复杂度的节点查找,还可以通过顺序性操作。

合理的数据编码

Redis 支持多种数据数据类型,每种基本类型,可能对多种数据结构。什么时候,使用什么样数据结构,使用什么样编码,是redis设计者总结优化的结果。

String: 如果存储数字的话,是用int类型的编码;如果存储非数字,小于等于39字节的字符串,是embstr;大于39个字节,则是raw编码。
List: 如果列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码
Hash: 哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable编码。
Set: 如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码。
Zset: 当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码。

合理的线程模型

1、I/O多路复用


多路I/O复用技术可以让单个线程高效的处理多个连接请求,而Redis使用用epoll作为I/O多路复用技术的实现。并且,Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。

2、什么是I/O多路复用?

I/O : 网络 I/O
多路 : 多个网络连接
复用: 复用同一个线程。
IO多路复用其实就是一种同步IO模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出cpu。

3、单线程模型

Redis是单线程模型的,而单线程避免了CPU不必要的上下文切换和竞争锁的消耗。也正因为是单线程,如果某个命令执行过长(如hgetall命令),会造成阻塞。Redis是面向快速执行场景的数据库。,所以要慎用如smembers和lrange、hgetall等命令。

Redis 6.0 引入了多线程提速,它的执行命令操作内存的仍然是个单线程。

虚拟内存机制

redis直接自己构建了VM机制,不会像一般的系统会调用系统函数处理,会浪费一定的时间去移动和请求。

Redis的虚拟内存机制是啥呢?

虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过VM功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。

以上就是Redis高效原因及数据结构分析的详细内容,更多关于Redis的资料请关注我们其它相关文章!

(0)

相关推荐

  • Redis不是一直号称单线程效率也很高吗,为什么又采用多线程了?

    Redis是目前广为人知的一个内存数据库,在各个场景中都有着非常丰富的应用,前段时间Redis推出了6.0的版本,在新版本中采用了多线程模型. 因为我们公司使用的内存数据库是自研的,按理说我对Redis的关注其实并不算多,但是因为Redis用的比较广泛,所以我需要了解一下这样方便我进行面试. 总不能候选人用过Redis,但是我非要问人家阿里的Tair是怎么回事吧. 所以,在Redis 6.0 推出之后,我想去了解下为什么采用多线程,现在采用的多线程和以前版本有什么区别?为什么这么晚才使用多线程?

  • redis单线程快的原因和原理

    Redis之所以执行速度很快,主要依赖于以下几个原因: (一)纯内存操作,避免大量访问数据库,减少直接读取磁盘数据,redis 将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度快: (二)单线程操作,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗: (三)采用了非阻塞I/O多路复用机制 多路复用原理: 用户首先将需要进行IO操作的socket添

  • Redis凭啥可以这么快

    在日常开发中,为了保证数据的一致性,我们一般都选择关系型数据库来存储数据,如 MySQL,Oracle 等,因为关系型数据库有着事务的特性.然而在并发量比较大的业务场景,关系型数据库却又往往会成为系统瓶颈,无法完全满足我们的需求,所以就需要使用到缓存,而非关系型数据库,即 NoSQL 数据库往往又会成为最佳选择. NoSQL 数据库最常见的解释是 non-relational,也有人解释为 Not Only SQL.非关系型数据库不保证事务,也就是不具备事务 ACID 特性,这也是非关系型数据库

  • Redis为什么快如何实现高可用及持久化

    前言 作为Java程序员,在面试过程中,缓存相关的问题是躲不掉的,肯定会问,例如缓存一致性问题,缓存雪崩.击穿.穿透等.说到缓存,那肯定少不了Redis,我在面试的时候也是被问了很多关于Redis相关的知识,但是Redis的功能太强大了,并不是一时半会儿能掌握好的,因为有些高级特性或是知识平时并不会用到. 所以回答的不好,人家就会觉得你对自己平时使用的工具都没有了解,自然就凉凉了.其实很早就有这个打算,打算好好总结一下Redis的知识,但也是由于自己都没有好好的了解Redis呢,所以一直没有开始

  • 为啥Redis使用pipelining会更快

    为啥Redis使用pipelining会更快? 这是一个很考究细节的问题,大部分人都会说:因为减少了网络开销,那么,看如下例子: import time import redis client = redis.Redis(decode_responses=True) count = 10000 def no_pipelining(): for i in range(count): client.set("test:nopp:{}".format(i), i, ex=100) def w

  • Redis高效率原因及数据结构分析

    目录 1.什么是redis?它主要用来干什么的? 2.redis为什么这么快? 基于内存存储实现 高效的数据结构 1.SDS简单动态字符串 2.字典 3.跳表 合理的数据编码 合理的线程模型 1.I/O多路复用 2.什么是I/O多路复用? 3.单线程模型 虚拟内存机制 Redis的虚拟内存机制是啥呢? 1.什么是redis?它主要用来干什么的? Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持

  • 关于redis可视化工具读取数据乱码问题

    先给大家介绍下redis可视化工具读取数据乱码问题. 更改序列化方式即可解决此问题,具体代码如下 @Configuration public class RedisConfig { @Autowired private RedisTemplate redisTemplate; @Bean public RedisTemplate redisTemplateInit() { //设置序列化Key的实例化对象 redisTemplate.setKeySerializer(new StringRedi

  • PHP结合Redis+MySQL实现冷热数据交换应用案例详解

    本文实例讲述了PHP结合Redis+MySQL实现冷热数据交换应用案例.分享给大家供大家参考,具体如下: 场景:某网站需要对其项目做一个投票系统,投票项目上线后一小时之内预计有100万用户进行投票,希望用户投票完就能看到实时的投票情况 这个场景可以使用redis+mysql冷热数据交换来解决. 何为冷热数据交换? 冷数据:之前使用的数据,热数据:当前使用的数据. 交换:将Redis中的数据周期的存储到MySQL中 业务流程 用户进行投票后,首先将投票数据保存到Redis中,这些数据就是热数据,然

  • Redis Cluster集群数据分片机制原理

    Redis Cluster数据分片机制 Redis 集群简介 Redis Cluster 是 Redis 的分布式解决方案,在 3.0 版本正式推出,有效地解决了 Redis 分布式方面的需求. Redis Cluster 一般由多个节点组成,节点数量至少为 6 个才能保证组成完整高可用的集群,其中三个为主节点,三个为从节点.三个主节点会分配槽,处理客户端的命令请求,而从节点可用在主节点故障后,顶替主节点. 如上图所示,该集群中包含 6 个 Redis 节点,3主3从,分别为M1,M2,M3,S

  • window环境redis通过AOF恢复数据的方法

    首先要启动AOF持久化配置,在redis.windows-server.conf配置文件中做出如下更改 ................ appendonly yes # The name of the append only file (default: "appendonly.aof") appendfilename "appendonly.aof" ..................................... # appendfsync alwa

  • SpringBoot整合Redis入门之缓存数据的方法

    目录 前言 为什么要使用Redis呢? 相关依赖 配置 数据库 实体类 RedisConfig Mapper Service接口 Service实现类 测试Redis Controller 前言 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发工作由VMware主持.从2013年5月开始,Redis的开发由Pivotal赞助. 为什么要使用Redis呢? 举个例子,

  • redis缓存数据库中数据的方法

    本文实例为大家分享了redis缓存数据库中数据的具体代码,供大家参考,具体内容如下 将数据库的数据保存到redis缓存 当第一次查询时,缓存没有对应的数据,则会查询数据库,并将数据更新到缓存当缓存中有对应的数据时,则会直接访问缓存,则不查询数据库这样在性能优化上有很大的帮助 ProvinceServiceImpl public class ProvinceServiceImpl implements ProvinceService {     private ProvinceDao dao =

  • C++访问Redis的mset 二进制数据接口封装方案

    需求 C++中使用hiredis客户端接口访问redis: 需要使用mset一次设置多个二进制数据 以下给出三种封装实现方案: 简单拼接方案 在redis-cli中,mset的语法是这样的: 复制代码 代码如下: /opt/colin$./redis-cli mset a 11 b 22 c 333 OK 按照这样的语法拼接后,直接使用hiredis字符串接口redisCommand传递: void msetNotBinary(redisContext *c, const vector<stri

  • 如何高效地向Redis插入大量的数据(推荐)

    最近有个哥们在群里问,有一个日志,里面存的是IP地址(一行一个),如何将这些IP快速导入到Redis中. 我刚开始的建议是Shell+redis客户端. 今天,查看Redis官档,发现文档的首页部分(http://www.redis.io/documentation)有一个专门的主题是讲述"Redis Mass Insertion"的,才知道自己的建议很low. 官方给出的理由如下: Using a normal Redis client to perform mass inserti

  • Quarkus集成redis操作Redisson实现数据互通

    目录 前言 集成redis 复制Redisson序列化 使用 前言 博主所在公司大量使用了redis缓存,redis客户端用的Redisson.在Quarkus集成redis时,博主尝试使用Redisson客户端直接集成,发现,在jvm模式下运行quarkus没点问题,但是在打native image时,就报错了,尝试了很多方式都是莫名其妙的异常.最后决定采用quarkus官方的redis客户端,但是Redisson客户端数据序列化方式是特有的,不是简单的String,所以quarkus中的re

随机推荐