1. redis常见的数据类型:string,list,set,hash,sorted set
  • redis使用SDS(simple dynamic string)作为默认字符串表示。
struct sdshdr{ 
//记录buf数组中已使用字节的数量 
//等于 SDS 保存字符串的长度 
int len; 
//记录 buf 数组中未使用字节的数量 
int free; 
//字节数组,用于保存字符串 
char buf[];
}

它不会出现字符串变更造成的内存溢出;获取字符串时间复杂度为1;二进制安全,字节数组保存的是二进制数据而不是字符,使用len而不是空字符来判断字符串是否结束;空间预分配(小于1M的翻倍,超过1M的多分配1M),惰性空间释放free字段,会留空间来防止多次多重分配内存。

  • List:用链表(或ziplist)实现;双端(有prev和next指针)、无环、带head和tail指针;带链表长度计数器len;多态:可以通过dup复制链表节点值,free释放链表节点值,match判断值是否相等。
  • Hash:
  1. 使用哈希表(或ziplist)实现,里面保存了指向哈希表节点数组的指针、哈希表的大小、已有节点对数量和sizemask;
  2. 哈希表的节点是一个单链表节点;
  3. 使用hash函数(MurmurHash算法)来计算key的hash值,和sizemask相与计算出索引,采用链地址法来解决键冲突,新节点被放在已有节点的前面;
  4. rehash:为字典指针ht[1]分配空间,大小取决于ht[0]的键值对数量,扩展操作是扩展到第一个大于ht[0].used*2的,收缩操作是缩小到第一个大于等于ht[0].used的;将ht[0]的键值对rehash后,释放ht[0],将ht[1]设置为ht[0]
  5. 渐进式rehash:为ht[1]分配空间,在字典中维持一个索引计数器rehashidx,设置为0;在程序每次对字典删查改(增会直接在ht[1]内执行)时,会将键值对rehash到ht[1]后,增加rehashidx,将所有的键值对都被rehash后,将rehashidx设置为-1,表明操作完成。
  • Set:使用hashtable(或inset)来实现,值为NULL
  • Sorted Set:使用hashmap和skipList来保持数据存储和有序,hashmap放成员到score的映射,skiplist放按照score排序的成员。skiplist通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
  1. redis单线程执行却依然很快的原因:纯内存操作,避免大量访问磁盘数据,不收到硬盘IO的限制;单线程也没有上下文的切换和加锁上锁,减少了对CPU的消耗;采用非阻塞IO多路复用机制。
  2. key过期删除:定时删除:设置timer,内存友好,但会占用CPU时间;惰性删除:CPU时间友好,但内存不友好;定期删除:CPU友好,内存也友好,但是难以确定删除操作执行的市场和频率;
  3. 旧版复制:分为同步(将从服务器状态更新到主服务器状态的操作)和命令传播(当主服务器数据库状态被修改,导致主从不一致,让主从重新一致的操作)。
  4. 旧版同步(sync):slave向master发送sync命令,master收到后执行BGSAVE生成RDB文件,salve载入文件,更新状态;master将缓冲区的写命令发送给slave,slave执行。
  5. 新版复制(PSYNC):分为完整重同步和部分重同步,前者和sync步骤一样,后者用于处理断线后重复制的情况,slave只执行在断线期间的写命令。
  6. 部分重同步是靠master和slave的复制偏移量、master的复制积压缓冲区和服务器的run ID来实现:通过对比主从服务器的复制偏移量来知道是否主从一致;积压缓冲区是一个固定长度的队列,master传播写命令时,不仅发送给slave,还会发送给复制积压缓冲区,保留着字节值和偏移量,如果slave断线重连后的offset依然在复制积压缓冲区内,会执行部分重同步,否则会执行完整重同步。run ID是服务器启动时生成的一个40位的十六进制字符,psync时master会将自己的运行ID传给slave,slave将其保存。断线重连后,slave会将这个ID传给master,如果一致就可以尝试执行部分重同步,否则就执行完整重同步。之后master执行一个写命令,会同步给slave
  7. 心跳检测:发送slave的复制偏移量,检测主从服务网络连接状态,辅助实现min_slaves,检测命令丢失。
  8. redis的高可用性:sentinel(哨兵)监视主从服务器,并在master下线时,将一个slave变为master。
  9. 集群:通过分片来进行数据共享,提供复制和故障转移功能。在集群中有若干个节点(node),当数据库中16384个槽都有节点在进行管理,这个集群进入上线状态。
  10. 当客户端向节点发送数据库有关的键命令时,节点会计算出属于哪个槽,如果是自己会直接执行命令,如果不是,会像客户端返回MOVED错误,并redirect到正确的节点再次发送相同指令。当节点A正在迁移槽到节点B,那么当A没找到属于该槽的key时,会返回ASK错误,引导客户端去节点B找到这个key。
  11. 集群分为master和slave,master处理槽,而slave复制master并在master下线后称为master。集群内的节点会通过互发消息来通信,包括MEET、PING、PONG、PUBLISH和FAIL,也通过判断对方节点是否下线,超过半数以上的节点认为一个主节点下线后,会将这个节点标记为下线并向集群广播FAIL,该节点的一个slave被选中称为master,并向集群广播一条pong消息。
  12. 事务:通过MULTI、EXEC来实现事务功能,Discard取消事务,WATCH监视一个或多个key
  13. 事务特征:redis不支持事务回滚机制,即使事务队列中某个命令执行时出现了错误,后续的命令也会继续执行。如果执行事务的过程中,出现停机,之后的语句都不会被执行;隔离性是由单线程执行保证的;当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。
  14. 缓存淘汰策略:1) 先进先出算法 2)最近使用最少(Least Frequently Used) 3)最长未被使用(Least Recently Used),存在热点数据时,LRU效率很好,但偶发性周期性的批量操作会导致LRU命中率下降,缓存污染比较严重。
  15. 缓存击穿:频繁请求查询系统中不存在的数据导致; 处理方法:
    (1):cache null策略,查询反馈结果为null仍然缓存这个null结果,设置不超过5分钟过期时间
    (2):布隆过滤器,所有可能存在的数据映射到足够大的bitmap中 google布隆过滤器:基于内存,重启失效不支持大数据量,无法在分布式场景 redis布隆过滤器:可扩展性,不存在重启失效问题,需要网络io,性能低于google
  16. 缓存雪崩:同一时间大量缓存失效; 处理方法:
    (1):缓存数据增加过期标记
    (2):设置不同的缓存失效时间
    (3):双层缓存策略C1为短期,C2为长期
    (4):定时更新策略
  17. RDB和AOF:
  • RDB:对于master,载入RDB文件时会只保存未过期的key,对于slave,会保存所有的key,因为主从同步时,slave数据库会被清空,所有没有什么影响。
  • RDB持久化:通过SAVE(阻塞)和BGSAVE(非阻塞)命令将某个时间点上的数据库状态以压缩二进制文件的形式保存到RDB文件中,保存到磁盘里。BGSAVE是fork一个子进程,创建后通过copy on write来和父进程桐乡读写服务,并逐渐将写进页面的数据和父进程分开来。服务器启动自动载入RDB文件
  • AOF:AOF文件载入是创建一个不带网络连接的伪客户端,从AOF文件中读取一条写指令并执行,直到所有写命令执行完毕。
  • AOF的重写:为了解决AOF文件体积膨胀,会进行AOF文件重写,是通过对数据库状态的读取来实现的:读取key的值,用一条命令来记录键值对。为了解决AOF重写期间阻塞线程,会新建一个子进程来执行AOF重写,但缺点是重写期间的数据库状态变化没办法被子进程获取到。

        为了避免这个问题,服务器在子进程创建后,设置了一个AOF重写缓冲区,父进程执行完写命令后,会将这个命令同时发送到AOF缓冲区和AOF重写缓冲区,当子进程完后才能后,会向父进程发送一个信号,父进程接收到之后,阻塞当前进程,将AOF重写缓冲区的内容写到新的AOF文件中,并且对新AOF文件改名,完成替换。

         AOF重写时也会对过期key进行检查,服务器开启了AOF持久化模式后,对于过期key没有任何处理,当过期key被删除后,会向AOF文件追加DEL命令。

  • AOF持久化:以redis命令的请求协议格式保存的,更新频率比RDB更高,会优先使用AOF来还原数据库。命令会先被写入到缓冲区,再根据设置的appendfsync时间来执行写入AOF文件。

