今年新版面试题及答案(上个月朋友面试成功后留下的题目)

Posted yongqi_wang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了今年新版面试题及答案(上个月朋友面试成功后留下的题目)相关的知识,希望对你有一定的参考价值。

1.kafka的读写效率快的原因(详细解释传送门;https://blog.csdn.net/lianggzone/article/details/112386378)
    (1) 利用 Partition 实现并行处理
        Kafka 是一个 Pub-Sub 的消息系统,无论是发布还是订阅,都要指定 Topic;
        Topic 只是一个逻辑的概念。每个 Topic 都包含一个或多个 Partition,不同 Partition 可位于不同节点。
        一方面,由于不同 Partition 可位于不同机器,因此可以充分利用集群优势,实现机器间的并行处理。
        另一方面,由于 Partition 在物理上对应一个文件夹,即使多个 Partition 位于同一个节点,也可通过配置让同一节点上的不同 Partition 置于不同的磁盘上,从而实现磁盘间的并行处理,充分发挥多磁盘的优势。
    (2) 顺序写磁盘
        Kafka 中每个分区是一个有序的,不可变的消息序列,新的消息不断追加到 partition 的末尾,这个就是顺序写。
        Kafka的删除并不是"读-写"这种形式,而是将Partition分为多个Segment,每个Segment对应一个物理文件,通过删除整个文件的方式去删除Partition 内的数据。这种方式清除旧数据的方式,也避免了对文件的随机写操作。
    (3) 充分利用 Page Cache
        [1] I/O Scheduler 会将连续的小块写组装成大块的物理写从而提高性能。
        [2] I/O Scheduler 会尝试将一些写操作重新按顺序排好,从而减少磁盘头的移动时间。
        [3] 充分利用所有空闲内存(非 JVM 内存)。如果使用应用层 Cache(即 JVM 堆内存),会增加 GC 负担。
        [4] 读操作可直接在 Page Cache 内进行。如果消费和生产速度相当,甚至不需要通过物理磁盘(直接通过 Page Cache)交换数据。
        [5] 如果进程重启,JVM 内的 Cache 会失效,但 Page Cache 仍然可用。
    (4) 零拷贝技术
        技术指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间。
        [1] 网络数据持久化到磁盘 (Producer 到 Broker)
            传统模式下:数据从网络传输到文件需要 4 次数据拷贝、4 次上下文切换和两次系统调用。
            ① 首先通过 DMA copy 将网络数据拷贝到内核态 Socket Buffer。
            ② 然后应用程序将内核态 Buffer 数据读入用户态(CPU copy)
            ③ 接着用户程序将用户态 Buffer 再拷贝到内核态(CPU copy)
            ④ 最后通过 DMA copy 将数据拷贝到磁盘文件
            kafka MMap:
            使用 mmap 的目的是将内核中读缓冲区(read buffer)的地址与用户空间的缓冲区(user buffer)进行映射。从而实现内核缓冲区与应用程序内存的共享,省去了将数据从内核读缓冲区(read buffer)拷贝到用户缓冲区(user buffer)的过程。它的工作原理是直接利用操作系统的 Page 来实现文件到物理内存的直接映射。完成映射之后你对物理内存的操作会被同步到硬盘上。
            使用这种方式可以获取很大的 I/O 提升,省去了用户空间到内核空间复制的开销。
        [2] 磁盘文件通过网络发送(Broker 到 Consumer)
            传统模式:
                ① 首先通过系统调用将文件数据读入到内核态 Buffer(DMA 拷贝)
                ② 然后应用程序将内 存态 Buffer 数据读入到用户态 Buffer(CPU 拷贝)
                ③ 接着用户程序通过 Socket 发送数据时将用户态 Buffer 数据拷贝到内核态 Buffer(CPU 拷贝)
                ④ 最后通过 DMA 拷贝将数据拷贝到 NIC Buffer
            kafka:
                Kafka 在这里采用的方案是通过 NIO 的 transferTo/transferFrom 调用操作系统的 sendfile 实现零拷贝。总共发生 2 次内核数据拷贝、2 次上下文切换和一次系统调用,消除了 CPU 数据拷贝
    (5) 批处理
        在很多情况下,系统的瓶颈不是 CPU 或磁盘,而是网络IO。
        因此,除了操作系统提供的低级批处理之外,Kafka 的客户端和 broker 还会在通过网络发送数据之前,在一个批处理中累积多条记录 (包括读和写)。记录的批处理分摊了网络往返的开销,使用了更大的数据包从而提高了带宽利用率。
    (6) 数据压缩
        Producer 可将数据压缩后发送给 broker,从而减少网络传输代价,目前支持的压缩算法有:Snappy、Gzip、LZ4。数据压缩一般都是和批处理配套使用来作为优化手段的。
        
2.redis数据主从同步
    (1)全量同步:
        需要在主库上进行一次bgsave将当前内存的数据全部快照到磁盘文件中,然后在将快照文件的内容全部传送到从节点。从节点将快照文件接收完毕后,立即执行一次全量加载,加载之前先要将当前内存的数据清空。
    (2)部分同步:
        只同步从服务器中没有的数据,涉及到赋值偏移量和赋值积压缓冲区。
    (3)命令传播:
        用于在master的数据库状态被修改时,将导致变更的命令传播给slave,从而让slave的数据库状态与master保持一致。
    (4)复制挤压缓冲区
        [1] 它是master维护的一个固定长度的先进先出的内存队列(FIFO),默认为1MB。当队列长度超过时,最先进入的元素被弹出。队列的生存时间默认为3600秒。如果master不在有与之相连接的slave,并且该状态持续时间超过了队列生成时间,master就会释放该队列,等到有需要的时候在创建。
        [2] 如果从服务器与master失联后重连成功,从服务器通过PSYNC命令将自己的赋值偏移量发送给master,master通过偏移量来决定采用全量同步还是增量同步。如果从服务器的复制偏移量之后的数据仍然存在于缓冲区中,则采用部分同步,否则采用全量同步
        [3] 3.0之后增加了WAIT命令,提供两个参数,第一个参数时从库的数量N,第二个参数时时间t,以毫秒为单位。它标识等待wait指令之前的所有写操作同步到N个从库,最多等待时间t,如果t=0则表示无限等待所有操作完成
    (5) 主从同步策略
        刚链接时进行全量同步,全量同步结束后进行增量同步。当有需要是slave可触发全量同步,但通常只会触发增量同步,只有增量同步失败才会尝试全量同步。
        
3.redis持久化
    (1) RDB
        [1] Redis是一个单进程的服务,通过fork产生子进程,父进程继续处理Client请求,子进程负责将快照写入临时文件中,完成后用临时文件替换原有的快照文件
        [2] rdbSave函数负责将内存中的数据库数据以RDB格式保存到磁盘中,如果RDB文件已存在,那么新文件将替换原有文件。Rdbload用于将RDB文件中的数据重新载入到内存中
        [3] SAVE和BGSAVE命令的区别
            ① SAVE直接调用rdbSave,阻塞Redis主进程,直到保存完成为止。
            ② BGSAVE则fork出一个子进程,子进程负责调用rdbSave,并在保存完成之后向主进程发送信号,通知保存已完成。
            ③ save配置
                a)save s number
                    s:时间单位秒  number:数据修改次数
                    例:save 100 10
                    含义:当客户端在100秒内对数据库中数据,进行了10次修改,则自动执行BGSAVE命令
        [4] 优点
            ① RDB文件紧凑,全量备份,非常适合用于进行备份和容灾处理恢复。 
            ② 生成RDB文件时,redis柱进行会调用fork()产生一个紫禁城来处理所有保存工作,柱进行不需要进行任何磁盘IO操作。
            ③ RDB在恢复大数据集时的速度比AOF的速度快。
        [5] 缺点
            RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式数据,粗出紧凑。当进行快照持久化时,会开启一个字进行负责快照的持久化过程,但当子进程在持久化过程中不会保存主进程的变动,可能会导致丢失数据。
    (2) AOF
        [1] 容写入缓冲区,子进程写完退出,父进程接收退出消息后,将缓冲区AOF写入临时文件,覆盖原文件
        [2] AOF协议文本,将所有对数据库进行写操作命令记录到文件中,以此达到记录数据库状态的目的
        [3] AOF生成文件过程
            ① 命令传播:Redis将执行完的命令、参数等信息发送到AOF程序中
            ② 缓存追加:根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的AOF缓存中
            ③ 文件写入:AOF缓存中的内容被写入AOF文件末尾,如果社零的AOF保存条件被满足的话,调用fsync函数或fdatasync函数,将写入的内容真正保存到磁盘
        [4] sync频率配置:
            通过appendfsync参数进行控制,有三个值
            ① AOF_FSYNC_NO    不保存
            ② AOF_FSYNC_EVERYSEC   每秒保存一次(默认)
            ③ AOF_FSYNC_ALWAYS     每执行一个命令保存一次
        [5] AOF文件加载过程
            ① 创建一个不带网络连接的伪客户端(fake client)
            ② 读取AOF所保存的文件,并根据内容还原出命令及参数等信息
            ③ 根据命令使用伪客户端进行执行
        [6] 优点
            ① AOF可以更好的保护数据不丢失,一般AOF会间隔1s,通过一个后台程序执行一次fsync操作,所以最多智慧丢失1s的数据。
            ② AOF日志文件没有任何磁盘寻址开销,写入性能搞,文件不容易破损
            ③ AOF日志文件及时过大的时候,出现后台重写操作也不会影响客户端。
            ④ AOF日志文件可以通过修改错误操作的日志文件内容的方式回复所有数据。
        [7] 缺点
            ① 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大。
            ② AOF开启后,支持的写QPS会比RDB支持的写QPS低。
            ③ AOF通过日志回复数据,不一定会与预期完全一致        
    (3) RDB与AOF的区别
        [1] RDB使用快照生成文件,崩溃恢复时在通过rdbloade将文件加载到内存中
        [2] AOF使用缓冲区记录所有已执行命令,将命令写入文件,崩溃恢复时通过创建一个伪客户端读取文件并执行
        
