- mybatispuls注解有哪些??、
实体类上
@TableName(“t_order_detail”)
属性上
@TableId
@TableField
@Tablelogic
服务实现类上
@Service
条件构造器
querywrapper
updataWrapper
PRC远程过程调用、springcloud,dubbo+zookeeper
-
抽象类????
抽象类和普通类的区别?
抽象类使用 abstract 关键字定义,不能被实例化,只能作为其他类的父类。普通类可以直接实例化。
抽象类可以包含抽象方法和非抽象方法。抽象方法没有方法体,必须由子类实现。普通类只能包含非抽象方法。 -
你们项目中有没有用到什么设计模式?
- 单例模式。 工具类(进程单例) spring(容器单例)
单例模式是指在内存中只会创建且仅创建一次对象的设计模式
单例模式常见的有两种类型:
懒汉式:
在需要用到对象时才实例化对象,正确的实现方式是:Double Check + Lock(双重检查加锁),解决了并发安全和性能低下问题
饿汉式:
在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。 - 工厂模式。 工厂模式主要是为创建对象提供了接口。
主要用于创建对象,而不暴露创建对象的逻辑给客户端。
其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。 - 策略模式。 支付 支付方式(支付宝,微信)
- 观察者模式。 观察者模式又被称作发布/订阅模式, 下单后的操作,通知减库存等
5)适配器模式 版本兼容
6)装饰者模式:实现同一个接口,把被装饰者传入装饰者中,
作用:不修改源码,增强类的功能。(ListeningExecutorService)
- 单例模式。 工具类(进程单例) spring(容器单例)
-
ThreadLocal是什么?
线程本地变量
如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是在操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了并发场景下的线程安全问题。
5.事务,编程式事务,声明式事务
编程式事务:获取JDBC连接,关闭事务自动提交,执行三条sql,没问题就提交事务,有问题catch就回滚,fanilly最后恢复自动提交。
声明式事务:直接默认做到上面的步骤
6.熔断
降级
降级也就是服务降级,当我们的服务器压力剧增为了保证核心功能的可用性,而选择性的降低一些功能的可用性,或者直接关闭该功能。这就是典型的丢车保帅了。
熔断
降级一般而言指的是我们自身的系统出现了故障而降级。而熔断一般是指依赖的外部接口出现故障的情况断绝和外部接口的关系。
例如你的A服务里面的一个功能依赖B服务,这时候B服务出问题了,返回的很慢。这种情况可能会因为这么一个功能而拖慢了A服务里面的所有功能,因此我们这时候就需要熔断!即当发现A要调用这B时就直接返回错误(或者返回其他默认值啊啥的),就不去请求B了。我这还是举了两个服务的调用,有些那真的是一环扣一环,出问题不熔断,那真的是会雪崩。
当然也有人认为熔断不就是降级的一种的,我觉得你非要说熔断也属于一种降级我也没法反驳,但是它们本质上的突出点和想表达的意思还是有一些不同的。
那什么时候熔断合适呢?也就是到达哪个阈值进行熔断,5分钟内50%的请求都超过1秒?还是啥?参考降级。
限流
上面说的两个算是请求过来我们都受理了,这个限流就更狠了,直接跟请求说对不起再见!也就是系统规定了多少承受能力,只允许这么些请求能过来,其他的请求就说再见了。
一般限制的指标有:请求总量或某段时间内请求总量。
请求总量:比如秒杀的,秒杀100份产品,我就放5000名进来,超过的直接拒绝请求了。
某段时间内请求总量:比如规定了每秒请求的峰值是1W,这一秒内多的请求直接拒绝了。咱们下一秒再见。
7.如何自己创建一个消息队列。
新建一个 Broker 类(工具类),内部维护一个 ArrayBlockingQueue 队列,提供生产消息和消费消息的方法, 仅仅具备存储服务功能
新建一个 BrokerServer 类(服务器),将 Broker 发布为服务到本地9999端口,监听本地9999端口的 Socket 链接,在接受的信息中进行我们的协议校验, 这里 仅仅具备接受消息,转发消息功能,校验协议;
新建一个 MqClient(客户端) 类,此类提供与本地端口9999的Socket链接 , 仅仅具备生产消息和消费消息的方法
测试:新建两个 MyClient 类对象,分别执行其生产方法和消费方法
8.CAP理论
C一致性,A高可用,P分区容错性
eureka只支持AP,短连接
nacos支持CP和AP两种,长连接
临时实例心跳模式,非临时实例主动检测模式
默认AP。存在非临时实例CP
9.Socket 本质上是一种通信的端点,它在网络上标识了一个通信链路的两端,并提供了通信双方所需的接口和功能。
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。
三个核心功能:建立连接、发送数据以及接收数据。
所以可以把Socket编程理解为对TCP协议的具体实现
ServerSocket serverSocket = new ServerSocket(5000); 服务器创建
Socket socket = new Socket(“localhost”, 5000); 客户端建立连接
socket.getInputStream() 接受数据
socket.getOutputStream() 发送数据
10.TCP向上层提供面向连接的可靠服务 ,UDP向上层提供无连接不可靠服务。
虽然 UDP 并没有 TCP 传输来的准确,但是也能在很多实时性要求高的地方有所作为
对数据准确性要求高,速度可以相对较慢的,可以选用TCP
11.单元测试(是指对软件中的最小可测试单元进行检查和验证的过程就叫单元测试)
单元测试框架 spring-boot-test
@springbootTest//表明当前单元测试是运行在Spring Boot环境中的
@test
13.springcloud
nacos,sentinel,sleuth为程序服务、
gateway为微服务
openfeign为内嵌式服务
nacos 注册中心
@EnableDiscoveryClient 启动类加
//配置文件:
spring.application.name=sentinel-service8401
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
management.endpoints.web.exposure.include=‘*’
sentinel流量哨兵 (服务器配置流控 降级 熔断规则)
// 配置文件:控制台端口,数据传输端口。兜底服务:feign开启sentinel
主动限流控制,被动降级返回兜底值。
sentinel 控制台 端口
spring.cloud.sentinel.transport.dashboard=localhost:8080
sentinel 哨兵数据 传送端口
spring.cloud.sentinel.transport.port=8719
#开启兜底服务(feign开启sentinel)
feign.sentinel.enabled=true
通过主动控制设置流控:资源名指定方法mapping地址,QPS等
使用:流控,降级,熔断等都可以通过sentinel的管理页面进行配置。(资源名级别,也就api调用级别)
资源名
单独对方法使用:这里在service层对需要限流的方法加上@SentinelResource,value = "getTest"代表资源名标识符。
对接口使用:默认请求路径
sleuth 链路监控
表示一请求链路,一条链路通过Trace Id来唯一标识,Span标识发起的请求信息,各span通过parent id 关联起来
名词解释
Trace:类似于树结构的Span集合,表示一条调用链路,存在唯一标识
span:表示调用链路来源,通俗的理解span就是一次请求信息
//配置文件:谁使用谁加配置
spring.zipkin.base-url=http://localhost:9411
spring.sleuth.sampler.probability=1 #采样率值介于0~1之间,1表示全部采样
一次请求后,管理页面上面有请求路径可以查看。zipkin(负责展现)?
openFeign 远程调用 负载均衡
@EnableFeignClients 启动类加
//该接口是由哪个微服务去实现
@FeignClient(value = “shop-cart”)
gateway(正向代理,代理客户端。反向代理,代理服务器。)
作用:反向代理 鉴权 请求过滤 流量控制 熔断降级
三大核心:路由,断言,过滤
要点:请求断言,请求鉴权过滤
断言配置
server:
port: 7000
routes: #路由数组[路由就是指定当请求满足什么条件的时候转到哪个微服务〕
- id: order_route #当前路由的标识,要求唯一,默认uuid。
uri: http://localhost:9000 #请求最终要被转发到的 订单服务 的地址
predicates: #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件)
- Path=/order-serv/** #当请求路径满足Path指定的规则时,才进行路由转发
使用网关之前
访问商品服务:http://localhost:9001/product/select/2
访问订单服务:http://localhost:9000/order/select/22
使用网关之后
访问商品服务:http://localhost:7000/product-serv/product/select/22
访问订单服务:http://localhost:7000/order-serv/order/select/22
鉴权过滤
//写一个全局拦截器 对于请求白名单要先登录
@Component
public class AccessFilter implements GlobalFilter
11.springboot注解
@SpringBootApplication
@RequestMapping
12.MySQL中处理高并发事务通常涉及到以下几个策略:
读写分离(Read Write Splitting):通过将读操作分散到从库,而写操作集中在主库,可以减轻主库的压力。这需要配置合适的连接池和复制策略。
批量提交(Batch Committing):对小批量的事务进行批量提交,而非频繁单条提交,减少磁盘I/O次数。
使用索引(Indexing):为经常作为查询条件的列创建索引,提高查询效率。
预分页(Pagination):如果应用支持,可以预先加载部分数据,避免一次性请求过多记录导致锁争用。
乐观锁(Optimistic Locking):利用版本字段,允许并发读取并尝试更新数据,只有在检查版本号冲突时回滚,降低冲突情况下的阻塞。
行级锁定(Row-Level Locking):使用InnoDB存储引擎的行锁可以减少锁定范围,提高并发性能。对于更新频繁的表,应尽可能地选择最小粒度的锁定。
设置适当的隔离级别(Isolation Level):MySQL有四种隔离级别,如选用较低的级别(如READ UNCOMMITTED),可能会牺牲一致性换取更高的并发性能。
使用事务补偿(Transaction Rollback On Error):当某个操作失败时,其他操作能够自动回滚,保持数据库的一致性。
硬件优化:升级硬件资源,如增加内存、使用更快的磁盘等,提升MySQL的整体处理能力。
13.分布式事务的解决方案
消息最终一致性。全局事务seate(没用上)
生产
在生产阶段,主要通过请求确认机制(broker),来保证消息的可靠传递。
1、 同步发送的时候,要注意处理响应结果和异常。 如果返回响应 OK,表示消息成功发送到了 Broker,如果响应失败,或者发生其它异常,都应该重试。
2、 异步发送的时候,应该在回调方法里检查。 如果发送失败或者异常,都应该进行重试。
3、 如果发生超时的情况,也可以通过查询日志的 API,来检查是否在 Broker 存储成功。
存储
存储阶段,可以通过配置可靠性优先的 Broker 参数来避免因为宕机丢消息,简单说就是可靠性优先的场景都应该使用同步。
1、 消息只要持久化到 CommitLog(日志文件)中,即使 Broker 宕机,未消费的消息也能重新恢复再消费。
2、 Broker 的刷盘机制:同步刷盘和异步刷盘,存储在内存中
3、 Broker 通过主从模式来保证高可用。Broker 支持 Master 和 Slave 同步复制
消费
从 Consumer 角度分析,如何保证消息被成功消费?
Consumer 保证消息成功消费的关键在于确认的时机,不要在收到消息后就立即发送消费确认,而是应该在执行完所有消费业务逻辑之后,再发送消费确认。因为消息队列维护了消费的位置,逻辑执行失败了,没有确认,再去队列拉取消息,就还是之前的一条。
13.MyBatis 如何执行批量操作?
第一种方法:使用 foreach 标签
open,close
separator mybatis会在每次迭代后给sql语句append上separator属性指定的字符,该参数为可选项,一般用于加个,
item 表示本次迭代获取的元素,若collection为List、Set或者数组,则表示其中的元素;若collection为map,则代表key-value的value,该参数为必选
index 在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项
第二种方法:使用 ExecutorType.BATCH
- rewriteBatchedStatements=true 配置文件增加(重要)
- SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.insert(list.get(i)); 循环多次插入
sqlSession.commit();
sqlSession.clearCache();
14.mybatis分页插件底层原理 (ORDER BY,LIMIT)
动态构建 SQL 语句
PageHelper 通过内部算法计算出起始位置(偏移量)和需要返回的行数,然后在此基础上动态构建 SQL 语句。核心逻辑包括:
排序 (ORDER BY):确保返回的数据是有序的。通常,默认排序可能是按照某个字段(如ID)升序排列。
分页限制 (LIMIT 或 OFFSET):使用 LIMIT 关键字来限定返回的行数,并使用 OFFSET 来跳过前面不需要的行数。
使用
PageHelper.startPage(1, 10);
List list =mapper.selectByExample(example);//查询
PageInfo pageInfo = new PageInfo(list);
-
线程池可以获取执行结果但是对执行成功和失败没有区分!所以使用谷歌线程池!
-
定时器
//每天晚上凌晨一点执行 0 0 2 * * ? *
// {秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}
//1.发送一个上架秒杀商品的消息
@Scheduled(cron = "0 41 9 * * ? ")
//发送消息只是起到一个通知的效果
OSS,S3
//简单的分享一下吧,仅仅给参考
1、封装工具类
//1.校验对象名是否存在
if(!checkName(objname)) {
//2.上传资源到oss服务器
PutObjectResult result = ossClient.putObject(BUCKET, objname, new ByteArrayInputStream(data));
//3.验证上传的结果
if (StringUtils.hasLength(result.getETag())) {
//4.成功,返回访问地址
return createURL(objname, urlDate);
}
}
2.使用校验
private String validateImg(MultipartFile file) {
if (file == null) {
throw new RuntimeException(“图片不能为空”);
}
if (file.getSize() >= 20 * 1024 * 1024) {
throw new RuntimeException(“图片大小超出最大限制”);
}
boolean isPic = FileUtil.isPic(file.getOriginalFilename());
if (!isPic) {
throw new RuntimeException(“图片格式错误”);
}
String fileName = Objects.requireNonNull(file.getOriginalFilename()).length() > 50 ? file.getOriginalFilename().substring(0, 50) : file.getOriginalFilename();
return fileName;
}
14.Caffeine(读写能力强,能设置删除时间等删除条件)
1. 但是在设计中,还是要考虑一些问题的,例如数据一致性问题。首先,两级缓存与数据库的数据要保持一致,一旦数据发生了修改,在修改数据库的同时,本地缓存、远程缓存应该同步更新。
另外,2. 如果是分布式环境下,一级缓存之间也会存在一致性问题,当一个节点下的本地缓存修改后,需要通知其他节点也刷新本地缓存中的数据,否则会出现读取到过期数据的情况,这个问题我们用Redis中的发布/订阅功能解决。(创建一个监听容器RedisMessageListenerContainer订阅频道)
分布式环境下 本地缓存一致性
在系统调用缓存的删除修改方法时,给消息队列发送一条key的消息,此时所有机器都会收到这个消息队列的消息,然后进而处理该机器自身内部的本地缓存。此时的消息带有本地的ip和端口号作为唯一标识,只有唯一标识不相同的机器,在收到这个消息后,才会对本机器的本地缓存进行处理。
数据加载
手动加载,自动加载,异步手动,异步自动
// 手动加载:自己设置
// 自动加载:为不存在的缓存元素添加默认值
// buildAsync使用异步
数据驱逐(自动删除)
Caffeine提供以下几种剔除方式:基于大小(size)、基于权重、基于时间、基于引用
基于大小(size):如果缓存的条目数量不应该超过某个值,那么可以使用Caffeine.maximumSize(long)。如果超过这个值,则会剔除很久没有被访问过或者不经常使用的那个条目。
基于权重(不同的实例占用空间大小不一样):说明驱逐是另外一个线程异步进行的
数据过期策略
expireAfterAccess:最后一次访问之后,隔多久没有被再次访问的话,就过期。访问包括了 读 和 写。
expireAfterWrite:某个数据在多久没有被更新后,就过期。
expireAfter:自定义缓存策略,满足多样化的过期时间要求
手动删除缓存:
// individual key
cache.invalidate(key)
// bulk keys
cache.invalidateAll(keys)
// all keys
cache.invalidateAll()
15.多实例的定时任务(@Scheduled)多次执行造成重复如何解决
(2)@SchedulerLock 加锁
@Scheduled(cron = “0 0/15 * * * ?”)
@SchedulerLock(name = “syncOrder”,
lockAtMostFor = 10 * 60 * 1000, lockAtLeastFor = 5 * 60 * 1000)
- 定时任务使用的是shedLock --jdbc分布式锁,当任务执行的时,
- 当前节点获取到锁,存储到sehdlock表中,表由框架维护,无需我们设置
- @Scheduled 定时任务时间表达式
- @SchedulerLock任务锁—
- name->锁名称
- lockAtMostFor–>设置锁的最大持续时间,防止当某个节点宕机,没有释放,导致其他节点不能执行下一次任务
- lockAtLeastFor–>保留锁的最短时间,防止如果定时的执行时间短,不同节点之间的服务器时间存在差异,导致定时重复执行
原理:shedLock 当多个节点都有定时,某个节点启动后,会获得数据中shedLock表中的锁,其他的定时再来时,无法获取锁对象,
认为其他的节点已经在执行了,所以就不在去执行。这个表是shedLock自带的表,无需程序员自己维护。
(3)借助redis的过期机制和分布式锁。
原理:为定时器在redis中定义键值对,可以为项目名称和服务ip,执行时现在redis中读取键,若没有,表示未被执行。同样的该台机器先去更新redis,再去触发定时任务。因为redis存在过期机制,可以通过过期时间设置来保证下次判断正常。
FileStream传递大文件 , PipedOutputStream
2.mysql调优
3.java多线程,加锁
4.redis,持久化机制,主从模式,哨兵模式
6.es结构特性
1.jvm
1.spring
1.mybatis
5.rabbitmq 特性