我为了保证原子性把几万条记录的批量处理塞进了一个数据库事务,结果这个事务跑了好几分钟,期间长时间持锁阻塞了其他业务、占满了连接池、还把回滚段撑得老大、主从延迟飙升:一次大事务拖垮整个库的深度复盘

我有个批量任务要更新几万条记录,想着这些操作得保证原子性、要么全成功要么全回滚,就放进一个事务里:开启事务→循环几万次逐条 update→提交。功能没问题,可一上线跑,整个数据库就开始抖:其他业务大面积报锁等待超时、连接池被占满、主从延迟从毫秒飙到几十秒、DBA 告警 undo 暴涨。复盘才理解大事务的危害:一个事务在开始到提交的整个期间会持有它改过的行的锁、占用一个连接、在 undo log 里保留回滚所需的旧版本、改动到提交才写 binlog 传从库;我把几万条塞进一个跑几分钟的事务,意味着这几分钟里它一直持锁阻塞别人、占着连接、累积 undo、延迟复制。根因是事务越大越长,霸占这些关键公共资源的量和时间就越大,拖垮整个库;而我误以为事务越大越安全。这篇复盘从故障现场讲到事务存续期间占用的资源、大事务的五大危害,再到分批提交、事务里别放慢操作与远程调用、用批量语句、分批加幂等达成最终一致的完整正解,以及其他长期霸占共享资源的坑,和独占公共资源的范围和时长决定对整体的阻塞代价、持有共享资源要范围小时间短及时释放、把大任务切小快进快出而非一次长霸占的认知。

我为了保证原子性把几万条记录的批量处理塞进了一个数据库事务,结果这个事务跑了好几分钟,期间长时间持锁阻塞了其他业务、占满了连接池、还把回滚段撑得老大、主从延迟飙升:一次大事务拖垮整个库、误以为事务越大越安全的深度复盘

那次"整个库都变慢、好多业务报超时"的故障,根子在我一个"为了安全"的设计。我有个批量数据处理任务,要更新几万条记录。我想"这些操作得保证原子性,要么全成功要么全回滚,那就放在一个事务里呗",于是写成了:开启事务 → 循环几万次逐条 update → 提交。功能上没问题,可一上线跑这个任务,整个数据库就开始抖:其他业务大面积报锁等待超时、接口变慢、连接池被占满、监控显示主从延迟从毫秒飙到几十秒、DBA 还告警说回滚段(undo)暴涨。复盘这件事,我才真正理解了"大事务"的危害,以及我犯的认知错误:问题出在我以为"事务越大、包得越全,越安全"。一个数据库事务,在它开始到提交/回滚的整个期间,会持有它修改过的行的锁、占用一个数据库连接、并在 undo log 里保留回滚所需的旧版本数据;我把几万条 update 塞进一个事务,意味着这个事务持续了好几分钟——在这几分钟里:①它持有的行锁一直不释放,其他要改这些行(或因间隙锁波及的行)的事务全在排队等锁、直至超时;②它一直占着那个数据库连接不放,连接池很快被这类长事务占满;③undo log 要保留这几分钟里所有改动的旧版本(供回滚和 MVCC),越积越大、撑爆回滚段;④这一大坨改动到提交才一起写 binlog、同步给从库,从库重放这个大事务期间主从延迟剧增;也就是说,事务持续越久、改动越多,它霸占关键公共资源(锁、连接、undo、复制)的时间和量就越大,对整个库的拖累就越严重。根本原因是:事务在其存续期间持有锁、占用连接、累积 undo、延迟复制;我为了"原子性"把海量操作塞进一个长时间运行的大事务,使它长期霸占这些关键公共资源,从而长时间阻塞其他业务、占满连接、撑大 undo、拖垮主从延迟。问题的根,是大事务——把过多操作放进一个长事务,使其长时间持锁、占连接、累积 undo、延迟复制,拖垮整个库;根源是误以为事务越大越安全,忽视了事务越长占用公共资源越久危害越大。这篇就把这次"大事务"的坑,从头到尾复盘一遍。

故障现场:一个几分钟的事务,拖垮整个库

问题在于一个长时间运行的大事务长期霸占关键资源:

