Java后端八股文之Redis

文章目录


八股文之Redis篇
在这里插入图片描述

0. Redis是什么?

Redis(Remote Dictionary Server)是一个基于 C 语言开发的开源 NoSQL 数据库(BSD 许可)。与传统数据库不同的是,Redis 的数据是保存在内存中的(内存数据库,支持持久化),因此读写速度非常快,被广泛应用于分布式缓存方向。并且,Redis 存储的是 KV 键值对数据。

为了满足不同的业务场景,Redis 内置了多种数据类型实现(比如 String、Hash、Sorted Set、Bitmap、HyperLogLog、GEO)。并且,Redis 还支持事务、持久化、Lua 脚本、多种开箱即用的集群方案(Redis Sentinel、Redis Cluster)。

1.Redis基本数据类型

Redis中常见的基本数据类型有:String、List、Set、ZSet, Hash。

1.1 String

Redis中的String不同java中的String,Java中的String是final类型的,不可以更改。而Redis中的String,是可以更改的字符串,内部结构类似Java中的ArrayList,是预先分配数组的大小(内存中以字节数组的形式存在的),当容量不够的时候,再进行扩容。
扩容机制是:当字符串的大小小于1M时,每次扩容都是容量加倍。当大小达到1兆时,每次扩容都是增加1M。容量最大不超过512M。)
C 语言里面的字符串标准形式是以 NULL 作为结束符,但是在 Redis 里面字符串不是这么表示的。因为要获取 NULL 结尾的字符串的长度使用的是 strlen 标准库函数,这个函数的算法复杂度是 O(n),它需要对字节数组进行遍历扫描,作为单线程的 Redis 表示承受不起。Redis 的字符串叫着「SDS」,也就是Simple Dynamic String。它的结构是一个带长度信息的字节数组。

struct SDS<T> {
	T capacity; //容量大小
	T len; //数组长度
	byte flags; //标志位
	byte[] content; //具体内容
}

1.2 List

Redis中的list结构,相当于Java中的LinkedList,是一个双向链表数据结构。支持前后顺序遍历,链表结构的插入和删除操作较快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为O(n)。
基本操作指令有:

  • lpush 从左边(头)向链表中插入一个数据
  • rpush 从右边(尾)向链表中插入一个数据
  • lpop 从左边(头)弹出一个数据
  • rpop 从右边(尾)弹出一个数据
  • lrange 从list中取出一部分数据
  • lindex 根据index取出list中的数据
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> lpush names tom
(integer) 1
127.0.0.1:6379> lpush names jerry
(integer) 2
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
127.0.0.1:6379> rpush names Amy
(integer) 3
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
3) "Amy"
127.0.0.1:6379> lindex names 3
(nil)
127.0.0.1:6379> lindex names 2
"Amy"
127.0.0.1:6379>
127.0.0.1:6379> lset names 2 Amy-to-Alice
OK
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
3) "Amy-to-Alice"
127.0.0.1:6379>

1.3 hash

Redis 中的字典相当于 Java 中的 HashMap,内部实现也差不多类似,都是通过 “数组 + 链表” 的链地址法来解决部分 哈希冲突,同时这样的结构也吸收了两种不同数据结构的优点。

127.0.0.1:6379> hset books java "thinking in java"
(integer) 1
127.0.0.1:6379> hset books python "python in action"
(integer) 1
127.0.0.1:6379> type books
hash
127.0.0.1:6379> hgetall books
1) "java"
2) "thinking in java"
3) "python"
4) "python in action"
127.0.0.1:6379> hget books java
"thinking in java"
127.0.0.1:6379> hlen books
(integer) 2
127.0.0.1:6379>

1.3.1 扩容与缩容

扩展和收缩的条件是什么呢?
正常情况下,当 hash 表中元素的个数等于第一维数组的长度时,就会开始扩容,扩容的新数组是原数组大小的 2 倍。不过如果 Redis 正在做 bgsave(持久化命令),为了减少内存过多分离,Redis 尽量不去扩容,但是如果 hash 表非常满了,达到了第一维数组长度的 5 倍了,这个时候就会 强制扩容。

当 hash 表因为元素逐渐被删除变得越来越稀疏时,Redis 会对 hash 表进行缩容来减少 hash 表的第一维数组空间占用。所用的条件是 元素个数低于数组长度的 10%,缩容不会考虑 Redis 是否在做 bgsave。

1.3.2 渐进式 rehash

Redis 中的 hash 存储的value只能是字符串值,此外扩容与 Java 中的 HashMap 也不同。Java 中的HashMap在扩容的时候是一次性完成的,而 Redis 考虑到其核心存取是单线程的性能问题,为了追求高性能,因而采取了渐进式 rehash 策略。

渐进式 rehash 指的是并非一次性完成,它是多次完成的,渐进式 rehash 会在 rehash 的同时,保留新旧两个 hash 结构,所以 Redis 中的 hash(字典) 会存在新旧两个 hash 结构,在 rehash 结束后也就是旧 hash 的值全部搬迁到新 hash 之后,就会使用新的 hash 结构取而代之。

1.4 Set

Redis 的集合相当于 Java 语言中的 HashSet,它内部的键值对是无序、唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值 NULL,集合中的最后一个元素被移除之后,数据结构被自动删除,内存被回收。

127.0.0.1:6379> sadd language Chinese
(integer) 1
127.0.0.1:6379> sadd language English
(integer) 1
127.0.0.1:6379> smembers language
1) "English"
2) "Chinese"
127.0.0.1:6379> scard language
(integer) 2
127.0.0.1:6379> spop language 1
1) "Chinese"
127.0.0.1:6379> smembers language
1) "English"
127.0.0.1:6379>

1.5 Zset

这可能使 Redis 最具特色的一个数据结构了,它类似于 Java 中 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以为每个 value 赋予一个 score 值,用来代表排序的权重。

