月度记录-2025-2月

167 阅读16分钟

1. idea在debug的时候,怎么看当前有没有事务存在?

在你关心的业务方法或事务边界处打断点,然后在调试器的“Evaluate Expression”窗口中输入:

org.springframework.transaction.support.TransactionSynchronizationManager.isActualTransactionActive()

如果返回 true,说明当前线程有一个活动事务。

2. Spring中factoryBean有何作用?

  • FactoryBean 是一个工厂模式的接口,可以动态创建 Bean,而不是直接将 FactoryBean 本身注册到容器。
  • 使用 &beanName 可以获取 FactoryBean 本身,而不是 getObject() 生成的 Bean。
  • 适用于创建复杂对象、代理对象、动态实例化等场景。

3. 我们现在都不用配置文件了,Springboot注入都是用注解,请问注解还能用Factorybean吗?为什么感觉开发中没用到过Factorybean

是的,在 Spring Boot 中仍然可以使用 FactoryBean,但由于 Spring Boot 提供了更优雅的方式(比如 @Bean、@Component、@ConfigurationProperties 等),绝大多数开发场景下不再需要手动实现 FactoryBean,这就是你感觉在实际开发中很少见到 FactoryBean 的原因。不过,在某些特殊场景下,比如创建代理对象、动态生成复杂 Bean 时,FactoryBean 仍然有其价值。

  • Spring Boot 中仍然可以用 FactoryBean,但一般不需要,@Bean 和 @Component 足够应对大多数场景。
  • FactoryBean 适用于创建 代理对象、动态实例化复杂 Bean、第三方框架集成 等场景。
  • 如果你的业务代码中很少涉及动态代理、复杂初始化或框架开发,那几乎不会遇到 FactoryBean。
  • 除非你在框架级开发或需要动态生成 Bean,否则 FactoryBean 不是日常开发中的首选方式。

4. 事件嵌套问题

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) 嵌套

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT),第二个事件会发不出去,这是为何?

因为TMD资源释放发生在finally中,第二个方法执行的时候第一个还没有释放事务资源。第二个其实是带着事务进去的,带着事务不会挂起当前事务也不会开启新事务,所以导致AFTER_COMMIT无法生效。想要生效,第二个需要设置传播行为为REQUIRES_NEW。

5. 在事务执行过程中,有没有事务id或者名字什么的,好让我在代码执行的时候观察到此线程上的事务是否发生改变。

事务名称,也可以debug的时候执行。

@Transactional
public void myMethod() {
    String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
    System.out.println("Current transaction name: " + transactionName);
}

6. startTransaction 和 prepareTransactionStatus有何区别?创建的事务一样吗?

startTransaction(definition, transaction, false, debugEnabled, suspendedResources);
prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);

1. startTransaction 方法主要功能:

startTransaction 负责启动一个新事务,或根据事务传播行为在现有事务中启动一个新事务。它主要用于创建和管理事务的生命周期,特别是涉及到事务的开始、挂起和恢复。

  • 事务传播行为:startTransaction 方法通常在 PROPAGATION_REQUIRES_NEW 或类似需要新事务的场景中调用。在这些情况下,它可能会挂起当前事务并启动一个新的事务。
  • 挂起资源:该方法的参数 suspendedResources 是当前挂起事务的资源,主要用于处理需要挂起当前事务的情况(如 PROPAGATION_REQUIRES_NEW)。如果有挂起的资源,新的事务会在这些资源的基础上启动。

工作流程:

  1. 如果当前事务传播行为要求新事务(如 PROPAGATION_REQUIRES_NEW),它会挂起现有事务并启动一个新的事务。
  2. 该方法会处理事务的开始,并可能涉及挂起现有事务资源的管理。
  3. 返回一个新的 TransactionStatus,代表正在进行的事务状态。

2. prepareTransactionStatus 方法主要功能:

prepareTransactionStatus 主要用于准备和创建一个新的事务状态,但它不负责启动新事务。它通常用于处理已经存在的事务,或者在特定的传播行为下(如 PROPAGATION_REQUIRED)参与现有事务。

  • 事务同步:prepareTransactionStatus 会初始化事务同步支持(通过 prepareSynchronization 方法),确保事务在当前线程上进行适当的同步操作。
  • 返回事务状态:它返回的是一个 TransactionStatus,这个状态代表当前事务的同步和状态,但并不一定启动一个新的事务。

工作流程:

  1. 它首先调用 newTransactionStatus 来创建一个新的事务状态。
  2. 然后,它通过 prepareSynchronization 初始化事务同步,确保事务状态可以正确同步到当前线程。
  3. 返回的 TransactionStatus 通常用于表示事务的状态和同步信息,而不会立即启动一个新的事务。

3. 关键区别

特性

startTransaction

prepareTransactionStatus

事务创建/启动

负责启动一个新的事务或在现有事务中启动新事务

不直接启动新事务,主要用于准备事务状态

挂起现有事务

可能会挂起当前事务并启动新事务(例如

PROPAGATION_REQUIRES_NEW)

不涉及挂起现有事务,只是在现有事务中参与或创建事务状态

事务同步

启动新事务时会处理同步操作(如需要)

初始化事务同步,确保事务能够在当前线程同步

事务状态返回

返回一个新的 TransactionStatus,代表新启动的事务

返回一个新的 TransactionStatus,表示事务的状态和同步信息

适用场景

通常用于 PROPAGATION_REQUIRES_NEW或事务的实际开始

用于在现有事务中创建或参与事务状态

  • startTransaction 是实际启动事务的地方,通常用于需要创建新事务的场景。
  • prepareTransactionStatus 是事务状态的准备工作,更多的是在现有事务中准备和管理同步,确保事务能正确地同步到当前线程。

7. Elasticsearch中refresh_interval(索引刷新频率)

刷新(Refresh) 是将内存中的 Lucene 索引数据(In-memory buffer)转换为可搜索的段(Segment)的过程。

写入 Elasticsearch 的数据会先暂存在内存缓冲区,只有经过 Refresh 后,这些数据才能被搜索到。

默认是1s刷新一次,调整这个参数可以优化写入性能或实时搜索性能。想写入快,就设置为-1,表示禁用刷新,或者设置30s,表示30s刷一次;想提升搜索的实时性,就设置的小一点,例如1s。

refresh_interval这个是动态参数,修改后立即生效。所以可以手动触发。

8. Lucene和Es

Lucene 是 Elasticsearch 的底层搜索引擎库,负责数据的存储和检索。其核心机制如下:

Lucene 类似汽车的发动机,Elasticsearch 则是一辆完整的汽车,整合了发动机(Lucene)、底盘(分布式架构)、方向盘(API)。

Segment特性:

9. vmware网络连接

需要对外服务或真实网络测试用桥接,便捷上网或隔离环境用NAT,不上外网用仅主机。

桥接模式(Bridged Mode)

  • 虚拟机直接连接到宿主机的物理网络适配器,相当于在物理网络中新增一个独立设备。
  • 虚拟机和宿主机处于同一局域网,共享同一网段(如宿主机IP为 192.168.1.100,虚拟机可能为 192.168.1.101)。
  • 暴露虚拟机到物理网络,可能面临外部攻击或网络策略限制。

NAT模式(Network Address Translation)

  • 虚拟机通过VMware创建的虚拟网络(如 192.168.122.0/24)连接,宿主机作为虚拟网络的网关和NAT设备。
  • 虚拟机与宿主机处于不同子网,通过宿主机的IP地址访问外网。
  • 天然隔离,虚拟机隐藏于宿主机后,安全性更高。

