postgresql shared_buffers 讲解

本文深入探讨PostgreSQL中shared_buffers参数的设置与优化,解析其在数据库性能提升中的关键作用,包括缓存机制、预热策略及对不同内存配置下TPS的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

postgresql shared_buffers 讲解

什么是shred_buffer,我们为什么需要shared_buffers?

1.在数据库系统中,我们主要关注磁盘io,大多数oltp工作负载都是随机io,因此从磁盘获取非常慢。

2.为了解决这个问题,postgres将数据缓存在RAM中,以此来提高性能,即使ssd的情况下RAM也要快很多。

3.shared_buffers是一个8KB的数组,postgres在从磁盘中查询数据前,会先查找shared_buffers的页,如果命中,就直接返回,避免从磁盘查询。

shared_buffers存储什么?

1.表数据

2.索引,索引也存储在8K块中。

3.执行计划,存储基于会话的执行计划,会话结束,缓存的计划也就被丢弃。

什么时候加载shared_buffers?

1.在访问数据时,数据会先加载到os缓存,然后再加载到shared_buffers,这个加载过程可能是一些查询,也可以使用pg_prewarm预热缓存。

2.当然也可能同时存在os和shared_buffers两份一样的缓存(双缓存)。

3.查找到的时候会先在shared_buffers查找是否有缓存,如果没有再到os缓存查找,最后再从磁盘获取。

4.os缓存使用简单的LRU(移除最近最久未使用的缓存),而数据库采用的优化的时钟扫描,即缓存使用频率高的会被保存,低的被移除。

shared_buffers设置的合理范围

1.windows服务器有用范围是64MB到512MB,默认128MB

2.linux服务器建议设置为25%,亚马逊服务器设置为75%(避免双缓存,数据会存储在os和shared_buffers两份)

os缓存的重要性

数据写入时,从内存到磁盘,这个页面就会被标记为脏页,一旦被标记为脏页,它就会被刷新到os缓存,然后写入磁盘。所以如果os高速缓存大小较小,则它不能重新排序写入并优化io,这对于繁重的写入来说非常致命,因此os的缓存大小也非常重要。给予shared_buffers太大或太小都会损害性能。

查看shared_buffers,os缓存
这里需要使用到两个插件,pg_bufferscache系统已经自带可以直接创建扩展,pgfincore需要安装详细的步骤

pg_buffered表占用缓存大小

pg_buffer_percent:表占用整个缓存的占比
percent_of_relation:缓存数据和该表数据占比
os_cache_mb:表占用os系统缓存大小
os_cache_percent_of_relation:os缓存和表占比
rel_size:整个表大小

pgbench=# select c.relname,pg_size_pretty(count(*) * 8192) as pg_buffered, 
       round(100.0 * count(*) / (select setting from pg_settings  where name='shared_buffers')::integer,1) as pgbuffer_percent,
       round(100.0*count(*)*8192 / pg_table_size(c.oid),1) as percent_of_relation,
       (select round( sum(pages_mem) * 4 /1024,0 ) from pgfincore(c.relname::text) ) as os_cache_MB , 
         round(100 * (  select sum(pages_mem)*4096 from pgfincore(c.relname::text) )/ pg_table_size(c.oid),1) as os_cache_percent_of_relation,
         pg_size_pretty(pg_table_size(c.oid)) as rel_size 
 from pg_class c 
 inner join pg_buffercache b on b.relfilenode=c.relfilenode 
 inner join pg_database d on (b.reldatabase=d.oid and d.datname=current_database()
            and c.relnamespace=(select oid from pg_namespace where nspname='public')) 
 group by c.oid,c.relname 
 order by 3 desc limit 30;
        relname        | pg_buffered | pgbuffer_percent | percent_of_relation | os_cache_mb | os_cache_percent_of_relation | rel_size 