zset底层实现使用了两个数据结构,第一个是hash,第二个是跳跃列表,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。跳跃列表的目的在于给元素value排序,根据score的范围获取元素列表。

127.0.0.1:6379> zadd language 4 English
(integer) 1
127.0.0.1:6379> zadd language 3 Japense
(integer) 1
127.0.0.1:6379> zrem language Japense
(integer) 1
127.0.0.1:6379> zadd language 3 Japanese
(integer) 1
127.0.0.1:6379> zrange language 0 -1 withscores
1) "Japanese"
2) "3"
3) "English"
4) "4"
5) "Chinese"
6) "5"
127.0.0.1:6379>

2. Redis为什么这么快?

Redis 内部做了非常多的性能优化,比较重要的有下面 3 点:

  • Redis 基于内存,内存的访问速度是磁盘的上千倍;
  • Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用(Redis 线程模式后面会详细介绍到);
  • Redis 内置了多种优化过后的数据类型/结构实现,性能非常高。

请添加图片描述

3. 为什么要使用缓存?

  • 1、高性能: 假如用户第一次访问数据库中的某些数据的话,这个过程是比较慢,毕竟是从硬盘中读取的。但是,如果说,用户访问的数据属于高频数据并且不会经常改变的话,那么我们就可以很放心地将该用户访问的数据存在缓存中。这样有什么好处呢? 那就是保证用户下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。
  • 2、高并发: 一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 Redis 的情况,Redis 集群的话会更高)。

由此可见,直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而,我们也就提高了系统整体的并发。

4. Redis几种使用场景:

    1. 分布式锁:通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。
    1. 限流:一般是通过 Redis + Lua 脚本的方式来实现限流。
    1. 消息队列:Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
    1. 延时队列:Redisson 内置了延时队列(基于 Sorted Set 实现的)。
    1. 分布式 Session :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
    1. 复杂业务场景:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。

5. Redis的Zset底层为什么要使用跳表而不是平衡树、红黑树或者B+树?

  • 平衡树 vs 跳表:平衡树的插入、删除和查询的时间复杂度和跳表一样都是 O(log n)。对于范围查询来说,平衡树也可以通过中序遍历的方式达到和跳表一样的效果。但是它的每一次插入或者删除操作都需要保证整颗树左右节点的绝对平衡,只要不平衡就要通过旋转操作来保持平衡,这个过程是比较耗时的。跳表诞生的初衷就是为了克服平衡树的一些缺点。跳表使用概率平衡而不是严格强制的平衡,因此,跳表中的插入和删除算法比平衡树的等效算法简单得多,速度也快得多。
  • 红黑树 vs 跳表:相比较于红黑树来说,跳表的实现也更简单一些,不需要通过旋转和染色(红黑变换)来保证黑平衡。并且,按照区间来查找数据这个操作,红黑树的效率没有跳表高。
  • B+树 vs 跳表:B+树更适合作为数据库和文件系统中常用的索引结构之一,它的核心思想是通过可能少的 IO 定位到尽可能多的索引来获得查询数据。对于 Redis 这种内存数据库来说,它对这些并不感冒,因为 Redis 作为内存数据库它不可能存储大量的数据,所以对于索引不需要通过 B+树这种方式进行维护,只需按照概率进行随机维护即可,节约内存。而且使用跳表实现 zset 时相较前者来说更简单一些,在进行插入时只需通过索引将数据插入到链表中合适的位置再随机维护一定高度的索引即可,也不需要像 B+树那样插入时发现失衡时还需要对节点分裂与合并。

6.Redis持久化

  • RDB (Redis database,快照)
  • AOF (append-only file)
  • RDB & AOF

6.1 什么是RDB持久化

Redis 可以通过创建快照来获得存储在内存里面的数据在 某个时间点 上的副本。Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。

6.1.1RDB创建快照会阻塞主线程吗?

Redis 提供了两个命令来生成 RDB 快照文件:

  • save : 同步保存操作,会阻塞 Redis 主线程;
  • bgsave : fork 出一个子进程,子进程执行,不会阻塞 Redis 主线程,默认选项。

6.2 什么是AOF持久化

与快照持久化相比,AOF 持久化的实时性更好。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化(Redis 6.0 之后已经默认是开启了),可以通过 appendonly 参数开启:

appendonly yes

开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到 AOF 缓冲区 server.aof_buf 中,然后再写入到 AOF 文件中(此时还在系统内核缓存区未同步到磁盘),最后再根据持久化方式( fsync策略)的配置来决定何时将系统内核缓存区的数据同步到硬盘中的。只有同步到磁盘中才算持久化保存了,否则依然存在数据丢失的风险,比如说:系统内核缓存区的数据还未同步,磁盘机器就宕机了,那这部分数据就算丢失了。AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof。

6.2.1 AOF持久化流程

AOF 持久化功能的实现可以简单分为 5 步:

    1. 命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
    1. 文件写入(write):将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用write函数(系统调用),write将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。
    1. 文件同步(fsync):AOF 缓冲区根据对应的持久化方式( fsync 策略)向硬盘做同步操作。这一步需要调用 fsync 函数(系统调用), fsync 针对单个文件操作,对其进行强制硬盘同步,fsync 将阻塞直到写入磁盘完成后返回,保证了数据持久化。
    1. 文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
    1. 重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。

6.3 如何选择RDB和AOF

6.3.1 RDB比AOF优秀的地方

  • RDB 文件存储的内容是经过压缩的二进制数据, 保存着某个时间点的数据集,文件很小,适合做数据的备份,灾难恢复。AOF 文件存储的是每一次写命令,类似于 MySQL 的 binlog 日志,通常会比 RDB 文件大很多。当 AOF 变得太大时,Redis 能够在后台自动重写 AOF。新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。不过, Redis 7.0 版本之前,如果在重写期间有写入命令,AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。
  • 使用 RDB 文件恢复数据,直接解析还原数据即可,不需要一条一条地执行命令,速度非常快。而 AOF 则需要依次执行每个写命令,速度非常慢。也就是说,与 AOF 相比,恢复大数据集的时候,RDB 速度更快。

