Redis试卷

  • Redis为什么那么快

单线程、基于内存、纯ANSI C编写、好的存储结构、i/o复用模型

  • 谈谈i/o复用模型和reactor设计模式

主要提供三种模型 select 、poll、epoll 、 evport、kqueue

1、支持一个进程所能打开的最大连接数

select:单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。

poll:poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的。

epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接。

2、FD剧增后带来的IO效率问题

select:因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。

poll:同上

epoll:因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。

3、 消息传递方式

select:内核需要将消息传递到用户空间,都需要内核拷贝动作

poll:同上

epoll:epoll通过内核和用户空间共享一块内存来实现的。

  • Redis的线程模型

  • 设计redis的key时要注意什么

 

  • Redis的数据结构,各种结构特点和用途

String、list、hash、set、zset

String:字典、文本、配置

list:双向链表,支持双向的Pop/Push,可以当消息队列、列表等

hash:key-HashMap 存放属性

set:与列表相似,集合中的元素是无序的,因此不能通过索引来操作元素;集合中的元素不能有重复。Redis还支持多个集合取交集、并集、差集

zset:有序集合,排行榜

  • redis的事务

没有原子性,有隔离性、一致性、持久性

  • Redis内存都有什么

对象内存+缓存内存+自身内存+内存碎片

  • 内存回收策略

1.惰性删除:查询key时若失效了就删除了

2.定时任务删除:定时任务。从失效的key中删除一部分

  • 内存溢出控制策略
  1. LRU算法删除超时属性的值
  2. LRU算法删除值
  3. 随机删除过期的key
  4. 随机删除key
  5. 根据ttl删除将要过期的key
  6. 不删除拒绝写入报错只读

 

  • 简单谈谈redis的数据存储
  1. dictEntry(包含key、value、next)
  2. key是存储在SDS结构中
  3. redisObject:Value(“world”)既不是直接以字符串存储,也不是像Key一样直接存储在SDS中,而是存储在redisObject中。
  4. Redis在编译时便会指定内存分配器;内存分配器可以是 libc 、jemalloc或者tcmalloc,默认是jemalloc。
  • 简单谈谈redisObject
    redisObject中的类型对象类型、内部编码类型、LRU计数时钟、引用计数器、数据指针
  1. type字段表示对象的类型,占4个比特;目前包括REDIS_STRING(字符串)、REDIS_LIST (列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。
  2. encoding表示对象的内部编码,占4个比特。对于字符串,有int、embstr、raw三种编码

  3. lru记录的是对象最后一次被命令程序访问的时间,占据的比特数不同的版本有所不同

  4. 引用计数器refcount:在于对象的引用计数和内存回收

  5. ptr指针指向具体的数据

  • reids中的内部编码
  1. 字符串:int、embstr(小于39字节)、raw(大于39字节),raw要进行2次内存分配、embstr一次就好内存是连续的
  2. 列表LIST:压缩列表(ziplist)或双端链表(linkedlist)
  3. 哈希:压缩列表(ziplist)和哈希表(hashtable)两种。

    压缩列表用于元素个数少、元素长度小的场景;其优势在于集中存储,节省空间;同时,虽然对于元素的操作复杂度也由O(n)变为了O(1),但由于哈希中元素数量较少,因此操作的时间并没有明显劣势。hashtable:一个hashtable由1个dict结构、2个dictht结构、1个dictEntry指针数组(称为bucket)和多个dictEntry结构组成。

  4. 集合:整数集合(intset)或哈希表(hashtable)。
  5. 有序集合:压缩列表(ziplist)或跳跃表(skiplist)。跳跃表是一种有序数据结构,通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。除了跳跃表,实现有序数据结构的另一种典型实现是平衡树;大多数情况下,跳跃表的效率可以和平衡树媲美,且跳跃表实现比平衡树简单很多,因此redis中选用跳跃表代替平衡树。跳跃表支持平均O(logN)、最坏O(N)的复杂点进行节点查找,并支持顺序操作。Redis的跳跃表实现由zskiplist和zskiplistNode两个结构组成:前者用于保存跳跃表信息(如头结点、尾节点、长度等),后者用于表示跳跃表节点。具体结构相对比较复杂,
  • Redis 内存如何优化
  1. 缩减key和value的长度
  2. 如果是整型/长整型,Redis会使用int类型(8字节)存储来代替字符串,可以节省更多空间。因此在可以使用长整型/整型代替字符串的场景下,尽量使用长整型/整型。
  3. 比如Map,List,Set等,这些集合都有一个特点可大可小,根据实际场景来定,一般情况下如果这些集合所包含的Entry不多,并且每个Entry所包含的Value不是很长的情况下,Redis内部使用紧凑格式来存储数据,紧凑格式存储数据在查询场景的算法复杂度是O(N),而类似Map或者Set他们的查询算法复杂度都是O(1)那为什么要这么做呢 ?为了能够节省内存空间,在N很小的时候其实和O(1)没什么区别。所以这里不的不介绍紧凑格式的代表ZIPMap,他的数据结构是这样:
  • RDB文件,如何写入,写入格式

触发分为手动触发和自动触发两种,

手动触发:save(直接保存到磁盘)和bgsave(创建新子进程数据保存到磁盘)

自动触发: sava n m,每过n秒至少操作了m次操作才会保存,具体实现:是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。 

serverCron是Redis服务器的周期性操作函数,默认每隔100ms执行一次;

dirty计数器是Redis服务器维持的一个状态,记录了上一次执行bgsave/save命令后,服务器状态进行了多少次修改(包括增删改);而当save/bgsave执行完成后,会将dirty重新置为0。

流程

1)Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(后面会详细介绍该命令)的子进程,如果在执行则bgsave命令直接返回。bgsave/bgrewriteaof 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。