-----------------------+-------------+------------------+---------------------+-------------+------------------------------+----------
 pgbench_accounts      | 471 MB      |              1.9 |                 7.3 |         495 |                          7.7 | 6416 MB
 pgbench_accounts_pkey | 139 MB      |              0.6 |                13.0 |         274 |                         25.6 | 1071 MB
 pgbench_history       | 2704 kB     |              0.0 |                86.9 |           3 |                         99.2 | 3112 kB
 pgbench_branches_pkey | 56 kB       |              0.0 |               100.0 |           0 |                        100.0 | 56 kB
 pgbench_tellers_pkey  | 240 kB      |              0.0 |               100.0 |           0 |                        100.0 | 240 kB
 pgbench_branches      | 2968 kB     |              0.0 |                70.7 |           4 |                         99.2 | 4200 kB
 pgbench_tellers       | 608 kB      |              0.0 |               100.0 |           1 |                         94.7 | 608 kB
(7 rows)--表缓存预热
pgbench=# select pg_prewarm('pgbench_accounts', 'buffer', 'main');
 pg_prewarm 
------------
     820956
(1 row)
--索引预热:
pgbench=#  select pg_prewarm('pgbench_accounts_pkey', 'buffer', 'main');
 pg_prewarm 
------------
     137099
(1 row)--预热后查看缓存
pgbench=# select c.relname,pg_size_pretty(count(*) * 8192) as pg_buffered, 
       round(100.0 * count(*) / (select setting from pg_settings  where name='shared_buffers')::integer,1) as pgbuffer_percent,
       round(100.0*count(*)*8192 / pg_table_size(c.oid),1) as percent_of_relation,
       (select round( sum(pages_mem) * 4 /1024,0 ) from pgfincore(c.relname::text) ) as os_cache_MB , 
         round(100 * (  select sum(pages_mem)*4096 from pgfincore(c.relname::text) )/ pg_table_size(c.oid),1) as os_cache_percent_of_relation,
         pg_size_pretty(pg_table_size(c.oid)) as rel_size 
 from pg_class c 
 inner join pg_buffercache b on b.relfilenode=c.relfilenode 
 inner join pg_database d on (b.reldatabase=d.oid and d.datname=current_database()
            and c.relnamespace=(select oid from pg_namespace where nspname='public')) 
 group by c.oid,c.relname 
 order by 3 desc limit 30;
        relname        | pg_buffered | pgbuffer_percent | percent_of_relation | os_cache_mb | os_cache_percent_of_relation | rel_size 
-----------------------+-------------+------------------+---------------------+-------------+------------------------------+----------
 pgbench_accounts      | 6414 MB     |             26.1 |               100.0 |        6414 |                        100.0 | 6416 MB
 pgbench_accounts_pkey | 139 MB      |              0.6 |                13.0 |         274 |                         25.6 | 1071 MB
 pgbench_history       | 2704 kB     |              0.0 |                86.9 |           3 |                         99.2 | 3112 kB
 pgbench_branches_pkey | 56 kB       |              0.0 |               100.0 |           0 |                        100.0 | 56 kB
 pgbench_tellers_pkey  | 240 kB      |              0.0 |               100.0 |           0 |                        100.0 | 240 kB
 pgbench_branches      | 2968 kB     |              0.0 |                70.7 |           4 |                         99.2 | 4200 kB
 pgbench_tellers       | 608 kB      |              0.0 |               100.0 |           1 |                         94.7 | 608 kB
(7 rows)

可以看到将数据加载至shared_buffers,并且os也缓存了一份。正常情况os不应该缓存这么多的数据。

如何设定shared_buffers?

使用pg_buffercache可查看缓存使用情况,以及命中次数和脏块

--1.缓存命中数
pgbench=# select usagecount,count(*),isdirty from pg_buffercache group by isdirty, usagecount order by isdirty, usagecount ; 
 usagecount |  count  | isdirty 
------------+---------+---------
          1 |    6651 | f
          2 |  762250 | f
          3 |   54684 | f
          4 |   12630 | f
          5 |    3940 | f
            | 2305573 | 