6.3.2 AOF比RDB优秀的地方

  • RDB 的数据安全性不如 AOF,没有办法实时或者秒级持久化数据。生成 RDB 文件的过程是比较繁重的, 虽然 BGSAVE 子进程写入 RDB 文件的工作不会阻塞主线程,但会对机器的 CPU 资源和内存资源产生影响,严重的情况下甚至会直接把 Redis 服务干宕机。AOF 支持秒级数据丢失(取决 fsync 策略,如果是 everysec,最多丢失 1 秒的数据),仅仅是追加命令到 AOF 文件,操作轻量。
  • RDB 文件是以特定的二进制格式保存的,并且在 Redis 版本演进中有多个版本的 RDB,所以存在老版本的 Redis 服务不兼容新版本的 RDB 格式的问题。
  • AOF 以一种易于理解和解析的格式包含所有操作的日志。你可以轻松地导出 AOF 文件进行分析,你也可以直接操作 AOF 文件来解决一些问题。比如,如果执行FLUSHALL命令意外地刷新了所有内容后,只要 AOF 文件没有被重写,删除最新命令并重启即可恢复之前的状态。

6.3.3 结论

  • Redis 保存的数据丢失一些也没什么影响的话,可以选择使用 RDB。
  • 不建议单独使用 AOF,因为时不时地创建一个 RDB 快照可以进行数据库备份、更快的重启以及解决 AOF 引擎错误。
  • 如果保存的数据要求安全性比较高的话,建议同时开启 RDB 和 AOF 持久化或者开启 RDB 和 AOF 混合持久化。

7.Redis单线程模型

Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。

  • 文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
  • 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

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

7.1 既然是单线程,那怎么监听大量的客户端连接呢?

Redis 通过 IO 多路复用程序 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否发生。这样的好处非常明显:I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗(和 NIO 中的 Selector 组件很像)。

8.Redis 6.0之后为什么引入了多线程?

Redis6.0 引入多线程主要是为了提高网络 IO 读写性能,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。

虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了,执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。

Redis6.0 的多线程默认是禁用的,只使用主线程。如需开启需要设置 IO 线程数 > 1,需要修改 redis 配置文件 redis.conf:

io-threads 4 #设置1的话只会开启主线程,官网建议4核的机器建议设置为2或3个线程,8核的建议设置为6个线程

另外:

  • io-threads 的个数一旦设置,不能通过 config 动态设置。
  • 当设置 ssl 后,io-threads 将不工作。

开启多线程后,默认只会使用多线程进行 IO 写入 writes,即发送数据给客户端,如果需要开启多线程 IO 读取 reads,同样需要修改 redis 配置文件 redis.conf :

io-threads-do-reads yes

但是官网描述开启多线程读并不能有太大提升,因此一般情况下并不建议开启。

9.过期数据的删除策略?

如果假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢?
常用的过期数据的删除策略就两个(重要!自己造缓存轮子的时候需要格外考虑的东西):

    1. 惰性删除:只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
    1. 定期删除:每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 定期删除+惰性/懒汉式删除 。但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。怎么解决这个问题呢?答案就是:Redis 内存淘汰机制。

10.Redis内存淘汰机制?

    1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。
    1. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。
    1. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。
    1. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
    1. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰。
    1. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

4.0 版本后增加以下两种:

    1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰。
    1. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key。

11.大量key集中过期的问题

对于过期 key,Redis 采用的是 定期删除+惰性/懒汉式删除 策略。定期删除执行过程中,如果突然遇到大量过期 key 的话,客户端请求必须等待定期清理过期 key 任务线程执行完成,因为这个这个定期任务线程是在 Redis 主线程中执行的。这就导致客户端请求没办法被及时处理,响应速度会比较慢。如何解决呢?
下面是两种常见的方法:

  • 给 key 设置随机过期时间。开启 lazy-free(惰性删除/延迟释放) 。
  • lazy-free 特性是 Redis 4.0 开始引入的,指的是让 Redis 采用异步方式延迟释放 key 使用的内存,将该操作交给单独的子线程处理,避免阻塞主线程。

个人建议不管是否开启 lazy-free,我们都尽量给 key 设置随机过期时间。

12. bigkey 大key问题

简单来说,如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey。具体多大才算大呢?有一个不是特别精确的参考标准:

  • String 类型的 value 超过 1MB
  • 复合类型(List、Hash、Set、Sorted Set 等)的 value 包含的元素超过 5000 个(不过,对于复合类型的 value 来说,不一定包含的元素越多,占用的内存就越多)。

13. bigkey 是怎么产生的?有什么危害?

bigkey 通常是由于下面这些原因产生的:

  • 程序设计不当,比如直接使用 String 类型存储较大的文件对应的二进制数据。
  • 对于业务的数据规模考虑不周到,比如使用集合类型的时候没有考虑到数据量的快速增长。
  • 未及时清理垃圾数据,比如哈希中冗余了大量的无用键值对。

bigkey 除了会消耗更多的内存空间和带宽,还会对性能造成比较大的影响。大 key 还会造成阻塞问题。具体来说,主要体现在下面三个方面:

    1. 客户端超时阻塞:由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。
    1. 网络阻塞:每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。
    1. 工作线程阻塞:如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令。大 key 造成的阻塞问题还会进一步影响到主从同步和集群扩容。
      综上,大 key 带来的潜在问题是非常多的,我们应该尽量避免 Redis 中存在 bigkey。

14. 缓存穿透

14.1 什么是缓存穿透?