2)FORK子进程:RDB写入时,会连内存一起Fork出一个新进程(子进程默认会与父进程共享相同的地址空间),遍历新进程内存中的数据写文件,这样就解决了些Snapshot过程中又有新的写入请求进来的问题。这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令 。父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令

3)子进程创建RDB文件:RDB会先写到临时文件,完了再Rename成,这样外部程序对RDB文件的备份和传输过程是安全的。而且即使写新快照的过程中Server被强制关掉了,旧的RDB文件还在。

3)可配置是否进行压缩,压缩方法是字符串的LZF算法,以及将string形式的数字变回int形式存储。
4)动态所有停止RDB保存规则的方法:redis-cli config set save “”

该持久化的主要缺点是定时快照只是代表一段时间内的内存映像,所以系统重启会丢失上次快照与重启之间所有的数据。

  • AOF文件,如何写入,写入格式,如何优化,文件缓存策略

手动触发(bgrewriteaof)和自动触发两种

工作原理:同步命令到 AOF 文件的整个过程可以分为三个阶段:
1、命令写入:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
2、追加AOF缓存:AOF 程序根据接收到的命令数据, 然后每隔一定的时间(比如每隔一秒)将协议内容写入AOF 缓冲区中.
3、文件写入保存:当达到 AOF同步条件,fsync 函数或者 fdatasync 函数会被调用,将 OS Cache 中的数据写入磁盘文件。
   随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

重写流程:

(1)触发重写:超过AOF的阈值,redis fork一个子进程,进行rewrite操作。子进程基于当前内存中的数据,构建日志,开始往一个新的临时AOF文件中写入日志,:temp-rewriteaof-pid.aof,pid为子进程pid。
(2)开启 AOF 重写缓冲区:同时开启 AOF 重写缓冲区,保存这段时间内新增的写指令。
(3)主进程继续写旧文件:之前的 AOF 操作保持,继续正常工:redis主进程,接收到client新的写操作之后,写入AOF 重写缓冲区,同时新的命令日志也继续写入旧的AOF文件appendonly.aof。
(4)AOF重写缓冲区的指令追加新文件: 重写子进程写入temp-rewriteaof-pid.aof文件结束,redis主进程将将 AOF重写缓冲区的指令追加至新AOF文件 temp-rewriteaof-pid.aof末尾。
(5)文件替换:最后用temp-rewriteaof-pid.aof替换旧AOF文件appendonly.aof。

三种 AOF 保存模式

1)AOF_FSYNC_NO :不保存。
2)AOF_FSYNC_EVERYSEC :每一秒钟保存一次、该策略为AOF的缺省策略。。
3)AOF_FSYNC_ALWAYS :每执行一个命令保存一次

 

  • RDB和AOF的优缺点