(6 rows)
--2.数据在缓存中占比
pgbench=# SELECT                                                        
c.relname,pg_size_pretty(count(*) * 8192) as buffered,
round(100.0 * count(*) /(SELECT setting FROM pg_settings WHERE name='shared_buffers')::integer,1)AS buffers_percent,
round(100.0 * count(*) * 8192 /pg_relation_size(c.oid),1)AS percent_of_relation
FROM pg_class c
INNER JOIN pg_buffercache b ON b.relfilenode = c.relfilenode
INNER JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database())
GROUP BY c.oid,c.relname
ORDER BY 3 DESC LIMIT 10;
        relname        |  buffered  | buffers_percent | percent_of_relation 
-----------------------+------------+-----------------+---------------------
 pgbench_accounts      | 6414 MB    |            26.1 |               100.0
 pgbench_accounts_pkey | 1071 MB    |             4.4 |               100.0
 pg_amop               | 56 kB      |             0.0 |                87.5
 pg_cast               | 16 kB      |             0.0 |               100.0
 pg_constraint         | 8192 bytes |             0.0 |               100.0
 pg_index              | 32 kB      |             0.0 |               100.0
 pg_opclass            | 16 kB      |             0.0 |                66.7
 pg_namespace          | 8192 bytes |             0.0 |               100.0
 pg_operator           | 120 kB     |             0.0 |               100.0
 pg_amproc             | 40 kB      |             0.0 |               100.0
(10 rows)

缓存中存储了完整的表,和索引,占总缓存的30%,占比很低缓存剩余很多。
1.如果大量的usagecount都是4或者5,那表明shared_buffers不够,应该扩大shared_buffers;

2.如果大量的usagecount都是0或者1,那表明shared_buffers过大,应该减小shared_buffers;

每当共享内存中使用一个块时,他就会增加一次时钟扫描算法,范围从1-5。4和5标识极高的使用数据块,高使用可能会保留在shared_buffers中(有空间),如果需要更高使用率的空间,则低使用率的块将被移除,一般简单的插入或者更新会将使用次数设置为1。

缓存占比低。可以确定的是如果我们的数据集非常小,那么设置较大的shared_buffers,没什么区别。

pgbench性能测试(shared_buffers 128MB,4GB,8GB,24GB)

PostgreSQL默认测试脚本,含UPDATE、INSERT还有SELECT等操作。通过修改shared_buffers大小来测试tps。

数据库版本:PostgreSQL 10.4 (ArteryBase 5.0.0, Thunisoft)

操作系统配置:CentOS Linux release 7 ,32GB内存,8 cpu

测试参数:初始化5000w数据:pgbench -i -s 500 -h localhost -U sa -d pgbench

测试方法:pgbench -c 500 -t 20 -n -r pgbench 模拟500客户端,每个客户端20个事务,每种配置参数执行三次,记录tps值。

数据库物理大小:数据库总大小7503 MB,其中表总大小pgbench_accounts:7487 MB,索引pgbench_accounts_pkey :1071 MB

测试脚本:

 - statement latencies in milliseconds:
         0.002  \set aid random(1, 100000 * :scale)
         0.001  \set bid random(1, 1 * :scale)
         0.001  \set tid random(1, 10 * :scale)
         0.001  \set delta random(-5000, 5000)
         9.478  BEGIN;
        14.575  UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
         6.758  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
       130.573  UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
       786.933  UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
         5.355  INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
      1242.835  END;

未预热缓存测试结果:


       序号                  |  第一次  | 第二次  | 第三次 | 平均值(tps) 
-----------------------------+---------+---------+---------+---------
 shared_buffers=128MB(默认)| 249      | 126     |  145  |  173            
 shared_buffers=4GB         | 357      | 357     |  373  |  362           
 shared_buffers=8GB         | 362      | 363     |  415  |  380              
 shared_buffers=24GB        | 378      | 368     |  397  |  381            
shared_buffers设置为8GB(25%)和设置为24GB(75%)差别不大。

预热缓存测试结果:

       序号                  |  第一次  | 第二次  | 第三次 | 平均值(tps) 
-----------------------------+---------+---------+---------+---------
 shared_buffers=128MB(默认)| 211      | 194     |  207   |  204            
 shared_buffers=4GB         | 1225     | 1288    |  1321  |  1278           
 shared_buffers=8GB         | 1176     | 1291    |  1144  |  1203              
 shared_buffers=24GB        | 1285     | 1250    |  1309  |  1281           