缓存穿透说简单点就是大量请求的 key 是不合理的,根本不存在于缓存中,也不存在于数据库中。这就导致这些请求直接到了数据库上,根本没有经过缓存这一层,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。

14.2 如何解决缓存穿透?

  • 加强数据校验
  • 缓存无效key
  • 布隆过滤器
  • 接口限流

15.缓存击穿

15.1 什么是缓存击穿?

缓存击穿中,请求的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期)。这就可能会导致瞬时大量的请求直接打到了数据库上,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。

15.2 解决办法

    1. 设置热点数据永不过期或者过期时间比较长。
    1. 针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
    1. 请求数据库写数据到缓存之前,先获取互斥锁,保证只有一个请求会落到数据库上,减少数据库的压力。

16. 缓存雪崩

16.1 什么是缓存雪崩

缓存雪崩描述的就是这样一个简单的场景:缓存在同一时间大面积的失效,导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。 这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。

另外,缓存服务宕机也会导致缓存雪崩现象,导致所有的请求都落到了数据库上。

16.2 如何解决

16.2.1 针对Redis服务不可用情况

  • 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
  • 限流,避免同时处理大量的请求。
  • 多级缓存,例如本地缓存+Redis 缓存的组合,当 Redis 缓存出现问题时,还可以从本地缓存中获取到部分数据。

16.2.2 针对热点缓存失效情况

  • 设置不同的失效时间比如随机设置缓存的失效时间。
  • 缓存永不失效(不太推荐,实用性太差)。
  • 缓存预热,也就是在程序启动后或运行过程中,主动将热点数据加载到缓存中。

16.3 缓存预热如何实现?

常见的缓存预热方式有两种:

  • 使用定时任务,比如 xxl-job,来定时触发缓存预热的逻辑,将数据库中的热点数据查询出来并存入缓存中。
  • 使用消息队列,比如 Kafka,来异步地进行缓存预热,将数据库中的热点数据的主键或者 ID 发送到消息队列中,然后由缓存服务消费消息队列中的数据,根据主键或者 ID 查询数据库并更新缓存。

17.Redis sentinel 哨兵

17.1什么是 Sentinel?有什么用?

Redis Sentinel是一个高可用性解决方案,用于监控和管理Redis集群。Redis是一个流行的开源内存数据库,但是在单个Redis实例故障或宕机时,可能会导致应用程序中断或数据丢失。Redis Sentinel通过提供自动故障检测、故障转移和集群管理等功能,帮助确保Redis集群的高可用性和稳定性。

Redis Sentinel的主要功能包括:

    1. 监控:Sentinel通过定期向Redis实例发送心跳检查,以检测实例的健康状态。它可以监控主节点、从节点和其他Sentinel节点。
    1. 故障检测:当Sentinel检测到Redis实例故障或宕机时,它可以及时通知其他节点,并采取相应的故障处理措施。
    1. 自动故障转移:当主节点发生故障时,Sentinel能够自动将一个从节点升级为新的主节点,并将其他从节点重新配置为复制新的主节点。这样可以确保Redis集群在主节点故障时继续提供服务。
    1. 配置提供:Sentinel可以提供Redis集群的配置信息,包括哪些节点是主节点、从节点以及其他Sentinel节点的信息。
    1. 服务发现:应用程序可以通过向Sentinel节点请求获取可用的Redis主节点地址,从而实现Redis集群的服务发现。

通过使用Redis Sentinel,可以提高Redis集群的可用性和容错性,减少因为单点故障而导致的系统中断。它还简化了Redis集群的管理和维护工作,提供了一种可靠的方式来监控和自动处理Redis实例的故障。

17.2 Sentinel 如何检测节点是否下线?主观下线与客观下线的区别?

Redis Sentinel使用以下机制来检测节点是否下线:

    1. 心跳检测(Heartbeat Checks):每个Sentinel节点会定期向Redis节点发送PING命令,以检测节点的健康状态。如果节点正常运行,它将会回复PONG响应。如果一个Sentinel节点在一定时间内未收到节点的PONG响应,它会将该节点标记为主观下线。
    1. 故障检测(Failure Detection):当一个Sentinel节点将节点标记为主观下线后,它会向其他Sentinel节点发送信息,询问它们对该节点的状态进行验证。其他Sentinel节点也会进行类似的主观下线检测,并回复验证结果。通过Sentinel节点之间的协作,可以达成对节点故障的共识,进一步确认节点是否下线。
    1. Quorum(法定人数):在判断节点是否客观下线时,Redis Sentinel使用了Quorum的概念。Quorum是指在Redis Sentinel集群中,至少需要多少个Sentinel节点认为节点下线才能达成共识。通常情况下,Quorum的值为总Sentinel节点数的一半加一。只有当足够多的Sentinel节点将节点标记为主观下线,并达到Quorum要求时,该节点才会被认定为客观下线。

通过这些机制,Redis Sentinel能够检测节点的健康状态并判断其是否下线。这样可以及时发现故障节点,并采取相应的故障转移措施,确保Redis集群的高可用性。

  • 主观下线(Subjective Down):主观下线是由Sentinel节点自身发起的节点故障检测。每个Sentinel节点会定期向其他节点发送PING命令来检测节点的健康状态。如果一个Sentinel节点在一定时间内无法收到节点的PONG响应,它会将该节点标记为主观下线。主观下线只是一个Sentinel节点认为节点不可用的主观判断,并不是全局性的判定。
  • 客观下线(Objective Down):客观下线是由多个Sentinel节点共同协作来进行的节点故障检测。当一个Sentinel节点将一个节点标记为主观下线后,它会向其他Sentinel节点发送信息,询问它们对该节点的状态进行验证。其他Sentinel节点也会进行类似的主观下线检测,并回复验证结果。当足够多的Sentinel节点都将节点标记为主观下线时,该节点会被认定为客观下线。客观下线是全局性的判定,表示整个Redis Sentinel集群都认为该节点不可用。

