java:
- 常见面试题汇总:最新Java面试题,常见面试题及答案汇总_Jack方-CSDN博客_java面试问题大全及答案大全
- https://blog.csdn.net/weixin_43495390/article/details/86533482/?ops_request_misc=&request_id=&biz_id=102&utm_term=java%E9%9D%A2%E8%AF%95&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-4-.first_rank_v2_pc_rank_v29&spm=1018.2226.3001.4187
- hashmap:
- 底层数据结构
- 红黑树链表转换的条件
- 为什么是长度为8的时候转红黑树,为什么是6的时候转回链表
- 为什么要转换成红黑树,而不是别的树;
- 数组容量为啥要是2的n次方;
- 扩容机制
- 什么条件下会扩容
- java7和java8扩容的机制分别是什么样
- 扩容并发问题
- put的流程/get的流程
- 将POJO对象作为key的时候,要做什么特殊处理
- 覆写hashcode和equal函数,为什么要覆写,怎么覆写
- HashMap常见面试题整理 - 曾聪聪 - 博客园
- HashMap面试题,看这一篇就够了! - 天之痕苏的小院子 - 博客园
- concurenthashmap
- ConcurrentHashMap底层实现原理及扩容机制_今天也要学习哦的博客-CSDN博客_concurrenthashmap底层实现原理
- 深入理解HashMap+ConcurrentHashMap的扩容策略 - 茶轴的青春 - 博客园 (扩容机制)
- java1.7的锁机制: 分段锁 (ReenterLock,自旋锁)
- java1.8的锁机制: 节点锁(没有段结构了)(synchronized,CAS)
- java1.8 put步骤:
- 该方法的步骤简述如下:https://blog.csdn.net/nazeniwaresakini/article/details/105576938
- 计算key的哈希码;
- 检查哈希桶数组是否为空,若为空,调用initTable()方法初始化;
- 调用tabAt()方法获得哈希码对应到哈希桶数组的下标,并获取该桶的头结点f;
- 若f为空(即为空桶),调用casTabAt()方法,通过CAS操作(Unsafe.compareAndSwapObject())将新元素插入为头节点。若CAS失败,说明有并发操作,重试之;
- 若f不为空,但是其hash值为MOVED(即-1),说明其他线程触发了扩容操作,调用helpTransfer()方法参与扩容;
- 若均不符合4和5步骤的条件,说明可以正常插入,用synchronized关键字在f上加锁,并在对应桶的链表或红黑树上插入新元素;
- 最后判断是否要将链表转换为红黑树,如果需要,调用treeifyBin()方法转换之。
- 底层数据结构
- jvm:
- 堆、方法区、虚拟机栈、本地方法栈、程序计数器
- 垃圾回收:
- 标记算法:引用计数法、根搜索
- gc root有哪些:
- 虚拟机栈中的引用对象
- 方法区中的引用对象
- 方法区中的类静态属性引用对象
- 本地方法栈中JNI引用对象
- gc root有哪些:
- 垃圾回收算法:标记清除、复制、标记整理
- 主流垃圾回收器:
- cms:初始标记,并发标记,重新标记,并发处理 (目的是减少stw时间)
- g1:
- 标记算法:引用计数法、根搜索
- 类加载机制:加载、验证、准备(在方法区为静态变量准备内存并初始化)、解析(将常量池中的符号引用替换为直接引用)、初始化、使用、卸载
- 类加载器:双亲委派 —— 避免重复加载、安全考虑,避免自定义类覆盖原始类(比如String类)
- 初始化顺序:父类静态变量/静态块、子类静态变量/静态块、父类普通变量/块、父类构造器、子类普通变量/块、子类构造器
- https://baijiahao.baidu.com/s?id=1663852327367330121&wfr=spider&for=pc
- 多线程:
- 线程中断机制:线程接到其他线程发来的interrupt请求时,会更新中断标志位
- 如果线程处于阻塞状态,则会抛异常,然后恢复
- 如果线程处于运行状态,则会根据需求处理中断,也就是线程自己的代码决定
- threadlocal的作用:
- 线程局部变量, 规避线程安全问题
- ThreadLocal 有什么缺陷?如果线程池的线程使用ThreadLocal 会有什么问题?_熊仙森滴Blog-CSDN博客
- sleep和wait的区别:
- sleep为Thread类的方法,阻塞时不会释放锁,到时间了就会恢复
- wait为Object类的方法,阻塞时会释放锁,恢复需要其他线程调用该对象的notify()方法才能恢复
- 锁:java中的各种锁机制 - StaticPackage - 博客园
- 乐观锁(cas)/悲观锁(synchronized)
- 自旋锁/适应性自旋锁
- 对于锁定时间很短的同步资源,线程不阻塞、原地等待资源释放
- 通过自旋来减少cpu切换以及恢复现场带来的开销
- cas实现自旋
- 无锁/偏向锁/轻量级锁/重量级锁 (synchronized)
- synchronized通过Monitor来实现线程同步
- 公平锁/非公平锁
- 可重入锁/不可重入锁
- 共享锁/独占锁(互斥锁)
- CAS:
- CompareAndSwap: 比较并交换, 实现乐观锁
- AtomicInteger/AtomicRefence 均为cas实现
- 存在的问题:
- ABA问题
- 循环时间长开销大问题
- 只能保证一个共享变量的原子操作
- cas分段机制:https://blog.csdn.net/qq_16570607/article/details/118996860?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-15.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-15.control
- AQS:
- AQS的核心原理分析_oldshaui的博客-CSDN博客
- AbstractQueuedSynchronizer: 抽象队列同步器
- 就是一个并发包的基础组件,用来实现各种锁,各种同步组件的。它包含了state变量、加锁线程、等待队列等并发中的核心组件
- state变量:加锁状态
- 加锁线程:现在占有锁的是哪个线程
- 等待队列:加锁失败则进去等待队列
- 条件队列:线程满足条件之后进入等待队列
- ReentrantLock(独占锁)/Semaphore(共享锁) 均为AQS实现
- 信号量:Semaphore可以控制多个线程同时对某个资源的访问
- 死锁的必要条件:
- 互斥条件:资源独占
- 请求和保持条件:已分配资源的情况下,又去申请别的资源,而该资源被其他线程占有被阻塞,但对自己的资源又不放手
- 不可剥夺条件:已获得的资源不可被其他进程剥夺,只能自己释放
- 环路等待:链中每一个进程已获得的资源同时被链中下一个进程所请求
- 如何预防死锁:
- 打破请求和保持条件:1)预先分配全部资源,满足则运行,不满足则等待 2)申请新资源前必须释放占有的资源
- 打破不可剥夺条件:当进程占有某些资源后又进一步申请其他资源而无法满足,则该进程必须释放它原来占有的资源。
- 打破环路等待条件:实现资源有序分配策略,将系统的所有资源统一编号,所有进程只能采用按序号递增的形式申请资源。
- 线程中断机制:线程接到其他线程发来的interrupt请求时,会更新中断标志位
- linux系统:load (1,5,15) 正在运行和等待运行的进程数,超过cpu的核数就有问题
- 网络协议(tcp、udp):
- 面试官,不要再问我三次握手和四次挥手_猿人谷-CSDN博客_三次握手和四次挥手
- TCP和UDP的最完整的区别 - 割肉机 - 博客园
- 面试题:UDP&TCP的区别_sifanchao的博客-CSDN博客_tcp和udp的区别 (面试题:UDP&TCP的区别)
- udp:UDP(User Data Protocol,用户数据报协议)是一个简单的面向数据报的运输层协议。它不提供可靠性,只是把应用程序传给IP层的数据报发送出去,但是不能保证它们能到达目的地。由于UDP在传输数据报前不用再客户和服务器之间建立一个连接,且没有超时重发等机制,所以传输速度很快。
- tcp:
- TCP(Transmission Control Protocol,传输控制协议)提供的是面向连接,可靠的字节流服务。即客户和服务器交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。并且提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端
- (请求连接)三次握手:客户端 -> 服务端(请求连接) 服务端 -> 客户端(应答) 客户端 -> 服务端(确认)
- (释放连接)四次挥手:A请求断开链接 -> b应答 -> b请求断开连接 ->a应答
- 可靠传输:
- 校验和:将发送的数据段当作16位二进制树,进行相加,然后取反得到校验和,接收方做同样的操作,然后比较发送方和接收方的校验和是否一致
- 序列号/确认应答:tcp发送数据时对每个字节数据进行编号,即为序列号;接收方在收到数据后会进行确认应答(ack),ack报文中带有对应的确认序列号,同时还能根据序列号排序,对数据去重
- 超时重传:发送方在一定的时间内没有收到ack时,会进行超时重传
- 连接管理:tcp面向连接的
- 流量控制/窗口控制:根据接收端处理数据的速度(主要是在ack时回传窗口大小),来决定发送端的发送速度
- 拥塞控制:慢启动机制,先发送少量数据试探,探清网络状态, 防止过多的数据灌入网络中,
- TCP 协议如何保证可靠传输 - 简书
- https://www.huaweicloud.com/zhishi/arc-11960278.html
- TCP的优点: 可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。 TCP的缺点: 慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。 而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。
- UDP的优点: 快,比TCP稍安全 UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。但UDP也是无法避免攻击的,比如:UDP Flood攻击…… UDP的缺点: 不可靠,不稳定 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。
- 基于上面的优缺点,那么: 什么时候应该使用TCP: 当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。 在日常生活中,常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输 ………… 什么时候应该使用UDP: 当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 比如,日常生活中,常见使用UDP协议的应用如下: QQ语音 QQ视频 TFTP ……
- 小结TCP与UDP的区别:
- 基于连接与无连接;
- 对系统资源的要求(TCP较多,UDP少);
- UDP程序结构较简单;
- 流模式与数据报模式 ;
- TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。
spring
- ioc: 依赖反转,管理依赖的对象
缓存(redis):
- redis核心主流程:启动服务器->初始化服务器配置->初始化服务器(创建时间事件、创建文件事件)->从磁盘加载数据->触发事件的调度与执行->调用i/o多路复用获取事件->处理已产生的事件->处理已到达的时间事件
- 高可用解决方案
- 哨兵模式
- 主观下线:ping
- 客观下线:询问
- 故障转移
- 选举:选举领头Sentinel
- 挑选:领头Sentinel从下线主服务器对应的从服务器中挑选一个升级为主服务器
- 更改:Sentinel将剩余所有从服务器改为复制新的主服务器
- 更新:Sentinel更新配置信息
- 集群模式
- 特点:
- 去中心化,数据按slot分布在各个节点,16384个槽
- 使用 CRC16 算法来计算 key 所属的槽:crc16(key,keylen) & 16383
- 所有redis节点彼此互联,通过ping-pong机制来进行节点间的心跳
- 分片内采用一主多从保证高可用,并提供复制和故障转移功能(异步复制)
- 客户端可与redis集群任意一节点直连
- 目标
- 高性能
- 水平扩展
- 写安全性
- 可用性
- MOVED错误:用来redirect, 代表该槽已经完全由另一个节点负责,会触发客户端刷新本地路由表,之后对于该槽的请求都会请求新的节点
- 故障转移
- 集群在线扩容的安全性
- ASK错误保障, 用来redirect:一种迁移槽位的临时措施,只会产生一次重定向
- 特点:
- 数据丢失场景:脑裂
- 网络分区
- 网络分区开始到故障转移这期间的写命令会丢失
- 哨兵模式
- Redis持久化机制:RDB、AOF、混合持久化
- 文件事件:接受连接、读取、写入、关闭连接
- 时间事件:serverCron,每100ms促发一次(清理过期键、aof后台重写、rdb的save point检查、将aof_buf内容写到磁盘)
- RDB:
- 快照,在某个时间点将redis内存中的数据库状态保存到磁盘中
- rdb文件二进制压缩,比较小, 适合做备份
- SAVE、BGSAVE
- 优点:
- rdb文件二进制压缩,占用空间小,保存了某个时间点的数据集,适合做备份
- 适合用于灾难恢复:只有一个文件,内容紧凑
- 可最大化redis的性能:父进程在保存rdb文件时唯一做的事件是fork一个子进程,剩下的事情都交给子进程来做
- RDB在恢复大数据集时速度比AOF恢复速度快
- 缺点:
- 故障时容易丢失数据(因为隔一段时间才触发rdb一次)
- 数据量较大时,fork可能会耗时很大,会造成redis停止处一段时间
- AOF:
- 保存redis所有写操作命令
- 持久化步骤:命令追加(aof_buf)、文件写入、文件同步
- 优点:
- 比rdb可靠(fsync策略:no, everysec, always),默认使用everysec,最多丢失一秒数据
- aof文件是纯追加的日志文件
- aof文件太大时,redis会自动在后台进行重写:重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合
- 可读性好
- 缺点:
- 相同的数据集,aof比rdb文件大
- 根据所使用的fsync策略,aof速度可能比rdb慢
- 混合持久化
- 混合持久化只发生于 AOF 重写过程。使用了混合持久化,重写后的新 AOF 文件前半段是 RDB 格式的全量数据,后半段是 AOF 格式的增量数据
- 优点:结合rdb和aof的优点,更快的重写和恢复
- 缺点:新的aof文件中rdb部分不再是aof格式,可读性差
- 为什么需要aof重写
- aof文件膨胀,不加以控制会对服务器造成影响,并且数据还原的时候需要的时间更长
- 在不打断服务端处理请求的情况下, 对 AOF 文件进行重建(rebuild)
- 重写:Redis 生成新的 AOF 文件来代替旧 AOF 文件,这个新的 AOF 文件包含重建当前数据集所需的最少命令。具体过程是遍历所有数据库的所有键,从数据库读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令
- 命令:REWRITEAOF,BGREWRITEAOF
- 后台子进程重写可能导致数据不一致
- 重写的过程中,主进程还需要继续处理命令请求,就可能对数据库状态进行修改
- 解决方案: AOF 重写缓冲区(aof_rewrite_buf_blocks),子进程重写完成后,servrCron会将重写缓冲区中的内容写入新的AOF文件
- 重写缓冲区内容过多怎么办:
- 重写缓冲区内容添加到aof是由父进程主导,内容过多会导致父进程阻塞时间长
- 建立父子进程通信管道
- 新增文件事件,该文件事件会将重写缓冲区的内容通过父子进程通信管道发送到子进程,子进程会尽量多的从主进程读取数据
- 通过这些优化,Redis 尽量让 AOF 重写缓冲区的内容更少,以减少主进程阻塞的时间
- 数据库和缓存一致性
- 强一致性得使用分布式事务,但会牺牲性能,违背了使用缓存的初衷
- 一般保证最终一致性
- 更新数据库,产生binlog
- 监听和消费binlog, 执行失效缓存操作
- 如果步骤2失效缓存失败,则引入重试机制,将失败的数据通过MQ方式进行重试,同时考虑是否需要引入幂等机制
- 使用场景:缓存、分布式锁、排行榜(zset)、计数(incrby)、消息队列(Stream)、地理位置(geo)、访客统计(hyperloglog)
- 数据结构:String、List、Hash、Set、ZSet
- ZSet底层数据结构:SkipList
- 数据淘汰机制:定期+惰性
- redis架构:https://www.cnblogs.com/mrhelloworld/p/redis-architecture.html
消息队列(kafka/rabbitmq):
- kafka原理: 深入浅出理解基于 Kafka 和 ZooKeeper 的分布式消息队列
- 高可用保障:https://jishuin.proginn.com/p/763bfbd5d7ed
- kafka高性能原因:
- 分区
- 日志分段存储
- 消息顺序追加
- 页缓存
- 零拷贝
数据库(mysql):
- acid
- 脏读、不可重复读、幻读
- 隔离级别:读未提交、读提交、可重复读、串行化
- 读未提交:脏读、不可重复读、幻读
- 读已提交:不可重复读、幻读
- 可重复读:幻读
- 串行化:无
- MVCC:Mutil-Version Concurrency Control(多版本并发控制)
- 目的:解决读写冲突的无锁并发控制
- 原理:3 个隐式字段,undo log ,Read View
- DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID
- 当前读:读最新版本
- 快照读:快照读就是 MySQL 实现 MVCC 理想模型的其中一个非阻塞读功能
- 【MySQL笔记】正确的理解MySQL的MVCC及实现原理_长路漫漫的歇脚处-CSDN博客
- 索引:加快查找速度
- hash索引:只支持内存存储引擎,无法范围查找和排序
- B树和B+树:
- B树:平衡搜索多叉树,每个节点存储key和value
- B+树:非叶子结点只存储key,叶子结点存储全部的索引key和value,并且是有序链表
- B+树相对B树的优点:
- 磁盘读写成本低:利用磁盘“预读取”原则,page cashe, 索引节点大小设计为page cache大小,一个索引节点放更多的数据,则磁盘io更少
- 查询速度更稳定:
- 支持范围查询:
- 聚簇索引的解释是:聚簇索引的顺序就是数据的物理存储顺序
- 聚簇索引的主索引的叶子结点存储的是键值对应的数据本身,辅助索引的叶子结点存储的是键值对应的数据的主键键值
- 非聚簇索引的解释是:索引顺序与数据物理排列顺序无关
- 什么时候要建立索引:
- 主键自动建立唯一索引
- 经常作为查询条件在where后的或order by中出现的列要建立索引
- 作为排序的列
- 高并发下键组合索引
- 用于聚合函数的列可以建立索引
- 什么时候不要建立索引:
- 经常增删改的列
- 有大量重复的列
- 表记录太少
- 深入理解MySQL索引原理和实现——为什么索引可以加速查询?_tongdanping的博客-CSDN博客_索引
- 主从原理:binlog
分布式事务:
2pc/3pc
https://segmentfault.com/a/1190000012534071