RDB:启动是加载
1.性能:Snapshot方式的性能是要明显高于AOF方式的,原因有两点:
 2.数据安全:AOF数据安全性高于Snapshot存储

  • 主从复制数据如何同步

全量复制和增量复制

全量数据同步:

先执行一次全同步 — 请求master BgSave出自己的一个RDB Snapshot文件发给slave,slave接收完毕后,清除掉自己的旧数据,然后将RDB载入内存。

增量数据同步:
再进行增量同步 — master作为一个普通的client连入slave,将所有写操作转发给slave,没有特殊的同步协议。

  • 哨兵的工作原理

心跳、主观下线、客观线下

 

  • 集群工作原理

集群发现meet

1)节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。

2)节点A根据CLUSTER MEET命令给定的IP地址和端口号,向节点B发送一条MEET消息。

3)节点B接收到节点A发送的MEET消息,节点B会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。

4)节点B向节点A返回一条PONG消息。

5)节点A将受到节点B返回的PONG消息,通过这条PONG消息节点A可以知道节点B已经成功的接收了自己发送的MEET消息。

6)之后,节点A将向节点B返回一条PING消息。

7)节点B将接收到的节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功的接收到了自己返回的PONG消息,握手完成。

8)之后,节点A会将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与节点B进行握手,最终,经过一段时间后,节点B会被集群中的所有节点认识。

 

集群消息处理clusterProcessPacket

1)更新接收消息计数器

2)查找发送者节点并且不是handshake节点

3)更新自己的epoch和slave的offset信息

4)处理MEET消息,使加入集群

5)从goosip中发现未知节点,发起handshake

6)对PING,MEET回复PONG

7)根据收到的心跳信息更新自己clusterState中的master-slave,slots信息

8)对FAILOVER_AUTH_REQUEST消息,检查并投票

9)处理FAIL,FAILOVER_AUTH_ACK,UPDATE信息

定时任务clusterCron

1)对handshake节点建立Link,发送Ping或Meet

2)向随机几点发送Ping

3)如果是从查看是否需要做Failover

4)统计并决定是否进行slave的迁移,来平衡不同master的slave数

5)判断所有pfail报告数是否过半数

 

心跳数据

 

发送消息头信息Header

1)所负责slots的信息

2)主从信息

3)ip port信息

4)状态信息

发送其他节点Gossip信息

1)ping_sent, pong_received

2)ip, port信息

3)状态信息,比如发送者认为该节点已经不可达,会在状态信息中标记其为PFAIL或FAIL

数据结构

 

clusterNode结构保存了一个节点的当前状态,比如节点的创建时间,节点的名字,节点当前的配置纪元,节点的IP和地址,等等。

1)slots:位图,由当前clusterNode负责的slot为1

2)salve, slaveof:主从关系信息

3)ping_sent, pong_received:心跳包收发时间

4)clusterLink *link:节点间的连接

5)list *fail_reports:收到的节点不可达投票

集群如何通信

redis选举协议

  • 集群中数据如何存储

槽 16348个 ,CRC16(key) % 16384来计算键key属于哪个槽

  • 集群中数据扩容

当槽x从Node A向Node B迁移时,Node A和Node B都会有这个槽x,Node A上槽x的状态设置为MIGRATING,Node B上槽x的状态被设置为IMPORTING。

MIGRATING状态

1)如果key存在则成功处理

2)如果key不存在,则返回客户端ASK,客户端根据ASK首先发送ASKING命令到目标节点,然后发送请求的命令到目标节点

3)当key包含多个命令,

     a)如果都存在则成功处理

     b)如果都不存在,则返回客户端ASK

     c)如果一部分存在,则返回客户端TRYAGAIN,通知客户端稍后重试,这样当所有的        key都迁移完毕的时候客户端重试请求的时候回得到ASK,然后经过一次重定向就           可以获取这批键

4)此时不刷新客户端中node的映射关系

IMPORTING状态

1)如果key不在该节点上,会被MOVED重定向,刷新客户端中node的映射关系

2)如果是ASKING命令则命令会被执行,key不在迁移的节点已经被迁移到目标的节点

3)Key不存在则新建

  • 集群如何处理读写请求(平时和迁移数据)

槽里面的key还未迁移,并且槽属于迁移中

假如k1属于槽x,并且k1还在Node A

4.2 MOVED请求

 