主观下线和客观下线的区别在于判断节点是否下线的依据和范围。主观下线是单个Sentinel节点的主观判断,而客观下线是整个Redis Sentinel集群的共识。只有当一个节点被足够多的Sentinel节点标记为主观下线时,它才会被认定为客观下线。这种机制可以减少误判,增加对节点故障的可靠性判断。

17.3 Sentinel 是如何实现故障转移的?

Redis Sentinel通过以下步骤实现故障转移:

    1. 主观下线检测:当一个Sentinel节点检测到主节点(master)下线时,它会将主节点标记为主观下线。主观下线是Sentinel节点的主观判断,表示该节点认为主节点不可用。
    1. 共识确认:Sentinel节点会向其他Sentinel节点询问它们对主节点的状态进行验证。如果足够多的Sentinel节点(达到Quorum要求)都将主节点标记为主观下线,则达成共识,确认主节点的客观下线。
    1. 选举新的主节点:一旦主节点被确认为客观下线,Sentinel节点会开始选举新的主节点。选举过程中,Sentinel会考虑所有可选的从节点(slave)作为潜在的新主节点,并根据一定的策略(例如优先级、复制偏移量等)选择其中一个从节点晋升为新的主节点。
    1. 重新配置从节点:一旦新的主节点选举完成,Sentinel节点会将其他从节点重新配置为复制新的主节点。这样可以确保Redis集群在主节点故障时继续提供服务,并保持数据一致性。
    1. 更新客户端连接:Sentinel节点会将新的主节点信息广播给Redis客户端,以便客户端能够更新连接信息,并将请求发送到新的主节点。

通过这个故障转移过程,Redis Sentinel可以保证在主节点故障时自动选择新的主节点,并使Redis集群继续对外提供服务。这种故障转移机制可以提高Redis集群的可用性和容错性,减少因为主节点故障而导致的系统中断。

17.4 为什么建议部署多个 sentinel 节点(哨兵集群)?

建议部署多个Sentinel节点(哨兵集群)的主要原因是提高Redis集群的高可用性和容错性。以下是几个关键的原因:

  • 故障检测和自动故障转移:通过部署多个Sentinel节点,可以增加对主节点故障的检测准确性。每个Sentinel节点会定期进行心跳检测,并监控主节点的健康状态。当主节点发生故障时,Sentinel集群能够迅速检测到,并自动进行故障转移,选举新的主节点,从而避免主节点故障导致的服务中断。
  • 共识机制和多数投票:多个Sentinel节点之间使用共识机制来判断主节点的健康状态和故障情况。通过多数投票的方式,确保对主节点故障的判定达成一致。如果只有单个Sentinel节点,可能由于网络问题或单点故障导致判断失误,增加了误判的风险。
  • 高可用性决策:在进行故障转移时,多个Sentinel节点能够共同决策选择新的主节点。通过多个节点的共同协作,可以选择具有最高优先级和复制偏移量的从节点作为新的主节点,确保数据的一致性和可用性。
  • 服务发现和客户端重定向:多个Sentinel节点共同提供服务发现功能,客户端可以通过其中任意一个Sentinel节点获取主节点的信息。如果某个Sentinel节点发生故障,客户端仍然可以通过其他可用的Sentinel节点获取到主节点的地址,并进行连接。这样可以提高Redis集群在Sentinel节点故障时的容错性。

综上所述,部署多个Sentinel节点能够增加Redis集群的鲁棒性和可用性,提供故障检测和自动故障转移的功能,并确保在主节点故障时能够快速选举出新的主节点,保持数据一致性和服务的连续性。

17.5 Sentinel 如何选择出新的 master(选举机制)?

在Redis Sentinel的故障转移过程中,选择新的主节点的策略是基于以下因素进行评估和决策:

  • 从节点的优先级(slave priority):每个从节点都可以配置一个优先级,表示成为新主节点的候选顺序。优先级较高的从节点更有可能被选为新的主节点。通过配置从节点的优先级,可以对新主节点的选举顺序进行控制。
  • 从节点的复制偏移量(slave replication offset):复制偏移量是指从节点与主节点之间的复制进度,表示从节点复制的数据量。在选举新的主节点时,Sentinel会选择复制偏移量最大的从节点作为新主节点。这样可以确保新主节点具有最新的数据。
  • 从节点的健康状态:Sentinel会考虑从节点的健康状态,选择处于正常运行状态的从节点作为新主节点。如果某个从节点也出现故障,则不会被选为新的主节点。
  • 额外配置的判断条件:除了上述因素,还可以根据需要进行额外的配置和判断条件。例如,可以配置需要满足的最小从节点数量,或者设置只在特定的网络条件下进行故障转移等。

总体而言,Redis Sentinel会综合考虑从节点的优先级、复制偏移量和健康状态等因素,选择出一个合适的从节点作为新的主节点。这个选择过程旨在保证新的主节点具有数据的一致性和可靠性,并尽可能地提高整个Redis集群的可用性。

17.6 如何从 Sentinel 集群中选择出 Leader ?

17.7 Sentinel 可以防止脑裂吗?

Redis Sentinel在一定程度上可以帮助防止脑裂(Split-Brain)问题的发生,但并不能完全消除脑裂的可能性。脑裂是指在分布式系统中,由于网络分区或其他原因,导致集群中的节点无法正常通信,进而导致数据一致性和可用性的问题。

Redis Sentinel采用了共识机制和多数投票的方式来进行故障检测和故障转移的决策,这有助于减少脑裂的影响。当主节点发生故障时,Sentinel节点会进行共识确认,并选举出一个新的主节点。这个过程要求至少有一半加一(Quorum)的Sentinel节点都达成共识,确保选举结果的一致性。

