我们正在使用PostgreSql-13作为核心服务器,遇到了性能瓶颈。
硬件包括2个CPU(AMD EPYC9754,每个128核256线程),128GB内存,硬件RAID0包括2 * 4T SSD(Samsung990Pro)。
我认为 PG 服务器未能充分发挥硬件的潜力。
在我在这里问这个问题之前,我已经做了以下事情:
- 检查我的 SQL 查询(准确使用主键,没有序列扫描);
- 确认我的程序运行正确(我虽然用 Python 和 C++ 编写了相同的逻辑,但都运行得很慢);
- 配置我的PG服务器使用HugePage(我总共配置了80GB的HugePage内存,并允许PG服务器使用64GB作为
shared_buffers
); - 增加每个 PG worker 的内存限制(
work_mem
,temp_buffers
,...); - 增加并行工作者的数量(
max_worker_processes
,max_parallel_workers_per_gather
,max_parallel_workers
,...); - 打开
force_parallel_mode
; - 降低 io 成本和 cpu 成本 cfg(
random_page_cost=1.01
,cpu_tuple_cost=0.0025
,cpu_index_tuple_cost=0.001
,...); - 最大
default_statistics_target
为1000; - 禁用复制;
- 启动32或64个进程并行连接到服务器,并使用不同的主键查询同一张表的不同行。
- 所有查询都是只读的,并且测试期间没有插入/更新/DDL 操作;
我的期望是:
- PG服务器至少使用50%的CPU/内存资源;
- 没有任何单个进程阻塞其他查询;
- 总吞吐量基本上随着我分配给 PG 服务器的资源量线性增加(也许我很天真?);
我的测试:
表定义:
CREATE TABLE IF NOT EXISTS ob_lots( t_stamp INT8 NOT NULL, trd_sec INT4 NOT NULL, o_level INT2 NOT NULL, i_olots INT4 NOT NULL, f_olots FLOAT4 NULL, CONSTRAINT prk_ob_lots PRIMARY KEY( t_stamp, o_level ) );
查询:
SELECT f_olots, t_stamp, trd_sec FROM ob_lots WHERE t_stamp BETWEEN $1 AND $2 ORDER BY t_stamp DESC, o_level DESC LIMIT 4096;
测试程序:启动我的程序的多个实例,使用上述查询 SQL 并行随机查询上述表格中的行。当客户端数量从 2 个增加到 4 个,或从 4 个增加到 8 个,或从 8 个增加到 16 个时,我们确实观察到总吞吐量每次都几乎翻倍。但从 16、32 或更高时,总吞吐量从未改变。
SQL解释:
Gather (cost=1000.28..1002.41 rows=1 width=18) (actual time=6.840..9.232 rows=0 loops=1) Workers Planned: 1 Workers Launched: 1 Single Copy: true Buffers: shared hit=8 -> Limit (cost=0.28..2.31 rows=1 width=18) (actual time=0.033..0.033 rows=0 loops=1) Buffers: shared hit=8 -> Index Scan Backward using prk_ob_lots_sc5555 on ob_lots_sc5555 (cost=0.28..2.31 rows=1 width=18) (actual time=0.031..0.031 rows=0 loops=1) Index Cond: ((t_stamp >= 123) AND (t_stamp <= 456)) Buffers: shared hit=8 Planning: Buffers: shared hit=109 Planning Time: 0.759 ms Execution Time: 9.274 ms
以下是我觉得奇怪的事情:
- 有一个 PG 进程使用了几乎 100% 的单个 CPU 核心,其他进程的使用率都很少;
- 测试期间没有io、没有交换,并且大量内存/CPU/io 资源处于空闲状态。似乎所有其他工作进程都在等待主进程(我猜是主进程占用了 100% CPU)PG 进程,从而导致瓶颈;
- 测试数据库使用了大约 4GB 的磁盘空间,因此足够小以至于可以完全由 PG 保存在内存中。事实上,正如我们所观察到的,根本没有 io 操作;
它在做什么?为什么所有工作进程都在等待单个进程?
这是我的 PG conf 的主要部分(我只列出我更改的行):
最大连接数 = 2048
共享缓冲区 = 64GB
huge_pages = 开启
临时缓冲区 = 256MB
最大准备事务数 = 256
工作内存 = 256MB
维护工作内存 = 16GB
autovacuum_work_mem = -1
dynamic_shared_memory_type = posix
有效 io 并发 = 1000
维护io并发性 = 1000
最大工作进程数 = 256
最大并行维护工作者数 = 256
每个聚集的最大并行工作者数 = 256
parallel_leader_participation = on
最大并行工作者数 = 256
fsync = off
同步提交 = off
全页写入 = 关闭
wal_compression = on
wal_buffers = -1
wal_writer_delay = 10000ms
wal_writer_flush_after = 1GB
提交延迟 = 100000
commit_siblings = 128
检查点超时 = 1d
最大wal大小=128GB
min_wal_size = 32GB
检查点完成目标 = 1.0
checkpoint_flush_after = 0
检查点警告 = 0
最大发送者数 = 0
seq_page_cost = 1.0
随机页面成本 = 1.01
cpu_tuple_cost = 0.0025
cpu_index_tuple_cost = 0.001
CPU 操作成本 = 0.00125
有效缓存大小 = 64GB
默认统计目标 = 1000
force_parallel_mode = on
自动清理 = 开启
top
和的输出如下iotop
:
顶部 - 16:38:16 启动 4:09,2 个用户,平均负载:14.16、9.14、3.97
任务:总计 1581 个,正在运行 2 个,正在休眠 1573 个,已停止 0 个,僵尸 6 个
%Cpu:3.5 us、4.3 sy、0.0 ni、92.1 id、0.0 wa、0.0 hi、0.1 si、0.0 st
GiB 内存:总计 125.6,可用 34.6,已使用 82.9,缓冲/缓存 9.1
GiB 交换:总计 1.0,可用 1.0,已使用 0.0。42.6 可用内存
进程号 USER PR NI VIRT RES SHR SWAP %CPU %MEM TIME+ COMMAND
31159 leon 20 0 4654.2米 105.1米 12.5米 0.0米 S 152.7 0.1 7:09.93 负载
3186 postgres 0 -20 66.7g 18.6m 16.2m 0.0m R 99.5 0.0 5:03.16 postgres #它是主服务器吗?
3192 postgres 0 -20 80.4m 6.8m 3.6m 0.0m S 8.2 0.0 0:24.97 postgres
32218 postgres 0 -20 66.7g 13.5米 9.9米 0.0米 S 5.8 0.0 0:12.90 postgres
31217 postgres 0 -20 66.7g 13.4m 9.9m 0.0m S 5.3 0.0 0:12.74 postgres
31234 postgres 0 -20 66.7g 13.5米 9.9米 0.0米 S 5.3 0.0 0:12.74 postgres
(很多很多的 postgres 进程...)
总磁盘读取量:0.00 B/s | 总磁盘写入量:0.00 B/s
当前磁盘读取:0.00 B/s | 当前磁盘写入:0.00 B/s
TID PRIO USER DISK READ DISK WRITE> COMMAND 1 be/4 root 0.00 B/s 0.00 B/s init 2 be/4 root 0.00 B/s 0.00 B/s [kthreadd] 3 be/0 root 0.00 B/s 0.00 B/s [rcu_gp] 4 be/0 root 0.00 B/s 0.00 B/s [rcu_par_gp] 5 be/0 root 0.00 B/s 0.00 B/s [slub_flushwq]