-- 我的写法: 为了"原子性", 把几万条更新塞进一个事务
START TRANSACTION;
-- 循环几万次, 逐条更新(伪代码)
UPDATE orders SET status = 'archived' WHERE id = 1;
UPDATE orders SET status = 'archived' WHERE id = 2;
-- ... 几万条 ...
UPDATE orders SET status = 'archived' WHERE id = 50000;
COMMIT;   -- 几分钟后才提交

/*
这个大事务在运行的几分钟里, 对整个库做了什么"坏事":
  ① 持有大量行锁不放: 它改过的每一行都加了锁, 直到COMMIT才释放;
     → 其他要改这些行的事务全在等锁 → Lock wait timeout exceeded(锁等待超时);
     → 还可能因间隙锁/Next-Key锁波及更多行, 阻塞范围更大; 死锁概率也上升;
  ② 长期占用一个数据库连接: 事务期间这条连接不能复用;
     → 这类长事务多了, 连接池被占满 → 其他请求拿不到连接 → 大面积超时;
  ③ undo log(回滚段)暴涨: 为了能回滚 + 支持MVCC, 要保留事务期间所有改动的旧版本;
     → 长事务保留的旧版本越来越多, undo膨胀, 还会阻碍purge(清理), 拖慢全库;
  ④ 复制延迟: 事务直到COMMIT才一次性写binlog、传给从库, 从库要重放这一大坨;
     → 主从延迟从毫秒飙到几十秒, 读从库的业务读到严重过期数据;
  ⑤ 万一中途失败要回滚: 回滚几万条改动非常慢, 期间还在持锁。

★ 核心: 事务从开始到提交的整个期间, 都在"持有锁 + 占用连接 + 累积undo + 待复制";
  事务越大(改动越多)、越长(持续越久), 霸占这些"关键公共资源"的量和时间就越大,
  对整个数据库的阻塞和拖累就越严重——"大事务"是数据库性能和稳定性的大敌。

我的认知错误: "把所有操作放一个事务里, 原子性最强, 所以最安全。"
  真相: 原子性是要的, 但不该用"一个巨大的长事务"来实现; 大事务用"长时间霸占公共资源"
        换来的"强原子性", 代价是拖垮所有人——安全要的是"小而快的事务 + 合理的一致性设计"。
*/

看着监控上"锁等待飙升、连接池占满、主从延迟几十秒"三条曲线同时报警,我又懊恼又后怕:"我以为'包在一个事务里=最安全',谁知道这个几分钟的大事务,像个霸占着公共道路几分钟不挪窝的大卡车,把整条路都堵死了……原子性是对的,但用这么大的事务去实现,代价太大了。"这个坑最反直觉的地方在于:它源于一个"看似负责任"的good intention——"保证原子性";事务本身、原子性本身都没错,错的是"把事务做得太大太长";而且它在数据量小、测试环境下(几十条、秒级)完全正常,只有数据量一大、并发一高才暴露成灾难下面就来拆解,大事务该怎么治。

第一件事:搞懂事务持有的资源与大事务的危害

我顺着这次事故,把事务的代价和大事务的危害彻底理清了。

事务在存续期间占用什么? 为什么大事务危害大?

【核心: 事务从开始到提交期间持有行锁、占用连接、累积undo、改动待复制; 事务越大越长, 霸占这些
   关键公共资源的量和时间越大, 阻塞其他业务/占满连接/撑大undo/拖垮主从延迟; 正解是拆成小而快的事务】

1. 一个事务在"存续期间"占用的关键资源:
   - 锁: 它改过的行(及波及的间隙)被加锁, 直到提交/回滚才释放 → 阻塞其他事务;
   - 连接: 占用一条数据库连接全程不放 → 连接池资源;
   - undo log: 保留事务期间所有改动的旧版本(供回滚和MVCC)→ 越长越大, 阻碍清理;
   - binlog/复制: 改动到提交才一次性写binlog传从库 → 大事务=大延迟。

2. 大事务(改动多/持续久)的危害(都来自"长时间大量霸占上述资源"):
   - 长时间持锁 → 锁等待超时、阻塞面广、死锁概率高;
   - 长时间占连接 → 连接池耗尽、其他请求拿不到连接;
   - undo膨胀 → 回滚段暴涨、purge受阻、全库变慢、磁盘压力;
   - 主从延迟 → 从库重放大事务期间, 延迟剧增, 读到过期数据;
   - 回滚慢 → 万一失败, 回滚几万条很慢且持续持锁。

