Redis

2021年11月23日 阅读数:3
这篇文章主要向大家介绍Redis,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

为何Redis能这么快node

100000 + QPS (QPS即query per second, 每秒内查询次数)react

1) 彻底基于内存,绝大部分的请求时纯粹的内存操做,执行效率高。nginx

(采用单进程单线程的KV数据库,有C语言编写,将数据存储在内存中。 读写内存的时候,都不会受到硬盘和IO的限制。)web

2) 数据结构简单,对数据操做也简单redis

3) 采用单线程,单线程也能处理高并发请求,想多核也可启动多实例。算法

由于是单线程,对同一个键进行读写操做,就不会有并发的问题,避免了频繁的上下文切换和锁竞争,是的Redis处理效率更高。数据库

单线程也能处理高并发(并发不是并行,并行意味着服务器能同时处理几个事情,具备多个计算单元。而并发性IO流意味着同一个计算单元能处理多个客户端的流请求)设计模式

4) 使用多路I/O复用模型,非阻塞IO缓存

 

 

多路I/O复用模型安全

FD: File Descriptor,文件描述符

一个打开的文件经过惟一的描述符进行引用,该描述符是打开文件的元数据到文件自己的映射。(文件描述符用一个整数描述)

 

传统的阻塞I/O模型

当使用Read或者Write对一个文件描述符FD进行读写时,若是当前FD不可读或者不可写时,整个Redis服务就不会对其它的操做进行响应,致使不可用。

 

 

 

多路I/O复用模型

Select系统调用

 

 Selector可以监控多个文件描述符的可读可写状况,返回可读可写的文件描述符个数。监听的任务交给Selector,系统去作别的事情,这样就不会被阻塞了。

 

Redis采用的I/O多路复用函数: epoll/kqueue/evport/select ?

 这些函数,如何选择

   因地制宜

  优先选择时间复杂度为O(1)的I/O多路复用函数做为底层实现

  以时间复杂度为O(n)的select做为保底

  基于react设计模式监听I/O事件

 

 Redis底层数据类型基础

一、简单动态字符窜

二、链表

三、字典

四、跳跃表

五、整数集合

六、压缩列表

七、对象

 

 

17九、什么是Redis? 都与哪些使用场景?

Redis是一个使用C语言开发的高速缓存数据库。

Redis使用场景

1) 记录帖子点赞数、点击数、评论数;

2) 缓存近期热帖

3) 缓存文章详情

4) 记录用会话信息。

 

180、Reids有哪些功能 ?

1) 数据缓存功能

2) 支持分布式锁的功能

3) 支持数据持久化

4) 支持事务

5) 支持消息队列

 

18一、Redis和memcache有什么区别

存储方式不一样: memecache把数据所有存在内存中,断电后会挂掉,数据不能超过内存大小; Redis有部分存在硬盘上,这样能保证数据的持久性。

数据支持类型: memecache对数据类型支持相对简单; Redis支持丰富的数据类型(Set,list等)

使用底层模型不一样: 它们之间底层实现方式,以及客户端之间的通讯的应用协议不同,Redis本身构建了vm机制,由于通常的系统调用系统函数的话,会浪费必定的实际去移动和请求。

value值大小不一样: Redis最大能够达到1gb; memecache只有1mb。

是否支持主从同步:Redis支持主从, memcache不支持主从

是否支持分片: Redis支持片(Redis 3.0开始),memcache支持分片(分片:简单理解为将大数据分配到多个物理节点上)

如何选择Redis和memcache?

对数据有持久化需求或者对数据结构和处理有高级要求的应用应选择Redis。

 

 

 

18二、Redis为何是单线程

由于cup不是Redis的瓶颈,Redis的瓶颈最有多是机器内存和网络带宽。既然单线程容易实现,并且cpu又不会是瓶颈,那就瓜熟蒂落地采用单线程方案。

关于Reids的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。

并且单线程并不表明慢,nginx和nodejs也都是高性能单线程的表明。

 

 

 

18三、什么是缓存穿透?怎么解决?

缓存穿透: 指查询一个必定不存在的数据,因为缓存是不命中时须要从数据库查询,查不到数据则写入缓存,这将致使这个不存在的数据每次请求都要到数据库去查询,形成缓存穿透。

解决方案:最简单粗暴的方法,若是一个查询返回的数据为空(无论是数据不存在,仍是系统故障),咱们就把这个空结果进行缓存,但它的过时时间会很短,最长不超过五分钟。

 

18四、Redis支持的数据类型有哪些?

Redis支持的数据类型: string(字符串),list(列表), hash(字典),set(集合), zset(有序集合)

 

18五、Redis支持的Java客户端有哪些?

Redission, jedis, lettuce等。

 

18六、jedis和Redission有哪些区别?

jedis: 提供了比较全面的Redis命令的支持

Redission: 实现了分布式和可扩展的Java数据结构,与jedis相比Redission的功能相对简单,不支持排序,事务,管道,分区等Redis特性。

 

18七、怎么保证缓存和数据库的一致性?