仅主机模式

  • 虚拟机仅能与宿主机通信,无法直接访问外部网络(如互联网或局域网)。
  • 宿主机与虚拟机之间通过VMware创建的虚拟私有网络连接,形成一个完全封闭的环境。
  • 仅主机模式是最封闭的网络模式,适合需要完全隔离的场景。

10. Spark和Hadoop MapReduce有什么异同,各自有哪些优势?

想象你有一仓库的图书(数据),需要快速完成以下任务:

传统单机软件(如Excel)无法处理这种规模的问题,而核心框架就是为解决这些问题而生的工具箱。

特性

Hadoop MapReduce

Spark

数据处理模型

基于磁盘的批处理

基于内存的批处理 + 流处理 + 交互式查询

执行引擎

分阶段(Map → Shuffle → Reduce)

基于DAG(有向无环图)优化任务执行流程

延迟

高延迟(分钟级)

低延迟(亚秒级,微批处理)

API灵活性

需编写大量底层代码(Java/Python)

高级API(Scala/Java/Python/R),支持SQL和DSL

迭代计算效率

每次迭代需读写磁盘,效率低

内存缓存中间结果,适合迭代算法(如机器学习)

生态扩展

依赖Hive、Pig等工具扩展功能

内置Spark SQL、MLlib、GraphX、Structured Streaming等库

Hadoop MapReduce的优势

  1. 成熟稳定:适合超大规模数据批处理,尤其对延迟不敏感的任务(如ETL)。
  2. 容错简单:通过磁盘持久化实现容错,适合硬件故障频繁的环境。
  3. 资源要求低:内存消耗较少,适合资源受限的场景。
  4. 与HDFS深度集成:本地化计算优化减少数据移动开销。

典型场景:日志分析、历史数据归档、离线报表生成。

Spark的优势

  1. 速度优势:内存计算比MapReduce快10~100倍(如迭代算法)。
  2. 统一引擎:支持批处理、流处理、机器学习、图计算等多种任务。
  3. 开发效率高:简洁的API和丰富的库(如DataFrame API、PySpark)。
  4. 实时处理:Structured Streaming支持准实时数据流处理。
  5. 优化器:Catalyst查询优化器自动优化执行计划。

典型场景:实时推荐系统、交互式数据探索、机器学习流水线、复杂DAG任务。

11. 数据湖是什么?为什么需要数据湖,其解决了哪些问题

数据湖(Data Lake)是一种集中式存储架构,用于存储企业所有原始格式(结构化、半结构化、非结构化)的海量数据,并支持按需处理和分析。它像“自然湖泊”一样容纳各种来源的“数据流水”,无需预先定义数据结构或用途,为后续灵活分析提供基础。

  • 数据仓库像“瓶装水工厂”:数据需过滤、消毒、封装后才能饮用(使用)。
  • 数据湖像“天然湖泊”:数据以原始形态存储,可按需取用(直接饮用、煮沸或加工成饮料)。

12. java nio中selector是什么?可以解决什么问题?

在传统的阻塞 I/O 中,每一个客户端连接通常都需要一个单独的线程来处理。而使用 Selector,一个线程就能够同时管理多个通道的 I/O 操作,从而大幅减少线程数量。对于高并发场景,能够极大提高系统的效率和资源利用率。

Selector 的工作流程通常如下:

  1. 注册通道:首先,将一个或多个 Channel 注册到 Selector 上。每个通道在注册时可以指定感兴趣的事件类型,例如 OP_READ(读取事件)、OP_WRITE(写入事件)、OP_CONNECT(连接事件)等。

    selector = Selector.open(); channel.configureBlocking(false); // 设置为非阻塞模式 channel.register(selector, SelectionKey.OP_READ); // 注册读取事件

  2. 事件轮询:Selector 会不断轮询已注册的通道,检查哪些通道有感兴趣的事件发生。当一个或多个通道的事件发生时,Selector 会返回相应的 SelectionKey,用于标识哪个通道已经准备好进行 I/O 操作。

    while (true) { int readyChannels = selector.select(); // 阻塞直到有事件发生 if (readyChannels == 0) continue;

    // 处理已就绪的通道
    Set<SelectionKey> selectedKeys = selector.selectedKeys();
    Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
    while (keyIterator.hasNext()) {
        SelectionKey key = keyIterator.next();
        keyIterator.remove();
        // 处理就绪的通道(例如读取数据)
    }
    

    }

  3. 处理事件:当 Selector 检测到某个通道有事件发生时,程序就可以对这些通道进行处理。例如,如果一个通道有数据可读,程序就可以读取数据;如果某个通道可写,程序就可以写数据。