3. 正解方向(化大为小):
   ① 分批提交: 把几万条拆成每批N条(如500/1000), 每批一个小事务, 分批commit;
      → 每个事务都短、持锁少、很快释放, 不再长期霸占资源;
   ② 事务里别放慢操作: 事务内绝不夹远程调用/HTTP/发消息/大计算/Thread.sleep
      (这些会拉长事务时间, 把"网络/外部"的不确定性带进持锁期间);
   ③ 事务尽量短小: 只把"必须原子"的几步放进事务, 其他移到事务外;
   ④ 一致性用别的手段: 不必强求"一个大事务", 可用分批+幂等(同586篇)+补偿/状态机
      达到"最终一致", 而非靠一个巨型事务的"强原子";
   ⑤ 批量操作用批量语句: 能用一条批量UPDATE/INSERT...(set-based)就别循环逐条。

4. 分批的权衡(要想清):
   - 分批提交后, "整体"不再是一个原子单元(可能批1成功批2失败);
   - 所以要配合: 幂等(可重跑)、记录进度(断点续批)、补偿/对账, 保证最终正确;
   - 这是"用最终一致 + 可恢复"换"避免大事务拖垮库", 是值得的取舍。

一句话: 事务存续期间持有锁/占连接/累积undo/待复制, 大事务会长时间大量霸占这些公共资源、拖垮全库;
   正解是拆成小而快的事务(分批提交)、事务内不放慢操作/远程调用、用分批+幂等达成最终一致而非巨型事务。

这套认知,是整个坑的根。事务存续期间占用的资源:行锁(到提交才放)、连接、undo log(保留旧版本)、binlog/复制大事务的危害:都来自"长时间大量霸占上述资源"——锁等待超时、连接池耗尽、undo 膨胀、主从延迟、回滚慢正解方向:分批提交(每批小事务)、事务里别放慢操作/远程调用、事务尽量短小、一致性用分批+幂等+补偿达成最终一致、批量操作用批量语句分批的权衡:分批后整体不再是一个原子单元,要配合幂等、进度记录、补偿对账保证最终正确——用最终一致+可恢复换避免大事务拖垮库一句话:事务存续期间持有锁/占连接/累积 undo/待复制,大事务会长时间大量霸占这些公共资源、拖垮全库;正解是拆成小而快的事务(分批提交)、事务内不放慢操作/远程调用、用分批+幂等达成最终一致而非巨型事务。

第二件事:正解——分批提交、事务短小、最终一致

知道了大事务的危害,正解就清楚了:把大事务拆成小而快的事务,事务里只留必须原子的。

// 正解1: 分批提交(把几万条拆成每批N条, 每批一个小事务)
int batchSize = 1000;
long lastId = 0;
while (true) {
    // 每批一个独立的短事务
    List ids = jdbc.queryForList(
        "SELECT id FROM orders WHERE id > ? AND need_archive = 1 ORDER BY id LIMIT ?",
        Long.class, lastId, batchSize);
    if (ids.isEmpty()) break;

    txTemplate.execute(status -> {                 // 每批一个小事务, 很快提交
        jdbc.batchUpdate("UPDATE orders SET status='archived' WHERE id=?", ids, ...);
        return null;
    });                                            // ← 每批commit, 锁很快释放, 不长期霸占
    lastId = ids.get(ids.size() - 1);
    // 可在这里记录进度lastId(断点续批), 控制速率(别打满DB)
}
// 每个事务只锁1000行、几十毫秒就提交 → 不再长时间阻塞别人。

// 正解2: 事务里绝不放慢操作 / 远程调用(把外部不确定性挡在事务外)
// ✗ 错误: 事务内夹远程调用, 把网络耗时算进持锁期间
//   @Transactional void bad(){ db.update(...); httpClient.call(...); db.update(...); }
// ✓ 正确: 远程调用移到事务外
@Transactional void updateDb(Order o){ db.update(o); }   // 事务只包DB操作, 短
void process(Order o){
    updateDb(o);                  // 短事务: 进出很快
    httpClient.notify(o);         // 远程调用在事务外, 不占着锁等网络
}

// 正解3: 能用批量语句就别循环逐条(set-based)
// ✗ for(id: ids) update(id);                          // 逐条, 慢且事务长
// ✓ UPDATE orders SET status='archived' WHERE id IN (...);   // 一条搞定一批