然而,脑裂问题可能在以下情况下发生:

  • 网络分区:如果Redis Sentinel集群中的节点之间发生网络分区,导致部分节点无法与其他节点正常通信,就有可能出现脑裂。在这种情况下,分区中的节点可能会形成多个子集群,并分别选举出各自的主节点。
  • Sentinel节点故障:如果发生Sentinel节点的故障,可能导致选举过程中无法达成共识,进而引发脑裂。特别是当无法满足Quorum要求时,可能会导致选举结果不一致。

为了减少脑裂的风险,可以考虑以下策略:

  • 适量增加Sentinel节点:增加Sentinel节点的数量可以提高集群的容错性和可用性,减少脑裂的概率。
  • 网络架构设计:设计合理的网络架构,尽量避免网络分区的发生。使用可靠的网络设备和配置,确保节点之间的通信畅通。
  • 定期监控和维护:定期监控Sentinel节点和Redis节点的健康状态,及时发现并处理故障情况。

需要注意的是,脑裂是分布式系统中常见的问题,对于高可用性和数据一致性的要求较高的应用场景,可能需要考虑使用更复杂的解决方案,如分布式一致性协议(如Paxos、Raft)或使用专门的分布式数据库系统来处理脑裂问题。

18. Redis cluster

18.1 为什么需要 Redis Cluster?解决了什么问题?有什么优势?

Redis Cluster是Redis提供的分布式解决方案,用于解决单节点Redis的性能和容量限制,并提供高可用性和数据冗余。它解决了以下问题并带来了一些优势:

    1. 高可用性:Redis Cluster通过将数据分布在多个节点上,实现了数据的冗余存储和故障转移。当节点发生故障时,集群可以自动进行主从切换,保证服务的持续可用性。
    1. 扩展性:单节点Redis在处理大规模数据和高并发请求时存在性能和容量限制。Redis Cluster通过将数据分片存储在多个节点上,允许横向扩展,实现数据和负载的均衡分布。这样可以提高系统的整体性能和吞吐量。
    1. 自动分片:Redis Cluster使用哈希槽(Hash Slot)的概念,将数据按照一定规则分配到不同的节点上。这样每个节点只负责管理一部分数据,简化了数据分片和路由的操作。同时,当集群的节点数发生变化时,Redis Cluster可以自动重新分配哈希槽,实现数据的动态迁移和负载均衡。
    1. 故障转移和自动恢复:Redis Cluster具有自动故障检测和转移的能力。当主节点发生故障时,集群会自动选举出新的主节点,并将从节点升级为主节点。同时,Redis Cluster还支持从节点的自动同步和数据恢复。
  • 5.集群管理和监控:Redis Cluster提供了集群管理和监控工具,方便管理和监控节点的状态、数据分布和性能指标。这些工具可以帮助管理员更好地了解集群的运行状况,进行故障排查和性能优化。

总的来说,Redis Cluster解决了单节点Redis的性能和容量限制,提供了高可用性、扩展性和自动化管理的优势。它适用于需要处理大规模数据和高并发请求的应用场景,如缓存、会话存储和实时数据处理等

18.2 Redis Cluster 是如何分片的?

Redis Cluster使用哈希槽(Hash Slot)的方式进行数据分片。哈希槽是一个固定数量的槽位集合,通常为16384个槽位(0-16383)。每个槽位可以被分配给集群中的一个或多个节点。
数据分片的过程如下:

    1. 哈希函数计算:当客户端发送一个命令到Redis Cluster时,根据命令中指定的键(Key),Redis Cluster会使用哈希函数计算出一个哈希值。
    1. 哈希槽映射:根据哈希值,Redis Cluster会将数据分配到对应的哈希槽上。每个哈希槽都有一个唯一的编号,范围从0到16383。
    1. 路由到节点:每个Redis Cluster节点负责管理一部分哈希槽。当Redis Cluster将数据分配到哈希槽后,它会根据哈希槽与节点的映射关系,将命令路由到负责该哈希槽的节点。
    1. 数据迁移:当节点加入或离开Redis Cluster时,集群会自动进行数据迁移,以保持数据的均衡分布。数据迁移的过程中,Redis Cluster会将相关的哈希槽从一个节点移动到另一个节点,确保数据的一致性和负载均衡。

通过使用哈希槽进行数据分片,Redis Cluster可以实现数据在集群中的均衡存储和路由。每个节点只负责管理一部分哈希槽,从而避免了单节点Redis的性能和容量限制。同时,当集群的节点数量发生变化时,Redis Cluster可以自动进行哈希槽的重新分配,实现数据的动态迁移和负载均衡。这样可以有效地提高系统的扩展性和性能。

18.3 为什么 Redis Cluster 的哈希槽是 16384 个?

Redis Cluster选择16384个哈希槽的数量是出于权衡和设计考虑。
以下是一些理由:

    1. 均衡性:16384个哈希槽提供了足够的细粒度,使得数据在集群中能够均匀分布。如果哈希槽数量较少,可能导致数据在节点之间的分布不均衡,某些节点负载过重,而其他节点负载较轻。
    1. 可扩展性:16384个哈希槽提供了足够的扩展性,可以支持大规模的分布式集群。通过哈希槽的数量,Redis Cluster可以方便地进行数据迁移和负载均衡操作,以适应节点的动态变化。
    1. 简化路由:16384个哈希槽数量的选择也是为了简化路由的计算和管理。客户端只需要计算键的哈希值,并将其映射到对应的哈希槽,然后将命令路由到管理该哈希槽的节点。这样的设计使得路由操作相对简单高效。
    1. 兼容性:选择16384个哈希槽的数量是为了与之前的Redis版本保持兼容性。在Redis Cluster之前,Redis的主从复制(Replication)也使用了16384个槽位的概念。通过保持相同的哈希槽数量,可以更容易地迁移和兼容现有的Redis部署。