1) 合理设置缓存的过时时间

2) 新增、更改、删除数据库操做时同步更新Redis,可使用事务机制来保证数据的一致性。

 

18八、Redis持久化有几种方式?

Redis的持久化有两种方式,或者说策略

RDB(Redis Database): 指定的时间间隔能对你的数据进行快照存储。

AOF(Append Only File): 每个收到的写命令都经过write函数追加到文件中。

RDB-AOF混合持久化方式 (Reids 4.0以后) 

 

1) RDB持久化

Redis的保存策略 进入 redis/redis.conf文件

 

save 900 1 : 900秒以内有1条是写入指令,就触发一条快照(理解为进行一次备份)

save 300 10 : 300秒内有10条写入,就触发一次快照

save 60 10000 60秒内有10000条写入,就触发一次快照

 

快照数据保存在dump.rdb文件中,该文件是如何生成的?

SAVE: 阻塞Redis的服务器进程,直到RDB文件被建立完毕(不多被使用)

BGSAVE: Fork出一个子进程来建立RDB文件,不阻塞服务器进程

 

自动触发RDB持久化的方式

根据redis.conf 配置里SAVE  m n定时触发(用的是BGSAVE)

组从复制时,主节点自动触发

执行Debug Reload

执行Shutdown且没有开启AOF持久化

 

RDB持久化缺点:

内存数据的全量同步,数据量大会因为I/O而严重影响性能

可能会由于Redis挂掉而丢失从当前至最近一次快照期间的数据。

 

2)、AOF(Append Only File)持久化,保存写状态

记录下除了查询之外的全部变动数据库状态的指令

以append的形式追加到AOF文件中(增量)

默认redis.conf 中,AOF是关闭的

 

 启动的话,将no改成yes

 

日志重写解决AOF文件大小不断增大的问题(假设对key改动了10次,就好保存10次记录,实际上咱们只须要最新的那次)

用用fork(), 建立一个子进程

子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件

主进程持续将新的变更同时写到内存和原来的AOF里

主进程获取子进程重写的AOF的完成信号,往新AOF同步增量变更

使用新的AOF文件替换旧的AOF文件。

 

RDB和AOF文件共存状况下的恢复流程

 

 

RDB和AOF的优缺点

FDB优势: 全量数据快照,文件小,恢复快

RDB缺点:没法保存最近一次快照以后的数据

AOF优势:可读性高,适合保存增量数据,数据不易丢失

AOF缺点: 文件体积大,恢复时间长

 

 3)RDB-AOF混合持久化方式

BGSAVE作镜像全量持久化,AOF作增量持久化

 

 

18九、Redis怎么实现分布式锁?

Redis分布式锁其实就是在系统里面占一个“坑“, 其余程序也要占“坑”的时候,占用成功了就能够继续执行,失败了就只能放弃或稍后重试。

占坑通常使用sennx(set if not exist) key  value指令,若是key不存在,则建立并赋值。只容许被一个程序占有,使用完调用del释放锁。

 

sennx(set if not exist) key  value

时间复制读 O(1)

返回值: 设置成功,返回1,设置失败,返回0

 

分布式锁须要解决的问题

互斥性: 任一时刻,只能有一个客户端获取锁

安全性: 只有持有该锁的客户端才能删除锁,其它客户端不能删除锁

死锁:  获取锁的客户端由于某些缘由Down机而未能释放锁,其它客户端不再能获取锁而致使的死锁。此时须要由机制避免死锁的发生。

容错:当有些Redis节点Down机的时候,客户端仍然能获取锁和释放锁。

 

 如何解决Setnx长期有效的问题

设置key的生存时间,当key过时时(生存时间为0)

 

 以下面的伪代码

 

 上述伪代码的缺点: 不能保证原则性,如setnx成功了,可是程序down了,expire设置过时没有被执行。

那怎么解决呢? 从redis 2.6.12 之后,就把setnx和expire结合起来

 SET key value [EX seconds] [PX milliseconds][NX|XX]

EX second : 设置键的过时时间为second秒

PX millsecond: 设置键的过时时间为millisecond毫秒

NX: 值在建不存在时,才对键进行设置操做。
XX: 只在键已经存在时,才对键进行设置操做。

SET操做成功完成时,返回OK,不然返回nil

 

 设置为10秒过时,10秒内再次设置,返回nil。 10秒后设置,返回OK

 以下面的伪代码:

 

 这样就能保住原子性了。

 

190、Redis分布式锁有什么缺陷?

Redis分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行若是超出了锁的超时时间,就会出现问题。

 

 

19一、Redis如何作内存优化?

尽可能使用Redis的散列表,把相关的信息放到散列表里存储,而不是把每一个字段单独存储,这样能够有效的减小内存使用。好比将web系统的用户对象,应该放到散列表里面再总体存储到Redis,而不是把用户、姓名、年龄、密码、邮箱等字段分别设置能够进行存储。

 

19三、Redis常见的性能问题有哪些? 该如何解决?