当shared_buffers=4GB时,数据6GB不能完全装下,所以优先预热索引,将索引加载到缓存,然后在加载数据。可以看到最终shared_buffers=4GB的tps和8GB,24GB表现差别不大。

内存结构

1.本地内存:work_mem,maintenance_work_mem,temp_buffer,进程分配

2.共享内存:shared_buffers,wal buffers,commitLog buffer

本地内存*max_connections+共享内存+服务器使用内存<=总内存

小结

1.大多数情况设置shared_buffers为内存的25%,当然为了最优可以根据命中,以及缓存占比调整。

2.设置shared_buffers为75%和25%相差不大,也和数据量一共只有7G+有关系。但是os系统缓存同样重要,而设置为75%,可能会超过总内存。

3.设置所有的缓存需要注意不要超过总内存大小。

4.在预热数据的过程中可以考虑先做索引的预热,因为要做索引的情况加载索引会比较慢。

转自

关于shared_buffers

<think>嗯,用户之前已经问过关于数据库性能优化的问题,现在他们进一步询问数据库参数调优的具体方法。我需要先回顾之前的回答,确保不重复但补充细节。用户提到了内存分配、连接数、缓存大小,以及不同数据库如MySQL和PostgreSQL的配置差异,还有InnoDB缓冲池和日志策略。 首先,用户可能对数据库内部机制有一定了解,但需要更具体的调优步骤。他们可能是开发人员或DBA,负责实际优化工作,所以需要实用、可操作的指导。要注意区分不同数据库,因为MySQL和PostgreSQL的配置项确实不同,不能混淆。 接下来,用户的问题集中在参数调整的具体方法,比如如何确定缓冲池大小是否足够,如何设置连接数等。可能需要解释每个参数的作用,以及如何根据系统资源进行调整。例如,缓冲池通常设置为物理内存的70%,但如果有其他服务运行,可能需要调整。 然后,日志写入策略方面,比如innodb_flush_log_at_trx_commit的不同设置对性能和数据安全的影响,用户可能需要权衡性能和数据一致性,根据业务场景选择合适的配置。 另外,用户可能遇到连接数过多或不足的问题,需要解释如何根据并发量调整max_connections,同时考虑连接池配置,避免资源耗尽。 还要考虑监控和调优工具的使用,比如如何通过监控工具识别瓶颈,动态调整参数。比如使用SHOW STATUS和SHOW VARIABLES来获取实时信息。 另外,需要提及PostgreSQL的不同配置项,如shared_buffers和work_mem,以及WAL设置,帮助用户理解不同数据库的差异。 可能还需要提醒用户,调优是一个持续的过程,需要根据实际负载不断调整,而不是一次性设置。同时,备份配置文件的重要性,避免调整导致系统不稳定。 总结来说,回答需要结构清晰,分数据库类型讲解关键参数,给出具体建议和示例,强调监控和逐步调整的重要性,确保用户能根据自身情况应用这些优化策略。</think>以下是针对 **数据库参数调优** 的详细实践指南,以 **MySQL 和 PostgreSQL** 为例,涵盖内存分配、连接数、缓存等核心配置的调整逻辑: --- ### 一、通用调优原则 1. **避免盲目调参** - 先通过监控工具(如Prometheus、Percona Monitoring)定位瓶颈(CPU、内存、磁盘IO、网络) - 优先优化SQL和索引,再调整参数 2. **渐进式调整** - 每次只修改1-2个参数,观察效果 - 备份原始配置文件(如`my.cnf`或`postgresql.conf`) --- ### 二、MySQL 关键参数调优(InnoDB引擎) #### 1. **内存分配** - **`innodb_buffer_pool_size`** - **作用**:缓存表数据和索引(对读性能影响最大) - **建议值**:物理内存的 **70%~80%**(若专用数据库服务器) - **计算公式**: $$ \text{innodb\_buffer\_pool\_size} = \text{总物理内存} \times 0.75 - \text{其他进程占用内存} $$ - **动态调整**(MySQL 5.7+): ```sql SET GLOBAL innodb_buffer_pool_size = 2147483648; -- 2GB ``` - **`innodb_log_file_size`** - **作用**:事务日志文件大小(影响写性能) - **建议值**:1~4GB(需保证 `innodb_log_files_in_group` × `innodb_log_file_size` ≥ 1小时写入量) #### 2. **连接与线程** - **`max_connections`** - **作用**:最大客户端连接数 - **建议值**: $$ \text{max\_connections} = \frac{\text{可用内存 (MB)}}{10 \sim 20} $$ (每个连接约消耗10-20MB内存) - **关联参数**: ```ini thread_cache_size = max_connections × 0.1 -- 线程缓存复用 ``` #### 3. **写入优化** - **`innodb_flush_log_at_trx_commit`** - `=1`:严格ACID(每次提交刷盘,安全但性能差) - `=2`:每秒刷盘(宕机可能丢失1秒数据,性能提升10倍+) - `=0`:依赖OS刷盘(风险最高,仅测试环境使用) - **`innodb_io_capacity`** - 根据磁盘类型设置(SSD建议4000~20000,HDD建议200~400) --- ### 三、PostgreSQL 关键参数调优 #### 1. **内存分配** - **`shared_buffers`** - **作用**:共享内存缓存数据页 - **建议值**:物理内存的 **25%~40%**(默认值通常过低) ```ini shared_buffers = 8GB -- 适用于32GB内存服务器 ``` - **`work_mem`** - **作用**:单个查询操作(排序、哈希)使用的内存 - **建议值**: $$ \text{work\_mem} = \frac{\text{可用内存}}{\text{max\_connections} \times 2} $$ (例如:4GB内存/100连接 → ~20MB) #### 2. **写入与WAL** - **`synchronous_commit`** - `=on`:严格保证事务持久化(默认) - `=off`:异步提交(提升写入性能,可能丢失少量数据) - **`wal_buffers`** - **建议值**:16MB~64MB(避免频繁WAL日志写入) #### 3. **并行查询** - **`max_parallel_workers_per_gather`** - 控制单个查询的并行工作线程数(建议4~8) - **`max_worker_processes`** - 总并行进程数(建议 = CPU核心数 × 2) --- ### 四、通用监控与验证方法 #### 1. **MySQL 状态检查** ```sql -- 查看缓冲池命中率(应>99%) SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%'; -- 计算命中率公式: $$ \text{命中率} = 1 - \frac{\text{Innodb_buffer_pool_reads}}{\text{Innodb_buffer_pool_read_requests}} $$ -- 查看线程利用率 SHOW STATUS LIKE 'Threads_%'; ``` #### 2. **PostgreSQL 状态检查** ```sql -- 查看缓存命中率 SELECT sum(blks_hit) / (sum(blks_hit) + sum(blks_read)) AS hit_ratio FROM pg_stat_database; -- 检查锁争用 SELECT * FROM pg_locks WHERE granted = false; ``` --- ### 五、参数调优示例对比 | 场景 | MySQL 参数调整 | PostgreSQL 参数调整 | |---------------------|---------------------------------------|--------------------------------------| | **内存不足** | 增加`innodb_buffer_pool_size` | 增加`shared_buffers`和`work_mem` | | **高并发写入** | `innodb_flush_log_at_trx_commit=2` | `synchronous_commit=off` | | **复杂查询慢** | 增加`tmp_table_size` | 增加`work_mem`和并行度 | | **连接数耗尽** | 调整`max_connections` + 连接池 | 增加`max_connections` + PgBouncer | --- ### 六、注意事项 1. **不要过度分配内存**:避免触发OOM(内存溢出)导致进程被杀死 2. **SSD优化**: - MySQL:设置`innodb_flush_neighbors=0`(禁用邻页刷新) - PostgreSQL:设置`random_page_cost=1.1`(降低随机读代价) 3. **版本差异**:MySQL 8.0与5.7、PostgreSQL 14与12的默认参数可能不同,需参考官方文档调整 --- 通过以上参数调整,通常可使数据库性能提升 **30%~300%**。但需注意:**参数优化无法弥补糟糕的 schema 设计或低效查询**,需结合索引优化和架构调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值