// 正解4: 一致性用"分批 + 幂等 + 进度/补偿"达成最终一致(同586篇幂等)
//   - 任务可重跑(幂等: archived再设archived无副作用);
//   - 记录处理进度(lastId), 中断后从断点续跑;
//   - 失败的批次单独重试/对账; 整体最终一致, 而非靠一个巨型原子事务。

// 核心: 化大为小——分批提交让每个事务短小快速释放资源; 事务内不放慢操作/远程调用;
//   用批量语句; 一致性靠分批+幂等+可恢复达成最终一致, 而非用长事务硬扛强原子。

这套正解的关键,是把"一个长时间霸占资源的大事务"拆成"许多个快速进出、及时释放资源的小事务"分批提交:把几万条拆成每批 N 条、每批一个小事务,每个事务只锁少量行、几十毫秒就提交释放,不再长期阻塞——这正是本次该做的。事务里别放慢操作/远程调用:把网络、外部调用挡在事务外,别把它们的不确定耗时算进持锁期间。用批量语句:能一条 set-based 语句搞定就别循环逐条。一致性靠最终一致:用分批 + 幂等 + 进度记录 + 补偿对账(同 586 篇)达成最终正确,而非靠一个巨型事务硬扛强原子。

第三件事:其他几个"长期霸占共享资源"的坑

顺着这次大事务,我把"长时间独占公共资源"的几类坑也一并理了:

几类"长期霸占共享资源"的坑(核心都是"独占范围太大太久, 拖累所有人"):

坑1: 长事务里夹远程调用——网络一慢, 锁就持有很久, 把外部不确定性带进DB(上文);
   正解: 远程调用移到事务外。

坑2: 锁的粒度太大/持有太久——synchronized锁了一大段、或锁里做IO/慢操作;
   正解: 缩小临界区, 只锁真正需要互斥的最小代码; 锁里别做IO/远程调用(同分布式锁562)。

坑3: 数据库连接借了不还/借太久——拿了连接做一堆慢活, 或忘了关; 连接池耗尽;
   正解: 尽快用完归还, try-with-resources; 别在持有连接时做无关慢操作。

坑4: 一次性全表扫/全量加载——SELECT * 几百万行进内存, 占内存又锁表久(同546深度分页);
   正解: 分页/游标/流式处理, 别一次性全量。

坑5: 批处理不限速打满DB——分批是对的, 但每批之间不歇、并发太高, 也会压垮DB;
   正解: 控制批大小和速率(限流), 给在线业务留资源。

坑6: 全局锁/表锁做长时间维护——DDL/大批量操作锁住整表, 在线业务全卡;
   正解: 用在线DDL工具(pt-osc/gh-ost)、低峰期、分批。

共同的根: 锁、连接、事务、临界区, 都是"独占某种公共资源"的机制; 独占的"范围越大、时间越久",
   被你挡在门外、排队等待的其他人就越多、越久; 凡独占公共资源, 都应"范围尽量小、占用尽量短、
   及时释放", 别贪图"一把全包"的简单而长期霸占、拖垮整体。

这些坑看似不同,根却是同一个:锁、连接、事务、临界区,本质都是"独占某种公共资源"的机制;你独占的"范围越大、时间越久",被你挡在门外、排队等待的其他人就越多、越久认清这个根("凡独占公共资源,范围尽量小、占用尽量短、及时释放"),很多性能和稳定性问题就能从源头预防。

第四件事:大事务 vs 分批小事务——两张对照表

我把大事务和分批小事务在各维度的对比、以及事务该放什么不该放什么,整理成对照表,贴在了团队的 DB 规范里:

维度 一个大事务 分批小事务
持锁时间 几分钟,阻塞面广 几十毫秒,快速释放
连接占用 长时间独占一条 每批用完即还
undo/回滚段 暴涨,阻碍清理 小,及时回收
主从延迟 提交时一次性,延迟大 分批同步,延迟小
对其他业务 大面积锁等待超时 影响小
原子性 整体强原子 批内原子,整体最终一致
失败恢复 全回滚,慢 从断点续跑(需幂等)
该不该放进事务 说明
✓ 必须原子的几条 DB 写 事务的本职
✗ 远程调用 / HTTP 网络耗时会拉长持锁,挪到事务外
✗ 发消息 / MQ 移到事务外或用事务消息
✗ 大计算 / sleep 无谓拉长事务
✗ 海量逐条循环 改批量语句或分批提交
✗ 读外部接口拿数据 先在事务外取好再开事务