13. java nio中selector工作原理最适合哪种io模型?

Selector 允许单线程管理多个通道(Channel)的 I/O 操作,支持非阻塞模式。在非阻塞模式下,线程可以轮询多个通道,检查它们是否准备好进行读取或写入,而不需要阻塞等待操作完成。这样可以有效提升资源利用率,尤其在处理大量并发连接时。

14. 永远不要对公共分支或已被他人拉取的分支执行 rebase。

15. 我有一张mysql表,其中有个时间字段,bigint类型的,存的是时间戳。现在准备按照日期分组,怎么写sql?

就是这么简单,我竟然一度以为没法实现,或者说需要我自己写很复杂的sql实现。

  • timestamp_column / 1000:时间戳单位降级(毫秒→秒)

  • FROM_UNIXTIME():将秒级Unix时间戳转为YYYY-MM-DD hh:mm:ss格式

    SELECT DATE(FROM_UNIXTIME(timestamp_column / 1000)) AS date_group, COUNT(*) AS total FROM your_table GROUP BY date_group ORDER BY date_group;

16. mysql直接使用datetime和用bigint存储时间戳,哪个好?

能力维度

BIGINT时间戳

DATETIME

时区支持

✅ 隐式存储UTC时间,需显式转换时区

❌ 无时区信息,存储字面时间(如2023-01-01 08:00:00)

时间精度

✅ 可自由定义(秒、毫秒、微秒)

✅ 最高支持微秒(如DATETIME(6))

日期运算

❌ 需用数学计算(如加减秒数)

✅ 原生支持(如DATE_ADD、INTERVAL)

日期函数

❌ 需先转日期类型(FROM_UNIXTIME)

✅ 直接使用(如DAY()、MONTH())

可读性

❌ 需转换才能理解

✅ 直接可读(如2023-01-01)

跨数据库兼容性

✅ 统一用整数存储

❌ 类型实现可能不同(如Oracle的DATE)

BIGINT时间戳 更适合需要 多时区、高精度时间、跨系统兼容 的场景。

DATETIME 更适合 单一时区、高频日期操作、开发友好性优先 的场景。

总之,尽量用时间戳。

17. java Queue LinkedList

Queue(队列)接口定义了一组操作,用于实现 先进先出(FIFO) 的数据结构,LinkedList实现了Queue接口,所以可以把LinkedList作为队列来使用。

Queue 接口的核心方法

方法

行为

成功时返回值

失败时行为

boolean add(E e)

将元素插入队列尾部

true

队列已满 → 抛出IllegalStateException

boolean offer(E e)

将元素插入队列尾部(推荐使用)

true(成功)

队列已满 → 返回false

E remove()

移除并返回队列头部的元素

被移除的元素

队列为空 → 抛出NoSuchElementException

E poll()

移除并返回队列头部的元素(推荐使用)

被移除的元素

队列为空 → 返回null

E element()

返回队列头部的元素(不删除)

队列头部的元素

队列为空 → 抛出NoSuchElementException

E peek()

返回队列头部的元素(不删除,推荐使用)

队列头部的元素

队列为空 → 返回null

18. MyBatis是如何将SQL执行结果映射到Java对象的?

MyBatis 将 SQL 执行结果映射到 Java 对象的过程主要通过以下几个步骤:

19 Consul

Consul 是一个由 HashiCorp 开发的开源工具,用于服务发现、配置管理和分布式系统的健康检查。它旨在简化微服务架构中的服务管理,特别是在动态环境中,支持服务之间的自动发现和通信。

20. SPEL

SPEL(Spring Expression Language)是 Spring 的表达式语言,允许在 Spring 配置文件、注解以及其他地方通过简洁的表达式动态地计算值。类似于脚本语言,支持计算、条件判断、对象访问、数组操作等。

@Value("#{2 * 3}")  // 计算表达式的结果 2 * 3
private int result;

@Value("#{person.getName()}")  // 调用 person bean 的 getName 方法
private String personNameFromMethod;

@Value("#{T(java.util.Arrays).asList('apple', 'banana', 'cherry')}")  // 创建一个 List
private List<String> fruits;

@Value("#{fruits[0]}")  // 访问 fruits 列表中的第一个元素
private String firstFruit;

21. micrometer + Prometheus + grafana

通过micrometer将数据暴露出去,给到Prometheus,然后用grafana展示数据,一套完整的监控。

22. T95和T99

T95 (95th percentile):表示数据集中有 95% 的数据点在该值以下,只有 5% 的数据点在该值以上。也就是说,T95 是数据集中的一个位置,超过这个值的 5% 的数据代表了最极端的部分。

T99 (99th percentile):表示数据集中有 99% 的数据点在该值以下,只有 1% 的数据点在该值以上。T99 是一个更高的百分位值,表示只有 1% 的数据点超出了这个值。

它们可以帮助开发者评估系统的响应时间分布,识别性能瓶颈,并优化系统以改善用户体验。

23. 接口的版本管理

在Controller中写相同的请求路径,然后加个自定义注解标记版本,这么做很好,避免了在接口前加V12345,还可以定义选择版本的策略。但是你需要继承RequestMappingHandlerMapping,自定义请求映射行为,这样才可以有相同的路径,然后根据注解选择合适的版本。

24. Jakarta EE(前身为 Java EE),为啥不接着叫java ee

Java EE 最初是由 Sun Microsystems 开发的,后来 Sun 被 Oracle 收购。随着时间的推移,Oracle 拥有了 Java 的商标和版权。由于 Oracle 对 Java EE 的控制,社区对其发展和管理的灵活性受到了一定限制。

因此,Jakarta EE 的更名是出于商标和版权的考虑,同时也是为了促进更开放的开发模式和社区参与。这个新名称代表了一个新的起点,旨在继续推动企业级 Java 应用程序的开发和创新。

25. spring boot 3.4.3 jetbrains.annotations.Nullable无了

import org.jetbrains.annotations.Nullable;
这个在spring boot3.2.4中还存在,到3.4.3中就没了
Spring Boot 3.2.x:可能在旧版本中通过间接依赖(如 Lombok 或其他第三方库)引入了 JetBrains 注解库,导致该注解可用。
Spring Boot 3.4.3:新版本调整了默认依赖管理,可能移除或更新了间接依赖,导致 JetBrains 注解库不再被自动引入

26. ES 单独指定对某个字段是否索引

PUT /my_index
{
  "mappings": {
    "properties": {
      "views": {
        "type": "integer",  // 整数类型
        "index": false  // 不索引,无法搜索
      }
    }
  }
}

27. 怎么定位慢sql?

启用慢查询日志

MySQL 提供了慢查询日志功能,可以记录执行时间超过指定阈值的查询。在 MySQL 配置文件(通常是 my.cnf 或 my.ini)中添加或修改以下配置。然后使用 EXPLAIN 分析查询

[mysqld]
slow_query_log = ON
slow_query_log_file = D:/mysql/mysql-8.0.22-winx64/logs/slow-query.log
# 记录执行时间超过 1 秒的查询
long_query_time = 1