PostgreSQL的版本推进的很快,目前已经到了PG18的非正式版本研发的状态。基于一些数据库产品在推进时容易出现的一些问题,比如新版本的性能在某些方面还不如老的版本这个问题。
我们将展开一个新的PostgreSQL的系列,从相近的版本去发现版本是否存在差异,来从版本的差异上去寻找一些问题。
找差异的方法有很多,我们就从最直接的方式来,我们准备了,两台同样的虚拟机,同样的配置,且虚拟机在同一台物理机上。同样去安装 POSTGRESQL 16.8 和 POSTGRESQL 17.4,用同样的方式去编译数据库,同样的参数。同样的压测的方法。我们来进行数据的采集,最终我们获得版本的差异为我们带来的不同。
一、数据的插入的性能差异。
数据的输入时的性能差异是很多数据库系统在一直优化,且努力让数据写入的速率的,这里我们通过基本pg_bench 来进行简易的测试,在同样配置的数据库中,插入1000万数据,我们来看差距是否存在。我们同样的事情至少做三遍并且我们会将数据列出来。
Initialization started at: 2025-06-02 06:47:53
[postgres@postgresql16 ~]$ pgbench -i -s 100 -U postgres -d my_pgbench_db
dropping old tables...
creating tables...
generating data (client-side)...
2025-06-02 06:48:00.362 CST [10778] LOG: checkpoint starting: wal.89 s)
10000000 of 10000000 tuples (100%) done (elapsed 7.08 s, remaining 0.00 s)
vacuuming...
creating primary keys...
donein 10.31 s (drop tables 0.06 s, create tables 0.06 s, client-side generate 7.32 s, vacuum 0.17 s, primary keys 2.71 s).
[postgres@postgresql16 ~]$ echo"Initialization finished at: $(date '+%Y-%m-%d %H:%M:%S')"
Initialization finished at: 2025-06-02 06:48:03
[postgres@postgresql16 ~]$ echo"end"
[postgres@postgresql17 ~]$ echo "Initialization started at: $(date '+%Y-%m-%d %H:%M:%S')"
Initialization started at: 2025-06-01 10:47:16
[postgres@postgresql17 ~]$ pgbench -i -s 100 -U postgres -d my_pgbench_db
dropping old tables...
creating tables...
generating data (client-side)...
vacuuming...
creating primary keys...
donein 6.86 s (drop tables 0.05 s, create tables 0.00 s, client-side generate 4.96 s, vacuum 0.16 s, primary keys 1.69 s).
[postgres@postgresql17 ~]$ echo"Initialization finished at: $(date '+%Y-%m-%d %H:%M:%S')"
Initialization finished at: 2025-06-01 10:47:23
[postgres@postgresql17 ~]$ echo"end"
测试编号 | 数据量 (万条) | 完成时间 | 版本 |
1 | 1000 | 4.46S | 16 |
2 | 1000 | 4.48S | 16 |
3 | 1000 | 6.63S | 16 |
4 | 1000 | 4.66S | 17 |
6 | 1000 | 4.59S | 17 |
7 | 1000 | 4.96S | 17 |
除此以外我们还进行了普通的测试,其中包含了客户端从10到50的逐渐数据的查询和DML的操作,下面我们将给出相关的测试脚本和过程。
[postgres@postgresql17 ~]$ bash test.sh
测试结果将保存到: pgbench_results_20250601_120531
---------------------------------------------------------
## 正在运行客户端数量为: 10 的测试 ##
开始时间: 2025-06-01 12:05:31
---------------------------------------------------------
## pgbench 命令执行失败,客户端数量为: 10 ##
请检查错误信息,日志文件: pgbench_results_20250601_120531/pgbench_c10_t30.log
暂停 5 秒,然后继续下一个客户端数量...
## 正在运行客户端数量为: 20 的测试 ##
开始时间: 2025-06-01 12:05:36
---------------------------------------------------------
## pgbench 命令执行失败,客户端数量为: 20 ##
请检查错误信息,日志文件: pgbench_results_20250601_120531/pgbench_c20_t30.log
暂停 5 秒,然后继续下一个客户端数量...
## 正在运行客户端数量为: 30 的测试 ##
开始时间: 2025-06-01 12:05:41
---------------------------------------------------------
## pgbench 命令执行失败,客户端数量为: 30 ##
请检查错误信息,日志文件: pgbench_results_20250601_120531/pgbench_c30_t30.log
暂停 5 秒,然后继续下一个客户端数量...
## 正在运行客户端数量为: 40 的测试 ##
开始时间: 2025-06-01 12:05:46
---------------------------------------------------------
## pgbench 命令执行失败,客户端数量为: 40 ##
请检查错误信息,日志文件: pgbench_results_20250601_120531/pgbench_c40_t30.log
暂停 5 秒,然后继续下一个客户端数量...
## 正在运行客户端数量为: 50 的测试 ##
开始时间: 2025-06-01 12:05:51
---------------------------------------------------------
## pgbench 命令执行失败,客户端数量为: 50 ##
请检查错误信息,日志文件: pgbench_results_20250601_120531/pgbench_c50_t30.log
所有测试阶段完成!
所有详细结果文件位于: pgbench_results_20250601_120531
#!/bin/bash
# --- 配置参数 ---
DB_USER="postgres" # 数据库用户
DB_NAME="my_pgbench_db" # 数据库名
PGBENCH_SCRIPT="custom_mixed.sql"# pgbench 脚本文件
SCALE_FACTOR=100 # 数据缩放因子 (与初始化时保持一致,确保脚本变量正确)
THREADS=8 # 每个pgbench实例使用的线程数 (通常设置为CPU核心数)
DURATION=30 # 每个测试阶段的运行时间 (秒)
PROGRESS_REPORT=5 # 每隔多少秒打印一次进度报告
START_CLIENTS=10 # 起始客户端数量
END_CLIENTS=50 # 结束客户端数量
CLIENT_STEP=10 # 每次增加的客户端步长
PAUSE_SECONDS=5 # 每个测试阶段之间暂停的秒数
OUTPUT_DIR="pgbench_results_$(date +%Y%m%d_%H%M%S)"# 结果输出目录
# --- 函数:打印分隔线 ---
print_separator() {
echo"---------------------------------------------------------"
}
# --- 创建结果输出目录 ---
mkdir -p "$OUTPUT_DIR"
echo"测试结果将保存到: $OUTPUT_DIR"
print_separator
# --- 循环执行 pgbench ---
for clients in $(seq $START_CLIENTS$CLIENT_STEP$END_CLIENTS)
do
echo"## 正在运行客户端数量为: $clients 的测试 ##"
echo"开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
print_separator
OUTPUT_FILE="${OUTPUT_DIR}/pgbench_c${clients}_t${DURATION}.log"
pgbench -f "$PGBENCH_SCRIPT" \
-c "$clients" \
-j "$THREADS" \
-T "$DURATION" \
-r \
-P "$PROGRESS_REPORT" \
-U "$DB_USER" \
-d "$DB_NAME" \
-s "$SCALE_FACTOR" \
> "$OUTPUT_FILE" 2>&1 # 将所有输出重定向到文件
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
echo"## pgbench 命令执行失败,客户端数量为: $clients ##" | tee -a "$OUTPUT_FILE"
echo"请检查错误信息,日志文件: $OUTPUT_FILE"
else
echo"## 客户端数量为: $clients 的测试完成 ##"
echo"结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo"结果已保存到: $OUTPUT_FILE"
print_separator
# 打印关键摘要到控制台
echo"摘要 (客户端: $clients):"
grep "tps =""$OUTPUT_FILE" | head -n 1
grep "latency average =""$OUTPUT_FILE"
print_separator
fi
if [ "$clients" -lt "$END_CLIENTS" ]; then
echo"暂停 ${PAUSE_SECONDS} 秒,然后继续下一个客户端数量..."
sleep "$PAUSE_SECONDS"
fi
done
echo"所有测试阶段完成!"
echo"所有详细结果文件位于: $OUTPUT_DIR"
-- custom_mixed.sql
`
-- 联合查询和写入的 pgbench 脚本
-- 随机选择一个账户ID,范围是 1 到 (scale_factor * 100000)
-- 假设 scale_factor = 10,则 aid 范围是 1 到 10000000
\set scale_factor 100
\set aid random(1, :scale_factor * 100000)
\set bid random(1, :scale_factor * 1)
\set tid random(1, :scale_factor * 10)
\set delta random(-5000, 5000)
-- 随机的金额变动 (注释移动到单独一行,或者删除)
BEGIN;
-- 查询操作 1: 查询特定账户的余额
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
-- 写入操作 1: 更新账户余额
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
-- 写入操作 2: 更新出纳员余额
UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
-- 写入操作 3: 更新分支余额
UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
-- 写入操作 4: 插入历史记录
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, now());
-- 查询操作 2: 联合查询一些数据(例如,从账户表和出纳员表联合查询)
SELECT a.aid, a.abalance, t.tbalance
FROM pgbench_accounts a, pgbench_tellers t
WHERE a.aid = :aid AND t.tid = :tid;
COMMIT;
测试批次 | 客户端数量 (C) | 线程数 (J) | 持续时间 (秒) | 事务总数 | 失败事务数 | 平均延迟 (ms) | 延迟标准差 (ms) | TPS (每秒事务数) | 备注 |
17 | 10 | 8 | 30S | 12993 | 0 | 14.975 ms | 629.864 ms | 667.573826 | |
17 | 20 | 8 | 30S | 20589 | 0 | 38.841 ms | 962.272 ms | 514.764547 | |
17 | 30 | 8 | 30S | 10505 | 0 | 87.947 ms | 1308.477 ms | 341.008574 | |
17 | 40 | 8 | 30S | 20872 | 0 | 107.095 ms | 1529.057 m | 90.696109 | |
17 | 50 | 8 | 30S | 24726 | 0 | 66.365 ms | 1278.599 ms | 752.918106 | |
16 | 10 | 8 | 30S | 109111 | 0 | 2.636 ms | 508.185 ms | 3634.577691 | |
16 | 20 | 8 | 30S | 120542 | 0 | 4.748 | 1.078 ms | 4013.857067 | |
16 | 30 | 8 | 30S | 123626 | 0 | 6.881 ms | 3.400 ms | 4116.228912 | |
16 | 40 | 8 | 30S | 125420 | 0 | 8.981 ms | 8.981 ms | 4175.357838 | |
16 | 50 | 8 | 30S | 128547 | 0 | 10.915 ms | 10.915 ms | 4279.730086 |
特殊的情况截图
从数据上看,PG17 PG16 各有自己的特点,但PG16的数据出奇的比PG17的好,我在测试的时候认为是突发情况,就对PG16的数据库又进行了4次测试,对PG17进行了同样的3次此时,最终我不得不把上面的数据呈现出来。
这也是我一直担心的一个问题,新版本的性能比老版本的性能低。出现这个问题在数据库业界并不是少数派事件,在某开源数据库的共识是新的版本的性能逐渐下降对比之前5.7的性能8.0的性能在一些简单的操作上更慢了。
所以这次我也对PG进行了测试没想到得出的结果也让我...... ,同时我也想提醒一件事,也是最近一个培训课上一些同学经常问我的问题,为什么不讲最新的版本。
我的解释是
1 培训大纲的要求
2 新版本未必比旧版本在实用性上强
3 一些同学盲目求新,新版本和旧版本的一些知识体系又了改变,则课上不会在讲之前的一些坑,因为新版本上解决了老版本的问题,这点尤其在PG上非常突出。教学的任务可能对我来说更轻松了,但如果你去的单位用的是老版本,那么你不进行体系化的对老版本的熟悉,你讲无法处理新版本已经不出现,老版本依然是问题的问题。
这也是我这篇文章想说的,数据库相对运维来说更严谨,新版本固然好,但是完全好吗,自己思索思索。
新版本一定好?