news 2026/10/10 13:04:27

Spring Boot异步操作实战:@Async线程池配置与踩坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot异步操作实战:@Async线程池配置与踩坑指南

聊到 Spring Boot 异步操作,我脑子里浮现的其实不是 @Async 注解兑现出“秒回”体验的成就感,而是一连串线上踩坑记录。短信通知莫名丢了、接口偶发超时、线程池把内存堆到报警、本想异步处理结果把日志链路全打断——这些问题有一个算一个,都是异步两个字惹出来的。Spring Boot 把异步的入口做得足够简单,加一个 @EnableAsync,再往方法上贴一个 @Async,看起来就结束了。可生产环境真正能用的异步,背后还压着一整套线程池参数、代理机制、异常兜底、事务边界和上下文传递问题,任何一个环节没想清楚,上线的就不是提速,而是埋雷。

这篇文章适合两类人:一类是刚接触异步,打算在项目里引入 @Async 做通知、报表、日志这类旁路任务的开发者;另一类是已经在用 @Async,但遇到过“怎么不生效”“线程池不生效”“查出慢得离谱”这类问题的同行。我会按自己真实踩坑的顺序来讲,从原理到配置再到排障,尽量把每一步“为什么这么做”说透。

1. 先用一个案例说清楚:异步解决什么,不解决什么

1.1 异步的本质:把“等待”变成“交办”

把异步这个概念用生活里的场景类比一下。同步就是你站在餐厅柜台前,等师傅把菜做完端到你手上,中间你什么都干不了;异步就是你先取号,回座位该干嘛干嘛,菜好了叫号你再去取。程序里的异步也是这个逻辑:一个短信发送、一段报表计算、一批数据推送,这些事往往要几百毫秒甚至几秒,如果都堵在业务主流程里,用户看到的就是接口转圈、页面白屏。

Spring Boot 里的 @Async 做的事就是把这类任务提交给另一个线程去执行,调用方立刻返回。举个例子,订单创建成功后要发通知、写积分、推送数据,这些和“订单创建完成”这个核心结果没有强依赖关系。把它们放到异步线程里,接口响应时间立刻从九百毫秒压到一百毫秒。

但这里有个必须一开始就说清楚的认知:异步不代表“更快”,它只是让主线程不用干等。任务的总耗时没有减少,甚至因为线程切换、队列排队还会增加一点。它换来的是主流程快速响应,代价是把一套原本顺序执行、错误立刻可见的逻辑,变成了并行执行、错误藏在后台的逻辑。所以,不是所有业务都适合异步化。

1.2 哪些场景适合异步,哪些千万别碰

我自己的判断标准很简单,往下问三个问题:

  • 这个任务的结果,调用方需不需要立刻知道?如果不需要,可以考虑异步。
  • 这个任务失败,主流程能接受吗?如果能接受延迟补偿、重试、失败入库,可以考虑异步。
  • 这个任务是不是独立的旁路逻辑?比如通知、统计、数据同步,脱离了主流程也能自洽,可以考虑异步。

适合异步的典型场景:短信、邮件、站内信通知;日志上报和审计数据落库;报表预计算;批量数据导入导出;调用第三方接口做资料同步;缓存预热。这些场景的共同特点是:任务可以晚一点完成,甚至偶尔失败一次也能在后续补偿中找补回来。

不适合异步的场景也不少。一个是强一致性的写操作,用户下单后立刻要看到库存扣减、订单状态变更,这种结果必须实时返给前端,就不能拆到异步线程里“有空再说”。另一个是后续业务分支依赖前一个任务的返回值,而且这个依赖发生在同一个事务里,拆开异步就是把原子性拆没了。还有就是任务量极小、执行只要几毫秒、但对响应时间要求也没那么苛刻的场景,引入异步反而是过度设计,线程池调度开销比任务本身还大。

1.3 线程池异步、消息队列、响应式,别混为一谈

广义的异步一共有三种常见形态,项目里经常有人混着用,先分清是好事:

方案本质优点合适场景
线程池 + @Async进程内内存队列,任务交给本地线程实现简单、改造成本低进程内旁路任务、轻量并发
消息队列独立中间件,消息持久化、可重试、死信可靠、削峰、跨服务解耦大流量削峰、跨系统事件通知
响应式编程事件驱动、非阻塞 IO用极少线程撑高并发IO 密集且吞吐要求极高