总的来说,选择16384个哈希槽数量是为了提供均衡性、可扩展性和简化路由的优势,同时与之前的Redis版本保持兼容性。这个数量经过实践和经验的验证,被认为是一个合理的折衷选择。

18.4如何确定给定 key 的应该分布到哪个哈希槽中?

确定给定键(key)应该分布到哪个哈希槽中,可以通过以下步骤进行:

    1. 计算键的哈希值:使用哈希函数(如CRC16、MD5等)对键进行哈希计算,生成一个哈希值。
    1. 映射到哈希槽:将哈希值与哈希槽的总数(例如,16384)取模,得到一个介于0和哈希槽总数之间的数字。这个数字就是对应的哈希槽编号。

例如,假设有一个键"mykey"需要分布到Redis Cluster的哈希槽中:

    1. 使用哈希函数对"mykey"进行哈希计算,得到哈希值。
    1. 将哈希值与哈希槽的总数(16384)取模,得到一个介于0和16383之间的数字,比如1234。
    1. 键"mykey"将被分配到哈希槽编号为1234的哈希槽中。

在Redis Cluster中,客户端会根据键的哈希值自动进行哈希槽的计算和路由。客户端可以通过使用Redis客户端库或者自定义的哈希函数来实现这个过程。对于大多数应用来说,这个过程是透明的,由Redis Cluster的客户端库来处理。客户端只需要指定键,而不需要关心具体的哈希槽计算过程。

18.5 Redis Cluster 支持重新分配哈希槽吗?

是的,Redis Cluster支持重新分配哈希槽。当集群的节点数量发生变化,例如节点的加入或离开,Redis Cluster可以自动进行哈希槽的重新分配,以实现数据的动态迁移和负载均衡。

哈希槽的重新分配过程如下:

    1. 节点变更检测:当节点加入或离开Redis Cluster时,集群会检测到节点的变化。
    1. 新节点接管哈希槽:如果有新节点加入集群,Redis Cluster会将一部分哈希槽从现有节点重新分配到新节点上。这样新节点就负责管理这些哈希槽所对应的数据。
    1. 数据迁移:在哈希槽重新分配期间,Redis Cluster会自动进行数据迁移。数据从旧的节点上的哈希槽移动到新的节点上的相应哈希槽。这确保了数据的一致性和均衡分布。
    1. 迁移完成:当数据迁移完成后,集群中的每个节点负责管理一部分哈希槽,数据得到了重新分配和负载均衡。

这种自动的哈希槽重新分配机制使得Redis Cluster能够适应节点的动态变化,实现数据的平衡分布和负载均衡。同时,这个过程对应用程序是透明的,应用程序无需手动干预,Redis Cluster会自动处理数据迁移和哈希槽的重新分配。

18.6 Redis Cluster 扩容缩容期间可以提供服务吗?

在Redis Cluster的扩容和缩容过程中,集群仍然可以提供服务,但可能会有一些短暂的影响和潜在的性能变化。

  • 在扩容期间,当新节点加入集群并接管一部分哈希槽时,数据迁移过程会发生。在数据迁移期间,如果客户端发送命令到正在迁移的哈希槽上,集群会自动将请求重定向到正确的节点。这意味着客户端可能会在迁移过程中经历一些请求的重定向和稍微增加的延迟,但整体上仍然可以继续提供服务。

  • 在缩容期间,当节点离开集群时,集群会将相应的哈希槽重新分配给其他节点,并进行数据迁移。在这个过程中,客户端的请求也会被重定向到正确的节点上。与扩容相似,客户端可能会经历一些请求重定向和轻微的延迟。

需要注意的是,数据迁移过程可能会对集群的整体性能产生一些影响。数据迁移可能会消耗网络带宽和节点资源,因此在迁移期间可能会出现一些性能波动。但一旦数据迁移完成,集群的性能应该恢复到正常水平,并且能够继续提供服务。

为了最小化影响,可以采取一些策略,如逐步扩容或缩容、控制迁移速率等。此外,合理的集群规划和节点配置也可以提高Redis Cluster在扩容和缩容期间的稳定性和性能表现。

总的来说,Redis Cluster在扩容和缩容期间可以继续提供服务,但可能会有一些短暂的影响和性能变化。这些影响通常是暂时的,一旦数据迁移完成,集群应该恢复到正常状态。

18.7 Redis Cluster 中的节点是怎么进行通信的?

在Redis Cluster中,节点之间通过节点间通信(Node-to-Node Communication)来进行协调和数据同步。节点间通信是基于Redis自定义的二进制协议实现的。

以下是节点间通信的基本原理:

    1. Gossip协议:Redis Cluster使用Gossip协议来进行节点之间的发现和状态信息传播。每个节点都与其他节点保持连接,并周期性地交换关于集群拓扑和节点状态的信息。这种信息交换使得每个节点都能了解到其他节点的存在和状态变化。
    1. 握手过程:当一个节点加入集群时,它会通过Gossip协议与其他节点进行握手,以获取集群的拓扑信息。握手过程包括节点之间的互相识别、集群拓扑的交换等。
    1. 请求重定向:当一个节点接收到一个请求,但该请求所对应的哈希槽并不在该节点上时,节点会通过Gossip协议获取哈希槽对应的节点信息,并将请求重定向到正确的节点。这样,请求就会被正确地处理。
    1. 节点间数据同步:当集群中的节点发生数据迁移、主节点切换等情况时,节点之间需要进行数据同步,以保证数据的一致性。Redis Cluster使用主从复制(Replication)机制来实现数据同步。其中,主节点负责将数据同步到从节点,确保从节点与主节点的数据保持一致。
    1. 心跳检测:节点之间会通过定期的心跳检测来监测其他节点的健康状态。如果一个节点长时间没有响应,其他节点会将其标记为故障节点,并进行故障转移等操作。