槽里面的key已经迁移过去,并且槽属于迁移完

假如k1属于槽x,并且k1不在Node A,而且槽x已经迁移到Node B

 

4.3 ASK请求

 

槽里面的key已经迁移完,并且槽属于迁移中

假如k1属于槽x,并且k1不在Node A,而且槽x还是MIGRATING状态

 

  • 通信故障如何处理

故障检测

集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此交换各个节点状态信息,检测各个节点状态:在线状态、疑似下线状态PFAIL、已下线状态FAIL。

当主节点A通过消息得知主节点B认为主节点D进入了疑似下线(PFAIL)状态时,

主节点A会在自己的clusterState.nodes字典中找到主节点D所对应的clusterNode结构,

并将主节点B的下线报告(failure report)添加到clusterNode结构的fail_reports链表中

如果集群里面,半数以上的主节点都将主节点D报告为疑似下线,那么主节点D将被标记为已下线(FAIL)状态,将主节点D标记为已下线的节点会向集群广播主节点D的FAIL消息,

所有收到FAIL消息的节点都会立即更新nodes里面主节点D状态标记为已下线。

将node标记为FAIL需要满足以下两个条件:

1.有半数以上的主节点将node标记为PFAIL状态。

2.当前节点也将node标记为PFAIL状态。

多个从节点选主

1)当从节点发现自己的主节点进行已下线状态时,从节点会广播一条

CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到这条消息,并且具有投票权的主节点向这个从节点投票

2)如果一个主节点具有投票权,并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条,CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点成为新的主节点

3)每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持

4)如果集群里有N个具有投票权的主节点,那么当一个从节点收集到大于等于集群N/2+1张支持票时,这个从节点就成为新的主节点

5)如果在一个配置纪元没有从能够收集到足够的支持票数,那么集群进入一个新的配置纪元,并再次进行选主,直到选出新的主节点为止

 

redis 集群方式

  1. 客户端分片
  2. 代理分片
  3.  Redis Cluster

 

1.1 客户端分片

这种方案将分片工作放在业务程序端,程序代码根据预先设置的路由规则,直接对多个Redis实例进行分布式访问。这样的好处是,不依赖于第三方分布式中间件,实现方法和代码都自己掌控,可随时调整,不用担心踩到坑。

这实际上是一种静态分片技术。Redis实例的增减,都得手工调整分片程序。基于此分片机制的开源产品,现在仍不多见。这种分片机制的性能比代理式更好(少了一个中间分发环节)。但缺点是升级麻烦,对研发人员的个人依赖性强——需要有较强的程序开发能力做后盾。如果主力程序员离职,可能新的负责人,会选择重写一遍。

所以,这种方式下,可运维性较差。出现故障,定位和解决都得研发和运维配合着解决,故障时间变长。这种方案,难以进行标准化运维,不太适合中小公司(除非有足够的DevOPS)。
 

1.2 代理分片

这种方案,将分片工作交给专门的代理程序来做。代理程序接收到来自业务程序的数据请求,根据路由规则,将这些请求分发给正确的Redis实例并返回给业务程序。

475525c5f1c364a92d492e865d3045199ad78fa7

这种机制下,一般会选用第三方代理程序(而不是自己研发),因为后端有多个Redis实例,所以这类程序又称为分布式中间件。这样的好处是,业务程序不用关心后端Redis实例,运维起来也方便。虽然会因此带来些性能损耗,但对于Redis这种内存读写型应用,相对而言是能容忍的。

这是我们推荐的集群实现方案。像基于该机制的开源产品Twemproxy,便是其中代表之一,应用非常广泛。

 

1.3 Redis Cluster

在这种机制下,没有中心节点(和代理模式的重要不同之处)。所以,一切开心和不开心的事情,都将基于此而展开。Redis Cluster将所有Key映射到16384个Slot中,集群中每个Redis实例负责一部分,业务程序通过集成的Redis Cluster客户端进行操作。客户端可以向任一实例发出请求,如果所需数据不在该实例中,则该实例引导客户端自动去对应实例读写数据。Redis Cluster的成员管理(节点名称、IP、端口、状态、角色)等,都通过节点之间两两通讯,定期交换并更新。

由此可见,这是一种非常“重”的方案。已经不是Redis单实例的“简单、可依赖”了。可能这也是延期多年之后,才近期发布的原因之一。