我这里后面要讲的主要是第一种,进程内线程池异步。很多人项目里真正需要的其实是消息队列,但图省事用了 @Async,任务在应用重启的一瞬间全部丢光,后来才回头补 MQ。反过来也有人任务量很小,非要去引入一套队列中间件,运维成本远超收益。我的建议是:任务可以丢、延迟可以忍、量也不大,先用线程池;要求不丢、要削峰、要跨服务解耦,再考虑消息队列。

2. 快速跑通:@Async 的几种写法和经典陷阱

2.1 基础三步:开开关、加注解、看线程名

让 Spring Boot 支持异步,第一步是在配置类或启动类上加 @EnableAsync:

@SpringBootApplication @EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

第二步是在需要异步执行的方法上标注 @Async:

@Service public class NotifyService { @Async public void sendSms(String mobile, String content) { // 模拟耗时请求 try { Thread.sleep(800); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("短信发送完成:" + mobile + " " + Thread.currentThread().getName()); } }

第三步,也是最容易被忽略的一步:验证是否真的异步。在方法里把当前线程名打出来,同步执行时打印的是请求线程,异步执行后打印的应该是线程池里的线程名。这一步千万别省,后面很多“不生效”的坑,靠这一条就能立刻暴露。

2.2 自调用失效:最经典的“为什么注解没反应”

@Async 不生效的原因有很多,真正常见的其实是同一个:自调用。我在代码评审里至少看到过十几次这种写法:

@Service public class OrderService { public void createOrder(String orderId) { // 前面一堆订单业务 this.sendNotify(orderId); // 期望异步,其实是同步执行 } @Async public void sendNotify(String orderId) { // 短信、推送、积分…… } }

出现这个问题的原因要说到 Spring 的代理机制。@Async 和 @Transactional 一样,都是基于 AOP 代理实现的。Spring 在容器启动时生成一个代理对象,外部调用方拿到的是代理,代理在处理请求时会把方法提交给线程池。但 createOrder 方法内部用 this 调用 sendNotify,这个 this 是当前对象本身,不是代理,等于直接绕过代理调用了原始方法,异步自然不生效。

解决办法有三种。最简单的推荐方案是把异步方法拆到另一个 Bean 里,比如单独建一个 NotifyService,让 OrderService 注入它再调用。第二种是自我注入,通过 ObjectProvider 拿到代理对象。第三种是用 AopContext.currentProxy(),但需要额外开启 exposeProxy 配置,比较绕,我一般不推荐。

2.3 异步方法的三种返回值写法

@Async 方法可以声明为 void,也可以返回 Future 或 CompletableFuture,不同写法的语义差别很大。

void 用于“发出去就不管”的任务,调用方不关心结果,也没法知道什么时候失败。比如发送通知、上报日志。

@Async public void sendLog() { // 不管结果 }

Future 用于调用方确实想拿结果,但又不想同步死等的场景。配合带超时的 get 方法,既能拿到结果,又不会无限阻塞:

@Async public Future<String> queryReport(String date) { // 模拟报表生成 return new AsyncResult<>("report:" + date); } // 调用方 Future<String> future = reportService.queryReport("2024-01-01"); String result = future.get(3, TimeUnit.SECONDS);

CompletableFuture 更高级一点,适合多个异步结果需要编排的场景。比如一个页面要同时查价格、库存、活动信息,串行调用要 250 毫秒,并行只要 120 毫秒。这种情况可以结合 supplyAsync 提交任务,用 allOf 等所有任务完成,再统一收集结果。

3. 真正的分水岭:线程池参数与 Bean 命名陷阱

3.1 为什么“不配置线程池”等于裸奔

网上很多教程只写 @EnableAsync + @Async,完全不提线程池,照着敲完功能也跑得起来,但这恰恰是最危险的地方。Spring Boot 的自动配置确实会在你没有自定义 Executor 时提供一个默认线程池,可这个默认线程池的队列是无界的,任务会一直往队列里塞。一旦生产环境出现短时流量尖峰,任务积压成百上千,内存占用直接往上飙,最后 OOM 或者响应全面劣化。

更隐蔽的坑是:当你在容器里自定义了一个 Executor,但命名和类型没落在 Spring 的查找规则上时,@Async 可能找不到合适的线程池,最终退回到最原始的 SimpleAsyncTaskExecutor。这个实现不复用线程,每次调用都 new 一个线程,用完全部丢弃。流量稍大一点,系统线程数疯狂上涨,程序直接假死。所以我的结论非常明确:用 @Async 的第一步,就是配好自己的线程池,不配等于裸奔。

3.2 线程池参数怎么定:一套可复用的推导流程

线程池参数没有银弹,但它有一套推导思路,我每次配置都按这个流程走。

第一步估算 IO 密集度。绝大多数异步任务都是 IO 密集型,比如调三方接口、读写数据库、发消息。CPU 密集型任务占比很低。经验公式是:IO 密集型核心线程数约为 CPU 核数的 2 倍;CPU 密集型则是核数 + 1。举例,一台 4 核服务器,主要跑 IO 任务,核心线程数可以取 8。

第二步定最大线程数和队列容量。很多人只配了核心和最大,忽略了队列,Spring 默认的队列类型是无界队列,最大线程数永远不会触发。正确做法是显式设置一个有界队列。队列容量怎么算?你可以用“允许积压的任务数 × 单个任务平均耗时”来反推合理延迟。假设单个任务平均耗时 100ms,业务上允许队列里的任务最多等 10 秒,那么队列容量 = 10 秒 / 100 毫秒 = 100。最大线程数可以先用核心线程数的 2 倍起,比如核心 8、最大 16,然后压测调整。

第三步配置线程名前缀、拒绝策略和优雅停机。线程名前缀看着小事,排障时价值极大,日志里一眼看出任务跑在哪个池。完整配置类长这样:

@Configuration public class AsyncPoolConfig { @Bean("taskExecutor") public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心线程数:IO密集型,核数4,取2倍 executor.setCorePoolSize(8); // 最大线程数 executor.setMaxPoolSize(16); // 有界队列,替代默认无界队列 executor.setQueueCapacity(100); // 非核心线程空闲回收时间 executor.setKeepAliveSeconds(60); // 线程名前缀,排障时靠它认线程 executor.setThreadNamePrefix("async-task-"); // 拒绝策略:由调用线程执行,不丢任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 优雅停机:等已提交任务完成,最多等30秒 executor.setWaitForTasksToCompleteOnShutdown(true); executor.setAwaitTerminationSeconds(30); executor.initialize(); return executor; } }

这里有个很关键的细节:custom 线程池的 Bean 名我特意叫 taskExecutor,这不是随手起的。Spring 的异步执行器查找规则是:先找容器中唯一的一个 TaskExecutor;如果没有唯一的,再找名为 taskExecutor 的 Bean;都找不到,才退回默认实现。你如果自定义 Bean 方法名用的是 myExecutor,而容器里没有其他 Executor 时它确实能生效,但一旦项目里又冒出别的 Executor,查找规则就乱了。为了稳妥,要么 Bean 名起成 taskExecutor,要么在 @Async 注解里显式指定池,比如 @Async("reportExecutor"),两者都做最保险。

3.3 拒绝策略选 CallerRunsPolicy 还是直接抛异常

拒绝策略是线程池满员、队列也满时的最后一道防线。常见选择有四种:

策略行为适用场景
AbortPolicy直接抛异常任务必须立刻失败告警
CallerRunsPolicy由提交任务的线程自己执行不能丢任务,可以牺牲主流程响应
DiscardPolicy静默丢弃能接受任务丢失
DiscardOldestPolicy丢弃队列头部最老任务只关心最新任务

我自己线上默认用 CallerRunsPolicy。理由很直接:业务异步任务大多是通知、报表、数据同步,丢了补不回来,由提交线程执行虽然会让现场慢一点,但任务还在,日志还能追踪。如果任务本身对时效特别敏感,比如实时风控,反而应该选 AbortPolicy 并配合监控告警,让它立刻暴露出压力问题,而不是悄悄拖慢。

4. 异步里的四大隐雷:异常、事务、上下文、监控

4.1 同步调用异常是一条直线,异步异常是一口枯井

同步方法抛异常,调用方能立刻 catch 到,日志里也有完整堆栈。异步方法完全不同:void 方法一旦抛异常,调用方早就返回了,谁来接这个异常?

Spring 提供了一个兜底接口 AsyncUncaughtExceptionHandler,只要方法没有返回值,异常就会交给它。注意,一旦方法声明了 Future 或 CompletableFuture 返回值,异常会被包装进 Future 里,由调用方在 get 的时候抛出来,走不到这个兜底接口。

生产上我建议实现一下这个接口,把异常接到监控告警里:

@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Bean("taskExecutor") public ThreadPoolTaskExecutor taskExecutor() { // 同上 } @Override public Executor getAsyncExecutor() { return taskExecutor(); } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex, method, params) -> { // 记录日志并把异常信息送到监控平台 System.err.println("异步任务执行异常:" + method.getName()); ex.printStackTrace(); }; } }

这里还要提醒一句:异步异常不像同步异常那么显眼,它默认是“吞噬”掉的,不处理的话线上出了故障你都不知道在哪个环节丢的。一定要在异常处理器里加告警,否则异步就是事故盲区。

4.2 事务边界:异步线程里的事务,和调用方不是一回事

@Async 和 @Transactional 同时出现在一个方法上时,很容易让人误以为还能保持同一个事务。实际完全不是那么回事。

Spring 的事务和线程绑定。调用方业务在自己的事务里执行,@Async 方法被提交到另一个线程,那个线程里默认是没有事务上下文的。所以下面这种写法,异步方法有事务,但它是一个全新开启的独立事务,提交、回滚都和调用方无关:

@Async @Transactional public void syncUserData(String userId) { // 这个事务只管理异步线程里的操作 }

更常见的坑还是自调用。同一个类里 this 调用,不仅 @Async 失效,@Transactional 也跟着失效。所以当你看到“异步方法里出异常了数据没回滚”这类问题,先检查是不是自调用,再检查事务方法本身有没有被外部 Bean 调用。

我自己的设计原则是:一个操作如果必须保证原子性,就别拆成异步;拆成异步,就要接受最终一致性和补偿方案。异步加上事务,能解决的是“异步线程内部自己的数据一致性”,解决不了“调用方事务和异步任务一起回滚”,想靠一个注解把两边事务绑在一起,那是做不到的。

4.3 ThreadLocal 与 MDC:跨线程时上下文不会自己跟过去

还有一些业务场景需要在异步线程里拿到主线程的上下文信息,最常见的是用户 ID、语言环境、日志追踪 ID。很多人直接用 ThreadLocal 存用户信息,然后在异步方法里取,结果取到 null,一脸懵。

ThreadLocal 是线程私有的,父线程里 set 的值,子线程默认拿不到。Spring 的 @Async 底层是把任务丢进线程池,执行线程完全可能是另一个线程,所以 ThreadLocal 自然丢了。普通 Executor 子类还能通过 TaskDecorator 做一些包装,ThreadPoolTaskExecutor 正好支持这个扩展。

比较实用的是日志 MDC 传递,ELK 之类的日志系统靠 traceId 串链路,异步线程丢了 MDC,整条日志链路就断了。解决办法是提交任务时把当前 MDC 内容复制一份,在任务执行前 set 回去:

executor.setTaskDecorator(runnable -> { Map<String, String> contextMap = org.slf4j.MDC.getCopyOfContextMap(); return () -> { if (contextMap != null) { org.slf4j.MDC.setContextMap(contextMap); } try { runnable.run(); } finally { org.slf4j.MDC.clear(); } }; });

注意最后一个细节:finally 里必须 clear。线程池里的线程会被复用,上一次任务留下的 MDC 不清掉,下一个任务继承的就是脏上下文,日志串味,排障能给你带沟里去。

4.4 监控:线程池不会主动告诉你它快扛不住了

线程池的问题往往不是突然发生的,而是慢慢堆积的。任务从核心线程开始跑,满了进队列,队列满了才拉最大线程,最大线程也满了才开始拒绝。如果没有监控,等到你发现接口变慢、任务丢失,已经是拒绝策略触发之后的事了。

我建议至少打一套定时监控日志,每隔一分钟记录线程池的关键指标:

  • 当前活跃线程数:executor.getActiveCount()
  • 队列积压数量:executor.getThreadPoolExecutor().getQueue().size()
  • 已完成任务总数:executor.getThreadPoolExecutor().getCompletedTaskCount()
  • 当前线程数:executor.getPoolSize()

最简单的做法是配一个定时任务把这些值打到日志里,性价比很高。更正式的方案是用 Actuator 或者 Micrometer 把线程池指标接入监控大盘,设置告警阈值,比如队列积压超过 80% 就报警。这一项很容易被忽略,但它在关键时刻能救你一次。

5. 三次实战复盘:从超时接口到并发批处理

5.1 案例一:接口超时,靠异步把 900ms 降到 100ms

某商城项目的下单接口,经过服务化拆分后,主流程只剩核心的几笔数据库操作,但接口还是很慢。排查日志发现,订单创建成功后串行调用了三个旁路逻辑:发短信通知、扣积分、往推荐系统推送数据。三方接口响应速度不稳定,平均每个 100ms 到 300ms,三个叠加起来接口就奔着秒级去了。

改造方案是把这三个旁路逻辑全部挪到 NotifyService 的 @Async 方法里,主流程只负责保存订单和提交成功响应。改造后接口从 900ms 左右降到 100ms 上下,效果立竿见影。但这里有个前提:旁路逻辑允许失败。我同时加了一张通知记录表,异步方法里失败时记录失败原因和重试次数,由定时任务扫描重试。异步之后如果不补一个补偿机制,等于把失败的隐患从明处挪到了暗处。

5.2 案例二:批量对账任务,用 CompletableFuture 控制并发切片

另一个项目里有个对账任务,每天要把上万条明细逐条调用第三方查询接口核对状态,串行执行要跑一个多小时。这个场景适合并发切片,但绝不能无脑开一万个线程,而是用线程池的固定并发度来限制。

我当时的做法是把大列表按固定大小切块,比如每块 50 条,提交到线程池里并行处理,再用 CompletableFuture.allOf 等待所有分片完成,最后统一收集失败明细。核心代码如下:

List<List<Record>> partitions = partition(records, 50); List<CompletableFuture<Void>> futures = partitions.stream() .map(batch -> CompletableFuture.runAsync(() -> processBatch(batch), taskExecutor)) .collect(Collectors.toList()); CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

线程池最大线程数设置在 16,等于并发度最高就是 16,而不是把一万个任务全怼上去。改造后整个任务从一个多小时压缩到十几分钟。这个案例里最重要的不是 CompletableFuture 的用法,而是“用线程池本身控并发”这个思路:限流不是靠代码手写,而是靠你给线程池设置 maxPoolSize。

5.3 案例三:一个页面查四处数据,异步编排比想想中顺手

还有一种常见场景是接口内部有多个互不依赖的远程调用。比如商品详情页要查库存、价格、优惠活动和评价摘要,四个接口串行调用,每个 50ms,总耗时 200ms。用 CompletableFuture 把这些调用并发化,总耗时约等于最慢的那个接口,而不是四个之和。

这类改造最大的收益不在第一个版本,而在后续接口越来越多的时候。每多接一个外部服务,串行方案就又增加一段延迟,异步编排方案几乎不增加总耗时。注意这里还是要指定线程池,默认的 ForkJoinPool.commonPool 不适合生产环境,并发度会受 CPU 核数影响,业务隔离也做不到。

6. 异步操作问题速查表与排查顺序

6.1 常见问题清单

现象大概率原因解决思路
@Async 方法同步执行,线程名不变自调用 / 没加 @EnableAsync / 方法是 private拆 Bean、检查注解、改为 public
异步方法莫名丢任务拒绝策略 AbortPolicy / 应用重启换 CallerRunsPolicy、补持久化机制
线程数疯狂涨,系统假死退回 SimpleAsyncTaskExecutor自定义线程池并显式绑定
队列一直涨,内存告警默认无界队列setQueueCapacity 设置容量
异步方法里事务没回滚自调用 / 误解事务边界确认代理调用,事务独立管理
异步线程里 ThreadLocal 取到 nullThreadLocal 不跨线程用 TaskDecorator 显式传递
日志链路断裂,traceId 丢失MDC 未跨线程在装饰器里复制 MDC 并清理
Future.get 一直阻塞任务卡住,无限等待用 get(timeout) 设超时
线程池参数不生效Bean 名不满足查找规则Bean 名用 taskExecutor 或显式指定

6.2 我自己的排查顺序

遇到异步相关问题时,我一般按这个顺序排:先看日志线程名前缀,确认任务到底跑没跑进自定义线程池;然后看 @Async 方法被谁调用,排查自调用;再看拒绝策略和队列积压;最后查异常处理器和监控日志,定位是被吞掉的异常还是线程池满造成的。

这套顺序排下来,绝大多数“奇怪”问题都能在十分钟内找到方向。实际工作里不要只盯着配置项,先把“任务到底在哪里执行”这个事实确认了,后面就好办了。异步的题,十有八九出在“你以为它在这个池子里跑,其实它根本没进去”。如果让我给刚接触异步操作的同行提一条建议,那就是:写完 @Async 先别急着上线,打开线程名前缀日志,把异常处理器、拒绝策略、补偿任务这三个位置全部过一遍,确认无死角再发布。这个习惯我每次改造异步逻辑都会重复一遍,大小事故都靠它挡住了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 13:04:19

从原理到实战:手写哈希表为什么是竞赛选手的必备技能?

哈希表这个东西&#xff0c;很多同学在洛谷上刷题迟早会撞上&#xff0c;P11615 这道【模板】题就是个很标准的敲门砖。我记得自己当年第一次见这题时&#xff0c;满脑子都是“这不就是 map 吗&#xff0c;凭什么要我自己写”&#xff0c;后来真在比赛里被卡了几次常数、被卡了…

作者头像 李华
网站建设 2026/10/10 13:01:40

基于MATLAB的多节点短路计算与Z矩阵应用实践

做电力系统分析这一行&#xff0c;短路计算是绕不开的基本功。我刚读研那会儿接到一个小任务&#xff1a;把一套几十个节点的区域电网做全节点三相短路电流扫描&#xff0c;输出一份用在保护整定上的数据表。当时心想&#xff0c;课本上不是学过戴维南定理嘛&#xff0c;拿起笔…

作者头像 李华
网站建设 2026/10/10 13:01:29

从零实现计算器界面程序:GUI开发核心实践

项目标题“A6&#xff1a;编写计算器界面程序”&#xff0c;看起来像是课程作业或者新手入门时的第一个图形界面项目。但别因为它叫“计算器”就小看它——一个像样的计算器界面程序&#xff0c;几乎能覆盖图形界面开发的全部核心知识点&#xff1a;布局管理、事件绑定、状态维…

作者头像 李华
网站建设 2026/10/10 13:00:48

深入解析SQL中ROW_NUMBER()与GROUP BY的本质区别及实战应用

1. 开头&#xff1a;为什么我把这两个语法放在一起“吃透”做SQL开发的人&#xff0c;几乎都会遇到一个诡异的场景&#xff1a;明明只是想去个重&#xff0c;用ROW_NUMBER()写出来的结果和用GROUP BY写出来的结果&#xff0c;乍一看好像一样&#xff0c;但细看却完全不同。更让…

作者头像 李华
网站建设 2026/10/10 13:00:29

2026年Windows C盘爆满真相与9种系统级清理方法

1. 这不是“删文件”而是系统级空间治理&#xff1a;C盘爆满的本质与2026年新挑战“C盘爆满了怎么办&#xff1f;”——这句话在2026年依然高频出现在各类技术社区、办公群和家庭微信群里&#xff0c;但它的背后早已不是十年前那个简单清空“下载”“桌面”“回收站”的逻辑。我…

作者头像 李华
网站建设 2026/10/10 12:58:20

AI数据中心超节点架构设计与工程实践全解析

写这篇东西之前&#xff0c;我先说说背景。从去年开始&#xff0c;AI算力的竞争焦点已经明显从单一芯片的峰值算力&#xff0c;转向了整个集群的系统级效率。业内有一个共识正在形成&#xff1a;GPU单卡的性能增长正在放缓&#xff0c;而大模型训练对算力的需求却以远超摩尔定律…

作者头像 李华