总结
怎么解决fullGC了
- 开启GC日志
- 分析最近上的代码
- 使用工具查看内存,对应年轻代码,老年代
- 分析是否有大对象创建,是否年轻代设置过小
- dump文件,分析dump文件,查找对应的大对象或是大量创建的对象
- 跟据这些信息跟踪程序
查询索引错误该怎么办
- 走执行计划,看为什么选的索引
- 优化sql,让索引大的先执行,比如联合索引时的最左原则
- 强制指定索引
- 重新设计索引,索引的拆分与合并
分布式锁加锁失败后的等待逻辑是怎么处理的
- 加锁失败后有直接返回的,等待执行,阻塞等待的,指数等待的,通过消息接收等待的,指定睡眠时间重试的
分布式锁加锁失败后的等待逻辑是怎么实现的
- 高并发场景:指数退避法
- 公平性要求:阻塞队列或等待唤醒
- 性能优先:简单重试+sleep
- 防止死锁:超时时间+直接返回
分库分表中的数据倾斜严重该怎么处理
- 分析为什么数据倾斜,是否有热点数据,热点数据具体原因具体分析
- 分片是否设计合理,选择更合理的分片算法
- 分片键+随机值打散
- 重新选择分片算法,重新分片
spring的事务管理
- 声明事事务,注解事务
- 编程式事务,使用transactionTemplate去包
- 事务管理机制transactionManager实现事务开启,提交,回滚
- 事务传播机制,required:如果有事务,则加入没有则创建。
- 事务回滚机制,指定异常回滚和RuntimeException自动回滚
事务有哪几种
- 原子性,一致性,隔离性,持久性
- 原子性:所有操作都必须是原子操作不可中断
- 一致性:A账户+100 = B账户 - 100
- 隔离性:两个事务执行时不允许相互影响
- 持久性:事务执行完成后,一定要落到表里
redis热key解决方案
- 多级缓存,在本地先缓存一层
- 对热key进行随机数
- 批量排队
- 业务限流
- 分布式多分片减少热key
- 使用更高效的数据结构
spring如何解决bean的依赖
- 三级缓存实现
- Bean A依赖 Bean B,Bean B依赖Bean A,当实例化A时,把A放到三级缓存中,这时发现A依赖B,将B实例化时,发现B依赖A,将A从缓存中拿过来实例化完B,将B放到一级存中,再将A实例化完成后放到一级缓存中
- scope 是 prototype时无法解决循环依赖问题
什么是重入锁,为什么需要重入锁
- 当线程获得一个锁时,当这个锁的代码需要再去调一个同样获得该锁的方法时,如果这个锁不是重入锁,它将发生死锁。
线程死锁
volatile
- 告诉编译器,每次取数据都从主内存中取。保证可见性,不保存原子性
- count++也不保存原子性
synchronized保证原子性
- 锁机制,在读取的时候就要等待操作完成后,才能读到值
java中如何实现cas操作
- unsafe的native方法
- 增加一个值,比较旧值,期望新值,返回true / false
- ABA问题,版本号、时间戳
synchronized
- 重量级锁
- 新版本升级锁
- 静态方法是class对象锁
- 普通方法是实例锁
reentryLock重入锁
- 与synchronized类似,只是更灵活
- 可以指定锁类型,可中断锁
threadLocal是什么
- threadLocal相当于是一个Map,key是当前线程名,value是泛型
- 由于是key是当前线程名,sevlet线程池线程不回收,threadLocal就无法被回收的
创建线程池的核心参数
- 核心线程数
- 最大线程数
- 队列
- 拒绝策略
- 线程数大于核心线程数时,空闲多久释放线程
- 时间单位
- 线程工厂
线程拒绝策略
- 线程队列满了,再来新的任务时
- 报错直接拒绝
- 不丢弃,让调用者线程去执行
- 不处理,直接丢弃
- 丢弃最早的任务
AQS抽象队列器
- 有个公共变量,如果变量没被占用,则当前线程为工作线程。如果被占用,需要有个队列将这些线程阻塞等待唤醒的机制。
- 获取不到锁就放到等待队列中
Semaphore(信号量)
- AQS中的公共变量是设定的,比如设定为5,那么5个工作线程分别获得共享资源后,后续的任务将进入队列等待
- 停车场,10个停车位,来了10辆车后,后面的车就要等。10辆车出去后,把资源让出来,后面才能进新的车
- Semaphore 更像是一个许可证,可以控制同时访问共享资源的线程数量。
CountDownLatch
- AQS中公共变量设定,比如设定为5,当每个任务执行完后自动阻塞,调用countDown,变量减1,变量为0后,主线程再执行
- 停车场里所有的车都离开了之后,才启动闸机。这个闸机就是CountDownLatch
- CountDownLatch 更像是一个倒计时器,用于等待多个线程完成任务。
Bloom过滤器
- 由多个hash函数散列到一个大的数组中
- 能判断一个对象一定不在这个数组中
- 判断一个对象在这个数组中不准确
- 判定一个数是否在这个大数组中。黑名单、缓存的前一层过滤器
数据表外键
- 不建议使用
- 在大的数据下,触发级联更新,全阻塞式,更新数据会引发数据风暴
- 分表不友好,无法建外键
myISAM
- 全文检索型,快,不支持事务,只支持表级锁,最大的问题是崩溃后不能安全恢复
- 不支持外键
- 非聚簇索引,查询时要回表,多一次IO
mysql三大日志
- binLog 归档日志
- redoLog 重做日志,恢复数据
- undoLog 回滚日志,事务回滚时的日志
redo log
- 更新了数据,记录redo log
- 事务提交,redo log刷盘
- 记录的是数据变更
- 事务提交前写入
- 保证事务提交前崩溃时,能够把数据还原回来
binLog
- 逻辑日志,记录数据变更的sql
- 事务提交后写入
- 同步数据
undo log
- 逻辑日志,删除一条数据,记录一条insert sql
- 回滚事务用的
redis 与 memCached
- redis支持更多的数据结构
- redis有容灾方案
- redis支持原生集群方案
- memCache是多路复用模型的多线程IO,redis 单线程IO
redis search
- 小型搜索引擎
- 数据限制
- 聚合能力较差
- 生态较差
redis 延时任务
- redis延时消息事件
- redis延时队列
zset原理
- 多层链表,每一层是下一层的索引
- 最底层包含所有元素,上一层是部分数据的索引,元素是被随机选为索引节点
- 查询是从顶层开始,直到找到数据
zset结构
- 跳表+hash
- 跳表用来查范围和排序
- hash用来记录分值
为什么使用跳表而不是二叉树
- 删除、更新、查询都是logN,但平衡树需要自旋达到平衡
- 算法更简单
HyperLogLog
- 大数据估算,适合大数据
- 数据不是精确值,但效率高
bitMap
- bit位的累计值
- bit位的范围查询
统计20亿用户统计多天的打卡总数
- setbit key offset value
- setbit 20241224 userId 1
- bitcount 20241224
- bitop
redis持久化机制
- RDB AOF
- RDB是某个时间点的快照,主从同步与重启恢复
- RDB会阻塞线程 或 fork线程出来非阻塞执行
- AOF默认更新一条数据,就写一条数据到缓冲区,再跟据磁盘策略刷盘
AOF
- AOF是命令提交成功后再写,与mysql不一致
- 不阻塞redis命令
pipeline与mulit命令区别
- pipeline是一次IO,多次执行,减少IO
- pipeline非原子,上次执行结果不影响后面执行结果
- mulit是多次IO,统一exec,原子操作
大key
- String超过1M
- 集合超过5000
- bigKey查大key
- 在从库查,避免阻塞
redis高可用
- 主从复制
- 哨兵模式,主从模式下的高可用
- 集群模式,hash槽
synchronized 底层原理
- monitor entry exit
- 获得对象锁计数器加1
- exit后,计数器减1
- 方法级别的不需要monitor entry 和 exit,只需要共享变量
为什么读锁不能升级成写锁
- 读锁是共享锁
- 写锁是独占锁
- 会产生死锁,前提是需要对方先释放锁
线程池工作原理
- 创建线程池时需要设定最小线程数,最大线程数,队列大小等
- 任务提交时,核心线程处理对应的任务
- 核心线程处理不及时时,放入队列
- 队列满了,激活最大线程
- 最大线程也处理不过来且队列又满了时,触发拒绝策略
JVM内存模型
- 堆、栈、本地内存
堆
- 所有对象创建时都在堆中
- 字符串常量池
栈
- 方法创建时的局部变量存在栈中
- 部分外部没有引用也不返回的局部对象存在栈中
java回收机制
- java把内存分为逻辑块 eden、S0、S1,老年代
- 创建对象时先放入eden区,当eden满了,触发minorGC
- 将还有引用的对象放入S0,计数+1
- 当S0满了,将S0 miniorGC后,复制到S1,计数+1
- 直到计数超过设定值后,没有被GC的对象复制到老年代
- 老年代内存快满时,触发majorGC,触发stop world
- 新晋升的对象无法放入老年代时,触发FullGC
CMS 、G1、ZGC
- CMS标记清除,碎片化严重
- G1,配置复杂,需要经验
- ZGC,需要更高的版本,java11
tomcat为什么需要打破双亲委派
- tomcat需要加载多个应用程序,每个应用程序中的类可能因为版本不同而不同
- 相同版本的类库需要共享复用
- tomcat是个容器,它有自己的类库,需要优先保证自己的类库优先加载和不被替换
- jsp等修改后,要能够自动加载,肯定需要重写类加载器
滑动窗口
- 固定时间内允许放出固定数的令牌,令牌数不累计
- 整个窗口期内,流量一定不会突破上线
- 令牌桶则是在固定时间放出固定数的令牌,令牌数累积
redis宕机了,如何保证数据恢复
- RDB & AOF
- RDB是快照,记录某一个时刻的数据
- AOF记录的是数据更新sql,并且记录速度更快更频繁
缓存穿透、击穿、雪崩
- 穿透:没有缓存。缓存Null或本地缓存或boolom过滤器拦截
- 击穿:有缓存,热key失效时,大量请求打到数据库。在读数据库时,加锁
- 雪崩:大量热key同时失效,造成数据库瞬间被打跨。避免热key同时失效,加锁
一致性hash是什么
- 一致性hash是将hash列表以环链的形式存在
- 将每个服务器以节点的形式映射到环上
- 作用是新加入的节点只需要在环上注册一个节点,并且将少量上就近节点的数据移到新节点上
- 解决节点增加与减少时,全量数据rehash的情况
redis为什么不使用一致性hash
- 因为redis提供了更高级的hash槽概念
- 默认为1万6的槽位,每个服务器接管一部分hash槽
- 每当有机器新加入时,只需要计算并接管部分hash槽
- 可以理解为hash槽是一致性hash的二级索引加持
- 有了这个二级索引,查数更快,映射内容更方便,数据节点容错与恢复更简单
B树与B+树
- B树任何节点都存数据,B+树只有叶子节点存数据
- B+树叶子节点是有序链表
- B树查询多条数据时,需要跨索引节点找。B+树可以按链表查
- B树高,B+数10亿内是3层,超过则4层
排查cpu过高
- 外部,是不是请求过高
- 内部,查找高cpu的服务,top命令
- 锁问题,排查是否有死锁或大量锁竞争 jstack
- IO问题,排查是否有大量的读写操作或网络IO
- 使用arthas排查调用次数、IO、耗时,cpu使用过高的线程
- 算法问题,实现的算法执行过多
jvm 内存模型
- 方法区:主要放类的定义,常量和一些编译后的代码,可以理解成元数据区
- 堆:java对象与数组创建存放的区域,GC主回收阵地
- 本地方法栈:native方法栈
- 虚拟机栈:方法调用时创建的栈,包含局部变量的创建和各种对象引用等,方法调用的入栈出栈
- 程序计数器:用来存代码执行行数的一小块内存
垃圾回收
- 标记清除:先标记哪些是活动对象,再清除未被标记的对象
- 复制算法:把标记为活着的对象复制到新的内存中,其他的全部清除
- 分代收集:新生代使用
为什么rocketMq能保证消息的顺序
- rocketMQ在底层是以消费者组的形式按顺序写入磁盘,所以,它的写是非常快的
- 读顺序因为只有一个group,虽然每次读都要2次IO,但实际上读也是按顺序读的
- kafka则因为写的时候按分片写入,虽然分片是顺序写,但多个分片是独立的,所以永远无法保证数据是全量顺序写
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南