主服务器写内存快照,会阻塞主线程的工做,当快照比较大时对性能影响是很是到的,会间断性暂停服务,因此主服务最好不要写内存快照。

Redis主从复制的性能问题,为了主从复制的速度和链接的稳定性,主从库最好在同一个局域网内。

 

 

从海量Key里查询出某个固定前缀的Key?

摸清数据规模,即问清楚边界。

方法一、KEYS pattern: 查找全部符合给定模式pattern的key

 

 keys指令一次性返回全部匹配的key

缺点: 键的数量过大会使服务卡顿

 

方法2:

SCAN cursor [MATCH pattern] [COUNT count]

基于游标的迭代器,须要基于上一次的游标延续以前的迭代过程

以0做为游标开始一次新的迭代,直到命令返回游标0完成一次遍历

不保证每次执行都返回某个给定数量的元素,支持模糊查询

一次返回的数量不可控,只能是大几率符合count参数

 

 

 

查看key的个数 : dbsize

 

大量的key同时过时注意事项

集中过时,因为清除大量的key很耗时,会出现短暂的卡顿现象

解决方案:在设置key的过时时间的时候,给每一个能够加上随机值 (不一样的key,设置不一样的过时时间)

 

如何使用Redis作异步队列?

使用List最为队列,RPUSH生产消息,LPOP消费消息

BLPOP Key [key ...] timeout: 阻塞直到队列有消息或者超时

 

 

 BLPOP的缺点: 只能供一个消费者消费

 

pub/sub :主题订阅模式

发送者(pub)发行消息,订阅者(sub)接收消息

订阅者能够订阅任意数量的频道(主题)

 subscript myTopic: 监听myTopic

public myTopic hello : 生产消息hello

以下图:两个订阅者都收到了

 

pub/sub 缺点:

消息的发布时无状态的,没法保证可达。

解决这个问题,要使用专用的消息队列,如kafka等。

 

使用Pipeline的好处

Pipeline和Linux的管道相似

Redis基于请求/响应模型,单个请求处理须要一一应答

Pipeline批量执行指令,节省屡次IO往返的时间

有顺序依赖的指令建议分批发送

 

Redis的同步机制

分为全同步过程和增量同步过程

 

 1) 全同步过程

Salve发送sync命令到Master

Master启动一个后台进程,将Redis中的数据快照保存到文件中(BGSAVE)

同时Master将保存数据快照期间接收到的写命令缓存起来(增量数据缓存)

Master完成写文件操做后,将该文件发送给Salve

Salve接收文件保存到磁盘中,而后加载文件到内存中去恢复数据快照,使用新的AOF文件替换掉旧的AOF文件

Master将这期间收集的增量写命令发送给Salve端。

以后全部写操做都在Master上进行,全部读操做在Salve上进行

 

2)增量同步过程

Master接收到用户的操做指令,判断是否须要传播到Slave

将操做记录追加到AOF文件

将操做传播到其余Slave: 一、对齐主从库(确保从数据库是该操做对应的数据库)  2) 往响应缓存写入指令

将缓存中的数据发送给Slave

 

Redis Sentinel

解决主从同步Master宏机后的主从切换问题。

(Redis主从的缺点,当Master挂了以后,Redis不能对外提供写操做。

而Redis Sentinel解决了这个问题)

其自己是一个独立的进程,它能监控多个Master和Slave集群,发现Master Down机后自动切换

主要功能有以下几点:

监控: 检查主从服务器是否运行正常

提醒:经过API向管理员或者其它应用程序发送故障通知。

自动故障迁移: 主从切换

 

Redis的集群原理

如何从海量数据快速找到所需?

分片: 按照某种规则去划分数据,分散存储在多个节点上

常规的按照哈希划分没法实现节点的动态增减(根据哈希值与节点数求模,可是节点增减或减小后,形成大量的key不被命中)。为了解决该问题,Reids引入了一致性哈希算法

一致性哈希算法:

1) 对2^32取模,将哈希值空间组织成虚拟的圆环

 

 2)将数据key使用相同的函数Hash计算出哈希值

 

 假设对四台服务器的IP或者主机名做为关键字进行哈希,这样每台服务器就能取得在哈希环上的位置。如上图。

接下来对数据key使用刚才一样哈希函数计算哈希值,并肯定此数据在环上的位置。

 

 如对象A计算出哈希环的位置,而后顺时针落到了服务Node A上。

同理对象B计算出哈希环的位置,而后顺时针落到了服务Node B上。

这样作的好处:

假设Node C down机了,对象A, B, D都不会受到影响, 对象C将会保存到Node D上。

 

 

假设增长了Node X,对象A, B, D都不会受到影响,对象C将会保存到Node X上。

 

 

 Hash环缺点: Hash环的数据切斜问题(指的是大部分数据被缓存在一台服务器上)

 

引入虚拟节点解决数据倾斜问题

为了解决Hash环的数据切斜问题,引入虚拟节点

 

能够在服务器IP或者主机名后面增长编号

将Node A变成Node A#1, Node A#2, Node A#3

 将Node B变成Node B#1, Node B#2, Node B#3