这令人想起一段历史。因为Memcache不支持持久化,所以有人写了一个Membase,后来改名叫Couchbase,说是支持Auto Rebalance,好几年了,至今都没多少家公司在使用。

这是个令人忧心忡忡的方案。为解决仲裁等集群管理的问题,Oracle RAC还会使用存储设备的一块空间。而Redis Cluster,是一种完全的去中心化……

本方案目前不推荐使用,从了解的情况来看,线上业务的实际应用也并不多见。

 

 

2. Twemproxy及不足之处

Twemproxy是一种代理分片机制,由Twitter开源。Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个Redis服务器,再原路返回。这个方案顺理成章地解决了单个Redis实例承载能力的问题。当然,Twemproxy本身也是单点,需要用Keepalived做高可用方案。

我想很多人都应该感谢Twemproxy,这么些年来,应用范围最广、稳定性最高、最久经考验的分布式中间件,应该就是它了。只是,他还有诸多不方便之处。

Twemproxy最大的痛点在于,无法平滑地扩容/缩容。这样导致运维同学非常痛苦:业务量突增,需增加Redis服务器;业务量萎缩,需要减少Redis服务器。但对Twemproxy而言,基本上都很难操作(那是一种锥心的、纠结的痛……)。

或者说,Twemproxy更加像服务器端静态sharding。有时为了规避业务量突增导致的扩容需求,甚至被迫新开一个基于Twemproxy的Redis集群。

Twemproxy另一个痛点是,运维不友好,甚至没有控制面板。Codis刚好击中Twemproxy的这两大痛点,并且提供诸多其他令人激赏的特性。
 

3. Codis实践