这两张表的核心,第一张是分批小事务在几乎所有运行维度都优于大事务,只在"整体强原子"上让步(用最终一致+可恢复补上);第二张是事务里只放必须原子的 DB 写,远程调用/发消息/大计算/慢操作一律挪到事务外。记住一条:事务要"短、快、纯"——时间短、提交快、只包必须原子的 DB 操作。

第五件事:关于事务的几组容易想当然的认知

这次事故也让我厘清了几组关于事务的、容易想当然的概念:

直觉以为 实际上
事务包得越全越安全 大事务长期霸占资源,拖垮全库
放一个事务里才能保证一致 分批+幂等+补偿也能保证最终一致
事务只是"要么全成要么全败" 它运行期间一直持锁/占连接/累 undo
事务里夹个远程调用没关系 网络一慢,锁就持有很久,危害大
批量逐条 update 在事务里很正常 事务长、慢;应批量语句或分批提交
测试没问题就没问题 数据量小时大事务也快,量大才暴露
分批就是放弃了一致性 是换成"最终一致 + 可恢复",仍正确

这张表里,我栽的是第一行和第二行:把"事务包得越全"等同于"越安全",以为"非得一个大事务才能保证一致",没意识到这个大事务正在长时间霸占资源、拖垮所有人厘清这些,核心是一个意识:事务不是"越大越安全"的护身符,而是一个"运行期间持续占用关键公共资源"的东西;一致性要保证,但要用"小而快的事务 + 合理的最终一致设计"来实现,而不是用"一个长时间霸占全库资源的巨型事务"硬扛。

第六件事:设计事务 / 批处理时,我现在的自检习惯

现在每当我要写一个事务、或一个批处理任务,我都会先按这张图问自己:

这张图的精髓,是"量大就分批小事务、事务里别放慢操作远程调用、用幂等+进度保最终一致"先问改动量大不大(大就分批)、事务里有没有慢操作(有就挪出去)、分批后怎么保证正确(幂等+进度)。这套习惯,让我从"为了原子性包一个大事务"变成了"小而快的事务 + 最终一致设计"——核心始终是:事务存续期间持锁/占连接/累 undo/待复制,大事务会长期霸占公共资源拖垮全库;拆成小而快的事务(分批提交)、事务内不放慢操作/远程调用、用分批+幂等达成最终一致。

我立下的几条规矩

这场"大事务拖垮整个库"的事故,换来了我写数据库操作时,刻进骨子里的几条铁律:

  1. 事务在存续期间持有行锁、占用连接、累积 undo、改动待复制;事务越大越长,危害越大。
  2. 大事务会长时间持锁阻塞、占满连接池、撑大 undo、拖垮主从延迟、回滚慢。
  3. 海量操作要分批提交:每批 N 条一个小事务,快速提交释放资源。
  4. 事务里绝不放远程调用/HTTP/发消息/大计算/sleep,只留必须原子的 DB 写。
  5. 批量操作优先用批量语句(set-based),别在事务里循环逐条。
  6. 分批后用幂等 + 进度记录 + 补偿对账保证最终一致,而非靠巨型事务的强原子。
  7. 事务要"短、快、纯";一切独占公共资源的东西(锁/连接/临界区)都要范围小、时间短、及时释放。

附:排查与定位大事务/长事务的命令

最后,把我现在排查"谁在搞大事务/长事务"常用的命令贴成一份清单(以 MySQL 为例)。

-- 1. 查当前正在运行、且持续时间长的事务(揪出长事务的元凶)
SELECT trx_id, trx_started,
       TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS duration_sec,
       trx_rows_modified, trx_query
FROM information_schema.innodb_trx
ORDER BY duration_sec DESC;        -- duration_sec 很大的就是长事务

-- 2. 查锁等待(谁在等谁的锁, 大事务常是阻塞源)
SELECT * FROM performance_schema.data_lock_waits;          -- MySQL 8
-- 或老版本: SELECT * FROM information_schema.innodb_lock_waits;

-- 3. 查主从延迟(大事务会推高它)
SHOW SLAVE STATUS\G       -- 看 Seconds_Behind_Master

