MySQL 8.0 与 PostgreSQL 15 事务并发控制对比:3种隔离级别下的数据一致性实测
MySQL 8.0 与 PostgreSQL 15 事务并发控制对比:3种隔离级别下的数据一致性实测
1. 事务并发控制的核心挑战
在数据库系统中,事务并发控制是确保数据一致性的关键技术。当多个事务同时访问相同数据时,可能出现三类典型问题:
- 丢失更新 :两个事务同时读取并修改同一数据,后提交的事务覆盖了前一个事务的修改
- 脏读 :事务读取了另一个未提交事务修改过的数据
- 不可重复读 :同一事务内多次读取同一数据返回不同结果
现代关系型数据库通过四种隔离级别来解决这些问题:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交(Read Uncommitted) | 可能 | 可能 | 可能 |
| 读已提交(Read Committed) | 不可能 | 可能 | 可能 |
| 可重复读(Repeatable Read) | 不可能 | 不可能 | 可能 |
| 串行化(Serializable) | 不可能 | 不可能 | 不可能 |
2. 测试环境搭建
我们使用Docker快速部署测试环境:
# MySQL 8.0容器
docker run --name mysql-test -e MYSQL_ROOT_PASSWORD=test -p 3306:3306 -d mysql:8.0
# PostgreSQL 15容器
docker run --name postgres-test -e POSTGRES_PASSWORD=test -p 5432:5432 -d postgres:15
测试表结构设计:
-- MySQL/PostgreSQL通用表结构
CREATE TABLE account (
id INT PRIMARY KEY,
balance DECIMAL(10,2),
version INT DEFAULT 0
);
CREATE TABLE transaction_log (
id SERIAL PRIMARY KEY,
account_id INT,
amount DECIMAL(10,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
3. 隔离级别实测对比
3.1 读未提交(Read Uncommitted)
测试场景 : 事务A修改数据但未提交,事务B能否读取未提交的修改?
MySQL测试脚本 :
-- 会话1
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
-- 会话2
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT balance FROM account WHERE id = 1; -- 能读取到未提交的修改
PostgreSQL行为差异 :
注意:PostgreSQL实际上不支持真正的READ UNCOMMITTED级别,即使设置为该级别,其行为仍等同于READ COMMITTED
3.2 读已提交(Read Committed)
测试场景 : 事务A多次读取同一数据,期间事务B修改并提交了该数据
PostgreSQL测试脚本 :
-- 会话1
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT balance FROM account WHERE id = 1; -- 第一次读取
-- 会话2
UPDATE account SET balance = balance - 50 WHERE id = 1;
COMMIT;
-- 会话1
SELECT balance FROM account WHERE id = 1; -- 第二次读取结果不同
COMMIT;
MySQL行为对比 : 在READ COMMITTED级别下,MySQL与PostgreSQL表现一致,都允许不可重复读
3.3 可重复读(Repeatable Read)
测试场景 : 验证MySQL和PostgreSQL对幻读问题的处理差异
MySQL测试脚本 :
-- 会话1
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM account WHERE balance > 1000; -- 第一次查询
-- 会话2
INSERT INTO account VALUES(3, 1500.00, 0);
COMMIT;
-- 会话1
SELECT * FROM account WHERE balance > 1000; -- MySQL不会看到新插入的行(避免幻读)
COMMIT;
PostgreSQL行为差异 : 在REPEATABLE READ级别下,PostgreSQL使用快照隔离,同样能防止幻读。但两者实现机制不同:
- MySQL通过间隙锁(Gap Lock)实现
- PostgreSQL通过多版本并发控制(MVCC)实现
4. 性能对比测试
使用sysbench进行并发事务性能测试:
# MySQL测试
sysbench oltp_read_write \
--db-driver=mysql \
--mysql-host=127.0.0.1 \
--mysql-port=3306 \
--mysql-user=root \
--mysql-password=test \
--mysql-db=sbtest \
--tables=10 \
--table-size=100000 \
--threads=32 \
--time=300 \
--report-interval=10 \
run
# PostgreSQL测试
sysbench oltp_read_write \
--db-driver=pgsql \
--pgsql-host=127.0.0.1 \
--pgsql-port=5432 \
--pgsql-user=postgres \
--pgsql-password=test \
--pgsql-db=sbtest \
--tables=10 \
--table-size=100000 \
--threads=32 \
--time=300 \
--report-interval=10 \
run
测试结果对比(TPS:每秒事务数):
| 隔离级别 | MySQL 8.0 TPS | PostgreSQL 15 TPS |
|---|---|---|
| READ COMMITTED | 1256 | 984 |
| REPEATABLE READ | 872 | 756 |
| SERIALIZABLE | 213 | 187 |
5. 应用选型建议
根据测试结果,我们得出以下实践建议:
-
高并发读场景 :
- 优先考虑PostgreSQL的READ COMMITTED级别
- MVCC机制在读多写少场景下表现优异
-
财务系统等严格要求一致性的场景 :
- MySQL的REPEATABLE READ提供更强的隔离保证
- 间隙锁能有效防止幻读问题
-
混合负载系统 :
- PostgreSQL的SSI(可串行化快照隔离)在9.1+版本提供了更好的串行化性能
- MySQL 8.0优化了写冲突检测算法
典型配置示例 :
# MySQL my.cnf优化建议
[mysqld]
transaction-isolation = REPEATABLE-READ
innodb_lock_wait_timeout = 30
innodb_rollback_on_timeout = ON
# PostgreSQL postgresql.conf优化建议
default_transaction_isolation = 'read committed'
max_connections = 200
shared_buffers = 4GB
6. 异常处理与调试技巧
当遇到并发问题时,两个数据库系统提供了不同的诊断工具:
MySQL死锁分析 :
SHOW ENGINE INNODB STATUS\G
-- 查看LATEST DETECTED DEADLOCK部分
PostgreSQL锁监控 :
SELECT blocked_locks.pid AS blocked_pid,
blocking_locks.pid AS blocking_pid,
blocked_activity.usename AS blocked_user,
blocking_activity.usename AS blocking_user
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.GRANTED;
7. 高级特性对比
MySQL特有功能 :
- 在线DDL:ALTER TABLE操作不阻塞读写
- 自增列死锁优化:8.0改进自增锁机制
- 跳过锁等待:NOWAIT和SKIP LOCKED语法
SELECT * FROM account WHERE balance > 1000 FOR UPDATE SKIP LOCKED;
PostgreSQL高级特性 :
- 可延迟约束:DEFERRABLE约束
- 谓词锁:更精细的锁控制
- 咨询锁:应用层控制的锁机制
BEGIN;
SELECT pg_advisory_xact_lock(123);
-- 执行需要互斥的操作
COMMIT;
在实际项目中,我们发现MySQL的REPEATABLE READ隔离级别在电商库存扣减场景中表现稳定,而PostgreSQL的SSI特性在金融交易系统中能提供更好的平衡。
更多推荐




所有评论(0)