# 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需要安裝詳細的[步驟](http://fibrevillage.com/database/382-postgre9-4-database-buffer-cache-and-os-cache)

```sql
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可查看緩存使用情況,以及命中次數和臟塊

```sql
--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

測試腳本:

```sql
 - 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;
```



未預熱緩存測試結果:

```sql
       序號                     |  第一次  | 第二次  | 第三次 | 平均值(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%)差別不大。

預熱緩存測試結果:

```sql
       序號                     |  第一次  | 第二次  | 第三次 | 平均值(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