PostgreSQL 元組統計與 pgstattuple 最佳化
第1章 簡介
1.1 參考文件
https://www.percona.com/blog/postgresql-tuple-level-statistics-with-pgstattuple/
1.2 關於pgstattuble
由於Postgres表膨脹會降低資料庫效能,因此我們可以透過消除表膨脹來提高其效能。我們可以使用pgstattuple擴充套件來識別膨脹的表。
這個擴充套件提供了幾個函式來獲取元級統計資訊。因為pgstattuple函式產生大量的頁面級資訊,所以預設情況下對它們的訪問是有限的。預設情況下,只有pg_stat_scan_tables角色有能力執行pgstattuple函式。
使用pgstattuple函式,我們可以列出死元組百分比高的表,並執行手動VACUUM來回收死元組佔用的空間。
第2章 pgstattuble 外掛安裝 & 測試
2.1 PG15 & pg_tde編譯
2.1.1 作業系統資訊
我的作業系統是CentOS 8.5,其核心資訊如下
[root@pg-server01 ~]# uname -aLinux pg-server01 4.18.0-348.el8.x86_64 #1 SMP Tue Oct 19 15:14:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux[root@pg-server01 ~]# cat /etc/redhat-release CentOS Linux release 8.5.2111
2.1.2 配置阿里雲 yum 源
# 參考如下連結 # 操作步驟略
https://www.cnblogs.com/hunttown/p/16287988.html
2.1.3 安裝相關依賴
yum -y install gcc readline readline-devel zlib-devel openssl-devel libicu-devel make json-c-devel git wget
2.1.4 下載PG15 原始碼&編譯
mkdir -p /data/software/pg/;cd /data/software/pg/;wget \--prefix=/opt/pgsql \--with-openssl make && make install
2.1.5 編譯 pgstattuble 外掛
cd /data/software/pg/postgresql-15.4/;cd ./contrib/pgstattuple;make && make install
2.1.6 編譯 btree_gin 外掛
cd /data/software/pg/postgresql-15.4/;cd ./contrib/btree_gin;make && make install
2.2 安裝配置PG
2.2.1 新增 postgres使用者並授權
useradd postgres chown -R postgres:postgres /home/postgres;chown -R postgres:postgres /opt/pgsql;
2.2.2 配置環境變數
vi /etc/profile # 新增如下內容# pg envexport PGHOME=/opt/pgsqlexport PATH=$PATH:$PGHOME/bin
2.2.3 初始化資料庫
# 1. 建立相關目錄,並授權
mkdir -p /data/pgdata/;chown postgres:postgres /data/pgdata/;
# 2. 切換到 postgres 使用者
su - postgres
# 3. 初始化資料庫
initdb -D /data/pgdata/ -U postgres --encoding=UTF8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8
# 4. 啟動資料庫
pg_ctl -D /data/pgdata/ start
2.3 測試 pgstattuble 外掛
2.3.1 開啟 pgstattuble 外掛
su - postgrespsqlselect * from pg_available_extensions where name='pgstattuple';create extension pgstattuple;select * from pg_available_extensions where name='pgstattuple'; # 我的操作輸出類似如下:[root@pg-server01 opt]# su - postgres[postgres@pg-server01 ~]$ psqlpsql (15.4)Type "help" for help. postgres=# select * from pg_available_extensions where name='pgstattuple'; name | default_version | installed_version | comment -------------+-----------------+-------------------+----------------------------- pgstattuple | 1.5 | | show tuple-level statistics(1 row) postgres=# create extension pgstattuple;CREATE EXTENSIONpostgres=# select * from pg_available_extensions where name='pgstattuple'; name | default_version | installed_version | comment -------------+-----------------+-------------------+----------------------------- pgstattuple | 1.5 | 1.5 | show tuple-level statistics(1 row) postgres=#
# 注意:預設情況下,只有超級使用者可以訪問 pgstattuple 函式; 但是,您可以透過將 pg_stat_scan_tables 角色授予非超級使用者來授予非超級使用者訪問許可權。
grant pg_stat_scan_tables to <nonsuperuser>;
2.3.2 pgstattuble測試前準備
接下來,在使用 pgstattuple 函式之前,讓我們建立一個表和索引來進行演示。
psqlcreate table workshop (jobno int);insert into workshop values (generate_series(1,80000));create index workshop_index on workshop (jobno);
# 我的操作輸出類似如下:
[postgres@pg-server01 ~]$ psqlpsql (15.4)Type "help" for help.postgres=# create table workshop (jobno int);CREATE TABLEpostgres=# insert into workshop values (generate_series(1,80000));INSERT 0 80000postgres=# create index workshop_index on workshop (jobno);CREATE INDEXpostgres=#
2.3.3 pgstattuple 功能
2.3.3.1 pgstattuple(regclass)
這個函式返回一個關係的物理長度,“死亡”元組的百分比,以及其他資訊。這可以幫助使用者確定是否需要真空。引數是目標關係的名稱(可選的模式限定)或OID。例如:
# 顯示 pg_catalog.pg_proc 表 的元組統計資訊。
SELECT * FROM pgstattuple('pg_catalog.pg_proc');
# 顯示 workshop 表 的元組統計資訊。
SELECT * FROM pgstattuple('workshop');
# 注意: Pgstattuple 只獲得關係上的讀鎖。因此,pgstattuple 輸出並不表示瞬時快照。併發更新將改變 pgstattuple 的輸出。
# 我的操作輸出類似如下:
postgres=# \xExpanded display is on.postgres=# SELECT * FROM pgstattuple('pg_catalog.pg_proc');-[ RECORD 1 ]------+-------table_len | 786432tuple_count | 3253tuple_len | 731586tuple_percent | 93.03dead_tuple_count | 12dead_tuple_len | 4790dead_tuple_percent | 0.61free_space | 22564free_percent | 2.87 postgres=# SELECT * FROM pgstattuple('workshop'); -[ RECORD 1 ]------+--------table_len | 2899968tuple_count | 80000tuple_len | 2240000tuple_percent | 77.24dead_tuple_count | 0dead_tuple_len | 0dead_tuple_percent | 0free_space | 10056free_percent | 0.35
2.3.3.2 下面是pgstattuple輸出列及其說明
-------------------------------------------------------------------| 欄位名 | 型別 | 說明 |-------------------------------------------------------------------| table_len | bigint | 物理關係長度,以位元組為單位 |-------------------------------------------------------------------| tuple_count | bigint | 活動元組的數量 |-------------------------------------------------------------------| tuple_len | bigint | 活元組的總長度(以位元組為單位) |-------------------------------------------------------------------| tuple_percent | float8 | 活元組的百分比 |-------------------------------------------------------------------| dead_tuple_count | bigint | 無效(死)元組的數量 |-------------------------------------------------------------------| dead_tuple_len | bigint | 無效(死)元組的總長度(以位元組為單位) |-------------------------------------------------------------------| dead_tuple_percent | float8 | 無效(死)元組的百分比 |-------------------------------------------------------------------| free_space | bigint | 總可用空間(以位元組為單位) |------------------------------------------------------------------| free_percent | float8 | 可用空間百分比 |------------------------------------------------------------------
2.3.3.3 讓我們在刪除或更新行時檢查元組統計資訊
# 1. 下面是引用的當前元組統計資訊。
postgres=# SELECT * FROM pgstattuple('workshop');-[ RECORD 1 ]------+--------table_len | 2899968tuple_count | 80000tuple_len | 2240000tuple_percent | 77.24dead_tuple_count | 0dead_tuple_len | 0dead_tuple_percent | 0free_space | 10056free_percent | 0.35 # 2. Delete the few ROWS from the table.DELETE FROM workshop WHERE jobno % 8 = 0;
# 3. 在下面的輸出中,dead_tuple_count 顯示 Postgres 將這些行標記為已刪除,但沒有從表中刪除它們,因為刪除這些行後表的長度是相同的。
postgres=# SELECT * FROM pgstattuple('workshop');-[ RECORD 1 ]------+--------table_len | 2899968tuple_count | 70000tuple_len | 1960000tuple_percent | 67.59dead_tuple_count | 10000dead_tuple_len | 280000dead_tuple_percent | 9.66free_space | 10056free_percent | 0.35
2.3.3.4 在表上執行VACUUM命令
# 1. 執行普通VACUUM後,我們看到:
vacuum workshop;SELECT * FROM pgstattuple('workshop');
# 2. 在執行VACUUM FULL命令後,我們可以看到 table_len 減少了。顯示執行VACUUM FULL後,os級空間被回收。
vacuum full workshop;SELECT * FROM pgstattuple('workshop');
# 我的操作輸出類似如下:
postgres=# vacuum workshop;VACUUMpostgres=# SELECT * FROM pgstattuple('workshop');-[ RECORD 1 ]------+--------table_len | 2899968tuple_count | 70000tuple_len | 1960000tuple_percent | 67.59dead_tuple_count | 0dead_tuple_len | 0dead_tuple_percent | 0free_space | 330412free_percent | 11.39 postgres=# vacuum full workshop;VACUUMpostgres=# SELECT * FROM pgstattuple('workshop');-[ RECORD 1 ]------+--------table_len | 2539520tuple_count | 70000tuple_len | 1960000tuple_percent | 77.18dead_tuple_count | 0dead_tuple_len | 0dead_tuple_percent | 0free_space | 10840free_percent | 0.43
2.3.3.5 pgstattuple 查詢檢查表膨脹
# 1. 我們可以使用下面的查詢列出死元組百分比高的表。
## 注意:如果pg16.0 版本的話, 下面查詢語句將報錯 "ERROR: only heap AM is supported",是pg16 的一個 bug,參考:%40alvherre.pgsql
select relname,(pgstattuple(oid)).dead_tuple_percent from pg_class where relkind = 'r' order by dead_tuple_percent desc limit 10;
## 我的查詢輸出類似如下:
postgres=# \xExpanded display is off.postgres=# postgres=# select relname,(pgstattuple(oid)).dead_tuple_percent postgres-# from pg_class postgres-# where relkind = 'r' postgres-# order by dead_tuple_percent desc limit 10; relname | dead_tuple_percent -----------------------+-------------------- pg_init_privs | 2.56 pg_extension | 1.28 pg_class | 0.75 pg_proc | 0.61 pg_depend | 0.47 pg_statistic | 0.36 pg_type | 0.29 pg_attribute | 0.06 pg_user_mapping | 0 pg_statistic_ext_data | 0(10 rows)
# 2. 下面的查詢將向您詳細顯示死元組百分比高的表的元組統計資訊。
SELECT relname, oid, relowner, (pgstattuple(oid)).* FROM pg_class WHERE relkind = 'r' order by dead_tuple_percent desc limit 5;
## 我的操作輸出類似如下:
postgres=# SELECT relname, oid, relowner,postgres-# (pgstattuple(oid)).* postgres-# FROM pg_class postgres-# WHERE relkind = 'r' order by dead_tuple_percent desc limit 5;-[ RECORD 1 ]------+--------------relname | pg_init_privsoid | 3394relowner | 10table_len | 24576tuple_count | 222tuple_len | 17316tuple_percent | 70.46dead_tuple_count | 9dead_tuple_len | 630dead_tuple_percent | 2.56free_space | 4568free_percent | 18.59-[ RECORD 2 ]------+--------------relname | pg_extensionoid | 3079relowner | 10table_len | 8192tuple_count | 2tuple_len | 210tuple_percent | 2.56dead_tuple_count | 1dead_tuple_len | 105dead_tuple_percent | 1.28free_space | 7816free_percent | 95.41-[ RECORD 3 ]------+--------------relname | pg_classoid | 1259relowner | 10table_len | 114688tuple_count | 412tuple_len | 79912tuple_percent | 69.68dead_tuple_count | 5dead_tuple_len | 860dead_tuple_percent | 0.75free_space | 28656free_percent | 24.99-[ RECORD 4 ]------+--------------relname | pg_procoid | 1255relowner | 10table_len | 786432tuple_count | 3253tuple_len | 731586tuple_percent | 93.03dead_tuple_count | 12dead_tuple_len | 4790dead_tuple_percent | 0.61free_space | 22564free_percent | 2.87-[ RECORD 5 ]------+--------------relname | pg_dependoid | 2608relowner | 10table_len | 114688tuple_count | 1854tuple_len | 90846tuple_percent | 79.21dead_tuple_count | 11dead_tuple_len | 539dead_tuple_percent | 0.47free_space | 2288free_percent | 1.99
2.3.4 pgstatindex(regclass)
2.3.4.1 這個函式返回一條顯示b樹索引資訊的記錄
# 1. 例如
SELECT * FROM pgstatindex ('workshop_index');
## 我的查詢輸出類似如下:
postgres=# SELECT * FROM pgstatindex ('workshop_index');-[ RECORD 1 ]------+--------version | 4tree_level | 1index_size | 1589248root_block_no | 3internal_pages | 1leaf_pages | 192empty_pages | 0deleted_pages | 0avg_leaf_density | 89.74leaf_fragmentation | 0
2.3.4.2 輸出列及其描述
-------------------------------------------------------------------| 欄位名 | 型別 | 說明 |-------------------------------------------------------------------| version | integer | b樹版本號 |-------------------------------------------------------------------| tree_level | integer | 根頁面的樹級別 |-------------------------------------------------------------------| index_size | bigint | 總索引大小(以位元組為單位) |-------------------------------------------------------------------| root_block_no | bigint | 根頁面的位置(如果沒有則為零) |-------------------------------------------------------------------| internal_pages | bigint | “內部”(上層)頁面的數量 |-------------------------------------------------------------------| leaf_pages | bigint | 葉子結點的個數 |-------------------------------------------------------------------| empty_pages | bigint | 空頁的個數 |-------------------------------------------------------------------| deleted_pages | bigint | 刪除頁面數 |-------------------------------------------------------------------
2.3.5 pgstatginindex(regclass)
2.3.5.1 這個函式返回一條顯示GIN索引資訊的記錄
GIN索引參考:
https://pganalyze.com/blog/gin-index
# 1. 例1
## 1.1 前期 建表、索引等相關 SQL
-- DROP TABLE IF EXISTS test;CREATE TABLE test ( id bigserial PRIMARY KEY, data jsonb);INSERT INTO test(data) VALUES ('{"field": "value1"}');INSERT INTO test(data) VALUES ('{"field": "value2"}');INSERT INTO test(data) VALUES ('{"other_field": "value42"}');-- CREATE INDEX ON test USING gin(data jsonb_path_ops);CREATE INDEX test_data_gin_idx ON test USING gin(data jsonb_path_ops);EXPLAIN SELECT * FROM test WHERE data @> '{"field": "value1"}';
## 1.2 測量GIN掛起列表開銷和大小
### 1.2.1 首先,我們可以將pgstatginindex函式與類似psql的\watch命令一起使用,以密切關注特定索引
SELECT * FROM pgstatginindex('test_data_gin_idx');
# 我的操作輸出類似如下:
postgres=# SELECT * FROM pgstatginindex('test_data_gin_idx'); version | pending_pages | pending_tuples ---------+---------------+---------------- 2 | 0 | 0(1 row)
## 注意:如果在建立索引的時候不指定”索引名稱”,類似語句 "CREATE INDEX ON test USING gin(data jsonb_path_ops);",需要先執行類似如下查詢,查到其資訊(包括索引名稱),再根據其查詢到的索引名稱,將其傳參到上面查詢(當然:其實如果不指定索引名稱的話,其索引命名也是有規律的)。
select *from pg_indexeswhere tablename = 'test';
### 1.2.2 其次,如果您執行自己的資料庫伺服器,您可以使用 “perf”動態跟蹤點 來測量對 Postgres 中 ginInsertCleanup 函式的呼叫
dnf install perfsudo perf probe -x /opt/pgsql/bin/postgres ginInsertCleanupsudo perf stat -a -e probe_postgres:ginInsertCleanup -- sleep 60
# 2. 例2
## 2.1 前期 建表、索引等相關 SQL
-- DROP TABLE IF EXISTS records;CREATE TABLE records ( id bigserial PRIMARY KEY, customer_id int4, data jsonb); CREATE EXTENSION btree_gin;CREATE INDEX ON records USING gin (data, customer_id); EXPLAIN SELECT * FROM records WHERE customer_id = 123 AND data @> '{ "location": "New York" }';EXPLAIN SELECT * FROM records WHERE customer_id = 123; select *from pg_indexeswhere tablename = 'records';
## 注意:如下查詢中的 "records_data_customer_id_idx" 就是 上面查詢結果的 indexname 欄位值
SELECT * FROM pgstatginindex('records_data_customer_id_idx');
# 3. pganalyze索引顧問中的GIN索引支援
## 注意:在 頁面測試
## 現在,我們已經向pganalyze index Advisor新增了對GIN和GIST索引建議的初始支援
## 下面是一個為現有的tsvector列推薦GIN索引的示例
CREATE TABLE post( id SERIAL PRIMARY KEY, title TEXT NOT NULL, content TEXT NOT NULL, author_id INT NOT NULL, language text NOT NULL DEFAULT('english'), document tsvector); SELECT id, title FROM postWHERE post.document @@ to_tsquery('english', 'Endangered & Species')ORDER BY ts_rank(post.document, to_tsquery('english', 'Endangered & Species')) DESC
2.3.6 pgstathashindex(regclass)
2.3.6.1 參考文件
https://postgrespro.com/blog/pgsql/4161321
2.3.6.2 測試
# 1. 例如
## 1.1 前期準備
### 1.1.1 從 下載 demo-small-en.zip 檔案到 /data/software/pg/demo-data/ 並解壓(解壓後生成 demo-small-en-20170815.sql 檔案
### 1.1.2 postgres 使用者下執行如下命令
cd /data/software/pg/demo-data/;
psql -f demo-small-en-20170815.sql
## 1.2 為 demo 資料庫的 flights 表的 flight_no 欄位 建立 hash 索引 & 相關測試
### 1.2.1 建立 hash 索引
psql
\c demo
create index on flights using hash(flight_no);
### 1.2.2 建立 pgstattuple 擴充套件
#### 因為切換到 demo 資料庫了,所以需要重新建立 pgstattuple 擴充套件
create extension pgstattuple;select * from pg_available_extensions where name='pgstattuple';
### 1.2.3 查詢剛才建立的索引
select *from pg_indexeswhere tablename = 'flights';
### 1.2.4 檢視相關 sql 執行計劃 & 檢視相關索引資訊
explain (costs off) select * from flights where flight_no = 'PG0001';select * from pgstathashindex('flights_flight_no_idx');
# 最後:我的查詢輸出類似如下
demo=# explain (costs off) select * from flights where flight_no = 'PG0001'; QUERY PLAN ---------------------------------------------------- Bitmap Heap Scan on flights Recheck Cond: (flight_no = 'PG0001'::bpchar) -> Bitmap Index Scan on flights_flight_no_idx Index Cond: (flight_no = 'PG0001'::bpchar)(4 rows) demo=# select * from pgstathashindex('flights_flight_no_idx'); version | bucket_pages | overflow_pages | bitmap_pages | unused_pages | live_items | dead_items | free_percent ---------+--------------+----------------+--------------+--------------+------------+------------+------------------- 4 | 128 | 13 | 1 | 0 | 33121 | 0 | 42.36979656038809(1 row)
#PG資料庫工程師的搖籃#PostgreSQL考試#PostgreSQL培訓
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69998444/viewspace-2997910/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 《PostgreSQL》 索引與最佳化SQL索引
- 列表與元組
- Python的元組()與字典{}Python
- Python的元組()與字典 { }Python
- PostgreSQL類微博FEED系統-設計與效能指標SQL指標
- PostgreSQL IO最佳化技巧SQL
- python元組與字典簡介Python
- Scala陣列、元組與集合陣列
- 計算機組成與系統結構 cache 原理與計算計算機
- PostgreSQL DBA(10) - 統計資訊SQL
- 最最佳化與計數
- postgresql配置引數最佳化SQL
- 尤拉計劃715:六元組
- PostgreSQL全文檢索-詞頻統計SQL
- 元組
- mongodb分組統計MongoDB
- 計算機系統4-> 計組與體系結構1 | 基礎概念與系統評估計算機
- 學習高校課程-系統設計與分析-最佳化設計(lec8)
- 構建企業級 Agent 系統:核心元件設計與最佳化元件
- 組合數學與一元n次方
- python 元組與列表的異同點 1125Python
- PostgreSQL 15: stats collector程式最佳化掉了SQL
- 計算機組成原理與作業系統 第二章 處理機組成與管理計算機作業系統
- 資料統計查詢最佳化
- Python 元組Python
- Python之列表與元組的區別詳解Python
- PostgreSQL Array 陣列型別與 FreeSql 打出一套【組合拳】SQL陣列型別
- 《設計模式四》觀察、組合、享元模式設計模式
- 微機原理與系統設計筆記4 | 組合語言程式設計與其他指令筆記組合語言程式設計
- 元宇宙系統概述與介紹元宇宙
- postgresql不考慮可見性、讀取髒頁、物理頁、被刪除/更新的元組SQL
- PostgreSQL統計資訊的幾個重要檢視SQL
- 智慧化升級:APS排產系統引領人員與裝置成本最佳化新紀元
- 世貿組織:2024年全球貿易展望與統計報告
- 小組貢獻統計表
- Python基礎:資料型別-列表與元組(6)Python資料型別
- Python學習筆記 5.0 元組 與 字典 與 集合 與 公共操作 與 推導式Python筆記
- 如何將PostgreSQL查詢最佳化100倍 - VadimSQL