Redis面经

这也是我最后去冲刺秋招前学的最后一个技术栈了,一年内学习了很多的东西,一直在不断地突破自己,可能也不会有很多人阅读,但是希望大家可以再最后看完这一篇文章!!跟我一起坚持下来。

网站大多数情况其实都是执行读操作,如果每次读的时候我们都要去查询数据库这样就十分的麻烦了,所以说如果我们想减轻压力,提高效率,那么我们就可以使用缓存来保证效率。缓存主要解决的是读的问题。

在数据量很大的时候,MySQL这些关系型数据库就不够用了,因为数据量太大了,而且变化也太快了。

什么是NoSQL?

非关系型数据库:

非结构化,无关联的,非SQL,BASE理论。

1.键值类型(Redis)2.文档类型(MongoDB)3.列类型(HBase)4.Graph类型(Neo4j)存储方式是内存,扩展性是水平扩展。对数据的安全性一致性要求低,对性能要求高。

关系型数据库:

结构化,关联的,SQL查询,ACID(事务的四个基本原则)。存储方式是磁盘,扩展性是垂直扩展。对数据安全性一致性要求高,性能一般。

了解:3V+3高

大数据时代的3V和3高:海量Volume 多样Variety 实时Velocity

大数据时代的3高:主要是对程序员的要求:高并发 高可拓(随时进行水平拆分,机器不够了,可以扩展机器来解决) 高性能(保证用户体验和性能)

NoSQL的四大分类?

KV键值对:

新浪:Redis

美团:Redis+Tair

阿里,百度:Redis+memecache

典型场景:内容缓存。

数据类型:Key指向Value的键值对,通常用hash table来实现。

优点:查找速度快。

缺点:数据无结构化,通常只被当作字符串或者二进制数据。

文档型数据库(bson格式 和 json一样):

MongoDB(一般必须要掌握)

MongoDB是一个基于分布式文件存储的数据库,C++编写的,主要用来处理大量的文档。

MongoDB是一个介于关系型数据库和非关系型数据中中间的产品。是非关系型数据库中功能最丰富,最像关系型数据库的。

ConthDB

列存储数据库:HBase

图形数据库:Neojs

Redis概述:

Redis(Remote Dictionary Server)远程字典服务。C语言编写的,支持网络,可基于内存也可以持久化的日志型,Key-Value数据库,并提供多种语言的API。是当下最热门的NoSQL技术之一,也被称为结构化数据库。

Redis能干什么:

键值(Key-value)型,value支持多种不同数据结构,功能丰富。

单线程,每个命令具备原子性。

低延迟,速度快(基于内存,IO多路复用,良好的编码)。

支持数据持久化。

支持主从集群和分片集群。

支持多语言客户端。

Redis安装过程:

直接从Linux里面操作就行。

1.下载gcc环境:yum install -y gcc tcl

2.然后直接下载redis: wget https://download.redis.io/releases/redis-7.0.11.tar.gz

3.然后解压:tar -zxvf redis-7.0.11.tar.gz

4.进入redis安装目录: cd redis-7.0.11.tar.gz

5.然后进行安装操作: make && make install

6.进入根目录下检查一下是否安装成功了: cd /usr/local/bin 然后查看文件列表 ll

第一种启动方式:

在任意目录下运行redis:redis-server

这个属于前台启动,会阻塞整个会话窗口,窗口关闭或者按下CTRL+C redis才会停止。

第二种启动方式:

从后台启动,先把redis.conf文件保存一下,因为后面要修改配置了。

cp redis.conf redis.conf.bck

然后进去配置文件去修改

vi redis.conf

修改配置的时候如果要搜索关键词,就很简单,直接是ESC然后再按/输入搜索内容就行了。然后保存的时候就是按ESC然后输入:wq再点Enter键就可以了!!!

在里面改一下 bind 0.0.0.0  然后是 Daemonize yes 最后是改requirePass, 然后配置了一个logfile。

如果登录的话就在这个redis的目录下执行:

redis-server redis.conf

然后因为在后台运行,所以要想看有没有成功就要使用:

ps -ef | grep redis

看到了进程,如果想要暂停的话就是:

kill -9 +进程id

第三种启动方式:

开机的时候自己启动。