通过以上机制,Redis Cluster中的节点能够进行通信、协作和数据同步,以实现集群的高可用性和数据一致性。这些机制使得Redis Cluster能够自动处理节点间的协调和故障恢复,从而提供稳定可靠的服务。

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 后端面试八股文是面试中常见的问题类型,其主要目的在于考察面试者对于 Java 后端开发基础知识的理解程度、实际应用经验以及解决问题的能力,以下是一份常见的 Java 后端面试八股文: 1. Java 基础知识:主要考察面试者对于 Java 语言的基本特性、常用类库、数据类型、面向对象思想等方面的掌握程度。 2. 网络通信:了解网络通信协议,如 TCP/IP 协议和 HTTP 协议,并掌握 socket 编程的基础知识。 3. 数据库:能够熟练编写 SQL 语句进行数据操作,了解关系型数据库和非关系型数据库的优劣及适用场景,如 MySQL、Redis、MongoDB 等。 4. 数据结构和算法:具备基本的算法和数据结构理解,如二叉树、哈希表、排序等,并能够运用算法解决实际问题。 5. 多线程编程:掌握多线程编程的原理和常用技巧,如线程池、锁机制等,以及对线程安全问题的处理。 6. 框架应用:熟悉常见的 Java 后端框架,如 Spring、SpringMVC、Hibernate 等,能够根据业务需求使用和扩展这些框架。 7. 性能优化:了解性能优化的方法和技巧,如内存泄漏、SQL 语句调优、缓存优化等,以提高系统的性能和可扩展性。 8. 实际应用:思路清晰,具备在复杂业务场景下的开发经验,熟悉大型互联网系统相关技术架构,具有较强的工程实践能力。 总之,Java 后端开发是一个庞大而又复杂的领域,其技术栈涵盖了从基础语言、框架应用到业务实践等多个方面,掌握好 Java 的相关知识,能够编写高质量、高性能的代码,并且不断提升自己的能力,是每一个 Java 后端开发工程师都应该具备的基本素质。 ### 回答2: 在Java后端面试中,八股文指的是面试官通常会问到的一些基础问题,这些问题的答案通常是非常标准化和公式化的。因此,这些问题很适合以八股文的形式来回答。 以下是几个常见的Java后端面试八股文题目和回答: 1. 什么是Java线程? Java线程是操作系统执行单元的基本单元。每个Java应用程序都至少有一个线程。Java线程允许同时执行多个任务,每个任务在单独的线程中运行,从而提高了应用程序的效率和响应性。 2. Java中的异常处理机制是什么? Java中的异常处理机制允许程序员在运行时捕获并处理错误。当出现异常时,程序会跳转到异常处理器中,并执行相应的代码。Java程序中的异常可以被自定义,也可以是预定义的异常类型,如NullPointerException、ArrayIndexOutOfBoundsException等。 3. 什么是Java虚拟机? Java虚拟机(JVM)是Java程序的运行环境,它通过将字节码翻译为本地机器指令来执行Java程序。JVM提供了垃圾回收机制、内存管理、安全性和多线程支持等功能,大大简化了Java程序的开发和管理。 4. 什么是Java的反射机制? Java的反射机制是指程序在运行时动态地获取和操作类的信息,包括类的字段、方法、构造函数等。Java中的反射机制支持动态地创建对象、调用方法和修改属性,使得Java程序具有更高的灵活性和可扩展性。 5. 什么是Java的集合框架? Java的集合框架是指一组可重用的数据结构和算法,包括list、set、map等。集合框架在Java程序中被广泛使用,它们提供了更加抽象的数据类型,使得程序员可以更加轻松地操作数据集合。集合框架是Java程序中最重要的库之一。 以上是几个常见的Java后端面试八股文题目和回答,这些题目都是非常基础的知识点,但在实际工作中却被广泛应用。对于准备面试的Java后端开发工程师来说,掌握这些知识点是非常重要的。 ### 回答3: Java后端面试常见的八股文内容包括:Java基础、数据结构与算法、操作系统、计算机网络、数据库等方面。以下是更详细的回答: 1. Java基础 Java基础是Java后端开发的基础,而Java编写的程序跑在JVM(Java虚拟机)上,因此对于Java基础的理解和掌握是至关重要的。Java基础八股文的常见问题包括:Java的数据类型、面向对象的概念以及Java中类与接口的作用等方面。 2. 数据结构与算法 数据结构与算法是所有程序员必须掌握的领域之一。在Java后端开发中,很多应用中会涉及到数据结构与算法,比如使用数据结构实现高效搜索、排序、分析大量数据等。因此,数据结构与算法的八股文占据着重要的地位。 3. 操作系统 操作系统也是Java后端开发中必须掌握的领域之一,因为操作系统是计算机系统中的关键部分,影响着整个系统的运行效率和稳定性。八股文中通常会涉及到操作系统的进程管理、内存管理、文件管理和输入输出等方面。 4. 计算机网络 计算机网络作为互联网的支持与基础,也是Java后端开发中的重要组成部分。八股文中常问计算机网络的协议、TCP/IP模型、网络的安全性以及常见的网络攻击方式等。 5. 数据库 数据库是大部分应用程序中不可或缺的组成部分,对于Java后端开发的常见数据库有MySQL、Oracle、SQL Server等。常见的数据库八股文问题包括SQL语法、数据库设计、事务管理、数据备份和恢复等方面。 以上内容就是Java后端面试八股文的一些常见问题与注意点,当然在实际面试中,还会涉及到其他方面的问题。总的来说,作为一名Java后端开发人员,需要对Java基础、数据结构与算法、操作系统、计算机网络、数据库等多方面知识与技能有着扎实的功底和熟练掌握,才能胜任Java后端开发岗位的工作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值