4.redis 键过期处理
    (1) Redis所有的数据结构都可以设置过期时间,时间到了会自动删除。Redis会将每个设置了过期时间的key放入一个独立的字典中,以后会定时遍历这个字典来删除到期的key(redis采用惰性删除和定期删除结合的策略,以达到合理使用cpu时间和避免浪费内存空间之间的平衡)
    (2) 定时删除:在设置key过期时间的同时创建一个定时器,让定时器在key过期时执行删除命令
    (3) 惰性删除:仅在每次收到对key的请求时才检查是否过期,如果已过期则删除key
    (4) 定期删除:每隔一段时间对key字典进行检查,删除过期的key
    (5) 从库的过期策略:从库对key的过期处理是被动的,会在AOF文件中添加一条del指令,同步到所有从库中,根据命令执行来删除过期key
    
5.redis 键踢出策略:
    当redis内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换(swap),交换会让redis的性能急剧下降。所以我们是不允许redis出现交换行为的。为了限制最大使用内存,redis提供了配置参数maxmemory来限制内存超出期望大小时的处理策略。
    (1) Noeviction:不踢出,当内存使用达到阈值的时候,所有引起申请内存的命令会报错(默认策略)
    (2) volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key,如果键不存在则报错
    (3) volatile-ttl:在设置了过期时间的键空间中,优先移除过期时间最近的key,如果键不存在则报错
    (4) volatile-random:在设置了过期时间的建空间中,随机移除个key
    (5) allkeys-lru:在主键空间中,优先移除最近未使用的key
    (6) allkeys-random:在主键空间中,随机移除某个key。踢出时机:每个命令执行之前(redis.c/processCommand)
    