首先 新建一个系统服务文件:

vi /etc/systemd/system/redis.service

怎么配置的这个文件就去看黑马的视频就行。

然后需要重载系统服务:

systemctl daemon-reload

然后启动:

systemctl start redis

停止:

systemctl stop redis

重启:

systemctl restart redis

查看状态:

systemctl status redis

Redis客户端:

命令行客户端:

redis-cli -h 127.0.0.1 指定要连接的redis节点的IP地址,默认是127.0.0.1

redis-cli -p 6379 指定要连接的redis节点的端口,默认是6379

redis-cli -a 123321 指定redis的访问密码

redis-cli [options] [commonds]

其中commonds就是Redis的操作命令,比如ping就是与redis服务端做心跳测试,服务端正常会返回pong。

不指定commond时,会进入redis-cli的交互控制台。

第一种连接方式:redis-cli -h 192.168.122.1 -p 6379 -a 123321

第二种连接方式:AUTH 123321(密码)

图形化桌面客户端:

这里去下载resp这个软件(rdm),然后记得,通过这种方式连接要关闭防火墙的。

关闭防火墙的命令是:

systemctl stop firewalld.service

这里我图形化界面连接redis失败了,我还是使用命令行把!!!

Redis数据结构介绍:

Redis通用命令:

KEYS:查看符合模板的所有key

但是这个执行的是模糊查询,不建议在生产环境设备上使用,否则压力和开销是非常大的。

示例如下:

DEL:删除一个指定的key

EXISTS:判断key存在与否,如果存在就是1,如果不存在就是0。

EXPIRE:给一个key设置有效期,有限期到期时候该key会被自动删除。 因为redis是基于内存存储的,如果我们一直插入但是不删除,早晚要把内存占满的。

这里面-1代表的就是永久有效。 

基本类型:

String

不管是什么故事,底层都是字节数组形式存储,只不过是编码方式不同而已,字符串类型的最大空间不能超过512m。

SET:添加或者修改已经存在的一个String类型的键值对

GET:根据key获取String类型的value

MSET:批量添加多个String类型的键值对

MGET:根据多个key获取多个String类型的value

INCR:让一个整型的key自增1

INCRBY:让一个整形的key自增并指定步长,例如:incrby num 2 让num值自增2

INCRBYFLOAT:让一个浮点类型的数字自增并指定步长

SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行

如果这个key存在,那么就不执行 返回0

如果这个key不存在,那么就执行 返回1

SETEX:添加一个String类型的键值对,并且指定有效期

set key value ex 10

setex aoteman 10 tailuo

Key的结构:

Redis的key允许有多个单词形成层级结构,多个单词之间用":"隔开,格式如下:

项目名:业务名:类型:id

加入一个项目叫做aoteman,有user和product两种不同类型的数据,可以有如下定义:

user相关的key:aoteman:user:1

product相关的key:aoteman:product:1

如果Value是一个Java对象,例如是一个User对象

KEY                                            VALUE  就是一个JSON字符串

aoteman:user:1                          {"id":1,"name":"Jack","age":21}

aoteman:procuct:1                     {"id":1,"name":"小米11","price":4699}

Hash

Hash类型,也叫做散列,其value是一个无序字典,类似于Java中的HashMap结构。

String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便。

Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD,我总结为Hash结构比String更加的原子化。

Hash类型的常见命令:

HSET key field value: 添加或者修改hash类型key的field的值

HGET key field:获取一个hash类型key的field的值

HMSET:批量添加多个hash类型key的field的值

HMGET:批量获取多个hash类型key的field的值

HGETALL:获取一个hash类型的key中的所有的field和value

HKEYS:获取一个hash类型的key中的所有的field

HVALS:获取一个hash类型的key中的所有的value

HINCRBY:让一个hash类型key字段值自增并指定步长

HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行。

List

Redis中的List类型与Java中的LinkedList类似,可以看作是一个双向链表结构。既可以支持正向检索,也可以支持反向检索。

特征也与LinkedList类似:

有序

元素可以重复

插入和删除块

查询速度一般

可以用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。

List的常见命令有:

LPUSH key element...: 向列表左侧插入一个或多个元素

LPOP key: 移除并返回列表左侧的第一个元素,没有则返回nil

RPUSH key element...: 向列表右侧插入一个或多个元素

RPOP key:移除并返回列表右侧的第一个元素

LRANGE key star end:返回一段角标范围内的所有元素

BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil

BLPOP user2 10            

BRPOP user2 10    

设置的超时时间都是10秒

这里得是另一个工作台去插入数据才行

Set

Redis的Set结构与Java中的HashSet类似,具备与HashSet类似的特征:

无序

元素不可重复

查找快

支持交集,并集,差集等功能

set的常见命令有:

SADD key member....:向set中添加一个或多个元素

SREM key member:移除set中的指定元素

SCARD key:返回set中元素的个数

SISMEMBER key member:判断一个元素是否存在于set中

SMEMBERS:获取set中的所有元素。

SINTER key1 key2...: 求key1和key2的交集。 

SDIFF key1 key2.....:求key1和key2的差集。

SUNION key1 key2.....:求key1和key2的并集。

SortedSet

Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加hash表。

SortedSet具备下列特性:

可排序

元素不重复

查询速度快

因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。

SortedSet类型的常见命令有:

ZADD key score member:添加一个或多个元素到sortedset,如果已经存在则更新其score值。

ZREM key member:删除sortedset中的一个指定元素。

ZSCORE key member:获取sortedset中的指定元素的score值。

ZRANK key member:获取sortedset中的指定元素的排名。

ZCARD key:获取sortedset中的元素个数。

ZCOUNT key min max:统计score值在给定范围内的所有元素的个数。

ZINCRBY key increment member:让sortedset中的指定元素自增,步长为指定的increment值。

ZRANGE key min max:按照score排序后,获取指定排名范围内的元素。

ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素。

ZDIFF,ZINTER,ZUNION:求差集,交集,并集。

所有的排名默认都是升序的,如果需要降序排名则在命令的Z后面添加REV即可。

将班级的下列学生得分存入Redis的SortedSet中:

Jack 85,Lucy 89,Rose 82,Tom 95,Jerry 78,Amy 92,Miles 76

并实现下列的功能:

删除Tom同学

获取Amy同学的分数

获取Rose同学的排名

查询80分以下有几个学生

给Amy同学加2分

查出成绩前3名的同学

查出成绩80分以下的所有同学

那个ZRANGE的写错了,因为他默认是升序,而我们想得到成绩前几名应该是降序:因此如下:

特殊类型:

GEO             

BitMap

HyperLog

Redis中提供了各种语言的客户端:

Jedis:以Redis命令作为方法名称,学习成本低,简单实用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用。

lettuce:Lettuce是基于Netty实现的,支持同步,异步和响应式编程方式,并且是线程安全的。支持Redis的哨兵模式,集群模式和管道模式。

Redisson:Redisson是一个基于Redis实现的分布式,可伸缩的Java数据结构集合,包含了注入Map,Queue,Lock,Semaphore,AtomicLong等强大功能。

1.引入Jedis依赖

<dependency>

         <groupId>redis.clients</groupId>

         <artifactId>jedis</artifactId>

         <version>3.7.0</version>

</dependency>

2.建立连接

3.测试string

4.测试hash

5.释放资源

Jedis连接池:

Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此需要Jedis连接池,代替Jedis直接连接的方式。

然后在Jedis创建的时候: 

SpringDataRedis:

SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis

提供了对不同Redis客户端的整合(Lettuce和Jedis)

提供了RedisTemplate统一API来操作Redis

支持Redis的发布订阅模型

支持Redis哨兵和Redis集群

支持基于Lettuce的响应式编程

支持基于JDK,JSON,字符串,Spring对象的数据序列化及反序列化。

支持基于Redis的JDKCollection实现。 

SpringDataRedis的使用步骤:

1.引入spring-boot-starter-data-redis依赖,然后还要引入common-pool2依赖。

2.在application.yml配置Redis信息。

3.注入RedisTemplate

RedisTemplate可以接受任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的。所以,我们要自己写一个!!!!!

StringRedisTemplate:

为了节省内存空间,我们并不会使用JSON序列化器来处理Value,而是统一使用String序列化器,要求只能存储String类型的key和Value,当我们需要存储Java对象时,手动完成对象的序列化和反序列化就可以了!

Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了自定义RedisTemplate的过程。

普通String字符串存入:

利用序列化存储Java对象:

1.首先要注意,使用JSON工具,new出来一个ObjectMapper,这个当作序列化器。

2.然后也要注意,这个要操作的Java对象要有无参数构造方法才行。

3.而且使用ObjectMapper的前提是得有一个依赖。

缓存更新策略:

三种缓存更新策略:

内存淘汰 :不需要自己维护,利用Redis的内存淘汰机制,当内存不足的时候自动淘汰部分数据。

下次查询时更新缓存。

超时剔除 :给缓存数据添加TTL时间,到期后自动删除缓存,下次查询时更新缓存。

主动更新 :编写业务逻辑,在修改数据库的同时,更新缓存。

低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存,这些基本上是不会修改的。

高一致性需求:主动更新,并以超市提出作为兜底方案。例如店铺详情查询的缓存。

主动更新策略:

Cache Aside Pattern: 由缓存的调用者,在更新数据库的同时更新缓存。(企业用的最多的)

Read/Write Through Pattern:缓存与数据库整合为一个服务,由服务器来维护一致性。调用者调用该服务,无需关心缓存一致性问题。

Write Behind Caching Pattern:这个和Read/Write Through Pattern都是为了简化调用者的开发。调用者只操作缓存,由其他线程异步的将缓存数据持久化到数据库,保证最终一致性。

那么对于企业用的最多的这种方法,有三个问题需要考虑:

1.当数据库更新的时候,我们是删除缓存还是更新缓存?

如果是更新缓存的话,那么数据库更新了100次的话,我们也要操作redis缓存一百次,这样就很没有效果。但是我们可以选择删除缓存,也就是当数据库修改的时候,我们删除掉redis中对应的缓存内容,只有当下次有请求访问数据库的时候,我们再将数据存入缓存当中,这样的话效率比较高。

2.如果保证缓存与数据库的操作的同时成功或失败?

说白了这里表示的就是原子性。

如果是一个单体系统,将缓存与数据库操作放在一个事务中。

如果是分布式系统,利用TCC等分布式事务方案。

3.先操作缓存还是先操作数据库?

先删除缓存,再操作数据库可能会发生的情况:

删除缓存到更新数据库的过程中被其他线程操作。

假设说缓存中存的a=100,数据库中存的a=100,那么现在我要将a改为200,有两个或多个应用线程在工作。就可能会出现一种情况是,线程1将缓存删除了,然后这时候线程2抢夺到了CPU时间片,线程2查询到了缓存的数据是空的,就去数据库中找数据,发现了数据是100,然后将数据取出来,又将100写入了缓存中,然后线程1抢夺到了CPU时间片,又将数据库里面的数据改成了200。那么现在这种情况下,redis缓存中是100,数据库中是200。这就造成了数据不一致的问题。

先操作数据库,再删除缓存。

操作数据库到删除缓存的过程中没有被其他线程操作,但是这整个过程影响了别的线程。

假设说缓存中存的a=100,数据库中存的a=100,那么现在我要将a改为200,有两个或多个应用线程在工作。就可能会出现一种情况是,比如说缓存失效了,那么线程1在查的时候查到了缓存是空的,于是查询数据库,查到了这个数据是100,然后这个时候线程2先修改了数据库值为200,然后将缓存中的数据删掉,但是其实缓存之前已经是空的了。然后线程1又抢夺到了CPU的使用权,然后将数据100存入了缓存中。那么现在这种情况下,redis缓存中是100,数据库中是200。这就造成了数据不一致的问题。

但是其实先操作数据库,再删除缓存这种出错的情况很少,因为线程1的第一步结束,然后线程2抢夺到时间片,但是在短短的时间里面完成了将数据库里面的数据修改并且删除缓存其实可能性很低。线程1的第二部是写入缓存,相对来说更容易实现。

缓存穿透:

客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库,如果客户端一直发这种请求,就会一直打到数据库。所以需要使用一些技术来阻止这种行为。

一.缓存空对象

第一次发送请求的时候达到数据库,然后数据库生成一个null对象放到缓存中。这样以后发送这种请求就会从缓存中找到并且返回对象给客户端,不至于再打到数据库中了。

特点:

1.比较简单但是有额外的内存消耗,存了一堆空对象没什么用。

所以可以第一次打到数据库然后放进缓存的时候给这个空对象设置一个TTL。这样的话过一段时间,这些对象自己就被清楚了,内存空间就又多了。

2.可能造成短期的数据不一致。

我们是真的给数据库里面插入了一条数据,但是给redis返回一个null对象,这样就造成了短期数据不一致,直到缓存中null值消失了,下一次客户端发出请求的时候,才可以将数据库中的值给redis。可以这样解决,就是当数据库插入一条数据的时候,主动将数据存入Redis缓存当中,覆盖掉null值。

二.布隆过滤

在客户端和redis中再加一层布隆过滤器。我理解的布隆过滤器就是一个二进制数组,里面存储的是二进制位,判断数据库中是否有这个数据的时候,并不是说将数据存储在布隆过滤器里面,只是将数据基于某种哈希算法,计算出哈希值然后将哈希值转换为二进制位保存到过滤器里面。如果说不存在,就是真的不存在,但是如果说存在,但是也不一定是肯定存在。

所以他占用内存少,但是存在误判的可能性。Redis中有布隆过滤器的实现。

缓存雪崩:

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案:

针对于大量的缓存key同时失效的问题:

针对于不同的Key的TTL添加随机值。这样的话就不至于大量的缓存key在同一个时间段全部失效了。

针对于Redis服务宕机的问题:

利用Redis集群提高服务的可用性。

给缓存业务添加降级限流策略。

给业务添加多级缓存。

缓存击穿:

缓存击穿问题也叫做热点key问题,就是一个被高并发访问并且缓存重建业务比较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

当一个热点key突然失效的时候,这个时候就会有一个线程去访问数据库,然后找到数据之后尝试写入缓存,但是这个过程中在实际开发的时候,可能不只是从一个数据库中找数据,可能还有涉及到表的连接什么的,很费时间。在这个线程没有将数据写入缓存之前,其他的线程来访问可能也会看到缓存中没有数据,从而就去访问数据库,那么这个过程会给数据库造成很大的压力,因为会有很多很多的线程去访问数据库(因为是热点key)。

两种解决方案:

互斥锁:上了个锁,就能避免多个线程多个请求直接访问数据库了。

很多的线程进入等待,可能是性能差一点的,但是至少不用全部访问数据库给数据库增加压力并且一致性有了保障!

逻辑过期

逻辑过期与互斥锁的区别就是,在存入热点key的时候会规定一个时限,但不是TTL那种自动清除的时限,当一个线程发现缓存里面的数据已经过期了,他就会对数据上锁,然后单独开一个线程去数据库里面读取数据然后写入缓存中,在这个过程中如果有线程尝试读取redis中的数据并且发现已经过期的时候,他也会尝试去数据库读取数据,但是在获取锁的时候会获取失败,就会自动明白,已经有线程正在为我进行数据更新,这个时候这个线程会选择先将旧的数据从redis中读取走,就不用像互斥锁一样,一直让大量的线程处于休眠等待状态。 

所以说逻辑过期的性能更高,但是一致性没有互斥锁高,到底选择哪种方式也是看具体的业务要求。

面试题:

1.Redis是什么?

Redis的全称是Remote Dictionary Server,是一个使用C语言编写的非关系型的键值对数据库。Redis的读写速度是非常快的,因为Redis会将操作的数据存入到内存当中,在项目中Redis数据库常被用来做缓存方向。但是Redis也可以将数据写入磁盘当中,来保证数据不丢失。Redis的操作也是原子性的。

2.Redis的优缺点?

优点:

1.Redis是基于内存的,所以读写速度非常快

2.Redis支持很多种数据类型:比如String,Hash,List,Set,ZSet。

3.Redis支持持久化,Redis支持的持久化机制主要有两个,RDB和AOF两种持久化机制。这样就避免了数据丢失的问题。

4.Redis支持事务。Redis单个的每一个操作都是具有原子性的。同时Redis中还可以将多个操作合并到一起构成一个大的操作,并且保证这个大的操作的原子性。

5.Redis支持主从复制。主节点会自动将数据同步到从节点,可以进行读写分离。

6.Redis命令的处理是单线程的,Redis6.0以后支持多线程,但是多线程只是用于处理网络数据的读写和协议解析的。

缺点:

1.Redis适合的场景是处理少量数据的场景,因为Redis是基于内存的,所以容量会受到物理内存的限制。

2.Redis在线扩容比较困难,当集群容量达到上限的时候,在线扩容是比较困难的。

3.Redis为什么这么快?

1.Redis是基于内存的,如果一个数据库是基于磁盘的话,那么也就是说将数据存储到磁盘中,对数据进行一次操作也就是要进行一次磁盘IO操作,速度肯定就不会太快。但是基于内存的话,数据存储在内存当中,读写速度自然会很快。

2.Redis对它支持的数据类型在底层做了优化,目的就是为了速度快。

3.Redis采用了IO多路复用技术,将数据库的操作都变成了事件,不在网络IO上浪费过多的时间。

4.既然Redis那么快,为什么不用它做主数据库,只用它做缓存?

Redis虽然快,但是存在着很多的限制。

1.数据安全方面,Redis不像主数据库那样有用户认证和访问控制。

2.数据处理方面,Redis只能处理像字符串,哈希,列表等这种简单的数据类型。而很多时候,我们需要使用关系型数据库的表来处理复杂的数据结构。比MySQL中,当我用Java写出了一个Student类,这个类有name,age和gender等属性,那这个时候就可以在MySQL中创建一个对应的表来表示这个student类,一行数据就记录了一个Student对象的信息。但是Redis这种非关系型数据库就做不到这个。

3.数据持久化方面,Redis确实有RDB和AOF这种持久化机制,但是这些也是有限制的,是远远不够的,比如服务器突然崩溃了或者断电了,那么数据存储在了内存当中也就会随之消失了。

4.在事务处理方面,Redis可以处理简单的事务,但是不适合处理复杂的事务,比如跨多个键的事务处理。

因此Redis适合做缓存,当Redis在项目中承担缓存的角色的时候,可以为主数据库减轻负载,承担不少压力。

5.讲讲Redis的线程模型?

Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构分为4部分,多个套接字,IO多路复用程序(常见的类型有select,epoll,kqueue),文件事件分派器,事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。

1.文件事件处理器使用I/O多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。

2.当被监听的套接字准备执行accept,read,write,close等操作的时候,与操作相对应的文件时间就会产生,这时候文件事件处理器就会调用之前与套接字关联好的的事件处理器去处理这些事件。

虽然文件事件处理器以单线程方式运行,但通过使用I/O多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与redis服务器中其他同样以单线程方式运行的模块进行对接,这保证了Redis内部单线程设计的简单性。

6.Redis应用场景有哪些?

1.缓存热点数据,缓解数据库的压力:

这个问题其实就是缓存击穿问题,缓存击穿问题就是一个被高并发访问的并且缓存重建业务有一些复杂的key失效了,导致大量的访问直击数据库,给数据库造成巨大压力的情景。

面对缓存击穿问题,有两种解决方法,首先是互斥锁,就是说当一个线程访问缓存中的热点key的时候,发现key已经失效了,这时候这个线程就会去访问数据库,尝试将数据库中的内容写入缓存中,在这个操作过程中,这个线程会将数据库上一个互斥锁,那么当其他大量的线程访问数据库的时候,他们就会进入休眠等待的状态。但是这种方式会造成系统资源的浪费,因为会有大量的线程进入等待状态。

另一种方式去解决缓存击穿问题的就是逻辑过期,就是说会对redis里面的一个热点key设置一个过期时间,但不是类似于TTL的那种限制,逻辑过期时间只不过是一个标识罢了。当一个线程访问redis数据库的时候发现该热点key已经过期了,这个时候这个线程就会去访问数据库,并且也会去先获取到互斥锁,在获取互斥锁成功之后,他会单独开启一个线程来完成将数据库里面的内容写入redis的操作。同时他自己返回旧的过期的数据。那么其他线程在访问数据库尝试获取互斥锁的时候会失败,这个时候其他的大量的线程就不会选择进入休眠等待,而是先返回redis数据库里面的过期的旧数据。那么这样的话就是不需要浪费系统资源了,但是却无法高度保证数据的一致性。

2.利用Redis原子性的自增操作可以实现计数器的功能,可以记录,用户点赞数,用户访问数等这些内容。Redis中的INCR,DECR可以保证即使在并发的条件下,也只能有一个操作去更改键的值。所以Redis当作计数器在很大程度上保证了准确性。

3.限速器:可以用于限制某个用户访问某个接口的频率,比如说秒杀场景用来防止用户快速点击所带来的不必要的压力。

7.Memcached和Redis的区别?

1.memcached速度没有Redis快。

2.memcached不支持持久化,重启之后数据就会消失,Redis支持持久化机制。

3.memcached数据结构单一,Redis支持多种数据类型。

4.Redis是单线程的多路IO复用模型,Memcached是使用多线程的非阻塞IO模型。

5.Redis提供主从同步机制和cluster集群部署能力,能够提供高可用服务。Memcached没有提供原生的集群模式。

8.Redis数据类型有哪些?

基本数据类型:

1.String:最常用的一种数据类型,String类型的值可以是字符串,数字或者二进制,但值最大不能超过512MB。

2.Hash:Hash是一个键值对集合。

3.Set:无序去重的集合,Set提供了交集,并集等方法,对于实现共同好友,共同关注等功能特别方便。

4.List:有序可重复的集合,底层是依赖双向链表实现的。

5.SortedSet:有序Set。内部维护了一个score的参数来实现。适用于排行榜和带权重的消息队列等场景。

特殊数据类型:

1.Bitmap:位图,可以认为是一个以位为单位数组,数组中的每个单元只能存0或者1,数组的下标在Bitmap中叫做偏移量。Bitmap的长度与集合中元素个数无关,而是与基数的上限有关。

2.HyperLogLog:HyperLogLog是用来做基数统计的算法,其优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的,并且是很小的。典型的使用场景是统计独立访客。

3.Geospatial:主要用于存储地理位置信息,并对存储的信息进行操作,适用场景如定位,附近的人等。

9.Redis的内存用完了会怎样?

如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)。

也可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。

10.Redis如何做内存优化?

11.Redis事务?

Redis的事务开启是Multi命令,然后后面写上我们需要执行的每一个的单个命令,然后使用EXEC命令进行提交,提交给Redis然后Redis去执行这个事务。WATCH命令会去监听特定的key,也可以监听多个key,当我们的监听的时候,事务就不会执行,如果事务中涉及到了对应的key。直到EXEC之后,才会取消监控。使用UNWATCH命令也可以取消WATCH命令对key的监控,所有的监控锁将会被取消。

12.Redis事务支持隔离性吗?

支持隔离性,Redis是单进程程序。事务会一直执行下去直到所有命令都结束。

13.Redis事务保证原子性嘛?支持回滚嘛?

不保证原子性,不能回滚。Redis的事务中有一个命令出问题,不影响其他命令的执行。

14.持久化机制?

持久化机制就是把内存中的数据写到磁盘中去,防止服务器宕机或者断电情况出现导致数据丢失的问题。

在Redis中有两种支持持久化的方式,分别为RDB和AOF。前者是根据制定规则将内存中的数据存储到磁盘中去,后者是当命令执行完之后将写操作的命令记录下来。一般将两者结合使用。

RDB方式:

是Redis默认的持久化方案,RDB持久化时会将内存中的数据写入到磁盘中,在指定目录下生成一个dump.rdb文件。Redis重启会加载dump.rdb文件恢复数据。

bgsave是主流的触发RDB持久化的方式,执行过程如下:

Redis中的父进程首先会判断当前是否存在正在执行的的子进程,如果存在的话,BGSAVE命令直接返回。如果不存在的话,那么父进程会执行fork操作,fork操作会开启一个子进程,当然在fork操作过程中父进程会进入阻塞状态,但是父进程完成fork操作之后,父进程就会继续接收并处理客户端的请求。fork操作开启的子进程负责将内存中的数据写入特定位置的RDB文件中的,然后将这个新生成的RDB文件替换旧的RDB文件。

当Redis启动的时候就会读取RDB快照文件,将数据从硬盘载入内存,通过这种方式,如果在一段时间后,Redis异常退出了,也只会丢失最近一次持久化以后更改的数据。

触发RDB持久化的方式:

手动触发:用户执行SAVE或者BGSAVE命令,推荐使用BGSAVE命令,因为SAVE命令执行快照会阻塞所有客户端的请求。BGSAVE可以异步的进行快照操作,快照的同时服务器可以继续响应客户端的请求。

被动触发。

AOF方式:

是Redis持久化的主流方式,通过记录下来Redis操作中的每一次写命令,然后当Redis再次启动的时候,就会执行这些写操作来恢复以前的数据。解决了数据持久化的实时性问题,但是Redis中默认没有开启AOF持久化方式。所以需要通过appendonly参数启动,设置 appendonly yes就可以了。开启AOF方式持久化后每执行一条写命令,Redis就会将该写操作的命令写进aof-buf缓冲区。可以将缓冲区中的数据同步到硬盘上:appendfsync everysec操作。

如果AOF文件太大了,那就需要进行AOF文件重写。AOF文件重写是把Redis进程中的数据转化为写命令同步到新的AOF文件过程。

15.RDB和AOF如何选择?

RDB相对来说数据恢复的速度会更快,但是AOF可以更好的保障数据不丢失。配置好了appendfsync everysec之后,就会每秒执行一次同步操作,将数据从缓冲区同步到磁盘中,最多丢失1秒的数据。

如果是缓存数据的话,可以只使用RDB,因为可以承受部分数据的丢失。

如果是用作内存数据的话,要使用Redis持久化,应该将RDB和AOF都开启。

如果只使用AOF的话,要设置appendfsync everysec,因为这样性能比较好,每一秒就写入一次数据到磁盘中。

如果RDB和AOF都开启的话,Redis会优先使用AOF恢复数据,因为AOF保存的文件比RDB文件更完整。RDB操作是BGSAVE是一个比较复杂的操作,不可能经常使用,所以对于RDB模式来说,最新的数据也只是最近一次执行BGSAVE操作后保存下来的数据。而AOF可以做到每秒都将数据写入磁盘中。

16.主从架构以及主从复制的原理?

架构做成主从结构,一主多从。主从结构的主负责写操作并且将数据复制到其他的slave节点,从节点负责读。当主数据库中的数据发生变化的时候,会自动将数据同步到从数据库的。

原理:当启动一个从节点的时候,会发送一个PSYNC命令给主节点。如果从节点是第一次连接主节点的话,主节点会进行一次全量复制,开始生成一个RDB快照文件,在这个过程中主节点会记录下来客户端发送来的写命令在内存中,RDB生成结束之后,将这个快照文件发送给从节点,从节点会先将RDB文件写入本地磁盘然后再从本地磁盘加载到内存。然后主节点会将记录下来的写命令发送给从节点,从节点用来同步数据。如果主节点和从节点之间由于网络问题断开了连接,那么主节点会将部分缺失的数据发送给从节点。

17.哨兵Sentinel?

当客户端连接Redis的时候是先连接哨兵,哨兵会告诉客户端Redis的主节点的地址。如果主节点宕机了,那么哨兵会检查到主节点宕机,这时候会在从节点中推选出一个表现最良好的从服务器来作主节点,并且通过订阅发布模式去通知其他的从节点主服务器更换的信息。

18.过期键的删除策略?

1.被动删除:在访问key时,如果发现key过期了,那就删除key。

2.主动删除:定时清理数据库,清理的时候会将所有数据库遍历一遍。发现有过期的key就删除,没发现的话就继续遍历其他的数据库。

3.内存不够时清理:会通过maxmemory参数给redis设置一个内存上限值,如果超过了这个内存上限值,那么就会按照设定好的内存淘汰机制去淘汰掉一部分key。

19.内存淘汰策略有哪些?

1.volatile-lru:按照LRU算法,清理掉那些已经设置过过期时间的key。

2.allkeys-lru:按照LRU算法,清理掉那些最近没怎么被使用的key。

3.volatile-ttl:在设置好过期时间的数据集中,清理掉那些快要到过期时间的key。

4.volatile-random:在设置好过期时间的数据集中,随机删除,随即淘汰。

5.allkeys-random:在数据集中随即删除,随机淘汰。

6.no-eviction:禁止删除数据,当内存不足时,也不允许删除数据,写入操作会报错。

 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值