-- 4. 监控 undo / 历史列表长度(History List Length, 长事务会让它居高不下)
SHOW ENGINE INNODB STATUS\G    -- 找 "History list length", 持续很大说明有长事务阻碍purge

-- 5. 设置兜底: 限制单事务最长执行时间 / 锁等待超时, 别让长事务无限拖
-- innodb_lock_wait_timeout = 50      -- 锁等待超时(秒)
-- 应用侧: 给批处理事务设超时; 监控告警长事务(duration > 阈值)

-- 排查思路: 业务变慢/锁超时 → 先看 innodb_trx 里有没有 duration 很大的事务 →
--   定位到那个大事务的 SQL 和来源 → 改成分批小事务。

这份清单的核心,是information_schema.innodb_trx 里那个 duration_sec——它能一眼揪出"跑了很久还没提交"的长事务元凶我现在还给批处理事务都加了"单事务时长监控告警":一旦有事务跑超过阈值就报警,把大事务消灭在"拖垮全库"之前,而不是等到锁等待、连接池、主从延迟三条线一起报警才回头查。

写在最后

回头看,这场由"一个为了原子性而塞得过大的事务"引发的、拖垮整个库的故障,真正教给我的,远不止"大事务要拆成分批小事务"这一个技巧。它让我对"当你为了'把一件事一次性、完整地办妥'而长时间独占一份大家共用的资源时, 你个体的'周全', 可能正是整体的'灾难'——因为在你独占的这段时间里, 所有其他需要这份资源的人, 都被你挡住了、堵死了",有了一次刻骨的体会。我栽跟头,是因为我只从"我这件事"的角度,追求它"尽善尽美的原子性"——"一个大事务全包, 要么全成要么全败, 多干净利落";可我完全没站在"整个数据库、所有其他业务"的角度看: 我这个"干净利落"的大事务, 是靠"几分钟死死攥住一大把锁和连接不放"换来的;在这几分钟里, 整个库的其他使用者, 都在为我这一个事务的"周全"而排队、超时、饿死——我用"全局的拥堵"换来了"我这件事的局部完美"这让我领悟到一个关于"独占、粒度与共享"的深刻认知:在一个有大量参与者共享有限资源的系统里(数据库、操作系统、团队、社会),任何一方"独占公共资源"的行为,其'独占的范围和时长',都直接决定了它对整体的'阻塞代价'——独占得越大、越久,挡住的人就越多、越久;所以"持有共享资源"的黄金法则是:"尽可能小的范围、尽可能短的时间、用完立刻释放"——不是因为你的事不重要, 而是因为"资源是共享的", 你多占一秒, 别人就多等一秒;那种"我把事一次性大包大揽办彻底"的简单和爽快, 在共享系统里往往是自私且危险的——真正的高手, 会把大任务切成小块、快进快出, 让出资源给别人, 用"许多次短暂的占用"替代"一次漫长的霸占"这给了我一种在共享系统里行事的自觉:每当我要"占用一份大家共用的资源"来完成我的任务时,要时刻意识到"我占着它的时候, 别人都在等",从而主动地把我的占用切小、缩短、尽快释放——哪怕这让我的代码/流程不那么"一气呵成", 也好过让我一个人的"周全"拖垮所有人;"以最小的范围和最短的时间占用共享资源、用完即还",是在任何共享系统里做一个'不拖累整体的好公民'的关键认清独占公共资源的范围和时长决定对整体的阻塞代价、持有共享资源要范围小时间短及时释放、把大任务切小快进快出而非一次长霸占——这,是我用一次大事务拖垮库的事故,换来的、关于数据库、也关于如何在共享系统里行事的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次想把一大批操作塞进一个事务时,顿一下、把它拆成分批提交的小事务,那我对着那三条同时报警的监控曲线排查的那个下午,就值了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

我在 Java 里用 Arrays.asList 把数组转成 List,以为得到了一个普通的可增删的列表,结果往里 add 一个元素就抛 UnsupportedOperationException,因为它根本不是 ArrayList 而是个固定大小的数组视图的深度复盘

2026-6-3 0:48:16

技术教程

我的服务通过域名调用下游,下游因为故障切换换了 IP,我的服务却还死死连着那个已经下线的旧 IP 一直报连接失败,重启之后才恢复,原来是 DNS 解析结果被缓存了很久不刷新的深度复盘

2026-6-3 1:01:30

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索