6.redis 最久未使用踢出策略:LRU算法
    (1) 新数据插入到链表头部
    (2) 每次访问命中缓存,则将数据移动到链表头部
    (3) 链表满时,移除链表尾部元素
    
7.redis 超时策略LRU-K
    (1) 数据第一次被访问时,加入到访问历史列表
    (2) 如果数据数据在访问历史列表后没有达到K次访问,则按照规则淘汰(FIFO,LRU)
    (3) 当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列转移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序
    (4) 需要淘汰数据时,淘汰缓存队列中排在末尾的数据    
    
8.redis哨兵
    (1) 每个哨兵每秒想它所知的master、slave及其他哨兵发送ping命令,检查存活状态
    (2) 如果一个实例距离最后一次有效回复ping命令时间超过预设值(down-after-milliseconds),则这个实例会被哨兵标记为主观下线
    (3) 如果一个master被标记为主观下线,则正在监视这个master的所有哨兵要每秒确认一次master的确进入了主观下线状态
    (4) 当有足够的哨兵在制定的时间范围内确认master的确进入了主观下线状态,则master会被标记为客观下线
    (5) 一般情况下,每个哨兵会以每10秒一次的频率想它所制定的所有master、slave发送info命令
    (6) 当master被哨兵标记为客观下线时,info命令的频率改为每秒一次
    (7) 若没有足够数量的哨兵同意master已经下线,则master的客观下线状态会被移除
    (8) 如果有足够数量的哨兵同意master已下线,则进行投票选举master,当被投slave的票数达到?n/2+1时,成为新的master
    
