# postgresql-shared_buffers


關於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.在預熱數據的過程中可以考慮先做索引的預熱,因為要做索引的情況加載索引會比較慢。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM