高并发财务计算中ArithmeticException是系统性风险,需分层防御:一、除零必须前置校验;二、整数溢出按场景选型(BigDecimal/DECIMAL优先);三、异常捕获仅作最后防线并记录完整上下文;四、用缓存与队列隔离不可信输入。

在高并发财务计算场景中,ArithmeticException(如除零、整数溢出)不是偶发错误,而是系统性风险点。它可能让一笔分账失败、一个对账任务中断,甚至引发连锁资损。关键不在于“捕获异常”,而在于**分层防御**:前置拦截 + 安全兜底 + 业务适配。
一、除零必须前置校验,不能依赖 catch
高并发下频繁抛异常会显著拖慢 JVM,且掩盖真实数据问题。GREL、Java、PHP 等环境均应将除数检查下沉到表达式/方法入口:
- 在 OpenRefine 的 GREL 中写:
if(isNonBlank(人数) && 人数 != 0, 销售额 / 人数, 0),而非直接写销售额 / 人数 - Java 方法签名建议带语义校验:
public BigDecimal calcPerCapita(BigDecimal total, BigDecimal headcount),内部第一行就 throw IllegalArgumentException if headcount.isZero() - 数据库 SQL 层也可用
NULLIF(人数, 0)配合COALESCE,避免应用层承担全部校验压力
二、整数溢出需区分场景选型,不能只靠 try-catch
财务金额若用 int 或 long 运算,32 位系统下 2147483647 + 1 会静默变负数,catch 不到——这是溢出,不是异常。
- 金额统一用
BigDecimal(Java)或DECIMAL(MySQL),运算时显式指定MathContext控制精度与舍入 - 高性能批量计算可用
GMP(PHP)或BigInteger(Java),但注意对象创建开销,适合单笔大额而非高频小额 - 若必须用原生整型,加法/乘法前做安全检测:
if (a > 0 && b > 0 && a > Long.MAX_VALUE / b) throw new OverflowException(...)
三、异常捕获仅作最后防线,且要带上下文记录
即使做了双重校验,仍需 try-catch,但目的不是“恢复”,而是可观测+可追溯:
- 捕获时记录完整上下文:订单 ID、用户 ID、原始分子/分母值、线程名、时间戳
- 不要只打印
e.getMessage(),要输出"[FINANCE-DIV] order=ORD-7892, dividend=1000000, divisor=0" - 对高频失败场景(如某类供应商的“人数”字段长期为 0),自动触发告警并推送至数据治理平台
四、用缓存与队列隔离不可信输入
外部导入的 CSV、API 接口传入的参数,是除零和溢出的重灾区。与其在核心计算链路里反复防御,不如前置过滤:
- 接入层用 Redis 做轻量校验缓存:对 “
supplier_id:divisor” key 设置 TTL,命中即跳过除零检查 - 批量任务拆成“预检队列”+“执行队列”:预检阶段过滤 divisor=0 或超阈值数据,只让合规数据进主计算流
- 前端表单增加实时校验规则(如 divisor 字段禁止输入 0),减少无效请求打到后端

















