bigywater.tech
Java · 315

JDBC 事务隔离级别

同样叫 REPEATABLE_READ,PostgreSQL 和 MySQL 的行为一样吗?哪个级别才真的能挡住丢失更新、写偏斜?这篇用两个真实数据库在 JDBC 里逐个构造异常,给出一张实测矩阵。

40001
PostgreSQL 在 REPEATABLE_READ 拒绝丢失更新、在 SERIALIZABLE 拒绝写偏斜时的 SQLState
999
MySQL READ_UNCOMMITTED 下读到的未提交值(PostgreSQL 在该级别没有脏读)
0 人
MySQL REPEATABLE_READ 下写偏斜后的值班人数(丢失更新也发生)

PostgreSQL

实测 · 本机执行输出
默认隔离级别:READ_COMMITTED;各级别 supportsTransactionIsolationLevel:[READ_UNCOMMITTED=true, READ_COMMITTED=true, REPEATABLE_READ=true, SERIALIZABLE=true]
设置的级别              脏读             不可重复读            幻读             丢失更新(先读后写)                         写偏斜(两人都值班时各自下班)                   
READ_UNCOMMITTED   没有(读到 100)     发生(100 → 500)    发生(2 → 3)      t1 提交成功;t2 提交成功;最终余额 110(丢失更新)     最后值班人数 0(两个人都下班了:写偏斜)
READ_COMMITTED     没有(读到 100)     发生(100 → 500)    发生(2 → 3)      t1 提交成功;t2 提交成功;最终余额 110(丢失更新)     最后值班人数 0(两个人都下班了:写偏斜)
REPEATABLE_READ    没有(读到 100)     没有(两次都是 100)     没有(两次都是 2)     t1 提交成功;t2 PSQLException(40001) ERROR: could not serialize access due to concurrent update;最终余额 110(有一方失败) 最后值班人数 0(两个人都下班了:写偏斜)
SERIALIZABLE       没有(读到 100)     没有(两次都是 100)     没有(两次都是 2)     t1 提交成功;t2 PSQLException(40001) ERROR: could not serialize access due to concurrent update;最终余额 110(有一方失败) 最后值班人数 1;t1:ok t2:提交失败 PSQLException(40001) ERROR: could not serialize access due to read/write dependencies among

MySQL

实测 · 本机执行输出
默认隔离级别:REPEATABLE_READ;各级别 supportsTransactionIsolationLevel:[READ_UNCOMMITTED=true, READ_COMMITTED=true, REPEATABLE_READ=true, SERIALIZABLE=true]
设置的级别              脏读             不可重复读            幻读             丢失更新(先读后写)                         写偏斜(两人都值班时各自下班)                   
READ_UNCOMMITTED   发生(读到 999)     发生(100 → 500)    发生(2 → 3)      t1 提交成功;t2 提交成功;最终余额 110(丢失更新)     最后值班人数 0(两个人都下班了:写偏斜)
READ_COMMITTED     没有(读到 100)     发生(100 → 500)    发生(2 → 3)      t1 提交成功;t2 提交成功;最终余额 110(丢失更新)     最后值班人数 0(两个人都下班了:写偏斜)
REPEATABLE_READ    没有(读到 100)     没有(两次都是 100)     没有(两次都是 2)     t1 提交成功;t2 提交成功;最终余额 110(丢失更新)     最后值班人数 0(两个人都下班了:写偏斜)
SERIALIZABLE       t2 的读被阻塞:异常:MySQLTransactionRollbackException(40001) Lock wait timeout exceeded; try restarting transaction 没有(t2 的写被阻塞:异常:MySQLTransactionRollbackException(40001) Lock wait timeout exceeded; try restarting transaction) 没有(t2 的插入被阻塞:异常:MySQLTransactionRollbackException(40001) Lock wait timeout exceeded; try restarting transaction) t1 MySQLTransactionRollbackException(40001) Lock wait timeout exceeded; try restarting transaction;t2 MySQLTransactionRollbackException(40001) Lock wait timeout exceeded; try restarting transaction;最终余额 100(有一方失败) 最后值班人数 1;t1:ok t2:MySQLTransactionRollbackException(40001) Deadlock found when trying to get lock; try restarting transaction

分步演示

JDBC 隔离级别
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12;自己起的 PostgreSQL 16.15(端口 9501)与 MySQL 26.7.0(端口 9601,innodb_lock_wait_timeout=3),用户自己的数据库实例没有被使用;pgjdbc 42.7.4、mysql-connector-j 9.1.0
    • 没有验证:SELECT ... FOR UPDATE / FOR SHARE 在各级别下的行为、MySQL 的间隙锁与 next-key 锁细节、PostgreSQL 的 SSI 谓词锁细节、更多异常类型(如只读事务异常)、其它数据库(Oracle、SQL Server)、真正并发下的概率性行为(本文的交错是人工排好的)
    京ICP备2026067502号-1京公网安备11010502063487号