MyBatis(持久层框架)执行、映射、缓存、事务与插件
本文沿一次持久化调用建立完整证据链:接口方法如何变成 MappedStatement(映射语句),参数如何安全进入预编译语句,结果如何还原对象,SqlSession(数据库会话)和连接如何参与 Spring(Java 应用框架)事务,缓存与批处理在什么时刻才形成数据库事实。学习目标不是会写映射文件,而是能解释调用链、识别失败窗口,并用数据库最终状态证明库存、支付、轨迹和导出任务是否正确。

flowchart LR
A["应用服务"] --> M["Mapper(映射器)代理"]
M --> S["SqlSession(数据库会话)"]
S --> E["Executor(执行器)"]
E --> H["StatementHandler(语句处理器)"]
H --> P["ParameterHandler(参数处理器)"]
P --> D["JDBC(Java 数据库连接)驱动"]
D --> R["ResultSetHandler(结果集处理器)"]
R --> A
E --> C1["一级缓存:会话范围"]
E --> C2["二级缓存:命名空间范围"]| 层次 | 主要对象 | 持有的事实 | 最常见误判 |
|---|---|---|---|
| 声明层 | Mapper(映射器)、MappedStatement(映射语句) | 方法与 SQL(结构化查询语言)、参数、结果规则 | 接口方法自己执行数据库 |
| 会话层 | SqlSession(数据库会话) | 一次工作单元、一级缓存、执行器 | 会话关闭等于事务提交 |
| 执行层 | Executor(执行器)、四类处理器 | 缓存、语句准备、参数、结果映射 | 返回成功就是已提交 |
| 资源层 | JDBC(Java 数据库连接)、连接、数据库 | 锁、日志、提交后的最终状态 | 应用日志能够代替数据库证据 |
1. 启动配置、映射注册与版本边界
启动阶段由配置构建器解析全局设置、类型别名、类型处理器、插件、环境、Mapper(映射器)接口和 XML(可扩展标记语言)映射。每条语句最终注册为 namespace.id 唯一键对应的 MappedStatement(映射语句),其中保存 SQL(结构化查询语言)来源、语句类型、参数映射、结果映射、缓存策略和超时。Spring(Java 应用框架)集成通常扫描接口并注册 MapperFactoryBean(映射器工厂对象);核心包、集成包和启动器是三个依赖,必须按项目依赖树核对兼容关系,不能用一个版本号代替全部行为。
| 配置来源 | 形成对象 | 冲突或失败 | 排查证据 |
|---|---|---|---|
| 全局配置 | Configuration(全局配置) | 设置拼错、插件顺序错误 | 启动日志与配置快照 |
| XML(可扩展标记语言)映射 | MappedStatement(映射语句) | 标识重复、结果映射缺失 | namespace.id 与资源路径 |
| 注解映射 | MappedStatement(映射语句) | 复杂语句可读性差 | 接口方法与注解内容 |
| 接口扫描 | Mapper(映射器)定义 | 扫描遗漏、重复对象 | 容器对象清单 |
flowchart TD
B["Spring Boot(快速开发框架)启动"] --> C["读取核心、集成与启动器版本"]
C --> G["构建 Configuration(全局配置)"]
G --> X["解析 XML(可扩展标记语言)与注解"]
X --> MS["注册 MappedStatement(映射语句)"]
G --> T["注册 TypeHandler(类型处理器)"]
G --> I["注册 Interceptor(拦截器)"]
MS --> F["扫描 Mapper(映射器)并注册工厂对象"]
F --> V{"语句标识、结果映射和版本兼容通过?"}
V -->|否| E["启动失败并保留最内层异常"]
V -->|是| R["容器可对外提供代理"]数据演绎 1:配置注册冲突。 OrderMapper.xml 和注解都声明 findById,组合键同为 com.acme.OrderMapper.findById。启动解析第二项时发现重复,不应通过删除异常日志或随机改扫描包规避;应确定唯一权威映射,保留语句标识、资源路径和依赖版本证据。若生产仅某节点失败,还要比较构建产物哈希和类路径是否混入旧映射文件。
热门面试题
- 问题(基础题):MappedStatement(映射语句)是什么?
- 考点:配置期产物和运行期入口。
- 回答思路:从唯一标识、包含信息和不可混同对象回答。
- 详细答案:MappedStatement(映射语句)是配置期为一条查询或更新建立的不可随请求变化的执行描述,按
namespace.id注册。它保存 SQL(结构化查询语言)来源、语句类型、参数映射、结果映射、缓存、超时和生成键策略。Mapper(映射器)代理运行时先定位它,再交给会话执行;它不是数据库连接,也不保存一次请求的参数值。 - 进阶追问:为什么标识必须稳定唯一?
- 进阶回答:代理依赖接口全名和方法名定位语句,重复会让行为不确定;稳定标识也用于日志、监控和事故回溯。
- 问题(原理题):启动阶段为什么要提前解析映射?
- 考点:快速失败与运行期开销。
- 回答思路:说明把结构错误前移和复用元数据。
- 详细答案:提前解析可以在接流量前发现资源缺失、语句标识重复、结果映射引用错误和类型处理器无法加载,并把可复用的映射元数据缓存到 Configuration(全局配置)。运行期只需要按标识取描述并绑定当次参数,既降低重复解析成本,也让错误在发布阶段而非订单高峰暴露。
- 进阶追问:启动成功能否证明所有 SQL(结构化查询语言)正确?
- 进阶回答:不能。动态分支、数据库方言、权限、表结构和真实数据分布仍需集成验证与运行证据。
- 问题(项目题):升级 MyBatis(持久层框架)时怎样控制风险?
- 考点:三件套兼容和行为回归。
- 回答思路:锁定依赖、核对发布说明、验证关键链路。
- 详细答案:先从依赖树分别记录核心包、MyBatis-Spring(Spring 集成模块)和启动器小版本,再核对兼容矩阵及缓存、生成键、代理和事务集成变更。用 WMS(仓储管理系统)条件扣减、支付流水唯一键、轨迹批量写和异步导出四条真实路径做回归,比较 SQL(结构化查询语言)、参数、影响行数、提交结果和性能,而不是只看应用启动。
- 进阶追问:为什么不能只跑单元测试?
- 进阶回答:数据库驱动、连接池、方言、真实事务和映射资源装配都不在纯单元测试的保证范围内。
2. Mapper(映射器)代理、方法解析与调用入口
Mapper(映射器)接口通常没有实现类。工厂创建 JDK(Java 开发工具包)动态代理,InvocationHandler(调用处理器)接收方法调用;对象基础方法走特殊分支,默认方法按语言规则处理,普通方法缓存为 MapperMethod(映射器方法)。它根据命令类型选择 selectOne、selectList、update 等会话操作,并把多个参数包装成名称到值的映射。接口代理只负责“把方法翻译成会话命令”,事务、安全、幂等和数据库约束仍在各自边界完成。
| 方法形态 | 代理动作 | 返回约束 | 风险 |
|---|---|---|---|
| 单对象查询 | selectOne | 零或一行 | 多行抛异常 |
| 集合查询 | selectList | 集合 | 无界结果撑爆内存 |
| 更新 | update | 影响行数 | 忽略零行导致假成功 |
| 游标查询 | Cursor(游标) | 流式消费 | 会话提前关闭 |
sequenceDiagram
participant A as 应用服务
participant P as Mapper(映射器)代理
participant C as 方法命令缓存
participant S as SqlSession(数据库会话)
A->>P: reserve(orderId, sku, qty)
P->>C: 按 Method(方法)查 MapperMethod(映射器方法)
alt 首次调用
C->>C: 解析 namespace.id、命令和返回类型
end
P->>P: 参数命名并包装
P->>S: update(statementId, params)
S-->>P: affectedRows
P-->>A: 影响行数数据演绎 2:条件更新返回零行。 库存 available=3,请求扣减 qty=5,语句条件为 available >= 5,数据库返回影响行数 0。Mapper(映射器)代理正常返回并不等于预占成功;应用服务必须把 0 解释为库存不足或状态冲突,不能继续写“预占成功”事件。若返回 1,仍要等外层事务提交后才成为可观察事实。
热门面试题
- 问题(基础题):Mapper(映射器)接口为什么能被调用?
- 考点:动态代理和会话委派。
- 回答思路:说明工厂、代理、方法缓存和会话命令。
- 详细答案:接口扫描把 MapperFactoryBean(映射器工厂对象)注册进容器,取对象时创建 JDK(Java 开发工具包)动态代理。调用进入 InvocationHandler(调用处理器)后,普通方法被解析成 MapperMethod(映射器方法),再按
namespace.id找 MappedStatement(映射语句),整理参数并委派给 SqlSession(数据库会话)。代理本身不拼接连接和事务。 - 进阶追问:每次调用都会重新解析方法吗?
- 进阶回答:通常会缓存已解析的命令和签名元数据;当次参数和会话仍按请求生成或取得。
- 问题(原理题):为什么更新方法要检查影响行数?
- 考点:状态条件和并发正确性。
- 回答思路:区分语句执行成功与业务状态迁移成功。
- 详细答案:数据库没有抛异常只说明语句被接受。带版本号、余额或状态前置条件的更新返回
0,表示当前事实不满足业务迁移;返回大于1又可能说明条件过宽。应用必须按预期行数决定成功、冲突或数据异常,库存防超卖尤其不能把零行包装成成功。 - 进阶追问:影响一行就一定不会超卖吗?
- 进阶回答:还要保证条件扣减原子、订单行幂等、事务内流水一致以及数据库约束未被旁路。
- 问题(项目题):怎样设计支付流水 Mapper(映射器)返回值?
- 考点:幂等与冲突识别。
- 回答思路:以唯一键、状态条件和查询首次结果组合回答。
- 详细答案:插入支付流水依赖商户号与渠道交易号唯一约束;更新支付单使用当前状态和版本条件,并检查影响行数。唯一键冲突时不能简单吞异常,要按原业务键查询首次记录,核对金额、币种和商户;完全一致才返回首次结果,不一致进入安全告警,避免把伪造或错单当幂等。
- 进阶追问:用分布式锁能替代唯一键吗?
- 进阶回答:不能。锁会过期、故障转移或被旁路,数据库唯一约束才是最终写入边界。
3. SqlSession(数据库会话)、Executor(执行器)与四处理器调用链
SqlSession(数据库会话)是面向调用方的工作单元,内部持有 Executor(执行器)。Executor(执行器)处理一级缓存、延迟加载、语句更新和事务委派,再创建 StatementHandler(语句处理器)。后者通过 JDBC(Java 数据库连接)准备语句;ParameterHandler(参数处理器)按参数映射调用 TypeHandler(类型处理器)绑定值;ResultSetHandler(结果集处理器)按结果映射读取列并构造对象。任何插件代理都可能包裹这些对象,因此排障必须记录最终 SQL(结构化查询语言)、参数、执行器类型、连接身份和结果行数。
| 对象 | 输入 | 输出 | 失败证据 |
|---|---|---|---|
| SqlSession(数据库会话) | 语句标识、参数 | 结果或影响行数 | 会话关闭、误用提交 |
| Executor(执行器) | MappedStatement(映射语句) | 缓存或数据库结果 | 缓存键、批次状态 |
| StatementHandler(语句处理器) | BoundSql(绑定语句) | JDBC(Java 数据库连接)语句 | 最终 SQL(结构化查询语言) |
| ParameterHandler(参数处理器) | 参数映射 | 驱动绑定值 | 类型和空值处理 |
| ResultSetHandler(结果集处理器) | 结果集 | 对象图 | 列名、类型、重复对象 |
flowchart TD
S["SqlSession(数据库会话)"] --> E["Executor(执行器)"]
E --> K{"一级缓存命中?"}
K -->|是| O["返回对象"]
K -->|否| H["StatementHandler(语句处理器)"]
H --> B["生成 BoundSql(绑定语句)"]
B --> P["ParameterHandler(参数处理器)"]
P --> J["JDBC(Java 数据库连接)预编译语句"]
J --> D["数据库执行"]
D --> R["ResultSetHandler(结果集处理器)"]
R --> E
E --> O数据演绎 3:一次轨迹查询。 输入运单 WB9001 和租户 T8,代理定位语句 TrackMapper.listByBill;绑定语句为 tenant_id=? and bill_no=?,参数按顺序为 T8、WB9001。数据库返回 23 行,结果处理器按 event_time 和 event_code 构造集合。若日志只有模板 SQL(结构化查询语言)而没有参数、租户和连接路由,就无法证明是否查错库或漏租户条件。
热门面试题
- 问题(基础题):SqlSession(数据库会话)和 Executor(执行器)怎样分工?
- 考点:外部接口与内部策略。
- 回答思路:说明会话提供契约、执行器实现缓存与执行。
- 详细答案:SqlSession(数据库会话)向调用方提供按语句标识查询、更新、提交、回滚和关闭的接口,并定义一级缓存作用域。它把具体操作委派给 Executor(执行器);后者负责缓存键、查询栈、更新清理、批处理队列和对事务对象的调用。会话是边界,执行器是可替换策略,两者都不等于数据库物理连接。
- 进阶追问:会话线程安全吗?
- 进阶回答:一般不应跨线程共享;它持有执行器、缓存和事务状态,并发使用会破坏边界和清理责任。
- 问题(原理题):四类处理器为何拆开?
- 考点:单一职责与扩展点。
- 回答思路:沿准备、绑定、执行、映射拆分。
- 详细答案:StatementHandler(语句处理器)关注语句准备和执行,ParameterHandler(参数处理器)关注 Java(编程语言)值到驱动参数,ResultSetHandler(结果集处理器)关注结果集到对象,Executor(执行器)统一缓存和调度。拆分让不同职责可测试、可替换并可被插件拦截,但插件链也可能增加代理成本和行为不透明。
- 进阶追问:慢查询应先看哪个对象?
- 进阶回答:先以最终 SQL(结构化查询语言)、参数和数据库执行计划定事实,再沿语句处理器、插件和连接等待定位框架侧耗时。
- 问题(故障题):日志显示执行完成但查不到数据,怎么判断?
- 考点:执行、刷新和提交三阶段。
- 回答思路:核对影响行数、执行器、连接、事务终局和查询库。
- 详细答案:先看是普通更新还是 BATCH(批处理执行器)仅入队,记录影响行数或批次结果;再确认当前 SqlSession(数据库会话)是否参加 Spring(Java 应用框架)事务、连接是否提交或回滚。最后核对读请求的数据源、复制延迟和隔离可见性。应用方法返回只能证明调用结束,不能替代数据库终局。
- 进阶追问:立刻在同方法查询到能证明提交吗?
- 进阶回答:不能,同一事务和一级缓存都可能看到未提交状态;应在独立连接和事务终局后验证。
4. 参数绑定、预编译安全与动态 SQL(结构化查询语言)
#{} 表示参数占位,框架生成 ? 并通过 PreparedStatement(预编译语句)绑定值,数据不会被解释为 SQL(结构化查询语言)结构。${} 是生成语句前的文本替换,适合无法参数化的受控标识符,如经过白名单映射的排序列;直接接收用户输入会造成注入、语义变化和执行计划离散。动态 SQL(结构化查询语言)的 if、choose、foreach、trim 必须同时处理空集合、可选条件、最大参数数和租户条件,不能靠插件事后猜测业务语义。
| 方式 | 进入语句的时刻 | 可否用于值 | 主要风险 |
|---|---|---|---|
#{} | 驱动执行前绑定 | 是 | 类型处理不当 |
${} | 文本生成时替换 | 仅受控结构 | 注入与计划污染 |
foreach | 动态生成占位符 | 批量值 | 空集合、参数过多 |
| 白名单映射 | 服务端选固定片段 | 排序列、方向 | 映射遗漏 |
flowchart TD
I["请求参数:值、排序键、筛选集合"] --> V{"参数用途"}
V -->|业务值| P["使用 #{} 生成 ?"]
P --> B["PreparedStatement(预编译语句)绑定"]
V -->|语句结构| W["服务端白名单映射固定片段"]
W --> T["受控文本拼入"]
V -->|直接用户文本| X["拒绝"]
B --> Q["最终 SQL(结构化查询语言)与参数"]
T --> Q
Q --> D["数据库执行"]数据演绎 4:排序注入与白名单。 客户端传 sort=created_at desc; drop table orders。若用 ${sort} 直接替换,输入成为语句结构。正确做法是将 createdAt 映射到固定片段 created_at,将 desc 映射到两个枚举之一,其他值返回参数错误;订单编号、租户和状态继续用 #{} 绑定。审计日志记录业务字段名,不记录可执行的原始文本。
热门面试题
- 问题(基础题):
#{}与${}的本质差异是什么?- 考点:值绑定与文本生成。
- 回答思路:从数据库看到的语句结构是否改变回答。
- 详细答案:
#{}产生参数占位符,值由 PreparedStatement(预编译语句)和 TypeHandler(类型处理器)绑定,数据库把它视为数据;${}在发送数据库前直接替换文本,会改变 SQL(结构化查询语言)结构。前者默认用于业务值,后者只有在标识符无法参数化且输入被服务端白名单收敛时才可使用。 - 进阶追问:预编译是否解决所有安全问题?
- 进阶回答:不能。越权条件遗漏、批量参数滥用、敏感数据返回和受控结构映射错误仍需业务授权与输入约束。
- 问题(原理题):动态 SQL(结构化查询语言)如何避免条件丢失?
- 考点:组合分支和强制约束。
- 回答思路:区分可选筛选与不可省略的租户、状态条件。
- 详细答案:先把租户、仓库、货主和逻辑删除等强制条件放在不可被可选分支移除的骨架中;可选查询条件再用
if或choose组合。对每种关键组合生成最终 SQL(结构化查询语言)快照并执行集成测试,尤其覆盖空集合、全空条件和大集合分片,避免where被裁掉形成全表更新。 - 进阶追问:插件统一补租户条件是否足够?
- 进阶回答:不足。复杂子查询、别名、批处理和特例容易漏改或重复改,领域层仍要传递并验证数据所有权。
- 问题(项目题):如何安全实现轨迹多条件查询?
- 考点:租户隔离、分页与参数上限。
- 回答思路:固定强制条件,白名单排序,大集合分片。
- 详细答案:
tenant_id、货主和时间窗作为强制绑定条件,运单集合使用foreach生成占位符并按数据库参数上限分片;排序键由服务端枚举映射,页大小设置上限。记录最终模板、参数数量和查询耗时,不输出完整敏感运单;无筛选条件的导出走异步任务而不是在线无界查询。 - 进阶追问:一万个运单放进一个
in条件可以吗? - 进阶回答:不应默认这样做;需评估参数上限、优化器和网络开销,通常按批分片、临时表或任务表连接处理。
5. ResultMap(结果映射)、TypeHandler(类型处理器)与 N+1 查询
ResultMap(结果映射)声明列到属性、构造参数、标识列、关联对象和集合的装配。id 标记帮助合并连接查询中的重复父对象;列别名必须稳定,自动映射不能掩盖同名列冲突。嵌套查询易形成 N+1:先查 N 个订单,再为每个订单发一次明细查询,总计 N+1 次往返。TypeHandler(类型处理器)负责单值的 Java(编程语言)与 JDBC(Java 数据库连接)类型转换,适合枚举、时间、加密值的边界转换,不应承载远程调用或领域流程。
| 映射策略 | 查询次数 | 优点 | 失败边界 |
|---|---|---|---|
| 连接嵌套结果 | 1 | 往返少 | 笛卡尔膨胀、分页失真 |
| 嵌套查询 | 1+N | 映射直观 | N+1 与懒加载失效 |
| 两阶段批量查询 | 2 | 可控、适合分页 | 需按键组装顺序 |
| JSON(JavaScript 对象表示法)聚合 | 1 | 数据库侧聚合 | 方言和对象过大 |
flowchart LR
Q1["查询 100 个订单"] --> N{"明细加载方式"}
N -->|嵌套查询| QN["再执行 100 次明细查询"]
N -->|两阶段| B["一次查询 100 个订单编号的明细"]
N -->|连接结果| J["一次连接但父行重复"]
QN --> C1["101 次网络往返"]
B --> C2["2 次往返并在内存组装"]
J --> C3["1 次往返,控制行膨胀"]数据演绎 5:订单导出 N+1。 一页 100 个订单,每单平均 8 行明细。嵌套查询产生 101 次数据库往返,若单次往返 4ms,仅延迟下限约 404ms,还未计算执行和映射。两阶段方案先查 100 个订单,再以订单编号批量查 800 行明细,总计 2 次往返;按订单编号分组并保持原分页顺序。若直接连接分页,必须先分页主键,避免 limit 100 实际只覆盖少量父订单。
热门面试题
- 问题(基础题):ResultMap(结果映射)解决什么问题?
- 考点:显式映射与对象身份。
- 回答思路:从列、属性、关联集合和类型转换回答。
- 详细答案:ResultMap(结果映射)把数据库列显式映射到对象属性、构造参数、关联对象和集合,并指定标识列与 TypeHandler(类型处理器)。它让命名差异、继承和对象图装配可控。复杂连接中正确的
id映射可让重复父行合并到同一对象,缺失则可能产生重复订单或覆盖错误。 - 进阶追问:自动映射为什么不适合所有场景?
- 进阶回答:同名列、别名变化、嵌套对象和类型边界可能静默错配,显式映射更便于审计和重构。
- 问题(原理题):N+1 查询是怎样产生的?
- 考点:对象导航触发数据库往返。
- 回答思路:用主查询与每行子查询计算次数。
- 详细答案:主查询返回
N个父对象,关联属性配置为嵌套查询或懒加载;访问每个父对象时又按外键执行一次子查询,因此总次数是1+N。测试小数据看不明显,生产页大小和网络延迟放大后会拖慢连接池。应通过查询计数、链路追踪和数据库会话证明,而不是只看单条 SQL(结构化查询语言)耗时。 - 进阶追问:连接查询一定优于两阶段查询吗?
- 进阶回答:不一定,多集合连接会造成行数乘法和分页失真;两阶段批量查询往往更稳定。
- 问题(项目题):异步导出如何避免对象图撑爆内存?
- 考点:分页、批量关联和及时释放。
- 回答思路:按稳定游标分块,不累计全部结果。
- 详细答案:按递增主键或业务游标每批读取
500个主对象,再批量读取关联数据,写入文件后释放对象并记录检查点;禁止把所有页累积到集合。监控每批查询数、行数、映射耗时、堆占用和文件偏移,失败从最后提交检查点重跑,同一导出任务使用唯一标识防重。 - 进阶追问:游标查询能自动解决内存问题吗?
- 进阶回答:不能,消费方仍可能积压对象;会话、连接和结果集还会长期占用,必须限制事务和处理时长。
6. 一级缓存、二级缓存与一致性边界
一级缓存绑定 SqlSession(数据库会话)与 Executor(执行器),缓存键通常由语句、分页、SQL(结构化查询语言)、参数和环境组成;同一会话重复查询可命中。任何被认定为写操作的语句会清空当前会话一级缓存,提交、回滚、关闭或显式清理也改变缓存状态,防止本会话继续读到明显过期对象。二级缓存按 Mapper(映射器)命名空间共享,结果通常在事务性缓存提交后才进入共享区;它无法感知其他服务、手工 SQL(结构化查询语言)或不同命名空间对同表的更新,因此一致性风险更大。
| 缓存 | 范围 | 何时可见 | 主要风险 |
|---|---|---|---|
| 一级缓存 | SqlSession(数据库会话) | 当前会话内 | 长会话读旧对象 |
| 二级缓存 | 命名空间 | 缓存事务提交后 | 跨服务和跨命名空间脏读 |
| 应用缓存 | 业务键 | 业务策略决定 | 双写窗口、穿透 |
| 数据库缓冲 | 存储引擎内部 | 数据库协议决定 | 不等于对象缓存 |
sequenceDiagram
participant A as 应用
participant L1 as 一级缓存
participant L2 as 二级缓存
participant D as 数据库
A->>L1: 查询 K
L1->>L2: 未命中
L2->>D: 未命中,执行查询
D-->>L1: 结果 V1
A->>L1: 同会话再次查询 K
L1-->>A: 返回 V1
A->>D: 执行写操作
A->>L1: 清空当前会话缓存
alt 事务提交
A->>L2: 提交可共享结果或失效
else 事务回滚
A->>L2: 丢弃暂存变化
end数据演绎 6:二级缓存脏读。 服务 A 通过 SkuMapper 查询 SKU9 可售量 20 并进入二级缓存;服务 B 用另一应用直接把可售量改为 12。服务 A 没有收到失效信号,再次查询仍可能得到 20。因此库存余额、支付状态等强一致决策不应依赖二级缓存;条件更新和数据库约束负责最终正确性,缓存最多服务可容忍短暂陈旧的读模型。
热门面试题
- 问题(基础题):一级缓存的作用域是什么?
- 考点:SqlSession(数据库会话)绑定。
- 回答思路:说明同会话命中、写清理和生命周期结束。
- 详细答案:一级缓存属于 SqlSession(数据库会话)内部的 Executor(执行器),不是线程全局或应用全局。相同语句和参数在同会话内可复用结果;执行写操作通常会清空,提交、回滚、关闭和显式清理也会改变它。长时间持有会话会扩大旧对象窗口,所以会话应围绕工作单元而非用户会话。
- 进阶追问:脏写为什么要清空一级缓存?
- 进阶回答:写操作可能改变此前任何查询的结果,若继续返回旧对象,会违反同一工作单元最基本的读己之写预期。
- 问题(原理题):二级缓存为什么有一致性风险?
- 考点:命名空间边界和外部写不可见。
- 回答思路:列出不同写入口并解释失效无法覆盖。
- 详细答案:二级缓存按命名空间管理,只有经过相关映射和缓存协议的写才能触发预期失效。同一张表可能被其他命名空间、其他服务、脚本或数据库任务更新,这些路径不会通知当前缓存。即使设置过期时间,也只是限制脏数据最长时间,不提供读取瞬间的一致性。
- 进阶追问:加短过期时间能用于库存扣减吗?
- 进阶回答:不能把它作为扣减依据;扣减必须由数据库条件更新、版本或约束判断,缓存只做提示性读取。
- 问题(故障题):同一请求两次查询结果不一致或一直一致,如何解释?
- 考点:隔离级别、一级缓存与查询路径。
- 回答思路:先判定是否同会话,再看数据库事务可见性。
- 详细答案:记录两次查询的 SqlSession(数据库会话)身份、缓存键、是否有写清理、连接和事务隔离。一直一致可能来自一级缓存或数据库快照;发生变化可能是会话不同、缓存作用域设为语句级、写操作清理或当前读。必须区分对象缓存返回与数据库真正执行,不能仅凭日志行数判断。
- 进阶追问:如何证明命中了一级缓存?
- 进阶回答:在可控环境记录执行器查询次数和数据库语句次数,用同会话、同参数复现,并排除二级缓存。
7. Spring(Java 应用框架)事务集成、会话绑定与多数据源
MyBatis-Spring(Spring 集成模块)通过 SqlSessionTemplate(数据库会话模板)代理会话操作。调用时从 SqlSessionUtils(数据库会话工具)按 SqlSessionFactory(数据库会话工厂)查找当前线程事务资源;若 Spring(Java 应用框架)事务已绑定会话,则复用同一会话和连接,事务结束由同步回调提交、回滚和关闭。业务代码不应对受管理会话手工 commit。切到新线程、使用错误会话工厂、路由到另一个数据源或绕过模板手工建会话,都会脱离原事务。
| 场景 | 会话来源 | 谁决定提交 | 失败方式 |
|---|---|---|---|
| 有 Spring(Java 应用框架)事务 | 线程绑定会话 | 事务管理器 | 误手工提交 |
| 无事务单次调用 | 临时会话 | 模板按规则结束 | 多语句不原子 |
| 异步线程 | 新线程的新会话 | 异步线程边界 | 外层回滚无效 |
| 多数据源 | 各自工厂和连接 | 各自管理器 | 选错库或部分提交 |
flowchart TD
A["调用 Mapper(映射器)"] --> T["SqlSessionTemplate(数据库会话模板)"]
T --> K{"当前线程按工厂绑定会话?"}
K -->|是| R["复用事务 SqlSession(数据库会话)"]
K -->|否| N["创建临时 SqlSession(数据库会话)"]
R --> C["复用事务连接"]
N --> C2["取得独立连接"]
C --> E["执行并等待事务终局"]
C2 --> E2["执行后关闭临时会话"]
E --> F{"提交或回滚"}
F --> Z["同步回调清理线程资源"]数据演绎 7:多数据源部分提交。 订单库事务管理器绑定连接 C1,业务误用账务库的 Mapper(映射器)工厂取得连接 C2。订单写入在 C1 上,账务流水在无事务的 C2 上先提交;随后订单写异常导致 C1 回滚,形成“有流水、无订单”。排查必须记录 Mapper(映射器)对应工厂、数据源身份、连接编号和两个库的最终事实,而不是只看到事务日志写着回滚。
热门面试题
- 问题(基础题):SqlSessionTemplate(数据库会话模板)为什么线程安全?
- 考点:模板代理与每次调用取会话。
- 回答思路:区分共享模板和实际会话。
- 详细答案:共享的是无请求状态的模板代理,不是同一个原始 SqlSession(数据库会话)。每次调用按当前线程和 SqlSessionFactory(数据库会话工厂)取得事务绑定会话,或创建一次性会话,完成后按规则释放。实际会话仍不应跨线程共享,线程安全来自受控获取与清理协议。
- 进阶追问:能把原始会话保存为对象字段吗?
- 进阶回答:不能,生命周期、事务和线程边界会被打破,还可能泄漏连接与一级缓存。
- 问题(原理题):为什么受管理会话禁止手工提交?
- 考点:单一终局控制者。
- 回答思路:说明外层事务仍可能失败。
- 详细答案:外层事务管理器拥有物理连接终局,多个 Mapper(映射器)操作要共同提交或回滚。内部若提前手工提交,就把部分结果变成永久事实;外层后来异常只能回滚未提交部分,破坏原子性。模板因此限制此类操作,让同步回调在事务终局统一处理。
- 进阶追问:刷新批处理是否等于手工提交?
- 进阶回答:不等于,刷新只是把队列发给数据库;仍需事务提交才形成最终事实。
- 问题(项目题):跨线程异步写为何不跟随外层事务?
- 考点:线程绑定资源。
- 回答思路:从事务同步管理器和连接所有权解释。
- 详细答案:会话和连接按线程绑定,任务线程没有请求线程的资源映射,会创建自己的会话和连接。请求线程回滚不能撤销异步线程已提交的数据;强行传连接又会造成并发使用和清理不确定。异步动作应在提交后通过可靠事件触发,并拥有自己的幂等事务和失败重试。
- 进阶追问:只传事务标识能传播事务吗?
- 进阶回答:不能,标识只用于追踪;物理连接、锁和提交控制权没有随之转移。
8. SIMPLE(简单执行器)、REUSE(复用执行器)与 BATCH(批处理执行器)
SIMPLE(简单执行器)每次准备语句;REUSE(复用执行器)在会话内按 SQL(结构化查询语言)复用语句;BATCH(批处理执行器)把相同语句和参数组累积后统一发送。批处理方法返回通常只代表已加入批次,flushStatements 才产生 BatchResult(批处理结果),而数据库最终提交仍由事务终局决定。批次中第 k 条失败可能出现驱动层部分执行、整批失败或更新计数不明确,必须依赖事务回滚、幂等键、检查点和失败明细恢复。
| 执行器 | 语句策略 | 适合场景 | 核心风险 |
|---|---|---|---|
| SIMPLE(简单执行器) | 每次新建 | 普通查询更新 | 高频准备开销 |
| REUSE(复用执行器) | 会话内复用 | 同模板重复执行 | 语句生命周期 |
| BATCH(批处理执行器) | 积累后刷新 | 大量同构写 | 内存、定位、生成键、未提交 |
flowchart TD
I["1000 行轨迹"] --> C["每 200 行一个批次"]
C --> A["addBatch:仅加入队列"]
A --> F["flushStatements:发送数据库"]
F --> R{"批次结果"}
R -->|全部可接受| N["记录检查点并继续"]
R -->|第 137 行失败| X["回滚当前事务,定位业务键"]
N --> M{"全部批次完成?"}
M -->|否| C
M -->|是| T["事务提交"]
T --> V["独立连接核验最终行数"]数据演绎 8:1000 行轨迹批写。 连接池上限 40,单任务将 1000 行按 200 行刷新,单条约 600 字节,队列参数约占 120KB,未计对象和驱动副本。第三批第 137 行唯一键冲突时,回滚当前事务则前 400 行也不成为最终事实;若设计为每批独立事务,则检查点停在 400,修复冲突后从第三批按业务唯一键重放。两种语义必须事先选择,不能从返回值猜测。
热门面试题
- 问题(基础题):BATCH(批处理执行器)为什么可能更快?
- 考点:摊薄往返和语句开销。
- 回答思路:从网络、驱动和数据库批处理解释。
- 详细答案:同构语句积累后批量发送,可减少网络往返、协议解析和部分语句准备成本,数据库也可能优化批执行。但它不是把任意大循环变快:SQL(结构化查询语言)模板不同会拆批,参数和生成键会占内存,事务过大还会增加锁与日志压力。
- 进阶追问:批次越大越好吗?
- 进阶回答:不是,要结合包大小、驱动内存、日志、锁时长和失败重放成本压测选择。
- 问题(原理题):批处理成功返回为什么不等于最终提交?
- 考点:入队、刷新、提交三阶段。
- 回答思路:明确每阶段可证明的事实。
- 详细答案:调用更新时可能只把参数加入本地队列;刷新时驱动把批次发送数据库并返回更新计数,但事务仍可能在后续语句、约束或提交阶段失败。只有事务管理器成功提交,其他连接才按隔离规则观察到最终事实。因此任务状态不能在入队或刷新后直接标记完成。
- 进阶追问:怎样确认数据库已提交?
- 进阶回答:监听事务成功终局,并用独立连接按业务键核验行数、校验和或状态,不依赖同会话缓存。
- 问题(项目题):批量轨迹部分失败如何恢复?
- 考点:批次边界、幂等和检查点。
- 回答思路:先定义整批或分批原子语义,再设计重放。
- 详细答案:以运单、事件编码和发生时间建立唯一业务键;按固定数量分批并记录文件号、批次号、偏移、参数摘要和终局。整任务原子时失败全部回滚,适合小批;大任务通常每批独立提交,失败批隔离到逐条或更小批定位,修正后按同一业务键重放,已提交批由唯一约束返回首次结果。
- 进阶追问:捕获异常后继续刷新下一批可以吗?
- 进阶回答:不可盲目继续,当前事务和驱动批次可能已不可用;应回滚、关闭会话并从确定检查点新建事务恢复。
9. 主键回填、分页与大结果集
主键回填依赖数据库生成策略、驱动能力、单条或批量执行形态和 keyProperty(主键属性)映射;语句返回影响行数不表示每个对象的主键都已准确写回。分页必须区分逻辑分页与数据库物理分页,在线接口应限制页大小;深页偏移会扫描并丢弃大量行,稳定的键集分页以唯一排序键和上一页游标继续。大结果集可使用 Cursor(游标)或结果处理器逐行消费,但会长时间占用连接、事务和数据库游标,必须限时、分片和及时关闭。
| 主题 | 可接受方案 | 主要证据 | 风险 |
|---|---|---|---|
| 单条主键回填 | 驱动生成键或返回语句 | 对象主键与数据库行 | 属性名错配 |
| 批量主键回填 | 经版本与驱动验证 | 每行键对应关系 | 顺序错位、部分失败 |
| 深分页 | 键集游标 | 唯一排序键 | 数据变动与重复遗漏 |
| 流式读取 | 游标逐行消费 | 连接时长和消费速率 | 连接长期占用 |
flowchart LR
R["分页请求"] --> S{"页码还是游标?"}
S -->|深页偏移| O["扫描 offset + limit 行"]
S -->|键集游标| K["where id > lastId order by id limit n"]
O --> H["成本随页码增长"]
K --> C["成本较稳定"]
C --> N["返回 nextLastId"]
N --> K数据演绎 9:异步导出分页。 表有 5,000,000 行,传统第 10,000 页、每页 500 行需要数据库处理约 5,000,000 个偏移位置。改为按 (created_at,id) 复合游标,每批 500 行,条件为大于上一批末尾键,排序键必须唯一稳定。每批写文件后保存游标和行数;任务中断从检查点继续,最终以总行数、主键去重数和文件摘要验收。
热门面试题
- 问题(基础题):主键回填有哪些前提?
- 考点:数据库、驱动、映射和执行形态。
- 回答思路:列出生成策略与对象属性对应关系。
- 详细答案:数据库必须能返回生成键或支持显式返回语句,驱动正确实现协议,映射的主键属性与列匹配,执行方式也必须被当前版本支持。单条可用不代表批量顺序和部分失败仍可靠,因此批量场景要用真实驱动验证每个对象与数据库行的对应关系。
- 进阶追问:影响行数正确能证明主键正确吗?
- 进阶回答:不能,影响行数只说明写入数量;键的顺序、缺失和错配需要逐对象核对。
- 问题(原理题):键集分页为什么比深偏移稳定?
- 考点:扫描成本和排序锚点。
- 回答思路:从索引定位和数据变化回答。
- 详细答案:偏移分页要定位并丢弃前面的记录,成本随页码增长;键集分页用上一页唯一排序键直接从索引范围继续,扫描量接近本页大小。它仍要求稳定唯一排序和明确快照语义,否则并发插入、更新时间变化可能造成重复或遗漏。
- 进阶追问:只按时间戳游标可以吗?
- 进阶回答:时间戳可能重复,应增加主键作为并列排序和游标组成部分。
- 问题(项目题):如何避免导出占满连接池?
- 考点:批次、限流和资源生命周期。
- 回答思路:限制并发、短查询、检查点和独立资源池。
- 详细答案:导出任务按游标小批查询,查询后尽快释放连接再进行文件编码;限制租户和全局并发,必要时使用隔离的数据源配额。记录连接持有时长、每批行数、写盘耗时和待执行任务数,在数据库高压时暂停拉取而不是继续堆积对象。
- 进阶追问:独立连接池就不会影响在线业务吗?
- 进阶回答:仍共享数据库计算、存储和网络,独立池只是隔离应用借连接配额,必须配合数据库限流。
10. 插件拦截链、分页改写与数据权限
Interceptor(拦截器)可代理 Executor(执行器)、StatementHandler(语句处理器)、ParameterHandler(参数处理器)和 ResultSetHandler(结果集处理器)的特定方法。多个插件按包装顺序形成代理链,实际调用顺序与注册、包装方向相关,必须以可执行验证为准。分页、审计、租户和数据权限插件可以统一基础设施动作,但 SQL(结构化查询语言)改写要理解语法树、别名、子查询、集合语句和方言;字符串拼接容易漏条件或注入。插件不能替代索引、唯一约束、条件更新和服务内领域授权。
| 插件用途 | 合适职责 | 禁止替代 | 性能证据 |
|---|---|---|---|
| 分页 | 方言化限制与计数策略 | 索引和排序设计 | 改写后执行计划 |
| 审计 | 语句标识、耗时、行数 | 业务审计流水 | 采样成本 |
| 租户 | 防御性补充固定条件 | 对象级授权 | 漏改和重复改写率 |
| 性能 | 阈值记录与指标 | 数据库慢日志 | 插件自身耗时 |
flowchart TD
A["Executor(执行器)目标对象"] --> P1["性能 Interceptor(拦截器)代理"]
P1 --> P2["租户 Interceptor(拦截器)代理"]
P2 --> P3["分页 Interceptor(拦截器)代理"]
P3 --> S["生成并执行最终 SQL(结构化查询语言)"]
S --> V{"语法、权限、索引与约束均成立?"}
V -->|否| X["插件不能兜底正确性"]
V -->|是| R["记录最终语句、参数摘要和耗时"]数据演绎 10:插件顺序冲突。 原查询按租户筛选后应有 2,300 行,分页大小 50。若分页插件先生成计数语句,租户插件只改数据查询而漏改计数,页面显示总数 90,000,实际只能看到 2,300。验收要比较计数和数据语句的租户条件、别名、参数及执行计划,并对嵌套查询、联合查询和空租户建立反例测试。
热门面试题
- 问题(基础题):MyBatis(持久层框架)插件能拦截什么?
- 考点:四类核心扩展点。
- 回答思路:说明有限方法签名而非任意代码。
- 详细答案:插件通过 Interceptor(拦截器)声明要代理的核心接口方法,常见对象是 Executor(执行器)、StatementHandler(语句处理器)、ParameterHandler(参数处理器)和 ResultSetHandler(结果集处理器)。框架创建代理包装目标,命中签名时进入插件,未命中则直接委派。它不是全局方法切面,拦截范围和顺序必须明确。
- 进阶追问:插件越多为什么越难排障?
- 进阶回答:代理嵌套会改变最终语句、参数和耗时归属,顺序冲突还可能让一个插件看见另一个插件改写后的结果。
- 问题(原理题):为什么租户插件不能替代领域授权?
- 考点:技术隔离和业务所有权。
- 回答思路:区分统一租户条件与仓库、货主、订单状态。
- 详细答案:插件最多根据可信上下文补通用租户条件,无法判断用户是否有权操作具体仓库、货主、订单状态或金额。复杂语句还可能漏改。服务方法必须做对象级授权,SQL(结构化查询语言)保留不可绕过的所有权条件,数据库约束保证并发最终正确,插件只做纵深防御。
- 进阶追问:插件自动加条件后还要代码传租户吗?
- 进阶回答:关键写入应显式携带并校验租户,使接口契约、日志和测试都能证明数据所有权。
- 问题(项目题):分页插件上线后查询变慢如何排查?
- 考点:改写后语句与计数成本。
- 回答思路:比较原语句、数据语句、计数语句和执行计划。
- 详细答案:按语句标识捕获插件前后 SQL(结构化查询语言)、参数摘要、分页值和插件耗时,分别在数据库执行数据语句与计数语句并查看计划、扫描行和排序。常见根因是去掉排序失败、复杂连接计数、深偏移或租户条件使索引失效;修复应改查询和索引或取消昂贵总数,不是关闭慢日志。
- 进阶追问:插件超时能直接跳过计数吗?
- 进阶回答:只有接口契约允许未知总数时才可降级,并明确返回是否还有下一页,不能伪造总数。
11. 多数据源、连接生命周期与路由边界
多数据源系统必须显式建立数据源、SqlSessionFactory(数据库会话工厂)、Mapper(映射器)扫描范围和事务管理器的对应关系。动态路由通常在取得连接前根据可信上下文选键;事务已开始并绑定连接后再改路由键不会让当前事务自动换库。读写分离还要处理提交后读从库的复制延迟;支付和库存终局校验应读主库或使用一致性令牌。连接在事务期间由管理器持有,结束后恢复自动提交、只读和隔离属性并归还池,任何泄漏都会污染后续请求。
| 边界 | 正确做法 | 常见事故 | 验证 |
|---|---|---|---|
| 工厂与扫描 | Mapper(映射器)包唯一归属 | 写错库 | 容器关系图 |
| 路由时机 | 事务前确定 | 事务中切键无效 | 连接身份 |
| 读写一致 | 关键终局读主库 | 提交后读旧值 | 复制位点 |
| 连接归还 | 恢复属性并清理绑定 | 串只读、串隔离 | 池指标与连接日志 |
flowchart TD
R["请求含租户和读写意图"] --> K["校验并选择路由键"]
K --> T{"事务是否已绑定连接?"}
T -->|否| G["路由数据源取得目标连接"]
T -->|是| B["继续使用已绑定连接"]
G --> X["绑定到当前事务"]
B --> Q["执行 SQL(结构化查询语言)"]
X --> Q
Q --> E["提交或回滚"]
E --> C["恢复属性、解绑、归还连接"]热门面试题
- 问题(基础题):多数据源为什么要分别配置会话工厂?
- 考点:映射、环境和连接身份。
- 回答思路:说明每个工厂持有自己的配置与数据源。
- 详细答案:SqlSessionFactory(数据库会话工厂)把映射配置、插件、类型处理器和环境绑定到特定数据源。分别配置并限定 Mapper(映射器)扫描范围,可在启动期建立明确归属;若多个包共享模糊默认工厂,事务管理器可能控制主库,而 Mapper(映射器)实际写到另一个库,形成无法回滚的部分提交。
- 进阶追问:同一映射能否用于多个库?
- 进阶回答:可以显式注册到多个工厂,但要验证方言、表结构、插件和事务归属,不能靠偶然扫描。
- 问题(原理题):事务内切换路由键为什么常常无效?
- 考点:连接获取与线程绑定时机。
- 回答思路:说明事务开始时连接已选定并绑定。
- 详细答案:事务管理器开始物理事务时已经通过路由数据源取得连接并绑定当前线程。后续 Mapper(映射器)从同一事务资源取得的仍是这条连接,修改线程路由键不会让数据库事务迁移到另一连接。若确需跨库,应重新设计边界并处理分布式一致性,而不是在事务中切字符串。
- 进阶追问:新开事务就一定切库吗?
- 进阶回答:还要确保挂起旧资源、使用目标管理器和工厂,并在新事务取连接前设置可信路由键。
- 问题(故障题):提交成功后立刻查询仍是旧状态,如何定位?
- 考点:路由、缓存与复制延迟。
- 回答思路:依次排查一级缓存、读库身份和复制位点。
- 详细答案:先确认查询是否复用旧 SqlSession(数据库会话)或二级缓存;再记录读连接的数据源和实例,判断是否路由到从库;比较主从复制位点、延迟和提交时间。资金、库存等关键确认应读主库或等待一致性令牌,不能用固定睡眠假装同步。
- 进阶追问:所有读都强制主库可以吗?
- 进阶回答:能保证读新但失去扩展收益,应只对需要读己之写和强终局的链路使用,并治理主库容量。
12. 慢 SQL(结构化查询语言)证据链、项目落地与设计思想
慢请求不等于慢 SQL(结构化查询语言)。端到端耗时可能来自连接池等待、插件改写、参数绑定、数据库排队与锁、执行计划、网络传输、结果映射、N+1 查询或业务消费。排查按请求标识串起语句标识、最终 SQL(结构化查询语言)、参数摘要、数据源、连接等待、数据库会话、计划、扫描/返回行数和映射耗时。设计上,Mapper(映射器)是端口适配器,不承载跨系统流程;插件负责横切基础设施,数据库索引与约束负责最终数据边界,事务负责本地原子性,消息负责跨边界收敛。
flowchart TD
A["接口 P99(第九十九百分位)升高"] --> B["按请求标识拆阶段耗时"]
B --> C{"连接等待高?"}
C -->|是| C1["查连接池、长事务和泄漏"]
C -->|否| D{"数据库执行高?"}
D -->|是| D1["查计划、锁、扫描行和参数分布"]
D -->|否| E{"查询次数或映射高?"}
E -->|是| E1["查 N+1、结果膨胀和类型转换"]
E -->|否| F["查插件、网络和下游消费"]
C1 --> V["止血、修复、回归和指标闭环"]
D1 --> V
E1 --> V
F --> V热门面试题
- 问题(基础题):慢 SQL(结构化查询语言)排查需要哪些最小证据?
- 考点:从请求到数据库的关联。
- 回答思路:列出语句、参数、连接、计划和行数。
- 详细答案:至少需要请求与业务标识、MappedStatement(映射语句)标识、最终 SQL(结构化查询语言)模板、脱敏参数摘要、数据源和数据库实例、连接获取耗时、数据库会话与执行时长、执行计划、扫描/返回/影响行数、结果映射耗时和事务终局。缺少参数和实例,单看模板计划容易误判。
- 进阶追问:能在生产完整打印所有参数吗?
- 进阶回答:不应,需脱敏、采样和按业务号临时提升,避免泄密与日志风暴。
- 问题(原理题):为什么 Mapper(映射器)不应承载业务流程?
- 考点:分层与一致性边界。
- 回答思路:区分数据访问原语和业务用例编排。
- 详细答案:Mapper(映射器)应表达查询、条件更新和流水写入等持久化原语;若在其中隐藏远程调用、跨库流程或复杂状态判断,事务边界、失败补偿和测试证据都会消失。应用服务编排用例,领域规则决定合法迁移,Mapper(映射器)执行明确数据动作并返回可判定结果。
- 进阶追问:动态 SQL(结构化查询语言)中能否写业务状态判断?
- 进阶回答:可把明确前置状态落实为原子条件,但合法迁移语义仍应在领域和应用层可见并测试。
- 问题(项目题):怎样串讲 WMS(仓储管理系统)库存、支付、轨迹和导出?
- 考点:不同负载的持久化策略。
- 回答思路:分别讲条件更新、唯一键、批处理和游标分页。
- 详细答案:库存用带可售量和版本条件的单行更新,检查影响行数并在同事务写预占流水;支付以商户单号和渠道号唯一键防重,冲突后核对金额币种;轨迹按业务唯一键分批写并记录检查点;导出按稳定游标小批读取、批量关联、及时释放连接。四者都记录最终 SQL(结构化查询语言)、事务终局和数据库事实,但优化目标不同。
- 进阶追问:这四类都能统一成一个通用仓储方法吗?
- 进阶回答:不宜,通用方法会抹掉状态条件、幂等键、批次终局和资源策略,应共享基础设施而保留领域语义。
章节正文结束
以上十二节构成机制正文,后续题库只做跨节串讲,不新增独立知识小节。
综合面试题库
问题:请完整说明一次 Mapper(映射器)方法调用如何到达数据库并返回对象。
- 回答思路:围绕“请完整说明一次 Mapper(映射器)方法调用如何到达数据库并返回对象。”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我会按“配置期元数据、运行期代理、会话执行、数据库资源、结果映射、事务终局”六段说明。启动时,框架把 XML(可扩展标记语言)或注解解析为以
namespace.id唯一定位的 MappedStatement(映射语句),其中保存语句类型、参数映射、结果映射、超时和缓存策略;接口扫描则注册 Mapper(映射器)工厂。业务调用接口时进入动态代理,代理把方法解析成 MapperMethod(映射器方法),整理具名参数并按命令类型调用 SqlSession(数据库会话)。Spring(Java 应用框架)环境中的 SqlSessionTemplate(数据库会话模板)先按会话工厂查当前线程是否有事务绑定会话,有则复用,没有则创建临时会话。Executor(执行器)构造缓存键、检查一级或二级缓存,未命中时创建 StatementHandler(语句处理器);后者生成 BoundSql(绑定语句)并准备 JDBC(Java 数据库连接)语句,ParameterHandler(参数处理器)借 TypeHandler(类型处理器)绑定值。数据库返回结果集或影响行数,ResultSetHandler(结果集处理器)按 ResultMap(结果映射)构造对象。最后要区分方法返回、批次刷新和事务提交:只有事务终局成功,数据库事实才稳定;排障必须把语句标识、参数摘要、连接身份、行数和提交结果串起来。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:代理会直接持有数据库连接吗? 回答:不会,代理只翻译方法;连接由会话、事务和数据源在调用时取得。
- 追问 2:查询命中缓存还会创建语句处理器吗? 回答:通常不会进入数据库执行链,但仍受查询栈和缓存作用域约束。
- 追问 3:方法正常返回能否证明提交? 回答:不能,外层事务可能随后回滚,批处理也可能尚未刷新。
- 详细机制
问题:MappedStatement(映射语句)为什么是 MyBatis(持久层框架)的核心元数据?
- 回答思路:围绕“MappedStatement(映射语句)为什么是 MyBatis(持久层框架)的核心元数据?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:MappedStatement(映射语句)把“某个接口方法要执行什么”从源码文本固化为运行期可复用的执行描述。它以
namespace.id为稳定标识,关联 SQL(结构化查询语言)来源、命令类型、参数映射、结果映射、缓存使用、刷新策略、超时、生成键和数据库标识等信息。Mapper(映射器)代理不会自己解析 XML(可扩展标记语言)并拼连接,而是先定位这个描述,再交给 SqlSession(数据库会话)与 Executor(执行器)。提前构建的价值有三点:第一,把语句标识重复、结果映射引用缺失和资源加载失败前移到启动阶段;第二,运行期只绑定本次参数,不必重复解析结构;第三,日志、监控和事故复盘可以用稳定语句标识关联同类调用。它也有边界:元数据注册成功不证明动态 SQL(结构化查询语言)的每个分支、数据库方言、表权限和真实数据计划都正确。版本升级时不能只看核心包,还要核对 MyBatis-Spring(Spring 集成模块)、启动器和驱动兼容性。生产出现同名语句差异时,我会比较构建产物、类路径资源和配置哈希,防止某节点混入旧映射。这个设计体现了“配置期编译、运行期解释执行”的思想,让错误尽早暴露,又保留参数化执行的灵活性。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:MappedStatement(映射语句)保存当次参数值吗? 回答:不保存,它描述映射;当次值进入 BoundSql(绑定语句)和参数处理链。
- 追问 2:启动成功能否删除映射集成测试? 回答:不能,动态分支、方言和数据库对象只有真实执行才能验证。
- 追问 3:标识重复该随机覆盖吗? 回答:不应,必须明确唯一权威定义并让启动失败。
- 详细机制
问题:
#{}和${}为什么安全性不同,项目中如何使用?- 回答思路:围绕“
#{}和${}为什么安全性不同,项目中如何使用?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。 - 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:两者差异不在符号,而在输入被数据库解释成“值”还是“语句结构”。
#{}在生成 SQL(结构化查询语言)时产生?占位符,ParameterHandler(参数处理器)再通过 TypeHandler(类型处理器)调用 PreparedStatement(预编译语句)绑定数据。即使订单号含引号或关键字,数据库仍把它当参数值,不会改变语法树。${}在发送数据库前直接替换文本,输入能够增加条件、改变排序甚至插入额外语句,因此绝不能直接接收用户文本。确实不能参数化的列名、排序方向或分表标识,也不是“校验一下字符串”就使用,而应把客户端枚举映射到服务端固定片段,例如createdAt -> created_at、DESC -> desc,未命中立即拒绝。动态 SQL(结构化查询语言)还要把租户、仓库、货主和逻辑删除条件放在不可被可选分支移除的骨架中,对空集合、全空条件和大集合建立反例。分页、租户插件可以做纵深防御,却不能替代服务内对象授权和数据库约束。审计时记录语句模板、参数数量与脱敏摘要,不打印可执行原始输入或支付敏感数据。这样既防注入,也能控制执行计划离散、越权和日志泄密风险。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:预编译是否让模糊查询安全? 回答:值仍应绑定,通配符语义按业务转义,避免用户扩大查询范围。
- 追问 2:表名可以用
#{}吗? 回答:通常不能,标识符需服务端白名单路由,不能直接接受用户表名。 - 追问 3:租户插件加条件后还需传租户吗? 回答:关键写入仍应显式传递并验证,插件只做防御补充。
- 详细机制
- 回答思路:围绕“
问题:请解释一级缓存的准确边界以及为什么写操作会清空它。
- 回答思路:围绕“请解释一级缓存的准确边界以及为什么写操作会清空它。”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:一级缓存不是应用共享缓存,也不天然等同于一个网页请求,它绑定具体 SqlSession(数据库会话)内部的 Executor(执行器)。查询时框架以 MappedStatement(映射语句)、分页、最终 SQL(结构化查询语言)、参数和环境等构造缓存键;同一会话、相同键的重复查询可能直接返回此前对象,减少数据库往返。这个优化要求缓存与当前工作单元保持基本一致。任何写操作都可能改变此前查询的结果集合、排序或关联对象,框架无法低成本精确计算哪些键受影响,所以通常清空当前会话一级缓存;提交、回滚、关闭和显式清理也会改变缓存状态。这就是“脏写会清空”的根本原因:宁可牺牲局部命中,也不能继续返回明显过期对象。排查“两次查询为何一直一样”时,我先记录是否同一 SqlSession(数据库会话)、是否真正下发第二条 SQL(结构化查询语言)、中间是否有写和缓存清理,再看数据库隔离级别;不能把一级缓存和数据库快照混为一谈。会话不应跨线程或长时间保存,因为它还持有执行器、事务和延迟加载状态。库存、支付终局验证要在事务完成后用独立连接按业务键查询,不能用同会话命中的对象证明已提交。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。
- 追问 1:更新另一个表也会清空吗? 回答:通常写语句触发当前会话清理,框架不尝试精确判断表级依赖。
- 追问 2:把缓存作用域改成语句级有什么代价? 回答:每条语句后清理,降低旧对象风险,也失去同会话重复查询收益。
- 追问 3:一级缓存能防并发超卖吗? 回答:不能,最终要靠数据库条件更新、锁或版本和唯一约束。
- 详细机制
问题:为什么二级缓存不适合承载库存和支付强一致决策?
- 回答思路:围绕“为什么二级缓存不适合承载库存和支付强一致决策?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:二级缓存扩大到 Mapper(映射器)命名空间,可被多个 SqlSession(数据库会话)共享,通常由事务性缓存先暂存结果,提交后再进入共享区,写语句则按配置清理命名空间。它比一级缓存范围大,但一致性边界并没有扩大到整个业务系统。同一张库存表可能被另一个命名空间、另一个服务、运维脚本、定时任务或数据库过程更新,这些路径不会可靠通知当前二级缓存;多个命名空间连接同一表时,一个空间清理也不保证另一个同步失效。设置短过期时间只是限制最大陈旧窗口,不能保证读取瞬间正确。库存扣减需要在数据库执行
available >= qty的原子条件并检查影响行数,支付状态推进需要状态条件、唯一流水和金额币种核对;缓存读值只能用于展示或预判,不能作为最终写入依据。若业务确实使用共享读缓存,我会定义可容忍陈旧时间、键的租户边界、失效和重建协议、回源保护及监控,并在强终局路径绕过缓存。事故中要同时检查一级缓存、二级缓存、应用缓存和读写路由,避免看到旧值就武断归因。缓存是性能副本,数据库约束和事务事实才是正确性底线。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:二级缓存提交后为何仍可能脏? 回答:它只知道本命名空间路径,其他写入口不会被完整感知。
- 追问 2:分布式锁能保护缓存一致吗? 回答:只能约束遵守同一锁协议的参与者,无法覆盖脚本、故障和读副本延迟。
- 追问 3:哪些数据较适合二级缓存? 回答:低频变化、可容忍短暂陈旧且失效边界清楚的参考数据。
- 详细机制
问题:MyBatis-Spring(Spring 集成模块)如何让多个 Mapper(映射器)参加同一事务?
- 回答思路:围绕“MyBatis-Spring(Spring 集成模块)如何让多个 Mapper(映射器)参加同一事务?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:关键不是注解扫描,而是同一线程、同一 SqlSessionFactory(数据库会话工厂)和同一受事务管理器控制的数据源最终取得同一资源。事务代理进入后,数据源事务管理器取得连接、关闭自动提交,并通过事务同步管理器把资源绑定到当前线程。业务调用 Mapper(映射器)时,SqlSessionTemplate(数据库会话模板)不会共享一个原始会话字段,而是借 SqlSessionUtils(数据库会话工具)按会话工厂查找当前线程绑定资源;首次调用可创建 SqlSession(数据库会话)并注册同步回调,后续同工厂 Mapper(映射器)复用该会话和连接。业务方法结束后,事务管理器统一提交或回滚,同步回调再关闭会话、清理线程绑定并恢复连接属性。受管理会话禁止手工提交,因为内部提前提交会把部分结果变成永久事实,外层异常无法撤销。若代码切到异步线程、手工打开会话、使用另一个会话工厂或路由到不受当前管理器控制的数据源,就会脱离原事务。排查时我会用业务号核对最终数据,再证明代理命中、管理器选择、工厂身份、数据源身份、连接编号和终局,而不是看到“事务开始”日志就认为所有写都受控。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。
- 追问 1:共享 SqlSessionTemplate(数据库会话模板)为何安全? 回答:模板每次按上下文取实际会话,共享的不是有状态原始会话。
- 追问 2:两个会话工厂指向同一数据源一定同事务吗? 回答:不应假定,还要看资源键、集成配置和实际连接绑定证据。
- 追问 3:异步线程能传递连接吗? 回答:不应,连接和事务终局不适合跨线程并发共享。
- 详细机制
问题:BATCH(批处理执行器)的入队、刷新和提交分别意味着什么?
- 回答思路:围绕“BATCH(批处理执行器)的入队、刷新和提交分别意味着什么?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:批处理最容易把三个时刻混成一个。第一,调用 Mapper(映射器)更新时,BATCH(批处理执行器)可能只是按 SQL(结构化查询语言)模板把参数加入驱动批次,此时返回值不是数据库最终影响行数,更不是提交。第二,调用
flushStatements或触发自动刷新后,驱动把批次发送到数据库并返回 BatchResult(批处理结果)与更新计数;数据库可能已执行语句,但物理事务仍未提交,后续约束、其他语句或提交阶段仍可失败。第三,事务管理器成功提交后,结果才成为其他连接按隔离规则可见的稳定事实。以1000行轨迹、每200行刷新为例,第三批第137行唯一键冲突,若一个事务覆盖全任务,则前400行即使刷新成功也随回滚消失;若每批独立事务,则前两批保留,检查点为400,第三批修正后按相同业务键重放。批次大小要平衡网络往返、驱动内存、事务日志、锁时长和失败定位成本。任务状态只能在事务成功回调后更新,恢复要重建会话和事务,不能在驱动已失败的批次上继续盲刷。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:批处理返回负数一定失败吗? 回答:要按驱动更新计数语义解释,不能脱离具体协议武断判断。
- 追问 2:每条都刷新还是批处理吗? 回答:形式上可以,但失去摊薄往返的主要收益。
- 追问 3:如何验证最终数量? 回答:事务提交后用独立连接按任务号、业务键和校验和核对。
- 详细机制
问题:如何设计
1000行批量写入的失败恢复和幂等?- 回答思路:围绕“如何设计
1000行批量写入的失败恢复和幂等?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。 - 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我先定义原子语义,再选技术。若
1000行必须共同成败,可使用一个事务,但会增加连接、锁、内存和日志占用,任一坏数据导致全部重做;更常见的是每100或200行一个独立事务。输入先固化任务号、文件摘要、批次号和行偏移,每条轨迹以租户、运单、事件编码和发生时间建立数据库唯一键。执行时只在内存保留当前批参数,刷新后读取批次结果,事务提交成功才把检查点从400推到600;响应丢失时按同一任务与业务键查询,不生成新键。某行失败后回滚当前批、关闭 SqlSession(数据库会话),记录失败行的脱敏参数与异常;可用二分小批定位,业务允许时把合法行和坏行分离,但不能在未知事务状态上继续。恢复从最后已提交检查点新建事务,唯一键冲突要核对首次数据是否完全一致,不一致进入人工异常。容量上测量参数对象和驱动副本、包大小、数据库日志、连接持有时长与锁等待,在数据库高压时暂停拉取。最后用独立连接核对输入总数、成功唯一键数、失败数、重复数与摘要,使“任务完成”成为可证明的数据库事实。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:唯一键冲突都可当成功吗? 回答:不能,必须核对业务字段;冲突数据不同代表污染或攻击。
- 追问 2:检查点何时更新? 回答:与当前批业务写同事务,或在成功终局后以幂等方式推进,不能先推进。
- 追问 3:为什么失败后要关闭会话? 回答:驱动批次和事务状态可能已不可继续,重建边界更可控。
- 详细机制
- 回答思路:围绕“如何设计
问题:N+1 查询如何发现、量化和修复?
- 回答思路:围绕“N+1 查询如何发现、量化和修复?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:N+1 不是“SQL(结构化查询语言)多”这么简单,而是对象导航把一次列表用例变成随父行数量线性增长的数据库往返。典型链路先查
N个订单,再由嵌套查询或懒加载为每个订单查明细,总次数1+N。发现时不能只看单条慢日志,因为每条子查询都可能只有几毫秒;要按一次请求或任务聚合语句次数、相同模板次数、连接占用和总数据库时长。比如100个订单、每次往返4ms,即使查询本身很快,网络下限也约404ms。修复方案要看基数:单集合且结果可控可用连接嵌套结果,但要正确配置父标识并防重复;多个集合连接会产生笛卡尔膨胀,通常先分页查父主键,再以in批量查关联,合计两次并在内存分组。大集合按参数上限分片,保持原分页顺序。异步导出按稳定游标每批500个父对象,批量加载关联、写盘后释放,不累计全量对象。验收比较修复前后每页查询次数、返回行、扫描行、网络量、堆占用和第九十九百分位,避免把 N+1 换成一条巨型连接查询后反而更慢。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:开启懒加载能消除 N+1 吗? 回答:不能,只把触发时机推迟;逐个访问仍会执行多次。
- 追问 2:连接查询为什么会分页失真? 回答:分页作用于重复连接行,可能只得到少量父对象。
- 追问 3:两阶段查询如何保持顺序? 回答:保存父主键顺序,关联结果按键分组后按原序组装。
- 详细机制
问题:ResultMap(结果映射)和 TypeHandler(类型处理器)分别解决什么问题?
- 回答思路:围绕“ResultMap(结果映射)和 TypeHandler(类型处理器)分别解决什么问题?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:ResultMap(结果映射)解决“结果集的一行或多行怎样还原对象结构”,TypeHandler(类型处理器)解决“单个 Java(编程语言)值怎样与 JDBC(Java 数据库连接)类型互换”,二者层级不同。结果映射可声明列别名、属性、构造参数、父对象标识、关联对象、集合和鉴别分支;连接查询中父
id标记决定多行是否合并到同一父对象,列别名不稳定或同名列未限定会造成静默错配。类型处理器负责枚举码、时间、数据库专有类型或受控加密字段的读写,必须处理空值、未知码、时区和兼容迁移。它应是确定、快速、无外部副作用的转换函数,不能在读取每行时调用远程服务或查询数据库,否则会制造隐蔽 N+1、事务延长和失败不确定。枚举遇到未知数据库值时,是拒绝、映射未知状态还是保留原码,应由业务兼容策略决定,不能默认转空掩盖数据。排障时记录原始列类型、别名、目标属性、处理器和失败行的业务键;对支付金额和币种尤其避免精度与默认值丢失。清晰分工让对象图装配与单值编码分别演进,也使数据库模式变更有明确的兼容测试位置。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:自动映射能替代 ResultMap(结果映射)吗? 回答:简单稳定命名可用,复杂连接和对象图应显式映射。
- 追问 2:类型处理器能做字段解密吗? 回答:可做受控本地转换,但需评估密钥、性能、日志和查询能力边界。
- 追问 3:未知枚举为什么不能直接转空? 回答:会把兼容或污染问题隐藏成普通空值,破坏审计和状态判断。
- 详细机制
- 问题:MyBatis(持久层框架)插件链如何工作,为什么不能滥用?
- 回答思路:围绕“MyBatis(持久层框架)插件链如何工作,为什么不能滥用?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:插件不是任意代码的全局切面,而是针对 Executor(执行器)、StatementHandler(语句处理器)、ParameterHandler(参数处理器)和 ResultSetHandler(结果集处理器)指定方法签名的代理。配置加载时注册 Interceptor(拦截器),创建核心对象时由插件逐层包装;调用命中签名进入
intercept,插件可查看或调整调用后继续委派。多个插件形成嵌套代理,实际进入和退出顺序受注册与包装方向影响,不能凭列表直觉判断,必须用最小调用记录验证。分页插件可能改写数据语句并生成计数语句,租户插件补条件,性能插件记录耗时;若顺序错误,计数和数据语句可能使用不同租户条件,或性能指标只覆盖内层。SQL(结构化查询语言)改写应基于可靠语法解析并覆盖别名、子查询、联合查询、批处理和方言,字符串拼接易漏条件与注入。插件还增加代理、解析和日志成本,高流量下必须采样与度量。最重要的是,它只能统一基础设施动作,不能理解仓库、货主、订单状态和金额等领域权限,也不能创造索引、唯一约束或事务原子性。上线前我会保存改写前后语句、参数、顺序、执行计划和反例;异常时可按语句或租户降级,但不能悄悄绕过安全条件。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:插件可以拦截 Mapper(映射器)接口吗? 回答:核心插件机制面向指定四类对象;接口层横切应使用合适代理机制。
- 追问 2:如何确认插件顺序? 回答:构造最小语句让每个插件记录进入和退出序号,并核对最终语句。
- 追问 3:性能插件能替代数据库慢日志吗? 回答:不能,它看应用链路,数据库日志和会话才证明执行、锁与扫描事实。
- 详细机制
- 问题:分页为什么会出现深页变慢、重复和遗漏,如何设计?
- 回答思路:围绕“分页为什么会出现深页变慢、重复和遗漏,如何设计?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:偏移分页通过
offset + limit表达第几页,数据库通常仍要定位并丢弃前面的记录,所以页码越深,扫描和排序成本越高。并发写入时,前面插入或删除还会移动后续记录的位置,导致同一遍遍历出现重复或遗漏。在线小页且总量有限时可以使用偏移,但必须有稳定排序、页大小上限和合适索引;百万级导出或深翻页更适合键集分页。键集方案以唯一稳定排序键保存上一页末尾位置,例如(created_at,id),下一页条件为严格大于该组合并使用相同排序,这使扫描量接近本页大小。只用时间戳不够,因为相同时间会造成边界不确定;排序字段若会更新,也会改变位置。若要求导出某一时刻的一致快照,还要固定任务范围,例如先记录最大主键或使用合适快照,不能只换分页语法。分页插件只负责生成方言语句和可选计数,不能替代索引、排序与一致性设计。验收需比较扫描行、返回行、计划、重复主键、遗漏主键、总耗时和连接持有时间,任务每批提交检查点,失败从稳定游标恢复。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:键集分页能跳到任意页吗? 回答:通常不能直接随机跳页,它优化连续遍历,需要游标或额外索引定位。
- 追问 2:总数查询一定要执行吗? 回答:不一定,接口可返回是否有下一页,避免复杂连接计数。
- 追问 3:导出期间新增数据怎么办? 回答:按业务定义固定范围或接受增量,并把范围写入任务元数据。
- 详细机制
- 问题:多数据源环境中如何证明事务控制了正确的库?
- 回答思路:围绕“多数据源环境中如何证明事务控制了正确的库?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我不会只看
@Transactional(事务注解)或“事务已开始”日志,而是沿对象身份证明资源闭环。首先确认调用经过 Spring(Java 应用框架)事务代理并选择了哪个事务管理器;再确认目标 Mapper(映射器)由哪个扫描配置注册、关联哪个 SqlSessionFactory(数据库会话工厂),该工厂又持有哪个数据源。事务开始时记录实际数据库实例、连接编号、自动提交和事务标识,执行语句时把 MappedStatement(映射语句)标识与连接关联,终局记录提交或回滚。动态路由必须在取得连接前确定可信路由键;事务已绑定连接后再改键,当前事务仍使用旧连接。若订单库管理器绑定C1,账务 Mapper(映射器)却从另一工厂取得自动提交C2,账务可能先永久写入,订单后续回滚,形成部分提交。排查时用同一业务号查询两个库最终流水,不以应用异常文本推断。修复是明确包到工厂到管理器的唯一关系,关键入口显式指定管理器,并为跨库动作采用补偿或可靠消息,而不是幻想两个本地管理器自动原子。集成测试要在第二库写入后注入故障,验证预期终局和恢复流程。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:数据源名称相同能证明同一对象吗? 回答:不能,要核对容器对象身份和实际连接目标。
- 追问 2:事务中改路由键为何无效? 回答:物理连接已取得并绑定,路由只在取连接时选择。
- 追问 3:两个本地事务顺序提交能原子吗? 回答:不能,第二次提交失败仍会留下第一库事实。
- 详细机制
- 问题:主键回填在批处理中有哪些风险,怎样验证?
- 回答思路:围绕“主键回填在批处理中有哪些风险,怎样验证?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:主键回填依赖四层共同成立:数据库生成键策略、驱动返回协议、MyBatis(持久层框架)映射配置以及单条或批量执行形态。单条插入能把键写回对象,不代表批量时驱动会按输入顺序返回全部键;驱动可能只返回首键、返回顺序不同,批次部分失败也会使键与对象错位。
keyProperty指向错误属性、复合键或数据库触发器生成值还会增加不确定。设计上,支付、库存等需要稳定业务身份的记录优先在应用侧生成业务号或使用明确返回语句,数据库自增键仅作内部标识;批量轨迹若依赖回填键建立后续关联,要在目标驱动和版本上做真实集成验证。测试输入使用可追踪序号,插入后用独立连接按唯一业务键查询数据库主键,再逐对象比较,覆盖完整成功、中间唯一键冲突、事务回滚和重试。不能用“影响100行”证明100个对象主键都正确。失败重试也不能因为对象残留了内存键就假定数据库有该行,必须按业务唯一键查首次事实。若兼容性不能证明,就拆成可控单条、显式键或数据库支持的返回集合方案,牺牲一点吞吐换取身份正确性。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:事务回滚后对象中的主键会自动清空吗? 回答:通常不会,应把内存对象与数据库事实分开,不据此判断成功。
- 追问 2:自增键可做跨系统幂等键吗? 回答:不合适,调用前未知且重试会变化,应使用稳定业务键。
- 追问 3:批量回填需要测哪些驱动? 回答:生产实际驱动、数据库版本和执行配置,不能用内存替代库推断。
- 详细机制
- 问题:如何建立慢 SQL(结构化查询语言)的端到端证据链?
- 回答思路:围绕“如何建立慢 SQL(结构化查询语言)的端到端证据链?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:第一步先定义慢在哪里:请求排队、取得连接、插件处理、数据库执行、结果传输、对象映射还是业务消费。按请求和业务标识记录 MappedStatement(映射语句)标识、最终 SQL(结构化查询语言)模板、脱敏参数摘要、数据源与实例、连接获取耗时、执行器类型、事务时长、返回或影响行数、映射耗时和总耗时。数据库侧用会话标识关联慢日志、锁等待、执行计划、实际扫描行、临时表和排序;同一模板必须按参数分布分析,测试值快不代表热点租户或极端时间窗快。若单条都很快但请求慢,聚合查询次数检查 N+1;若数据库执行短而总耗时长,检查连接池、插件、网络和大结果对象构造。止血可限制页大小、暂停导出、按租户限流或关闭非必要总数,但不能直接加索引或扩连接池后宣布解决。长期修复根据证据调整索引、查询形态、批次和事务边界,并用同分布数据回归计划、扫描/返回比、连接时长和第九十九百分位。生产日志需采样与脱敏,支付参数、用户信息不可完整输出;必要时按业务号短时提升证据级别,结束后恢复。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。
- 追问 1:执行计划显示走索引就一定快吗? 回答:不一定,还要看选择性、扫描行、回表、排序和实际参数。
- 追问 2:连接池等待算慢 SQL(结构化查询语言)吗? 回答:不是数据库执行慢,但会表现为持久层调用慢,必须拆阶段。
- 追问 3:能只看平均耗时吗? 回答:不能,应看高分位、参数分布和长尾业务键。
- 详细机制
- 问题:WMS(仓储管理系统)库存防超卖如何落到 Mapper(映射器)和事务?
- 回答思路:围绕“WMS(仓储管理系统)库存防超卖如何落到 Mapper(映射器)和事务?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我先定义单库不变量:可售量不能小于零,同一订单行同一动作只能预占一次,余额变化必须有预占流水,成功预占必须产生待传播事件。Mapper(映射器)提供明确的原子操作,而不是“先查再改”:
update stock set available=available-#{qty}, reserved=reserved+#{qty}, version=version+1 where tenant_id=#{tenant} and sku=#{sku} and available>=#{qty} and version=#{version}。应用服务在短事务中执行并检查影响行数,0表示余量或版本冲突,不能继续写成功;1后插入以订单行和动作唯一的预占流水,并写 Outbox(发件箱)事件,任一步异常共同回滚。唯一键冲突时查询首次流水,核对 SKU(库存单位)、数量和租户,一致才返回首次结果,不一致告警。一级缓存不能作为并发判断,二级缓存只可展示近似库存,插件也不能替代条件更新和约束。事务提交后发布器异步发送事件,消费方按事件键幂等;响应超时则按原订单行查询,不换键重复扣减。排障按订单行关联最终 SQL(结构化查询语言)、参数摘要、影响行数、连接、事务终局、库存行、流水和事件,证明是库存不足、并发冲突、回滚还是消息延迟。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:只用版本号够吗? 回答:还要检查余量、幂等流水和事务内事件,版本只解决一类冲突。
- 追问 2:条件更新返回零行要重试吗? 回答:先区分库存不足与版本冲突,有限重读重试且保持原业务键。
- 追问 3:Redis(远程字典服务)锁能省掉数据库条件吗? 回答:不能,数据库仍是最终写入和旁路防线。
- 详细机制
- 问题:支付流水如何用映射、唯一键和状态条件保证幂等?
- 回答思路:围绕“支付流水如何用映射、唯一键和状态条件保证幂等?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:支付幂等不是捕获重复异常,而是稳定业务身份、内容核对和合法状态迁移的组合。入口先验签、防重放,以商户支付号和渠道交易号定位支付意图,金额、币种、商户不一致不能按重复成功。数据库为渠道号与商户范围建立唯一约束,Mapper(映射器)插入尝试流水;冲突时查询首次记录并逐项核对。支付单更新使用前置状态和版本条件,例如只允许从处理中到成功,并检查影响行数;同一本地事务还写账务记录和 Outbox(发件箱)事件。Mapper(映射器)只返回行数与记录,不吞异常决定业务成功,应用服务解释冲突和状态。事务方法返回不等于渠道和本地都最终一致,提交后发布事件,履约消费按事件和订单唯一键幂等;本地超时保留原支付号进入未知状态,主动查渠道,禁止换号重扣。二级缓存不参与支付终局判断,读写分离下确认查询读主库。事故证据包括渠道原始报文摘要、签名结果、两个业务号、最终 SQL(结构化查询语言)和参数、唯一键异常、影响行数、事务终局、账务和事件状态,最终由对账发现并关闭遗漏。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。
- 追问 1:唯一键冲突为何不能直接返回成功? 回答:冲突记录可能金额或商户不同,必须核对首次事实。
- 追问 2:回调重复要再次发履约事件吗? 回答:不重复推进;查询首次事件状态,由可靠发布流程保证可达。
- 追问 3:支付成功后从库读旧值怎么办? 回答:关键确认读主库或使用一致性位点,不用固定睡眠。
- 详细机制
- 问题:跨境物流轨迹批量同步如何兼顾吞吐、顺序和可恢复性?
- 回答思路:围绕“跨境物流轨迹批量同步如何兼顾吞吐、顺序和可恢复性?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:轨迹同步首先区分接收顺序与业务发生顺序。每条事件保存租户、运单、渠道事件号、事件编码、发生时间和接收时间,以渠道事件号或稳定字段组合建立唯一键;显示顺序按发生时间和确定的并列键排序,不能因晚到事件覆盖已知终态。拉取任务记录渠道游标、请求摘要和批次号,输入先落任务或暂存事实,再按
200行使用 BATCH(批处理执行器)写入。刷新只证明批次送达数据库,事务提交后才推进检查点;当前批失败则回滚并重建 SqlSession(数据库会话),通过缩小批次定位坏数据。每批独立提交时,前批保留,恢复从最后成功游标按同一业务键重放;重复事件核对内容后一致即幂等,不一致进入异常队列。Mapper(映射器)语句使用预编译参数,租户条件不可省略,批次大小根据驱动内存、网络包、数据库日志和锁时长压测。下游订单履约只消费提交后的规范事件,按状态机处理乱序和重复。监控拉取延迟、批次耗时、重复率、失败行、游标停滞和单运单事件异常增长,避免单个异常运单拖垮全渠道。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:数据库自增编号能表示轨迹发生顺序吗? 回答:不能,它更接近接收顺序,应使用业务发生时间和并列键。
- 追问 2:坏数据能直接跳过吗? 回答:只有业务允许并进入可追踪异常闭环时,不能静默丢失。
- 追问 3:整批一个事务还是每批事务? 回答:按原子要求和恢复成本选择,大规模同步通常每个可重放批次独立事务。
- 详细机制
- 问题:异步导出如何避免 OOM(内存溢出)和连接池耗尽?
- 回答思路:围绕“异步导出如何避免 OOM(内存溢出)和连接池耗尽?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:导出要把数据库读取、对象映射、文件写入和任务恢复拆成有界流水线。任务创建时固化租户、筛选条件摘要、导出范围和唯一任务号,禁止在线接口直接返回百万行。读取使用稳定键集游标,例如
(created_at,id),每批500行;主对象查询后批量加载关联,避免 N+1,再立即写文件并释放集合,不把所有页积累到内存。文件编码通常不需要继续占数据库连接,因此查询结束后尽快关闭会话或结束短事务,再写盘和上传;Cursor(游标)虽能逐行读取,却会长期持有连接和结果集,只适合消费稳定、限时且并发受控的场景。每批记录游标、已写行数、文件偏移和摘要,检查点只有在对应输出可靠落地后推进;崩溃按任务号和游标继续,避免重复文件片段。限制单租户、全局任务和数据库查询并发,导出池即使独立也仍共享数据库计算与存储。监控堆占用、每批对象数、查询与写盘耗时、连接持有、待执行任务和暂停次数。出现内存或数据库压力时先暂停拉取和降低并发,保留检查点;修复后用总行数、主键去重数、文件摘要和抽样字段验收。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:流式查询一定比分页省资源吗? 回答:省对象峰值但长期占连接,消费慢时可能更危险。
- 追问 2:检查点和文件写入如何一致? 回答:采用临时分片落盘、原子发布和幂等检查点,避免先推进后丢文件。
- 追问 3:导出可用二级缓存吗? 回答:通常不应,数据量大且快照边界不清,会污染缓存并读到不一致副本。
- 详细机制
- 问题:读写分离下如何处理提交后立刻读取旧数据?
- 回答思路:围绕“读写分离下如何处理提交后立刻读取旧数据?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我先区分三种可能:同一 SqlSession(数据库会话)的一级缓存返回旧对象、二级或应用缓存未失效、查询路由到复制尚未追上的从库。写事务成功只证明主库提交,不保证所有从库立即应用日志。关键链路要定义读己之写语义:支付确认、库存预占结果和订单状态推进在提交后短窗口读主库,或携带提交位点让路由只选择已追平的副本;普通列表可容忍延迟则继续读从库并在界面表达处理中。不能用固定睡眠作为一致性协议,因为延迟随故障和负载变化。动态路由键必须在取连接前确定,事务已经绑定主库连接时修改键不应被误解为切库;事务结束后的新查询还要清理会话缓存,记录实际数据库实例和复制位点。故障排查按业务号比较主库值、从库值、提交时间、复制延迟、缓存命中和查询连接身份。止血可把特定业务键或强一致接口临时路由主库并限制流量,避免全站强制主库引发容量事故。长期用可观测位点、延迟告警、路由策略和降级契约闭环,数据库约束仍负责写正确性。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。
- 追问 1:事务内查询会读从库吗? 回答:正确配置通常复用已绑定写连接,但要以实际路由与连接证据确认。
- 追问 2:从库延迟能靠重试解决吗? 回答:盲重试会放大负载,应读主库、等待位点或返回处理中。
- 追问 3:缓存失效后还旧一定是复制延迟吗? 回答:不一定,还需排除一级缓存、错误实例和事务快照。
- 详细机制
- 问题:Mapper(映射器)层、应用服务和数据库约束怎样分工?
- 回答思路:围绕“Mapper(映射器)层、应用服务和数据库约束怎样分工?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我把三层看成不同强度的保证。应用服务围绕一个业务用例编排权限、幂等、领域状态、事务和跨系统动作,决定“应该做什么”;Mapper(映射器)把领域意图转换成明确的数据访问原语,例如按业务键查询、带前置状态条件更新、插入唯一流水并返回影响行数,决定“如何表达数据库动作”;数据库约束、原子更新和事务在所有入口与并发下守住最终不变量,决定“即使上层出错也不能出现什么”。如果把流程塞进 Mapper(映射器),远程调用、跨库步骤和错误补偿会藏进数据层,事务范围与测试证据不清;如果只在应用先查再改,没有条件更新和唯一约束,并发请求仍会超卖或重复入账;如果只靠数据库异常,业务无法区分库存不足、幂等重复和数据污染。以库存为例,应用验证请求并开启短事务,Mapper(映射器)执行余量与版本条件扣减并返回行数,数据库保证余额不越界和流水唯一;应用再解释结果并写事件。插件可统一日志、分页和防御性租户条件,但不代替任何一层。这样的分工体现端口适配、显式结果和纵深防御,既便于面试下钻,也利于事故定位。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。
- 追问 1:状态条件放 SQL(结构化查询语言)中算业务泄漏吗? 回答:它是领域不变量的原子落地,语义仍应在应用接口和测试中显式。
- 追问 2:数据库有唯一键还需幂等代码吗? 回答:需要解释冲突、核对首次内容并返回稳定结果。
- 追问 3:Mapper(映射器)可以返回布尔值吗? 回答:可用但行数更有诊断力,调用方应明确零、一和异常多行语义。
- 详细机制
- 问题:同一事务里查询结果为何可能来自缓存而非数据库,怎么验证?
- 回答思路:围绕“同一事务里查询结果为何可能来自缓存而非数据库,怎么验证?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:同一事务中的多个 Mapper(映射器)调用通常经 SqlSessionTemplate(数据库会话模板)复用事务绑定的 SqlSession(数据库会话),因此共享 Executor(执行器)和一级缓存。第一次查询按语句、分页、最终 SQL(结构化查询语言)、参数和环境构造键,结果进入缓存;第二次相同键可能直接返回对象,不产生数据库语句。中间若执行写操作,当前会话缓存通常清空,后续查询才重新下发;若只由外部连接改库,当前会话不知道变化,仍可能返回旧对象。数据库隔离级别也会影响重新查询的可见性,所以“值没变”不能直接归因缓存,“日志没打印”也可能是日志级别问题。验证时在可控集成环境固定二级缓存关闭,记录 SqlSession(数据库会话)身份、缓存键和执行器实际查询次数,用同会话同参数复现,再插入写操作观察清理;另用独立连接修改数据,比较同会话与新会话结果。业务上不要通过反复查询同一会话证明提交或跨请求一致,支付与库存终局应在事务完成后用独立连接查询。若确需每次打数据库,可显式清理或缩小缓存作用域,但要理解数据库快照仍可能返回相同版本。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。
- 追问 1:同事务不同 Mapper(映射器)一定共享缓存吗? 回答:只有实际复用同一会话和执行器,且缓存键与配置满足条件。
- 追问 2:写后查询为何通常能读到新值? 回答:写清理一级缓存,查询重新执行,并在同连接看到自己的写。
- 追问 3:新会话一定看见最新提交吗? 回答:还取决于事务隔离、读写路由和复制延迟。
- 详细机制
- 问题:批处理部分失败时如何判断数据库到底写了哪些行?
- 回答思路:围绕“批处理部分失败时如何判断数据库到底写了哪些行?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:首先停止根据应用循环位置猜测,因为失败可能发生在本地入队、驱动发送、数据库执行、约束检查或事务提交。保存任务号、批次号、SQL(结构化查询语言)模板、每行稳定业务键、参数摘要、驱动返回的更新计数、异常链和连接事务状态。若当前批位于一个尚未提交的本地事务,最稳妥动作是回滚整个事务、关闭会话,再用独立连接按业务键核验;回滚成功意味着此前刷新结果不应成为最终事实。若每批独立提交,则以最后一次明确提交成功的检查点为界,之后状态都按未知处理,逐批查询唯一键和行内容,不能只数总行数。驱动可能用特殊更新计数表示成功但未知行数,也可能在第
k条失败前执行部分语句,因此恢复依赖数据库事务和幂等键,而不是数组下标。已存在行必须核对租户、运单、事件和时间,一致才算首次成功,不一致进入数据异常。若提交响应丢失,先查终局再决定重放。长期把批次设计成可回滚、可查询、可重放的工作单元,检查点在提交后推进,并用故障注入覆盖中间冲突、网络断开和提交未知。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:更新计数能完全定位失败行吗? 回答:不一定,取决于驱动协议,业务键核验才可形成终局证据。
- 追问 2:回滚后还要查数据库吗? 回答:高风险场景应查,排除错误连接、自动提交或跨库写入。
- 追问 3:提交未知时可以直接重放吗? 回答:先按原业务键查事实;重放必须幂等且核对内容。
- 详细机制
- 问题:动态 SQL(结构化查询语言)和租户插件如何避免越权与全表更新?
- 回答思路:围绕“动态 SQL(结构化查询语言)和租户插件如何避免越权与全表更新?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我把条件分为不可省略的安全骨架和可选的业务筛选。租户、货主、仓库归属、逻辑删除与前置状态属于强制条件,必须由可信上下文得到并在 Mapper(映射器)契约中显式出现;名称、时间窗等才是动态可选。使用
where、trim或set时,对全空输入和空集合建立失败分支,写语句要求至少有业务主键或受控范围,必要时在应用和数据库权限上禁止无条件更新。值全部使用预编译绑定;排序列、方向和受控分表只能由服务端枚举映射。租户插件可解析最终语句补防御条件,但必须覆盖别名、子查询、联合语句、计数和批处理,检测已有条件防止重复,还要拒绝无可信租户,而不是默认公共租户。它不能判断用户是否能操作某仓库或订单状态,所以服务方法继续做对象级授权,数据库更新继续带所有权条件并检查影响行数。测试生成每个关键动态组合的最终 SQL(结构化查询语言)与参数,特别覆盖条件全空、恶意排序、嵌套查询和插件顺序。生产记录语句标识、租户摘要和影响行数,出现异常多行立即阻断与告警。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:插件解析失败时应放行吗? 回答:涉及数据隔离的写操作应失败关闭,不能静默放行。
- 追问 2:只限制数据库账号权限够吗? 回答:账号通常覆盖整套表,无法表达每个租户和对象权限。
- 追问 3:影响零行是越权还是状态冲突? 回答:对外可统一拒绝防泄露,内部结合授权和当前状态审计分类。
- 详细机制
- 问题:如何从源码关键方法理解 MyBatis(持久层框架)的设计思想?
- 回答思路:围绕“如何从源码关键方法理解 MyBatis(持久层框架)的设计思想?”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我不会背全部类,而是跟一次状态变化。入口从 MapperProxy(映射器代理)的
invoke开始,普通方法被缓存为 MapperMethod(映射器方法),其execute根据命令类型调用 SqlSession(数据库会话)。默认会话的查询进入 Executor(执行器)的query,先计算 CacheKey(缓存键)并处理查询栈,再由基础执行器创建 StatementHandler(语句处理器);其prepare、parameterize和query/update分别对应准备连接语句、参数绑定和执行。ParameterHandler(参数处理器)借 TypeHandler(类型处理器)完成值转换,ResultSetHandler(结果集处理器)完成对象图映射。更新路径会清理本地缓存;BATCH(批处理执行器)把语句与参数积累到批次,刷新和提交仍分离。Spring(Java 应用框架)侧再看 SqlSessionTemplate(数据库会话模板)如何经工具类取得事务同步会话。这里体现代理把接口转命令、建造器把配置编译成元数据、模板与策略统一执行骨架、责任链拆分处理阶段、装饰器形成插件、线程资源绑定参与事务。理解这些模式的目的不是炫源码,而是能预测缓存、代理、批处理和事务失效的位置,并知道应该采集哪个对象的证据。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:为什么 Executor(执行器)使用委派包装? 回答:可在统一执行策略外叠加二级缓存等职责,保持核心边界。
- 追问 2:插件体现什么模式? 回答:代理与拦截链,在有限接口方法周围扩展横切行为。
- 追问 3:源码版本变化怎么办? 回答:抓稳定职责与调用证据,具体方法以项目实际小版本核对。
- 详细机制
- 问题:请给出一次 MyBatis(持久层框架)线上事故的完整排查话术。
- 回答思路:围绕“请给出一次 MyBatis(持久层框架)线上事故的完整排查话术。”,先给出适用边界,再沿配置、代理、会话、执行器、连接与事务终局解释机制,最后用失败窗口、数据库证据和项目案例验证。
- 详细答案:下方口述答案从定义、调用链、数据变化、设计原因、错误方案、线上证据和恢复闭环完整展开;阅读时应同步对照本题详情链接中的流程图、表格与数据演绎。
- 进阶追问:如果缓存、批处理、插件、多数据源或跨线程改变了默认调用链,原结论是否仍成立,怎样证明?
- 进阶回答:不能只依据方法返回或框架日志。需要重新确认会话和连接身份、最终 SQL(结构化查询语言)与参数、影响行数、事务提交或回滚、独立连接查询以及业务唯一键;保证范围发生变化时,应缩小结论并采用幂等、补偿或人工闭环。
- 口述答案:我先用业务事实定义事故,而不是先猜框架:某次发布后 WMS(仓储管理系统)预占接口第九十九百分位从
120ms升到2.8s,连接池40条全部活跃,少量订单返回成功但预占流水缺失。止血先限制异步导出和批量轨迹并发,为库存核心链路保留连接;暂停自动换号重试,按原订单行查询。保存发布版本、线程栈、连接池指标、数据库会话、锁等待、慢日志和受影响业务号。证据链显示新增插件为每次库存更新同步执行无索引计数,事务持连接时间增长;同时异常分支把更新影响行数0当成功,导致未写流水仍返回。修复分两层:移除无业务价值计数并为必要查询建立正确索引和采样指标;应用服务严格要求条件扣减影响1行,随后在同事务写唯一预占流水和 Outbox(发件箱),零行返回冲突。回归覆盖余量不足、版本冲突、插件启停、提交前断网、重复订单行和高并发,独立连接核对库存、流水与事件。复盘增加语句标识级耗时、连接等待、影响零行率和插件耗时告警,并规定插件不可替代索引、约束与领域判断。这样表达包含影响、止血、现场、根因、修复、验证和制度改进。在项目验收中,我还会构造正常、重复、并发、超时、提交未知和回滚六类样本,记录业务键、语句标识、脱敏参数、数据源、连接编号、影响行数和事务终局,再由独立连接核对数据库记录、唯一约束与状态机。若证据不完整,只能把结果标为未知并停止扩大副作用;修复后用同量级数据复测吞吐、连接占用、扫描行、重复率和失败恢复,确保性能优化没有破坏正确性。 - 追问 1:为什么不先扩连接池? 回答:根因是持有时间和数据库额外负载,扩池可能把压力继续推向数据库。
- 追问 2:返回成功但流水缺失如何补? 回答:冻结后续履约,按订单与库存权威事实生成可审计补偿,不能直接改终值。
- 追问 3:怎样防止插件再次引发事故? 回答:建立顺序、语句快照、执行计划、性能预算和可快速关闭的发布门禁。
- 详细机制
复习清单
- 能从 Mapper(映射器)代理口述到数据库提交和结果映射,并区分方法返回、刷新与提交。
- 能说明
#{}、${}、动态 SQL(结构化查询语言)和租户条件的安全边界。 - 能画出一级缓存、二级缓存的范围与失效,明确写操作清空当前会话缓存。
- 能用
1000行案例解释三种 Executor(执行器)、批次大小、部分失败、主键回填和检查点。 - 能证明 MyBatis-Spring(Spring 集成模块)会话、连接、工厂、数据源和事务管理器的一致身份。
- 能解释 ResultMap(结果映射)、TypeHandler(类型处理器)、N+1、分页和大结果集资源边界。
- 能说明插件顺序、改写风险及“插件不能替代索引、数据库约束和领域授权”。
- 能用 WMS(仓储管理系统)库存、支付流水、跨境轨迹和异步导出完成项目串讲。
- 能按请求标识建立慢 SQL(结构化查询语言)端到端证据链,并给出止血、修复和回归。