9.缓存穿透
    查询一个一定不存在的数据。由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
    解决方案:
    (1)布隆过滤
        [1] 概念 
        对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。
        [2] 实现方式
        将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
        [3] 基本原理及要点:位数组+k个独立hash函数。
    将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,很明显这个过程并不保证查找的结果是100%正确的。同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字。所以一个简单的改进就是counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了。添加时增加计数器,删除时减少计数器。
    (2) 缓存空对象
10.缓存雪崩
    如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
    解决方案:
    (1) 加随机过期时间
        为所有并发查询的的缓存时间添加一个额外时间随机数在1~15分钟使缓存尽量不会再同一时间失效
    (2) 加锁排队
        在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去loaddb,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。
    (3) 数据预热
        可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,    让缓存失效的时间点尽量均匀
    (4) 二级缓存或双缓存
        A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
    (5) 接口限流:计数器、时间窗口、令牌等算法实现
        
11.CMS和G1区别
    (1) CMS执行过程  是一种以获取最短回收停顿时间为目标的收集器
        [1] 初始标记:标记GC Root可直接连接的对象,运行期间会停止其他用户操作
        [2] 并发标记:在初始标记的基础上继续向下追溯标记,以所有已标记对象为根节点继续搜索向下标记。
        [3] 并发预清理:,虚拟机查找在执行并发标记阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。通过重新扫描,减少下一个阶段”重新标记”的工作,因为下一个阶段会Stop The World。
        [4] 重新标记 :停止其他用户操作,收集器线程扫描在CMS堆中剩余的对象。扫描从”根对象”开始向下追溯,并处理对象关联。
        [5] 并发清理 :清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。
        [6] 并发重置:清理CMS栈。
    (2) G1运行机制  有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。并且停顿时间可预测。
        [1] 初始标记:仅标记GC Roots能直接到达的对象,并且修改TAMS的值,让下一阶段用户程序并发运行时能在正确可用的Region中创建新对象。
        [2] 根区域扫描:从GC Roots开始对对已标记的引用扫描,并标记对老年代的引用。
        [3] 并发标记:在整个堆中查找可访问的对象并标记。
        [4] 最终标记:多线程修正在并发期间因用户程序执行而导致标记产生变化的标记。切将对象的变化记录在Remembered Set Logs里,把这里的数据合并到Remembered Set中。
        [5] 筛选回收:对每个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来指定回收计划。    

未完待续,今天太晚了明后天有空再把后面的补上


12. 设计模式,会的每种一个demo编写。
13.Lucene 
14.ES
15.Quartz 分布式定时原理
16.线上如何压力测试 
17.如何影子表 隔离 压力数据
18.故障演练如何做
19.mysql事务隔离级别
20.手写有序数组二分查找
21.分布式锁的实现
22.2pc,xa协议
23.mysql是如何做crash-safe的
24.ThreadLocal的使用场景
25.redis数据结构
26.juc下面公平非公平锁的实现区别?
27.spring循环依赖
28.各种分布式算法场景等
    1.分布式一致性算法
    2.投票选举算法
29.限流算法
30.熔断处理
31.降级处理

有几个涉及到公司项目相关的问题及设计方案就不写了

以上是关于今年新版面试题及答案(上个月朋友面试成功后留下的题目)的主要内容,如果未能解决你的问题,请参考以下文章

最全Java面试题及答案整理(2023最新版)

Java面试题及答案整理汇总(2023最新版)

前端面试题及答案整理(2022最新版)

600+ 道 Java面试题及答案整理(2021最新版)

600+ 道 Java面试题及答案整理(2021最新版)

SpringCloud面试题及答案 300道,springcloud面试题总结 (持续更新)