java后端知识点总结
2025-08-12 23:36:45 3 举报
AI智能生成
java后端知识点总结
作者其他创作
大纲/内容
基本数据结构
基本数据类型
常见集合
HashMap&ConcurrentHashMap
JVM
内存划分
线程私有
程序计数器
存储下一条的指令地址
程序计数器私有主要是为了线程切换后能恢复到正确的执行位置
本地方法栈
虚拟机栈
Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用
线程共享
堆
新生代
Eden
s0
s1
老年代
永久代
方法区
常量池和静态区域
内存分配的两种方式
指针碰撞
Serial,ParNew
空闲列表
CMS
内存分配并发问题
CAS+失败重试
TLAB
垃圾回收
判断对象是否死亡
引用计数法
无法解决循环引用问题
可达性分析
可以从GCRoots出发
类加载过程
加载、链接、初始化
链接
验证、准备、解析
类加载器
启动类加载器
拓展类加载器
应用程序类加载器
双亲委派模型
JVM参数
https://javaguide.cn/java/jvm/jvm-parameters-intro.html#_2-%E5%A0%86%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3
xms,xmx,设置的是堆内存最小和最大大小
-XX:NewSize=256m
-XX:MaxNewSize=1024m
-XX:MaxNewSize=1024m
新生代
-Xmn256m
垃圾回收算法
标记-清除算法
复制算法
标记-整理算法
垃圾回收器
Serial
串行,新生代采用标记-复制算法,老年代采用标记-整理算法。
ParNew 收集器
串行,新生代采用标记-复制算法,老年代采用标记-整理算法。
Parallel Scavenge
新生代采用标记-复制算法,老年代采用标记-整理算法。
Serial Old 收集器
针对老年代
Parallel Old
老老年代,标记-整理算法
CMS 收集器
标记-清除
优点
并发收集、低停顿
缺点
对 CPU 资源敏感;
无法处理浮动垃圾
它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。
java14中移除
G1
满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征
并行与并发
分代收集
管理整个堆
空间整合
可预测的停顿
ZGC
ZGC 可以将暂停时间控制在几毫秒以内,且暂停时间不受堆内存大小的影响
分类
串行
并行
指的是垃圾回收器并行处理,此时用户线程还是停止状态
ParNew、 Parallel Scavenge、 Parallel old
并发
指的是垃圾回收线程和用户线程同时执行
CMS,G1
CMS
G1
总结
JVM线上问题排查和性能调优
spring框架
spring注解
https://javaguide.cn/system-design/framework/spring/spring-knowledge-and-questions-summary.html#%E6%B3%A8%E5%85%A5-bean-%E7%9A%84%E6%96%B9%E5%BC%8F%E6%9C%89%E5%93%AA%E4%BA%9B
Spring AOP
概念
面向切面编程:核心思想就是将横切关注点从核心业务逻辑中分离出来,形成一个个的切面
AOP 的目的是将横切关注点(如日志记录、事务管理、权限控制、接口限流、接口幂等等)从核心业务逻辑中分离出来,通过动态代理、字节码操作等技术,实现代码的复用和解耦,提高代码的可维护性和可扩展性
项目应用
日志记录
@OperationLog,接口上加入这个进行日志管理
事务管理
@Transactional
只能用于pulic方法
避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
事务的几种传播行为(7种)
REQUIRED
如果当前存在事务,则加入该事务
如果当前没有事务,则创建一个新事务
适用场景:大多数业务方法
如果当前没有事务,则创建一个新事务
适用场景:大多数业务方法
SUPPORTS
如果当前存在事务,则加入该事务
如果当前没有事务,则以非事务方式执行
适用场景:查询方法,可有可无事务
如果当前没有事务,则以非事务方式执行
适用场景:查询方法,可有可无事务
MANDATORY
如果当前存在事务,则加入该事务
如果当前没有事务,则抛出异常
适用场景:强制要求必须在事务中执行的方法
如果当前没有事务,则抛出异常
适用场景:强制要求必须在事务中执行的方法
REQUIRES_NEW
总是创建一个新事务
如果当前存在事务,则挂起当前事务
适用场景:需要独立事务的方法,如日志记录
如果当前存在事务,则挂起当前事务
适用场景:需要独立事务的方法,如日志记录
NOT_SUPPORTED
以非事务方式执行
如果当前存在事务,则挂起当前事务
适用场景:不需要事务支持的方法
如果当前存在事务,则挂起当前事务
适用场景:不需要事务支持的方法
NEVER
NESTED
权限控制
@Interface和@CommonUserRole
接口限流
@Async
原理
AOP实现
失效场景
private方法
static方法
Spring 的异步机制是通过代理实现的,由于静态方法不属于实例而是属于类且不参与继承,Spring 的代理机制(无论是基于 JDK 还是 CGLIB)无法拦截静态方法来提供如异步执行这样的增强功能
同一类中调用异步方法
通过动态代理实现,同一个类可能会导致动态代理失效
忘记开启异步支持
注解的方法所在的类必须是 Spring Bean
特有场景
未启用异步支持
线程池配置问题
可能遇到问题
异常处理
https://javaguide.cn/system-design/framework/spring/async.html#%E9%81%BF%E5%85%8D-async-%E6%B3%A8%E8%A7%A3%E5%A4%B1%E6%95%88
线程池资源导致问题
事务
https://mp.weixin.qq.com/s/FySv5L0bCdrlb5MoSfQtAA
@Transactional
只能作用于public方法
static方法失效
同类内部调用
失效特有场景
未启用事务管理
异常类型不正确
传播行为设置
总结
代理方式
静态代理
JDK 动态代理
如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,
基于接口实现
而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理 使用CGLIGB
字节码文件
AspectJ
基于类实现,加载类的.class文件,修改字节码文件生成他的子类,所以如果是final修饰,无法动态代理
IOC
概念
IoC (Inversion of Control )即控制反转/反转控制。它是一种思想不是一个技术实现。描述的是:Java 开发领域对象的创建以及管理的问题
将原本在程序中手动创建对象的控制权交给第三方比如 IoC 容器
对象之间的耦合度或者说依赖程度降低;
资源变的容易管理;
资源变的容易管理;
IoC 最常见以及最合理的实现方式叫做依赖注入(Dependency Injection,简称 DI)
应用
Bean
循环依赖
三级缓存(实际都是三个Map)
一级缓存
存储完整的Bean
二级缓存
避免多重循环依赖问题
避免重复创建对象
三级缓存
存放Bean工厂对象,用于生成原始Bean
https://blog.csdn.net/a745233700/article/details/110914620
spring中bean的线程安全
绝大部分 Bean 都可以声明为 singleton 作用域
唯一 bean 实例,Spring 中的 bean 默认都是单例的。也就是单例模式
类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中
BeanFactory 和FactoryBean
BeanFactory是Spring的核心IoC容器,负责管理Bean的生命周期和依赖关系;而FactoryBean是一种特殊的Bean,用于自定义复杂对象的创建逻辑。两者的核心区别体现在功能定位上:BeanFactory作为容器框架管理所有Bean,而FactoryBean作为扩展机制增强单个Bean的创建能力
bean生命周期(也就是bean的创建和销毁)
spring事务
https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486668&idx=2&sn=0381e8c836442f46bdc5367170234abb&chksm=cea24307f9d5ca11c96943b3ccfa1fc70dc97dd87d9c540388581f8fe6d805ff548dff5f6b5b&token=1776990505&lang=zh_CN#rd
事务是逻辑上的一组操作,要么都执行,要么都不执行
ACID
分类
编程式事务管理
侵入式管理
声明式事务管理
非侵入式管理,是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
spring设计模式
单例模式
单例模式有什么作用:
它的主要作用是确保一个类只有一个实例,并提供一个全局访问点
数据库连接池、线程池
日志管理log
配置管理
现代替代方案
spring里面的Bean
饿汉式
浪费内存
懒汉式
多线程下不安全
双重检测锁模式
如果先执行了1,3操作,另一个线程判断lazyman!=null,但其实还没有执行构造方法,初始化对象,所以要用下面的这种方式
双重检测锁模式(反射可以破坏单例模式)
枚举单例模式
子主题
模板模式
jdbcTemplate,以template结尾
动态代理模式
springboot
自动装配原理
springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refreshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean,而它是通过启动类的SpringBootApplication注解进行开始解析的,他会根据EnableAutoConfiguration开启自动化配置,这个里面还有一句@Import(AutoConfigurationImportSelector.class), 里面有个核心方法getCandidateConfigurations会调用loadFactoryNames(获取所有的加载配置,实现在下面),根据classpash路径以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解(@ConditionalOn…可能是class,property),先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器.
常用注解
spring cloud
注册中心
nacos
服务注册,发现
权重管理,健康检查
服务优雅上线下线
在线编辑,历史版本,一键回滚,灰度发布
zookeeper
spring cloud gateway
请求路由:根据请求本身的属性把请求转发到不同的微服务
权限校验:某些业务场景在处理用户请求时需要先对用户进行权限校验
限流
流量统计
灰度
ngix
反向代理
负载均衡
nginx和gateway区别
Nginx
流量网关,与业务网关相反,定义全局性的、跟具体的后端业务应用和服务完全无关的策略网关。流量网关通常只专注于全局的Api管理策略,比如全局流量监控、日志记录、全局限流、黑白名单控制、接入请求到业务系统的负载均衡等
gateway
业务网关:对于具体的后端业务应用或者是服务和业务有一定关联性的策略网关。业务网关针对具体的业务需要提供特定的流控策略、缓存策略、鉴权认证策略
RPC框架
Dubbo
OpenFeign
并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
负载均衡
Ribbon
停止维护-netflix
LoadBalancer
官方组件,只有轮询和随机负载策略
seata
sentinel
RocketMQ
消息队列作用:
异步,解耦,削峰填谷
将耗时的任务异步化,通过消息队列缓存任务,从而实现消息发送方和接收方的解耦,使得任务的处理能够异步、并行,从而提高系统或集群的吞吐量和可扩展性
消息种类
消息发送的三种模式
同步、异步、单向
可选择同步、异步或单向发送。同步:Producer 发出一条消息后,会在收到 MQ 返回的 ACK 之后再发送下一条消息。异步:Producer 发出消息后无需等待 MQ 返回 ACK ,直接发送下一条消息。单向: Producer 仅负责发送消息,不等待,MQ 也不返回 ACK。
两种消息消费模式
集群模式
一个消息只会被同一个消费组内的消费者中的一个消费
广播模式
消息会被推送到消费组内的所有消费者,保证每个消费者都消费到
消息类型
顺序消息
全局顺序
对于指定的一个 Topic ,所有消息按照严格的先入先出的顺序进行发布和消费 (同一个 queue)
分区顺序
对于一个指定的 Topic ,所有消息根据 sharding key 进行分区,同一个分区内的消息按照严格的 FIFO 顺序进行发布和消费,分区之间彼此独立。
延时消息
比如电商里,提交了一个订单就可以发送一个延时消息,1h后去检查这个订单的状态,如果还是未付款就取消订单释放库存。
使用限制
不支持任意时间的延时,需要设置几个固定的延时等级,从1s到2h分别对应着等级1到18
批量消息
批量消息应该有相同的topic
超过4M最好分割发送
过滤消息
消费者将接收包含TAGA或TAGB或TAGC的消息。但是限制是一个消息只能有一个标签
consumer.subscribe("TOPIC", "TAGA || TAGB || TAGC");
SQL
consumer.subscribe("TopicTest", MessageSelector.bySql("a between 0 and 3");
事务消息
缺图
https://mp.weixin.qq.com/s/EEkjBrVYQFwBiGQObrM_TQ
1、producer发送消息给mq服务端;
2、服务端持久化消息后发送ACK确认收到(此时消息为半消息);
3、producer执行本地事务;
4、根据本地事务执行结果向服务端提交二次确认(COMMIT,ROLLBACK);服务端收到后决定是把消息标记为可投递,还是删除半消息;
5、在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后 MQ Server 将对该消息发起消息回查。
6、producer收到消息回查后,检查对应消息本地事务执行结果;
7、发送方根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server 仍按照步骤 4 对半消息进行操作。
2、服务端持久化消息后发送ACK确认收到(此时消息为半消息);
3、producer执行本地事务;
4、根据本地事务执行结果向服务端提交二次确认(COMMIT,ROLLBACK);服务端收到后决定是把消息标记为可投递,还是删除半消息;
5、在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后 MQ Server 将对该消息发起消息回查。
6、producer收到消息回查后,检查对应消息本地事务执行结果;
7、发送方根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server 仍按照步骤 4 对半消息进行操作。
事务消息状态
* TransactionStatus.CommitTransaction: 提交事务,它允许消费者消费此消息。
* TransactionStatus.RollbackTransaction: 回滚事务,它代表该消息将被删除,不允许被消费。
* TransactionStatus.Unknown: 中间状态,它代表需要检查消息队列来确定状态。
* TransactionStatus.RollbackTransaction: 回滚事务,它代表该消息将被删除,不允许被消费。
* TransactionStatus.Unknown: 中间状态,它代表需要检查消息队列来确定状态。
使用限制
事务消息不支持延时消息和批量消息。
broker检查消息状态有次数限制
1. 事务消息不支持延时消息和批量消息。
2. 为了避免单个消息被检查太多次而导致半队列消息累积,我们默认将单个消息的检查次数限制为 15 次,但是用户可以通过 Broker 配置文件的 `transactionCheckMax`参数来修改此限制。如果已经检查某条消息超过 N 次的话( N = `transactionCheckMax` ) 则 Broker 将丢弃此消息,并在默认情况下同时打印错误日志。用户可以通过重写 `AbstractTransactionCheckListener` 类来修改这个行为。
3. 事务消息将在 Broker 配置文件中的参数 transactionMsgTimeout 这样的特定时间长度之后被检查。当发送事务消息时,用户还可以通过设置用户属性 CHECK_IMMUNITY_TIME_IN_SECONDS 来改变这个限制,该参数优先于 `transactionMsgTimeout` 参数。
4. 事务性消息可能不止一次被检查或消费。
5. 提交给用户的目标主题消息可能会失败,目前这依日志的记录而定。它的高可用性通过 RocketMQ 本身的高可用性机制来保证,如果希望确保事务消息不丢失、并且事务完整性得到保证,建议使用同步的双重写入机制。
6. 事务消息的生产者 ID 不能与其他类型消息的生产者 ID 共享。与其他类型的消息不同,事务消息允许反向查询、MQ服务器能通过它们的生产者 ID 查询到消费者。
2. 为了避免单个消息被检查太多次而导致半队列消息累积,我们默认将单个消息的检查次数限制为 15 次,但是用户可以通过 Broker 配置文件的 `transactionCheckMax`参数来修改此限制。如果已经检查某条消息超过 N 次的话( N = `transactionCheckMax` ) 则 Broker 将丢弃此消息,并在默认情况下同时打印错误日志。用户可以通过重写 `AbstractTransactionCheckListener` 类来修改这个行为。
3. 事务消息将在 Broker 配置文件中的参数 transactionMsgTimeout 这样的特定时间长度之后被检查。当发送事务消息时,用户还可以通过设置用户属性 CHECK_IMMUNITY_TIME_IN_SECONDS 来改变这个限制,该参数优先于 `transactionMsgTimeout` 参数。
4. 事务性消息可能不止一次被检查或消费。
5. 提交给用户的目标主题消息可能会失败,目前这依日志的记录而定。它的高可用性通过 RocketMQ 本身的高可用性机制来保证,如果希望确保事务消息不丢失、并且事务完整性得到保证,建议使用同步的双重写入机制。
6. 事务消息的生产者 ID 不能与其他类型消息的生产者 ID 共享。与其他类型的消息不同,事务消息允许反向查询、MQ服务器能通过它们的生产者 ID 查询到消费者。
不理解??
消息存储& 发送
存储结构
文件系统
数据库MYSQL----不用
存储流程
缺图
采用顺序写,速度比较快
消息发送:采用“零拷贝”技术,提高消息存盘和网络发送的速度。
台服务器 把本机磁盘文件的内容发送到客户端,一般分为读和写,实际是四个过程
1. 从磁盘复制数据到内核态内存;
2. 从内核态内存复 制到用户态内存;
3. 然后从用户态 内存复制到网络驱动的内核态内存;
4. 最后是从网络驱动的内核态内存复 制到网卡中进行传输。
2. 从内核态内存复 制到用户态内存;
3. 然后从用户态 内存复制到网络驱动的内核态内存;
4. 最后是从网络驱动的内核态内存复 制到网卡中进行传输。
存储结构
缺个图
ConsumeQueue
消息的逻辑队列,存储的是指向物理存储的地址
CommitLog
真正的物理存储文件
IndexFile
为了消息查询提供了一种通过key或时间区间来查询消息的方法,这种通过IndexFile来查找消息的方法不影响发送与消费消息的主流程
刷盘机制
同步
SYNC_FLUSH
异步
ASYNC_FLUSH
高可用
broker的主从机制
brokerId为0表示master,>1表示slave
消费高可用
自动切换Consumer这种机制,当一个Master角色的机器出现故障后,Consumer仍然可以从Slave读取消息,不影响Consumer程序。这就达到了消费端的高可用性。
发送高可用
当一个Broker组的Master不可 用后,其他组的Master仍然可用,Producer仍然可以发送消息
消息主从复制
如果一个Broker组有Master和Slave,消息需要从Master复制到Slave 上,有同步和异步两种复制方式。
同步复制
数据完整性高,增大数据写入 延迟,降低系统吞吐量
异步复制
统拥有较低的延迟和较高的吞吐量,但是如果Master出了故障,有些数据因为没有被写 入Slave,有可能会丢失
说明:结合刷盘机制,一般设置为,异步刷盘&主从同步复制
负载均衡
Producer负载均衡
roducer端,每个实例在发消息的时候,默认会轮询所有的message queue发送,以达到让消息平均落在不同的queue上
Consumer负载均衡
集群模式
每条消息只需要投递到订阅这个topic的Consumer Group下的一个实例即可
RocketMQ采用主动拉取的方式拉取并消费消息
广播模式
由于广播模式下要求一条消息需要投递到一个消费组下面所有的消费者实例,所以也就没有消息被分摊消费的说法
死信队列
当一条消息初次消费失败,消息队列 RocketMQ 会自动进行消息重试;达到最大重试次数后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息,此时,消息队列 RocketMQ 不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中。
死信消息具有以下特性
- 不会再被消费者正常消费。
- 有效期与正常消息相同,均为 3 天,3 天后会被自动删除。因此,请在死信消息产生后的 3 天内及时处理
- 有效期与正常消息相同,均为 3 天,3 天后会被自动删除。因此,请在死信消息产生后的 3 天内及时处理
死信队列具有以下特性:
- 一个死信队列对应一个 Group ID, 而不是对应单个消费者实例。
- 如果一个 Group ID 未产生死信消息,消息队列 RocketMQ 不会为其创建相应的死信队列。
- 一个死信队列包含了对应 Group ID 产生的所有死信消息,不论该消息属于哪个 Topic。
- 如果一个 Group ID 未产生死信消息,消息队列 RocketMQ 不会为其创建相应的死信队列。
- 一个死信队列包含了对应 Group ID 产生的所有死信消息,不论该消息属于哪个 Topic。
如何保证顺序消费、如何解决重复消费
顺序消费
只能保证局部顺序,配合分布式锁,同一个消费者组下同一个消息队列只会被一个消费者客户端获取
重复消费
幂等性
https://mp.weixin.qq.com/s/EEkjBrVYQFwBiGQObrM_TQ
副作用
重复消费,顺序消费,分布式事务问题,消息堆积问题
MySQL
基础引擎
存储引擎,innodb和myIsam的区别
https://javaguide.cn/database/mysql/mysql-questions-01.html#myisam-%E5%92%8C-innodb-%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB
是否支持行锁(都支持表锁),是否支持事务(myisam航行锁都不支持,怎么支持事务),是否支持外键,是否支持MVCC
都是B+树,但是索引实现方式不同
innodb数据文件和索引文件放一起
myisam索引文件和数据文件是分开的
性能
InnoDB 的性能比 MyISAM 更强大
些读密集的情况下,使用 MyISAM 也是合适的
基本语法使用
DDL和DML区别
DDL是数据定义语言
create,alter,drop,truncate
不能回滚
DML是数据操作语言
增删改查
支持回滚
delete,drop,trunctate
delete不加where和trancate一样,只是删除数据,drop是删除表结构
基本数据类型
CHAR和VARCHAR
前者是定长,后者是变长
DATETIME和TIMESTAMP
前者和时区无关,时间范围比较大
后者和时区有有关,时间范围比较小,1970~2037,更好的索引性能
前者是8个字节,后面是4个字节
DATE只有年月日,没有后面分钟秒
不要用字符串存储时间,效率低下,时间戳也可以存储时间
DECIMAL 和 FLOAT/DOUBLE 的区别是什么?
DECIMAL 和 FLOAT 的区别是:DECIMAL 是定点数,FLOAT/DOUBLE 是浮点数。DECIMAL 可以存储精确的小数值,FLOAT/DOUBLE 只能存储近似的小数值。DECIMAL 用于存储具有精度要求的小数,例如与货币相关的数据,可以避免浮点数带来的精度损失。在 Java 中,MySQL 的 DECIMAL 类型对应的是 Java 类 java.math.BigDecimal
不推荐使用BLOB和TEXT类型
不能有默认值。在使用临时表时无法使用内存临时表,只能在磁盘上创建临时表(《高性能 MySQL》书中有提到)。检索效率较低。不能直接创建索引,需要指定前缀长度。可能会消耗大量的网络和 IO 带宽。可能导致表上的 DML 操作变慢。
https://javaguide.cn/database/sql/sql-syntax-summary.html
null和' '的区别
事务ACID
原子性
undo log保障
MVCC也需要用到
一致性
其他三大特性来保障
个事务执行之前和执行之后数据库都必须处于一致性状态
隔离性
mvcc
并发环境下,不同的事务的修改要隔离
持久性
redo log
mysql,NoSql
https://javaguide.cn/database/nosql.html#sql-%E5%92%8C-nosql-%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB
mysql,redis的区别
锁分类
几种行锁
记录锁(Record Lock)
单个行记录上的锁
间隙锁(gap lock)
保证某个间隙内的数据在锁定情况下不会发生任何变化。比如mysql默认隔离级别下的可重复读(RR)。(锁定某个区间,不包括本行)
临键锁(Next-key Locks)
临键锁是行锁+间隙锁,即临键锁是是一个左开右闭的区间
粒度
表级锁
select * from student where name = 'tom' for update
行级锁
select * from student where id > 10 for update
级别
共享锁(读锁)
排它锁(写锁)
意向锁
OLTP和OLAP
OLTP(transaction)
联机事务处理
处理数据库事务
存储的主要是与业务直接相关的数据,强调准确、低时延、高并发
OLAP(analysis)
联机分析处理
以多维数据集格式存储数据,主要是用来数据分析,
HBase,Hive
并发事务
存在问题
脏读
丢失修改(暂时不记忆)
不可重复读
不可重复读的重点是内容修改或者记录减少比如多次读取一条记录发现其中某些记录的值被修改
针对的是某一条数据多次查询结果不一致
幻读
幻读的重点在于记录新增比如多次执行同一条查询语句(DQL)时,发现查到的记录增加了
针对的是查询的多条记录结果不一致
总结
事务隔离级别
读未提交
读已提交
MVCC可以解决
可重复读(默认级别)
MVCC可以解决
串行化
基于锁实现
MVCC+临键锁
解决幻读问题
对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读
串行化,效率比较低
并发事务控制方式
锁
MVCC
多版本并发控制
就是维护一个数据的多个版本,主要是为了提高数据库并发性能,它能很好地处理MySQL的读写冲突,做到尽量不加锁,大大降低系统的开销,使得读写不阻塞
是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别
是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别
可以解决脏读,不可重复读;和临键锁一起可以解决幻读问题
MVCC的两种方式
快照读
默认
当前读
for update
MVCC原理
通过3个隐藏字段,undolog,read view
RR级别下如何解决幻读问题
MVCC+ nextkeylock
快照读依靠MVCC控制,当前读通过 next-key lock 解决(MVCC 解决了快照读情况下的幻读,next-key lock 解决当前读情况下的幻读)。
日志
slow query log
慢查询日志
binlog
二进制日志
记录更改数据库的所有操作
应用场景
主从复制
数据恢复
数据库级别的任务恢复
redo-log
重做日志
保证事务持久性(已经提交的事务持久化数据库当中)
binlog是数据库级别的任务恢复,redo-log事务级别的数据恢复
对于已经COMMIT的事务产生的数据变更,即使是系统宕机崩溃也可以通过它来进行数据重做,达到数据的持久性
undo log
撤销日志
保证事务的原子性,提供事务回滚能力
MVCC的实现
多版本并发控制方法
用于在多个并发事务同时读写数据库时保持数据的一致性和隔离性。它是通过在每个数据行上维护多个版本的数据来实现的。当一个事务要对数据库中的数据进行修改时,MVCC 会为该事务创建一个数据快照,而不是直接修改实际的数据行。
读写分离&分库分表
分库分表
ShardingSphere-jdbc进行分表
数据迁移问题
备份数据
老表数据备份到新表数据
预计好几天,所以放弃
双写方案
读写分离
读写分离基于主从复制,MySQL 主从复制是依赖于 binlog
分库 就是将数据库中的数据分散到不同的数据库上。分表 就是对单表的数据进行拆分,可以是垂直拆分,也可以是水平拆分
ShardingSphere-jdbc原理
性能优化
索引类型
底层结构划分
聚簇索引
索引结构和数据一起放的索引
InnoDB 中的主键索引就属于聚簇索引
非聚簇索引
索引结构和数据分开放的索引
myisam引擎的索引都是这种类型,innodb的辅助索引也都是这种类型
应用维度
主键索引
不能为NULL
非叶子节点存储索引,叶子节点存储索引和索引对应的数据
普通索引
唯一索引、
可以有NULL
覆盖索引
查询所需的所有列都包含在索引中。这意味着数据库可以完全从索引中读取数据,而不必访问表,减少IO次数,从而显著提高查询性能
联合索引
多列值组成一个索引,专门用于组合搜索
全文索引:对文本的内容进行分词,进行搜索。目前只有 CHAR、VARCHAR、TEXT 列上可以创建全文索引。一般不会使用,效率较低
如何优化慢SQL
怎么发现慢SQL
Slow Query Log
公司平台
如何优化
EXPLAIN进行执行计划分析
type,表的访问方法
key
实际使用的索引,如果是NULL,表示没有用到索引
rows:预估扫描多少行才能得到所需要的数据
Extra
Using filesort:在排序时使用了外部的索引排序,没有用到表内索引进行排序。
Using temporary:MySQL 需要创建临时表来存储查询的结果,常见于 ORDER BY 和 GROUP BY。
Using index:表明查询使用了覆盖索引,不用回表,查询效率非常高。
Using index condition:表示查询优化器选择使用了索引条件下推这个特性。
Using where:表明查询使用了 WHERE 子句进行条件过滤。一般在没有使用到索引的时候会出现。
Using join buffer (Block Nested Loop):连表查询的方式,表示当被驱动表的没有使用索引的时候,MySQL 会先将驱动表读出来放到 join buffer 中,再遍历被驱动表与驱动表进行查询。
Using temporary:MySQL 需要创建临时表来存储查询的结果,常见于 ORDER BY 和 GROUP BY。
Using index:表明查询使用了覆盖索引,不用回表,查询效率非常高。
Using index condition:表示查询优化器选择使用了索引条件下推这个特性。
Using where:表明查询使用了 WHERE 子句进行条件过滤。一般在没有使用到索引的时候会出现。
Using join buffer (Block Nested Loop):连表查询的方式,表示当被驱动表的没有使用索引的时候,MySQL 会先将驱动表读出来放到 join buffer 中,再遍历被驱动表与驱动表进行查询。
优化措施
建立合适的索引,使用过程中,避免索引失效的场景
建立索引
最左前缀原则
创建索引
覆盖索引
like
函数计算
索引失效的几种场景
建立不当或者没有按照最左匹配原则
遇到范围查询会停止匹配
索引列使用了函数
SUM(**)
索引列有计算
B-1=5
like匹配 %sss
使用OR,OR前后的几个字段不是同时有索引
b=1 or c=2
IN使用不当
取值范围较大时可能不走索引,大于30%
not in,not exists
同上
order by使用不当
不走索引 走全表,相比走索引+回表可能更快
语法优化
避免select *尽量使用具体字段
不需要的字段可能 会导致回表查询
使用连接查询代替子查询
不需要建立临时表,子查询的临时表没有索引
合理使用分页查询
深度分页优化
范围查询
子查询
# 通过子查询来获取 id 的起始值,把 limit 1000000 的条件转移到子查询
SELECT * FROM t_order WHERE id >= (SELECT id FROM t_order where id > 1000000 limit 1) LIMIT 10;
SELECT * FROM t_order WHERE id >= (SELECT id FROM t_order where id > 1000000 limit 1) LIMIT 10;
延迟关联
-- 使用 INNER JOIN 进行延迟关联
SELECT t1.* FROM t_order t1 INNER JOIN (SELECT id FROM t_order where id > 1000000 LIMIT 10) t2 ON t1.id = t2.id;
SELECT t1.* FROM t_order t1 INNER JOIN (SELECT id FROM t_order where id > 1000000 LIMIT 10) t2 ON t1.id = t2.id;
覆盖索引
避免回表操作
随机IO变成顺序IO
避免多表JOIN
三个以内
自己关联
表里面做冗余数据
不要用外键
union all 代替UNION
批量操作
减少回表次数
覆盖索引
随机IO变成顺序IO
索引下推的概念
COUNT(*)COUNT(列名)、COUNT(常量)和COUNT(*)之间的区别
数据库表结构
根据字段使用频率进行大表拆分
架构层面
合适的数据类型,读写分离,分布式架构
使用shardingsphere进行分表后,如何进行分页查询
插入性能会提高,查询性能会降低?
https://3ms.huawei.com/km/groups/2027333/blogs/details/18196641#2.2-%E6%95%B0%E6%8D%AE%E5%88%86%E7%89%87
https://www.cnblogs.com/cg-ww/p/16614454.html#%E5%88%86%E5%B8%83%E5%88%86%E8%A1%A8%E5%BA%94%E7%94%A8%E5%92%8C%E9%97%AE%E9%A2%98
基本原理
https://blog.csdn.net/zuoshengdong/article/details/106454888
业务层的SQL传递给JDBC中间层,经过SQL解析,查询优化,SQL路由,SQL改写,SQL执行,结果归并,最终返回给业务结果
MyBatis
缓存问题
一级缓存
一级缓存是指SQLSession,一级缓存的作用域是SQlSession, Mabits默认开启一级缓存。
SQLSession级别,默认开启,无法关闭,多个SQLSession不共享,Session 关闭或执行更新操作的时候才会清空缓存
短周期重复查询
二级缓存
二级缓存是mapper级别的,Mybatis默认是没有开启二级缓存的。
可以配置开启,多个SQLSession共享,适用于长时间不变的数据查询
#{}和${}区别
#{}:SQL参数占位符替换为?,可以防止SQL注入,变量替换后,会自动加上单引号
${}:拼接符,不能防止SQL注入,变量替换后,不会加上单引号,也可以表示properties文件中的变量占位符
${}:拼接符,不能防止SQL注入,变量替换后,不会加上单引号,也可以表示properties文件中的变量占位符
常见标签
<resultMap>、 <parameterMap>、 <sql>、 <include>、 <selectKey>、trim|where|set|foreach|if|choose|when|otherwise|bind。
系统设计
认证和授权
认证和授权
认证 (Authentication)
你是谁
授权 (Authorization)
你有权限干什么
RBAC模型
子主题
session和cookie
Session 的主要作用就是通过服务端记录用户的状态。 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。
Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。相对来说 Session 安全性更高。如果使用 Cookie 的一些敏感信息不要写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密
验证方案
JWT
定义:JWT (JSON Web Token)
https://javaguide.cn/system-design/security/advantages-and-disadvantages-of-jwt.html#%E9%80%82%E5%90%88%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%BA%94%E7%94%A8
https://javaguide.cn/system-design/security/advantages-and-disadvantages-of-jwt.html#%E9%80%82%E5%90%88%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%BA%94%E7%94%A8
一种基于 Token 的认证授权机制。 从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token
组成部分
头信息(Header)
JWT的元数据,包括签名算法和token的类型
有效载荷(Payload)
实际要传输的数据
默认不加密,不可以存放敏感信息
签名(Signature)
服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成,防止被篡改,密钥是存放在服务端,不能被泄露出去。
xxxxx.yyyyy.zzzzz
总体公式
HMACSHA256(
base64UrlEncode(header) + "." +base64UrlEncode(payload)+ '.'+secret)
base64UrlEncode(header) + "." +base64UrlEncode(payload)+ '.'+secret)
JWT验证过程
用户向服务器发送用户名、密码以及验证码用于登陆系统。如果用户用户名、密码以及验证码校验正确的话,服务端会返回已经签名的 Token,也就是 JWT。用户以后每次向后端发请求都在 Header 中带上这个 JWT 。服务端检查 JWT 并从中获取用户相关信息。
建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。请求服务端并携带 JWT 的常见做法是将其放在 HTTP Header 的 Authorization 字段中;;
密钥一定保管好,一定不要泄露出去。JWT 安全的核心在于签名,签名安全的核心在密钥。
密钥一定保管好,一定不要泄露出去。JWT 安全的核心在于签名,签名安全的核心在密钥。
注销等场景下JWT依然有效问题
存放在Redis当中
过期就删除
加入黑名单当中
先判断是否在黑名单当中
单点登录
https://javaguide.cn/system-design/security/sso-intro.html#%E8%B7%A8%E5%9F%9F%E7%99%BB%E5%BD%95%E3%80%81%E7%99%BB%E5%87%BA
Redis
redis对象/数据类型
string
list
hashmap
set
有序集合zset
相比Set 类型多了一个排序属性 score
可以用来排行榜
底层实现一般是由压缩列表或者跳表
hyperloglog(基数统计)
数量巨大(百万、千万级别以上)的计数场景
BitMap位存储
用户签到情况、活跃用户情况
geospatial(地理 位置)
附近的人
redis底层数据结构(上面五种基本数据类型的底层实现)
动态字符串SDS
链表
字典-Dict
跳表-skiplist
整数集合IntSet
压缩列表ziplist
quicklist
SETNX
set if not exist
实现redis分布式锁
re
幂等性
https://mp.weixin.qq.com/s/yvKASWcRLfOok-NFPrIRsw
redis
为什么那么快
单线程,省去了上下文切换,也不需要锁的性能消耗
基于内存,读写速度快
I/O多路复用,可以处理大量的客户端 Socket 请求
性能瓶颈
内存、网络,不是CPU
IO 多路复用机制是指一个线程处理多个 IO 流
持久化方式
RDB
快照持久化
RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储
适合大规模的数据恢复
对数据完整性要求不高
AOF
追加文件
把所有命令都记录下来(读操作不记录),恢复的时候把所有命令都再执行一边。所以比较慢
运行效率也比rdb慢,Redis默认配置就是rdb持久化
数据更完整
redis主从复制
数据冗余备份
故障回复
负载均衡
集群模式,高可用
如何解决redis缓存和数据库一致性
同步删除:更新数据库,删除缓存
如果业务量不大,这种方案基本上也能满足需求。
延迟双删
删除缓存,更新数据库,删除缓存,等一会再删除缓存
监听binlog配合消息队列删除+重试
更新请求过来先更新数据库。
缓存管理服务订阅binlog,将删除缓存消息推送至消息队列来删除缓存。
如果删除失败则进行重试。
缓存管理服务订阅binlog,将删除缓存消息推送至消息队列来删除缓存。
如果删除失败则进行重试。
https://blog.csdn.net/weixin_45433817/article/details/137750937
子主题
缓存雪崩、缓存穿透、缓存击穿、缓存预热、缓存更新、缓存降级
缓存雪崩
缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉
缓存穿透(缓存和数据库当中都没有的数据)
布隆过滤器
缓存击穿
由于某个key比较热点,不停的扛着大并发,集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库的数据。
其他问题
big key:这个key值存储的value过大
问题:
性能下降,读写的时候阻塞服务
删除key的时候会阻塞
分批删除
集群场景下,会导致数据倾斜
解决
拆分成多个key,用mget
大value存储到hash里面,用hget
Linux&docker
linux
docker
多线程&高并发
分布式ID
数据库自增
支持并发量不大
UUID(项目用的)
生成速度通常比较快、简单易用
存储消耗空间大(32 个字符串,128 位)、 不安全(基于 MAC 地址生成 UUID 的算法会造成 MAC 地址泄露)、无序(非自增)、没有具体业务含义、需要解决重复 ID 问题(当机器时间不对的情况下,可能导致会产生重复 ID
雪花算法
需要解决重复 ID 问题(ID 生成依赖时间,在获取时间的时候,可能会出现时间回拨的问题,也就是服务器上的时间突然倒退到之前的时间,进而导致会产生重复 ID)、依赖机器 ID 对分布式环境不友好(当需要自动启停或增减机器时,固定的机器 ID 可能不够灵活)
生成速度比较快、生成的 ID 有序递增、比较灵活
线程池
创建线程的几种方式(本质上只有一种,Thread.run())
https://mp.weixin.qq.com/s/NspUsyhEmKnJ-4OprRFp9g
Thread
runnable
callable
线程池
compteableFuture
Thread.run和start方法区别
start:用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run: run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
线程池介绍
https://javaguide.cn/java/concurrent/java-thread-pool-summary.html#singlethreadexecutor
线程池好处
降低资源消耗
重复利用已创建的线程降低线程创建和销毁造成的消耗
提高响应速度
当任务到达时,任务可以不需要等到线程创建就能立即执行
方便管理线程
线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
创建线程池的两种方式
通过Excutors创建(不推荐)
FixedThreadPool
固定线程数量的线程池
SingleThreadExecutor
只有一个线程的线程池
CachedThreadPool
可根据实际情况调整线程数量的线程池。线程池的线程数量不确
ScheduledThreadPool
给定的延迟后运行任务或者定期执行任务的线程池
SingleThreadScheduledExecutor
不推荐原因
通过new ThreadPoolExecutor创建
几个参数介绍
核心线程
最大线程数量
动态线程池
CPU密集型
这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1。比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
核心线程数量等于最大线程数量,过多的线程会导致频繁上下文切换,降低性能
I/O密集型
这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。
实际中考虑因素
考虑JVM内存(每个线程需要栈内存,默认1MB,可通过-Xss调整)
任务执行时间长短
任务是否相互独立
业务吞吐量
响应时间
最佳实践
避免使用无界队列:可能导致OOM
合理设置线程存活时间:keepAliveTime通常设为60s
使用有意义的线程名称:便于问题排查
考虑使用线程池工厂:统一管理线程池配置
监控指标
活跃线程数:executor.getActiveCount()
队列大小:executor.getQueue().size()
完成任务数:executor.getCompletedTaskCount()
拒绝任务数:可通过RejectedExecutionHandler统计
动态修改线程池参数
https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html
手写动态线程池
https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247533016&idx=1&sn=845cbf76d0bc749e74c5288dba7a9935&scene=21#wechat_redirect
核心要点
通过set方法修改线程池参数
@RefreshScope这个注解用来支持 nacos 的动态刷新功能
nacosConfigManager.getConfigService().addListener:配置监听,nacos 配置变更时实时修改线程池的配置。
可以再加一个controller打印线程池的具体信息
任务队列,用来存储排队的任务
LinkedBlockingQueue
有界阻塞队列
FixedThreadPool
SingleThreadExecutor
DelayedWorkQueue
延迟队列
ScheduledThreadPool
DelayedWorkQueue 添加元素满了之后会自动扩容,增加原来容量的 50%,即永远不会阻塞,最大扩容可达 Integer.MAX_VALUE,所以最多只能创建核心线程数的线程。
SingleThreadScheduledExecutor
SynchronousQueue
同步队列
CachedThreadPool
没有容量,不存储元素,目的是保证对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务。也就是说,CachedThreadPool 的最大线程数是 Integer.MAX_VALUE ,可以理解为线程数是可以无限扩展的,可能会创建大量线程,从而导致 OOM。
ArrayBlockingQueue
有界阻塞队列
底层由数组实现,容量一旦创建,就不能修改
空闲线程存活时间
线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁。
存活时间单位
拒绝策略
提交任务超过最大线程池数量时任务拒绝策略
AbortPolicy
抛异常拒绝新任务
CallerRunsPolicy
调用执行者自己的线程运行任务,也就是直接在调用execute方法的线程中运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务,会导致住线程执行线程池里面的任务导致其他请求会阻塞。影响程序的正常运行。
不允许丢弃任务可以选择这个
DiscardPolicy
不处理新任务,直接丢弃掉
DiscardOldestPolicy
略将丢弃最早的未处理的任务请求。
有没有办法既能保证任务不被丢弃且在服务器有余力时及时处理呢
任务持久化
放到MySQL
放到Redis
任务提交到消息队列里面。
线程池处理流程
线程池异常后,销毁还是复用
使用execute()提交任务:当任务通过execute()提交到线程池并在执行过程中抛出异常时,如果这个异常没有在任务内被捕获,那么该异常会导致当前线程终止,并且异常会被打印到控制台或日志文件中。线程池会检测到这种线程终止,并创建一个新线程来替换它,从而保持配置的线程数不变。
使用submit()提交任务:对于通过submit()提交的任务,如果在任务执行中发生异常,这个异常不会直接打印出来。相反,异常会被封装在由submit()返回的Future对象中。当调用Future.get()方法时,可以捕获到一个ExecutionException。在这种情况下,线程不会因为异常而终止,它会继续存在于线程池中,准备执行后续的任务。
https://mp.weixin.qq.com/s/9ODjdUU-EwQFF5PrnzOGfw
使用线程池的一些注意事项
https://javaguide.cn/java/concurrent/java-thread-pool-best-practices.html#spring-%E5%86%85%E9%83%A8%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E5%9D%91
AQS队列
https://javaguide.cn/java/concurrent/aqs.html#aqs-%E6%A0%B8%E5%BF%83%E6%80%9D%E6%83%B3
https://mp.weixin.qq.com/s/jEx-4XhNGOFdCo4Nou5tqg
抽象队列同步器,AQS 为构建锁和同步器提供了一些通用功能的实现, 帮助我们快速的构建锁,比如Retreentlock,CountDownLanch,Semophere
Atomic
Atomic 原子操作类是基于无锁 CAS + volatile 实现的,并且类中的所有方法都使用 final 修饰,进一步保证线程安全;volatile可以保证有序性,可见性,CAS保证原子性
乐观锁实现,修改失败则重试
存在ABA问题
Theadlocal
是一个将在多线程中为每一个线程创建单独的变量副本的类; 当使用ThreadLocal来维护变量时, ThreadLocal会为每个线程创建单独的变量副本, 避免因多线程操作共享变量而导致的数据不一致的情况。
ThreadLocalMap 的 key 是 ThreadLocal 对象,value 是线程本地变量的值。这种设计使得每个线程都可以通过自己的 ThreadLocalMap 获取到属于自己的本地变量,实现了线程隔离的效果
内存泄漏
key是弱引用,value是强引用,所以垃圾回收的时候,key会被回收掉,但是value不会被回收,而 Entry 又被 threadLocalMap 对象引用,threadLocalMap 对象又被 Thread 对象所引用,那么当 Thread 一直不终结的话,value 对象就会一直存在于内存中,也就导致了内存泄漏,直至 Thread 被销毁后,才会被回收
那么如何避免内存泄漏呢
在使用完 ThreadLocal 变量后,需要我们手动 remove 掉,防止 ThreadLocalMap 中 Entry 一直保持对 value 的强引用,导致 value 不能被回收
使用场景
微服务之间的调用,日志通过traceId跟踪,如何传递traceId
线程间数据隔离,各线程的 ThreadLocal 互不影响
CompletableFuture 详解
一个接口可能需要同时获取多种不同的数据,然后再汇总返回,考虑到这些任务之间有大部分都是 无前后顺序关联 的,可以 并行执行
多线程异步任务编排
线程池里面的线程异常后
当执行方式是execute时,可以看到堆栈异常的输出,线程池会把这个线程移除掉,并创建一个新的线程放到线程池中
https://mp.weixin.qq.com/s/16cv_VFfdEhiI_ShPf86KQ
当执行方式是submit时,堆栈异常没有输出。但是调用Future.get()方法时,可以捕获到异常,不会把这个线程移除掉,也不会创建新的线程放入到线程池中。
https://www.cnblogs.com/thisiswhy/p/17036727.html
JMM相关
锁
悲观锁
synchronized
ReentrantLock
但在高并发场景下会导致线程阻塞、上下文切换频繁,从而影响系统性能,并且还可能引发死锁问题。
乐观锁
CAS
版本号机制
乐观锁避免了线程阻塞和死锁问题,在读多写少的场景中性能优越。但在写操作频繁的情况下,可能会导致大量重试和失败,从而影响性能。
JMM介绍
JMM(Java 内存模型)主要定义了对于一个共享变量,当另一个线程对这个共享变量执行写操作后,这个线程对这个共享变量的可见性。
指令重排序
编译器优化重排
编译器(包括 JVM、JIT 编译器等)在不改变单线程程序语义的前提下,重新安排语句的执行顺序。
禁止特定类型的编译器重排序的方式来禁止重排序
指令并行重排
现代处理器采用了指令级并行技术(Instruction-Level Parallelism,ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
插入内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)的方式来禁止特定类型的处理器重排序。
并发编程三个重要特性
原子性
借助synchronized和lock
原子类,atomic
可见性
借助synchronized和lock
volatile
有序性
volatile
分布式系统典型问题
https://mp.weixin.qq.com/s/yvKASWcRLfOok-NFPrIRsw
https://mp.weixin.qq.com/s/yvKASWcRLfOok-NFPrIRsw
互斥
单机多线程情况下
ReentrantLock
synchronized
分布式环境
分布式锁
幂等性
场景
前端重复提交
接口超时重试
消息重复消费
实现方式
基于token机制实现
生成token,携带token掉请求,然后从redis中删除
基于mysql实现
唯一索引
分布式锁
在多线程环境中,如果多个线程同时访问共享资源(例如商品库存、外卖订单),会发生数据竞争,可能会导致出现脏数据或者系统问题,威胁到程序的正常运行。
本地锁(单机多线程情况下)
synchronized
ReentrantLock
具备条件
互斥
任意一个时刻,锁只能被一个线程持有
高可用
一个锁服务出现问题,能够自动切换到另外一个锁服务。并且,即使客户端的释放锁的代码逻辑出现问题,锁最终一定还是会被释放,不会影响其他线程对共享资源的访问
可重入
一个线程获取了锁之后,还可以再次获取锁
高性能
获取和释放锁的操作应该快速完成,并且不应该对整个系统的性能造成过大影响
非阻塞
如果获取不到锁,不能无限期等待,避免对系统正常运行造成影响
实现方式
基于MYSQL实现
zookeeper
redis
读写分离&分库分表
分库分表
ShardingSphere进行分表
https://mp.weixin.qq.com/s/0nMoiqhtGauKenL5Tps7jA
数据迁移问题
备份数据
老表数据备份到新表数据
预计好几天,所以放弃
双写方案
读写分离
读写分离基于主从复制,MySQL 主从复制是依赖于 binlog
分库 就是将数据库中的数据分散到不同的数据库上。分表 就是对单表的数据进行拆分,可以是垂直拆分,也可以是水平拆分
高并发常见问题
接口幂等性
如何设计一个高可用系统
1、集群代替单机
redis
2、限流和流量控制
超时和重试机制
降级和熔断
异步调用
使用缓存
避免系统雪崩问题
负载均衡
作用
把用户的请求分担到不同的服务器上处理,用于提高系统整体的处理能力和可靠性
常见算法
随机发
轮训
一致性hash
相同参数的请求放到同一台机器上
最小连接数
找到活动连接数最小的一台机器
DNS解析
反向代理
降级和熔断
操作系统
进程和线程
区别
线程是进程划分成的更小的运行单位,一个进程在其执行的过程中可以产生多个线程。
线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。
线程执行开销小,但不利于资源的管理和保护;而进程正相反
协程
协程,又称微线程,是一种用户态的轻量级线程,协程的调度完全由用户控制(也就是在用户态执行)。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到线程的堆区,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
有了进程为什么还需要线程?
进程切换是一个开销很大的操作,线程切换的成本较低
线程更轻量,一个进程可以创建多个线程
多个线程可以并发处理不同的任务,更有效地利用了多处理器和多核计算机。而进程只能在一个时间干一件事,如果在执行过程中遇到阻塞问题比如 IO 阻塞就会挂起直到结果返回。
同一进程内的线程共享内存和文件,因此它们之间相互通信无须调用内核。
线程间的同步方式(线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。)
互斥锁(Mutex)
读写锁(Read-Write Lock)
信号量(Semaphore)
屏障(Barrier)
事件(Event) :
进程的状态
创建,就绪,运行,阻塞,结束
进程间通信方式
概念
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信
通信方式
管道
有名管道
信号
消息队列
信号量
共享内存
套接字
常见的进程调度算法
僵尸进程&孤儿进程
僵尸进程:子进程已经终止,但是其父进程仍在运行,且父进程没有调用 wait()或 waitpid()等系统调用来获取子进程的状态信息,释放子进程占用的资源,导致子进程的 PCB 依然存在于系统中,但无法被进一步使用。这种情况下,子进程被称为“僵尸进程”。避免僵尸进程的产生,父进程需要及时调用 wait()或 waitpid()系统调用来回收子进程。
孤儿进程:一个进程的父进程已经终止或者不存在,但是该进程仍在运行。这种情况下,该进程就是孤儿进程。孤儿进程通常是由于父进程意外终止或未及时调用 wait()或 waitpid()等系统调用来回收子进程导致的。为了避免孤儿进程占用系统资源,操作系统会将孤儿进程的父进程设置为 init 进程(进程号为 1),由 init 进程来回收孤儿进程的资源。
死锁
死锁(Deadlock)描述的是这样一种情况:多个进程/线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于进程/线程被无限期地阻塞,因此程序不可能正常终止。
四个必要条件
互斥
资源是非共享
占有和等待
一个进程至少占有一个资源,并且等待其他资源
非抢占
资源必须是使用者释放
循环等待
在等待其他资源释放
内存管理
磁盘调度算法
先来先服务算法
最短寻道时间优先算法
扫描算法
循环扫描算法
边扫描边观察算法
均衡循环扫描算法
计算机网络
五层网络分层模型
应用层
工作在操作系统中的用户态(其他都是内核态),主要是为了用户提供服务
常见协议:
http
基于TCP,超文本传输协议
ftp
基于TCP,文件传输协议
ssh
基于TCP,安全的网络传输协议
dns
基于UDP,域名解析,解决域名和 IP 地址的映射问题
传输层
位两台主机进程之间的通信提供提供通用的数据传输服务
常见协议
TCP
面向有链接的,可靠的
UDP
面向无连接的,不可靠的,尽可能传输多的数据,但是不保证数据传输的可靠性
网络层
路由和寻址,选择合适的网间路由和交换结点, 确保数据及时传送,实现主机与主机之间的通信。
寻址
我们去往下一个目的地该朝哪个方向走
路由
具体的传输路径
数据链路层
负责通过一条链路从一个结点向另一个物理链路直接相连的相邻接点传送数据报
主要是对数据包中的MAC地址进行解析和封装,这一层数据叫做帧
物理层
实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异
HTTP
http和https区别
HTTP 默认是 80,HTTPS 默认是 443。
前缀
安全性和资源消耗
HTTP 是超⽂本传输协议,信息是明⽂传输,存在安全⻛险的问题。 HTTPS 则解决 HTTP 不安全的缺陷,在TCP 和 HTTP ⽹络层之间加⼊了SSL/TLS 安全协议,使得报⽂能够加密传输
HTTP 连接建⽴相对简单, TCP 三次握⼿之后便可进⾏ HTTP 的报⽂传输。⽽ HTTPS 在 TCP 三次握⼿之后,还需进⾏ SSL/TLS 的握⼿过程,才可进⼊加密报⽂传输
Http优点
窃听⻛险,⽐如通信链路上可以获取通信内容,⽤户号容易没。
混合加密
篡改⻛险,⽐如强制植⼊垃圾⼴告,视觉污染,⽤户眼容易瞎
摘要算法
校验数据完整性
冒充⻛险,⽐如冒充淘宝⽹站,⽤户钱容易没
公钥放到数字证书里面,解决冒充问题
输入url,发生了什么
过程
浏览器解析URL,确定WEB服务器和文件名,根据这些信息生成HTTP请求信息
DNS解析,获取域名对应的IP地址
浏览器根据IP和端口号,向目标服务器建立TCP请求连接
浏览器在连接基础上,向服务器发送HTTP请求,获取内容
服务器收到请求内容后,处理请求,返回Http响应报文
浏览器收到报文,解析响应体,渲染
不需要通信后,主动关闭TCP链接
用到的协议
HTTP
DNS
TCP
IP
ARP
获取物理地址
ICMP
状态码
http1.0,2.0,3.0区别
1.1
相比1.0版本,用了TCP长连接,支持管道传输(发送第一个,可以继续发第二个请求)
请求头没有压缩
服务器响应是按照顺序,可能会阻塞住
没有请求优先级控制
请求只能从客户端开始
2.0
基于HTTPS,安全性可以保证
优势
会压缩头,减小空间
数据流方式,不需要按照顺序发送
多路复用
不会出现队头阻塞问题
弊端
多个http请求服用一个TCP链接,下层的 TCP 协议是不知道有多少个 HTTP 请求的。所以⼀旦发⽣了丢包现象,就会触发 TCP 的重传机制,这样在⼀个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。
3.0
新增基于UDP的QUIC协议(Quick UDP Internet Connections)
QUIC集成了TLS协议,具备数据迁移、拥塞控制、流量控制
URI和URL区别
URI(Uniform Resource Identifier) 是统一资源标志符,可以唯一标识一个资源。
URL(Uniform Resource Locator) 是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。
TCP和UDP
TCP
TCP 是⾯向连接的、可靠的、基于字节流的传输层通信协议
TCP 是⾯向连接的传输层协议,传输数据前先要建⽴连接
TCP 是⼀对⼀的两点服务,即⼀条连接只有两个端点
TCP 是可靠交付数据的,数据可以⽆差错、不丢失、不重复、按需到达
有拥塞控制和流量控制机制,保证数据传输的安全性。
TCP 是流式传输,但保证顺序和可靠。
UDP
是不需要连接,即刻传输数据
UDP ⽀持⼀对⼀、⼀对多、多对多的交互通信
是尽最⼤努⼒交付,不保证可靠交付数据。
即使⽹络⾮常拥堵了,也不会影响 UDP 的发送速率
部只有 8 个字节,并且是固定不变的,开销较⼩。
UDP 是⼀个包⼀个包的发送,是有边界的,但可能会丢包和乱序。
TCP
如何保证可靠传输
基于数据块传输,划分为他认为最合适的块
每个包都有一个序列号,能对接收的数据进行排序
校验和,首部和数据进行校验,检测传输过程中是否发生了变化
重传机制
基于计时器重传
快速重传
流量控制
接收方处理不了,放到缓存区,缓存区溢出就会出现丢包,防止丢包
拥塞控制
当网络拥塞时,减少数据的发送。
对方接收不了
网络拥堵
实现
TCP如何实现流量控制
TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收
TCP三次握手
三次握手
四次挥手
https://blog.csdn.net/Austrialia/article/details/118902091?spm=1011.2415.3001.5331
websocket
websockt
双向实时通信
数据头比较小,网络开销小
http
单向通通信协议
PING
DNS
项目相关
任务持久化
引擎线程池动态配置
通过 CompletableFuture 实现多任务编排,减少接口响应时间
注意事项
1、使用自定义线程池,不要使用默认
2、避免父线程和子线程使用同一个线程池
避免使用 get() 防止主线程长时间阻塞,加入超时时间;
引入AI大模型
看看能不能包装一下????
leetcode
动态规划
72(编辑距离)和1143(最长公共子序列)题,二维动态规划
300(最长上升子序列)
动态规划解法
贪心+二分(寻找第一个插入位置)
爬楼梯,零钱兑换1,2
注意是组合问题还是排序问题
滑动窗口
双指针
11和84题区别
二分法
left=mid的时候需要向上取整
在排序数组中查找元素的第一个和最后一个位置
搜索旋转排序数组(有没有重复元素)34,81
nums[mid]和nums[right]比较
有重复元素的时候,多一个right--
寻找旋转排序数组中的最小值(有没有重复元素)153 154
nums[mid]和nums[right]比较
有重复元素的时候,多一个right--
dfs
全排列,全排列II
讲究顺序,需要用used变量,只能使用for循环,无法使用二叉树解法
需要使用for循环
子集,子集II
对于子集,需要使用begin变量,两种都可以使用,子集II不能使用二叉树解法
需要使用for循环
组合总和,无顺序要求
使用begin变量
需要使用for循环
树
链表
不熟悉题目
栈
394. 字符串解码
84. 柱状图中最大的矩形
HARD
动态规划
32.最长有效括号
300(最长上升子序列)
动态规划解法
贪心+二分(寻找第一个插入位置)
72(编辑距离)和1143(最长公共子序列)题,718. 最长重复子数组 二维动态规划
64. 最小路径和 221. 最大正方形 62. 不同路径
面试复盘
招银网络
CPU飙升定位
redis多机部署时获取锁会不会失败
分布式锁的具体应用场景
实现分布式锁的几种方式
基于数据库的分布式锁
唯一索引:插入一条记录,成功就说明获取到了锁;遇到 duplicate entry 错误,则说明加锁失败
基于共享存储的分布式锁:使用共享存储如Redis、ZooKeeper等
基于乐观锁的分布式锁:通过使用版本号或者时间戳等方
线上问题定位,线上定位工具(日志不够详细情况下)
OOM
1、主动生成dump文件)(这个貌似是定位OOM的)
top命令查看pid,jmap -dump:format=b,file=user.dump 1246
被动生成文件
在很多时候我们是不知道何时会发生OOM,需要在发生OOM时自动生成dump文件。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\tmp
dump文件
是一个进程或者系统在某一个给定的时间的快照。
Jprofiler
shardingsphere分库分表,原理,执行过程
mysql
深度分页为什么那么慢,怎么优化;
为什么慢
查询偏移量过大时,MySQL 的查询优化器可能会选择全表扫描而不是利用索引来优化查询。这是因为扫描索引和跳过大量记录可能比直接全表扫描更耗费资源。
优化方案
范围查询,前提是ID连续性
子查询;
子查询 (SELECT id FROM t_order where id > 1000000 limit 1) 会利用主键索引快速定位到第 1000001 条记录,并返回其 ID 值。
主查询 SELECT * FROM t_order WHERE id >= ... LIMIT 10 将子查询返回的起始 ID 作为过滤条件,使用 id >= 获取从该 ID 开始的后续 10 条记录。
主查询 SELECT * FROM t_order WHERE id >= ... LIMIT 10 将子查询返回的起始 ID 作为过滤条件,使用 id >= 获取从该 ID 开始的后续 10 条记录。
# 通过子查询来获取 id 的起始值,把 limit 1000000 的条件转移到子查询
SELECT * FROM t_order WHERE id >= (SELECT id FROM t_order where id > 1000000 limit 1) LIMIT 10;
SELECT * FROM t_order WHERE id >= (SELECT id FROM t_order where id > 1000000 limit 1) LIMIT 10;
缺点:ID需要有顺序,而且子查询的结果会产生一张新表,会影响性能
延迟关联
延迟关联与子查询的优化思路类似,都是通过将 LIMIT 操作转移到主键索引树上,减少回表次数。相比直接使用子查询,延迟关联通过 INNER JOIN 将子查询结果集成到主查询中,避免了子查询可能产生的临时表。在执行 INNER JOIN 时,MySQL 优化器能够利用索引进行高效的连接操作(如索引扫描或其他优化策略),因此在深度分页场景下,性能通常优于直接使用子查询。
-- 使用 INNER JOIN 进行延迟关联
SELECT t1.*
FROM t_order t1
INNER JOIN (SELECT id FROM t_order where id > 1000000 LIMIT 10) t2 ON t1.id = t2.id;
SELECT t1.*
FROM t_order t1
INNER JOIN (SELECT id FROM t_order where id > 1000000 LIMIT 10) t2 ON t1.id = t2.id;
覆盖索引
避免 InnoDB 表进行索引的二次查询
可以把随机 IO 变成顺序 IO 加快查询效率:
mysql执行计划,type的含义,那些类型的速度
null>system>const>ref>eq_ref>range>index(索引物理文件全扫描,和ALL差不多)>ALL
SQL性能优化的级别至少要达到range,要求是ref,const最好
1、null,MySQL优化器在优化阶段分解查询语句,在优化过程中就已经可以得到结果,那么在执行阶段就不用再访问表或索引。
2、const和system:const出现在用 primary key(主键) 或 unique key(唯一键),最多有一个匹配行,读取1次,速度非常快
3、system是const的特例,表中数据只有一条匹配时为system
4、eq_ref:primary key(主键)或 unique key(唯一键) 索引的所有构成部分被join使用 ,只会返回一条符合条件的数据行
5、ref:普通索引或者联合唯一性索引的部分前缀
6、range:出现在 in(),between ,> ,<, >= 等操作符中
7、index:扫描全表索引,索引物理文件全扫描,和ALL也差不多;
8、全表扫描,需要从头到尾去查找所需要的
2、const和system:const出现在用 primary key(主键) 或 unique key(唯一键),最多有一个匹配行,读取1次,速度非常快
3、system是const的特例,表中数据只有一条匹配时为system
4、eq_ref:primary key(主键)或 unique key(唯一键) 索引的所有构成部分被join使用 ,只会返回一条符合条件的数据行
5、ref:普通索引或者联合唯一性索引的部分前缀
6、range:出现在 in(),between ,> ,<, >= 等操作符中
7、index:扫描全表索引,索引物理文件全扫描,和ALL也差不多;
8、全表扫描,需要从头到尾去查找所需要的
https://cloud.tencent.com/developer/article/1730827
extra
using index:高性能表现
using temporary:用临时表,需要优化
distinct,group by,orderby,子查询
usingfilesort:索引外排序
order by
using where:被查询的列未被索引覆盖
添加适当的索引
日志出现mysql No operations allowed after connection closed是什么问题
Mysql服务器默认的“wait_timeout”是8小时,也就是说一个connection空闲超过8个小时,Mysql将自动断开该connection。
增加 wait_timeout 的时间。
减少 Connection pools 中 connection 的 lifetime。
测试 Connection pools 中 connection 的有效性。
减少 Connection pools 中 connection 的 lifetime。
测试 Connection pools 中 connection 的有效性。
微服务yaml那些关于数据库的配置
数据库url,用户名,密码,类型,连接池最大数量,链接超时时间,空闲连接存活时间,
疑问:
微服务架构,用的那些,服务发现、服务注册原理,openfeigh,nginx,负载均衡原理
redis zset。
分表框架原理
mysql:间隙锁,读写锁,乐观锁,悲观锁实现方式
innodb,myisam区别
redis实现分布式锁的几种方式,如果发生抢占锁会发生什么
redis zset。
分表框架原理
mysql:间隙锁,读写锁,乐观锁,悲观锁实现方式
innodb,myisam区别
redis实现分布式锁的几种方式,如果发生抢占锁会发生什么

收藏
0 条评论
下一页