mybatis 不支持 oracle rac 故障转移,需依赖 jdbc 驱动、tnsnames.ora 中正确配置 failover_mode(嵌套于 connect_data 内)、jdbc url 引用别名、连接池启用连接验证与超时机制,并避免在 mybatis 层盲目重试。

tnsnames.ora)+ 连接池行为。直接在 MyBatis XML 或 Java 配置里加个属性就想启用 TAF,行不通。
tnsnames.ora 必须完整配置 FAILOVER_MODE
MyBatis 调用 dataSource.getConnection() 时,最终走的是 Oracle JDBC Thin 驱动对 tnsnames.ora 的解析。如果里面没写 FAILOVER_MODE 块,哪怕有多个 ADDRESS,也只触发建连时的 failover(即 Client-Side Connect Time Failover),连接建立后节点挂了照样报 ORA-03113。
正确写法(必须嵌套在 CONNECT_DATA 内):
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = rac1-vip)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = rac2-vip)(PORT = 1521))
(CONNECT_DATA =
(SERVICE_NAME = orcl)
(FAILOVER_MODE =
(TYPE = SESSION)
(METHOD = BASIC)
(RETRIES = 3)
(DELAY = 5)
)
)
)
-
TYPE = SESSION:适用于绝大多数 OLTP 场景,故障后重连新实例,不续查 -
TYPE = SELECT:仅对长查询有意义,但会显著增加 PGA 消耗,且要求应用未手动 commit/rollback -
METHOD = BASIC:推荐,连接池每次只连一个实例;METHOD = PRECONNECT会导致连接数翻倍,易触发ORA-12519 -
RETRIES和DELAY要匹配业务容忍度,比如DELAY=5+RETRIES=3≈ 最多 15 秒不可用
JDBC URL 必须引用 tnsnames 别名,不能用 Easy Connect
Oracle JDBC 驱动只有在解析 tnsnames.ora 时才会读取 FAILOVER_MODE。如果你的 MyBatis url 配置成 jdbc:oracle:thin:@//rac1-vip:1521/orcl 这类 Easy Connect 格式,TAF 完全不生效。
正确做法:
- 把上面那段配置存为
$ORACLE_HOME/network/admin/tnsnames.ora(或通过TNS_ADMIN环境变量指向自定义路径) - MyBatis 的
url改为jdbc:oracle:thin:@RAC(其中RAC是tnsnames.ora中的 alias) - 确保 Java 进程启动前已设置
TNS_ADMIN,否则驱动找不到文件
连接池要配合 socketTimeout 和 validation-query
即使 TAF 配置正确,若连接池没做配套设置,仍可能卡住或抛出陈旧异常:
- 必须设
validation-query=SELECT 1 FROM DUAL,否则连接池无法感知连接是否已因 RAC 切换而失效 - 设
socketTimeout=60000(60 秒),避免网络卡顿导致线程长时间阻塞 -
max-lifetime要比数据库端SQLNET.EXPIRE_TIME小至少 2 分钟,防止连接被服务端静默 kill 后还在池中复用 - HikariCP 等主流池默认不开启连接有效性检测,需显式配
connection-test-query和validation-timeout
MyBatis 层不要自己封装重试逻辑
有人试图在 MyBatis 的 @Update 方法上加 @Retryable,这是危险的:
- Spring Retry 默认重试同一
Connection对象,而该连接在第一次失败后大概率已 close,再 execute 会抛SQLException: Closed Connection - 事务中盲目重试更危险:如果已执行过
executeUpdate()并 commit,第二次重试会破坏数据一致性 - 真正该重试的是「获取连接 + 执行」这个原子动作,不是某条 SQL
所以推荐方式是:让连接池负责底层连接可靠性(靠 TAF + validation),MyBatis 只专注 SQL 映射;网络类错误(如 getErrorCode() 是 3113、17002)由上层服务捕获并按需重试,且每次重试都走全新连接。
最关键的细节常被忽略:TAF 不是“开个开关就自动生效”,它依赖 tnsnames.ora 文件存在、路径可读、别名被 JDBC URL 引用、驱动版本支持(ojdbc8+)、以及 VIP 解析正常——少一环,FAILOVER_MODE 就等于没写。











