Java面试集锦(一)之Redis

  • 作者: 凯哥Java
  • 面试宝典
  • 时间:2020-08-04 21:24
  • 135人已阅读
简介 1.为啥在项目里要用缓存呢用缓存,主要是俩用途,高性能和高并发高性能image.png高并发image.png2.介绍Redis是一个开源的使用ANSIC语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API的非关系型数据库。传统数据库(关系型数据库)遵循ACID规则。而Nosql(非关系型数据库)(NotOnlySQL的缩写,是对不同于

1. 为啥在项目里要用缓存呢

用缓存,主要是俩用途,高性能和高并发

高性能

8d92a139c06ce5e50f949461ccb122a6.png

高并发
a18aecb1758b6520aa3356ef27342c97.png

2.介绍

Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API的非关系型数据库。

传统数据库(关系型数据库)遵循 ACID 规则。而 Nosql(非关系型数据库)(Not Only SQL 的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称) 一般为分布式,而分布式一般遵循 CAP 定理。

CAP理论

C:consistency(一致性)
A:avalibility(可用性)
P:Partition(分区)-tolerence to partition(分区容忍度)
分区:一个分布式系统,网络不通讯,导致连接不通,系统被分割成几个数据区域
原因:数据不连通了,产生数据分区

影响:
查还好一点
数据修改时,必须要求数据一致--加锁,实现数据一致性【需求要求数据一致性】
数据修改时,可以数据不一致--不用加锁【需求不要求数据一致性】

分区容忍度
数据的一致性要求高,容忍度高,加锁
数据的一致性要求低,容忍度低,可以不加锁
预期结果,保持数据的一致

可用性

请求在一定时间段内都应该有响应
为了解决锁一直加着
CP理论:【一致性+分区】数据的一致性要求高-加锁
AP理论:【可用性+分区】数据的一致性要求低-不加锁

CAP总结
分区是常态,可不避免,三者不可共存
可用性和一致性是一对冤家
一致性高,可用性低
一致性低,可用性高

3. 为什么说redis能够快速执行

  1. 绝大部分请求是纯粹的内存操作(非常快速)

  2. 采用单线程,避免了不必要的上下文切换和竞争条件

  3. 非阻塞IO - IO多路复用

4. Redis支持的数据类型?

String字符串:
格式: set key value
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个键最大能存储512MB。

Hash(哈希)
key=150
value={
“id”: 150,
“name”: “zhangsan”,
“age”: 20
}
hash类的数据结构,主要是用来存放一些对象,把一些简单的对象给缓存起来,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值
格式: hmset name key1 value1 key2 value2
Redis hash 是一个键值(key=>value)对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

List(列表)
key=某大v
value=[zhangsan, lisi, wangwu]
比如可以通过list存储一些列表型的数据结构,类似粉丝列表了、文章的评论列表了之类的东西
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
格式: lpush name value
在 key 对应 list 的头部添加字符串元素
格式: rpush name value
在 key 对应 list 的尾部添加字符串元素
格式: lrem name index
key 对应 list 中删除 count 个和 value 相同的元素
格式: llen name
返回 key 对应 list 的长度
Set(集合)
可以基于set玩儿交集、并集、差集的操作,比如交集吧,可以把两个人的粉丝列表整一个交集,看看俩人的共同好友是谁
格式: sadd name value
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

zset(sorted set:有序集合)

格式: zadd name score value
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
比如说你要是想根据时间对数据排序,那么可以写入进去的时候用某个时间作为分数,人家自动给你按照时间排序了
排行榜:将每个用户以及其对应的什么分数写入进去,zadd board score username,接着zrevrange board 0 99,就可以获取排名前100的用户;zrank board username,可以看到用户在排行榜里的排名

5.什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis 提供了两种持久化方式:RDB(默认) 和AOF RDB内存快照和AOF日志文件
RDB(快照的方式):
生成一个文件,便于复制,移动,性能比较好
rdb是Redis DataBase缩写
功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数
AOF:
Aof是Append-only file缩写
每当执行服务器(定时)任务或者函数时flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作

aof写入保存:
WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件
SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
存储结构:
内容是redis通讯协议(RESP )格式的命令文本存储。
比较:
1、aof文件比rdb更新频率高,优先使用aof还原数据。
2、aof比rdb更安全也更大
3、rdb性能比aof好
4、如果两个都配了优先加载AOF

6. redis的底层数据结构

redis底层有6种数据结构,分别是简单动态字符串(SDS),链表,字典,跳跃表,整数集合,压缩列表。

跳跃表
Redis使用跳跃表作为有序集合键的底层实现之一,若一个有序集合包含的元素数量比较多,或者有序集合中的成员是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。
有序集合使用两种数据结构来实现,从而可以使插入和删除操作达到O(log(N))的时间复杂度。这两种数据结构是哈希表和跳跃表。向哈希表添加元素,用于将成员对象映射到分数;同时将该元素添加到跳跃表,以分数进行排序。
和链表、字典等数据结构被广泛地应用在Redis内部不同,Redis只在两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群结点中用作内部数据结构。除此之外,跳跃表在Redis里面没有其他用途。
跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表是一种随机化的数据,跳跃表以有序的方式在层次化的链表中保存元素,效率和平衡树媲美 ——查找、删除、添加等操作都可以在对数期望时间下完成,并且比起平衡树来说,跳跃表的实现要简单直观得多。
Redis 只在两个地方用到了跳跃表,一个是实现有序集合键,另外一个是在集群节点中用作内部数据结构。

跳跃表的定义

c04e3aa306d7630801bbdd79c45155e2.png


我们先来看一下一整个跳跃表的完整结构:
     Redis 的跳跃表 主要由两部分组成:zskiplist(链表)和zskiplistNode (节点)

7.使用redis有哪些好处?

(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2)支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

8.redis的淘汰机制

Redis提供了下面几种淘汰策略供用户选择,其中默认的策略为noeviction策略:
· noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
· allkeys-lru:在主键空间中,优先移除最近未使用的key。
· volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。
· allkeys-random:在主键空间中,随机移除某个key。
· volatile-random:在设置了过期时间的键空间中,随机移除某个key。
· volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。

9.Master&Slave是什么?

也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机
的master/slaver机制,Master以写为主,Slave以读为主。

它能干嘛
1、读写分离;
2、容灾恢复

哨兵监控
当master出现问题后,奖slaver切换为master

10. redis常见性能问题和解决方案

(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…

这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

11.redis包含三种集群策略

1.主从复制: 在主从复制中,数据库分为俩类,主数据库(master)和从数据库(slave)。
其中主从复制有如下特点:
1.1. 主数据库可以进行读写操作,当读写操作导致数据变化时会自动将数据同步给从数据库
1.2. 从数据库一般都是只读的,并且接收主数据库同步过来的数据
1.3. 一个master可以拥有多个slave,但是一个slave只能对应一个master、

主从复制工作机制
当slave启动后,主动向master发送SYNC命令。master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这 段时间的命令,然后将保存的快照文件和缓存的命令发送给slave。slave接收到快照文件和命令后加载快照文件和缓存的执行命令。

12. Redis 有哪些架构模式?讲讲各自的特点

单机版

932f89350fe530a6cf8778a84666280b.png



特点:简单
问题:
1、内存容量有限 2、处理能力有限 3、无法高可用。

主从复制

7914866a9305a5a2f4d08191a44d5225.png


Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。 只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。

特点:
1、master/slave 角色
2、master/slave 数据相同
3、降低 master 读压力在转交从库
问题:
无法保证高可用
没有解决 master 写的压力

哨兵

995f0e309365daa0ed748012d7b94845.png


Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:

监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。

特点:
1、保证高可用
2、监控各个节点
3、自动故障迁移

缺点:主从模式,切换需要时间丢数据
没有解决 master 写的压力

集群(proxy 型):

a621bf619a82ef8ed23a74136d2df3ce.png


Twemproxy 是一个 Twitter 开源的一个 redis 和 memcache 快速/轻量级代理服务器; Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII 协议和 redis 协议。

特点:
1、多种 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
2、支持失败节点自动删除
3、后端 Sharding 分片逻辑对业务透明,业务方的读写方式和操作单个 Redis 一致

缺点:增加了新的 proxy,需要维护其高可用。
failover 逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预

集群(直连型):

21c0dcc025d02a3a883dd962ae7c43ed.png


从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。

特点:
1、无中心架构(不存在哪个节点影响性能瓶颈),少了 proxy 层。
2、数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
3、可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。
4、高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本
5、实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提升。

缺点:
1、资源隔离性较差,容易出现相互影响的情况。
2、数据通过异步复制,不保证数据的强一致性

13. 使用过Redis分布式锁么,它是怎么实现的?

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!

缓存雪崩和缓存穿透问题解决方案

缓存雪崩
简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。解决办法(中华石杉老师在他的视频中提到过,视频地址在最后一个问题中有提到):
* 事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
* 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
* 事后:利用 redis 持久化机制保存的数据尽快恢复缓存

f9532b85a71724e3f83a992bea284352.png

缓存穿透
简介:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。解决办法: 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。


如何解决 Redis 的并发竞争 Key 问题

所谓 Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同!
推荐一种方案:分布式锁(zookeeper 和 redis 都可以实现分布式锁)。(如果不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能)
基于zookeeper临时有序节点可以实现的分布式锁。大致思想为:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。完成业务流程后,删除对应的子节点释放锁。
在实践中,当然是从以可靠性为主。所以首推Zookeeper。

如何保证缓存与数据库双写时的数据一致性?

你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?
一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况
串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。

14. Springboot与redis的使用

1.

<!-- 引入redis依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.
然后,资源配置文件application.yml(或properties)中加入redis相关配置:

spring:
      redis:
        #redis数据库地址
        host: localhost
       port: 6379
        password: root
        timeout: 1000
        #redis数据库索引,默认0
       database: 1 


3.

这里我们引入了StringRedisTemplate类,这个类也是Java中整合Redis主要使用的操作类。这里首先使用MyBatis向数据库添加一条user数据,如果添加成功,则使用StringRedisTemplate中的opsForValue.set()方法,将该user数据Json格式化后添加到Redis数据库中,以"user : "+该user的id属性值作为键

BoundHashOperations<StringObjectObject> hashOperations = this.stringRedisTemplate.boundHashOps(KEY_PREFIX);
//判断是否存在此K值
if (hashOperations.hasKey(KEY_PREFIX)) {
hashOperations.delete(KEY_PREFIX);
}
seckillGoods.forEach(goods -> hashOperations.put(goods.getSkuId().toString(), goods.getStock().toString()));

}
redisTemplate.opsForValue().set("user : "+user.getUserId(), JsonUtils.objectToJson(user))

redisTemplate.opsForValue().get("user : "+userId)

15.Redis相比memcached有哪些优势?

(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
(2) redis的速度比memcached快很多
(3) redis可以持久化其数据
(4)Redis支持数据的备份,即master-slave模式的数据备份。

https://www.cnblogs.com/wuhen8866/p/11882142.html


Top Top