Codis由豌豆荚于2014年11月开源,基于Go和C开发,是近期涌现的、国人开发的优秀开源软件之一。现已广泛用于豌豆荚的各种Redis业务场景(已得到豌豆荚@刘奇同学的确认,呵呵)。从3个月的各种压力测试来看,稳定性符合高效运维的要求。性能更是改善很多,最初比Twemproxy慢20%;现在比Twemproxy快近100%(条件:多实例,一般Value长度)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、什么是 Redis? 2、Redis 相比 memcached 有哪些优势? 3、Redis 支持哪几种数据类型? 4、Redis 主要消耗什么物理资源? 5、Redis 的全称是什么? 6、Redis 有哪几种数据淘汰策略? 7、Redis 官方为什么不提供 Windows 版本? 8、一个字符串类型的值能存储最大容量是多少? 9、为什么 Redis 需要把所有数据放到内存? 10、Redis 集群方案应该怎么做?都有哪些方案? 11、Redis 集群方案什么情况下会导致整个集群不可用? 12、MySQL 里有 2000w 数据,Redis 只存 20w 的数据, 如何保证 Redis 的数据都是热点数据? 13、Redis 有哪些适合的场景? 14、Redis 支持的 Java 客户端都有哪些?官方推荐用哪个? 15、RedisRedisson 有什么关系? 16、Jedis 与 Redisson 对比有什么优缺点? 17、Redis 如何设置密码及验证密码? 18、说说 Redis 哈希槽的概念? 19、Redis 集群的主从复制模型是怎样的? 20、Redis 集群会有写操作丢失吗?为什么? 21、Redis 集群之间是如何复制的? 22、Redis 集群最大节点个数是多少? 23、Redis 集群如何选择数据库? 24、怎么测试 Redis 的连通性? 25、Redis 的管道有什么用? 26、怎么理解 Redis 事务? 27、Redis 事务相关的命令有哪几个? 28、Redis key 的过期时间和永久有效别怎么设置? 29、Redis 如何做内存优化? 30、Redis 回收进程如何工作的? 31、Redis 回收使用的是什么算法? 32、Redis 如何做大量数据插入? 33、为什么要做 Redis 区? 34、你知道有哪些 Redis 区实现方案? 35、Redis 区有什么缺点? 36、Redis 持久化数据和缓存怎么做扩容? 37、布式 Redis 是前期做还是后期规模上来了再做好?为 什么? 38、Twemproxy 是什么? 39、支持一致性哈希的客户端有哪些? 40、Redis 与其他 key-value 存储有什么不同? 41、Redis 的内存占用情况怎么样? 42、都有哪些办法可以降低 Redis 的内存使用情况呢? 43、查看 Redis 使用情况及状态信息用什么命令? 44、Redis 的内存用完了会发生什么? 45、Redis 是单线程的,如何提高多核 CPU 的利用率? 46、一个 Redis 实例最多能存放多少的 keys?List、Set、 Sorted Set 他们最多能存放多少元素? 47、Redis 常见性能问题和解决方案? 48、Redis 提供了哪几种持久化方式? 49、如何选择合适的持久化方式? 50、修改配置不重启 Redis 会实时生效吗?
### 回答1: Redis 字符串是 Redis 最基本的数据类型。它是一种键值对存储方式,键是字符串类型,值也是字符串类型。 Redis 字符串的底层实现是基于双向链表和字典(dictionary)的。在 Redis ,所有的键值对都存储在一个字典,字典的每一个节点都是一个键值对,同时也是一个双向链表的节点。字典本身是一个哈希表,用于快速查找和插入键值对。 当 Redis 的一个字符串被修改时,Redis 会将旧的字符串值从字典删除,然后将新的字符串值插入到字典。这样,就可以保证 Redis 字符串的原子性,同时也保证了字符串的高效存储。 总结一下,Redis 字符串的底层原理就是基于字典和双向链表实现的键值对存储方式。 ### 回答2: Redis 字符串的底层实现原理是基于简单动态字符串(SDS)和字典(dict)。 简单动态字符串(SDS)是 Redis 底层的字符串实现,它是一个动态配的字符数组,并且可以在 O(1) 复杂度下进行字符串长度的获取和修改。SDS 的结构体包含字符串指针、字符串长度、已配内存长度等字段,通过这些字段可以方便地对字符串进行操作。 字典(dict)是 Redis 底层用于存储字符串键值对的数据结构。在 Redis 字符串,键相当于字符串的名字,值则是存储的实际数据。字典采用哈希表作为底层实现,使用哈希函数将键映射到哈希桶,以提高查找效率。在 Redis ,哈希表的长度会根据实际数据的增加和删除进行动态扩容和缩容,以保证哈希表的平均负载因子不超过一个特定的值。 Redis 字符串的底层实现成为一个 SDS 字符串结构,它与字典结构之间是相互独立的。当一个字符串被确定为一个键或值时,它会被存储在一个 SDSDICT 字典,其键为字符串本身,值则是一个指向 SDS 结构的指针。 总结来说,Redis 字符串的底层实现原理是基于简单动态字符串(SDS)和字典(dict)。SDS 是一个动态配的字符数组,可以方便地进行字符串长度的获取和修改。而字典用于存储字符串键值对,通过哈希表提高查找效率。在 Redis ,字符串被存储在一个 SDSDICT 字典,其键为字符串本身,值为指向 SDS 结构的指针。 ### 回答3: Redis字符串的底层原理是通过使用简单动态字符串(简称SDS)实现的。SDS是Redis自己实现的以C字符串结构为基础的字符串库,它解决了C字符串的一些限制,使得Redis可以支持更多的操作和功能。 在Redis,每个字符串对象都由一个redisObject结构表示,该结构包含了一个指向SDS的指针和其他元数据。SDS结构由以下几部组成: 1. len:记录字符串的长度,即字节数。 2. free:记录SDS结尾未使用的字节数,方便扩展字符串时无需重新配内存。 3. buf:实际的字符数组,用于存储字符串的内容。 Redis字符串对象的底层原理有以下几个特点: 1. 动态扩展:SDS提供了高效的内存扩展机制,当字符串长度增加时,可以动态调整内存大小,避免了频繁的内存重新配操作,提高了性能。 2. O(1)时间复杂度:SDS支持通过偏移量来直接访问字符串的某一位置的字符,所以读取和修改字符串的某一位置的操作时间复杂度为O(1)。 3. 惰性空间释放:当从字符串删除部字符时,SDS并不立即释放所占用的内存,而是通过将free字段增加相应的值来标记该内存已被释放,以备将来再次使用。 4. 兼容C字符串:SDS结构与C字符串之间可以相互转换,方便Redis与其他系统进行兼容。 总的来说,Redis字符串的底层原理是通过使用SDS实现的,SDS提供了高效的内存扩展和访问机制,使得Redis可以高效地处理字符串操作,提高了性能和灵活性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值