Redis与Memcached的区别

两者都是非关系型内存键值数据库,现在公司一般都是用 Redis 来实现缓存,而且 Redis 自身也越来越强大了!Redis 与 Memcached 主要有以下不同:

对比参数

Redis

Memcached

类型

1. 支持内存 2. 非关系型数据库

1. 支持内存 2. 键值对形式 3. 缓存形式

数据存储类型

1. String 2. List 3. Set 4. Hash 5. Sort Set 【俗称ZSet】

1. 文本型 2. 二进制类型

查询【操作】类型

1. 批量操作 2. 事务支持 3. 每个类型不同的CRUD

1.常用的CRUD 2. 少量的其他命令

附加功能

1. 发布/订阅模式 2. 主从分区 3. 序列化支持 4. 脚本支持【Lua脚本】

1. 多线程服务支持

网络IO模型

1. 单线程的多路 IO 复用模型

1. 多线程,非阻塞IO模式

事件库

自封转简易事件库AeEvent

贵族血统的LibEvent事件库

持久化支持

1. RDB 2. AOF

不支持

集群模式

原生支持 cluster 模式,可以实现主从复制,读写分离

没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据

内存管理机制

在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘

Memcached 的数据则会一直在内存中,Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题。但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes,只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了。

适用场景

复杂数据结构,有持久化,高可用需求,value存储内容较大

纯key-value,数据量非常大,并发量非常大的业务

(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型

(2) redis的速度比memcached快很多

(3) redis可以持久化其数据