Add tuning_psql.sh
This commit is contained in:
commit
842d083b5c
292
tuning_psql.sh
Normal file
292
tuning_psql.sh
Normal file
@ -0,0 +1,292 @@
|
||||
#!/usr/bin/env bash
|
||||
# Autor: Mattia Tadini
|
||||
# File: autoscale_cluster.sh
|
||||
# Version: 1.00
|
||||
# Date 14/11/2025
|
||||
# - Supporto PG 14 / 15 / 16
|
||||
# - Profili: PRIVATO (100), SHARED (10000), ANALYTICS (10000)
|
||||
|
||||
set -Ee # niente -u per evitare abort su variabili non settate
|
||||
|
||||
# --- Guard rail: esecuzione come root
|
||||
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||
echo "Errore: lo script deve essere eseguito come root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
show_error() {
|
||||
echo "ERRORE: $1" >&2
|
||||
if command -v whiptail >/dev/null 2>&1; then
|
||||
whiptail --title "Errore Critico" --msgbox "$1" 20 74
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
show_msg() {
|
||||
if command -v whiptail >/dev/null 2>&1; then
|
||||
whiptail --title "$1" --msgbox "$2" 20 74
|
||||
else
|
||||
echo -e "\n[$1]\n$2\n"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Rilevamento risorse
|
||||
detect_ram_mb() {
|
||||
if [ -r /proc/meminfo ]; then
|
||||
awk '/MemTotal:/ { printf "%d\n", $2/1024 }' /proc/meminfo
|
||||
else
|
||||
free -m | awk '/Mem:/ {print $2}'
|
||||
fi
|
||||
}
|
||||
|
||||
detect_cpu_cores() {
|
||||
if command -v nproc >/dev/null 2>&1; then
|
||||
nproc
|
||||
else
|
||||
getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Selezione versione PostgreSQL tra quelle presenti
|
||||
get_postgres_version() {
|
||||
local available=() v sel
|
||||
for v in 14 15 16; do
|
||||
if [ -d "/etc/postgresql/${v}/main" ]; then
|
||||
available+=("$v")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${#available[@]}" -eq 0 ]; then
|
||||
show_error "Nessuna installazione trovata in /etc/postgresql/{14,15,16}/main"
|
||||
fi
|
||||
|
||||
if command -v whiptail >/dev/null 2>&1; then
|
||||
local menu_args=()
|
||||
for v in "${available[@]}"; do
|
||||
case "$v" in
|
||||
14) menu_args+=("14" "PostgreSQL 14 (profilo Zucchetti)") ;;
|
||||
15) menu_args+=("15" "PostgreSQL 15") ;;
|
||||
16) menu_args+=("16" "PostgreSQL 16 (profilo Zucchetti)") ;;
|
||||
esac
|
||||
done
|
||||
sel=$(whiptail --title "Versione PostgreSQL" --menu \
|
||||
"Seleziona la versione DA TUNARE (già installata):" 20 74 10 \
|
||||
"${menu_args[@]}" 3>&1 1>&2 2>&3) || show_error "Operazione annullata."
|
||||
echo "$sel"
|
||||
else
|
||||
echo "Installazioni trovate:"
|
||||
printf ' - %s\n' "${available[@]}"
|
||||
read -r -p "Versione da tunare [${available[*]}]: " v
|
||||
case "$v" in 14|15|16) echo "$v" ;; *) show_error "Versione non valida" ;; esac
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Scelta profilo max_connections
|
||||
get_connection_profile() {
|
||||
local p sel
|
||||
if command -v whiptail >/dev/null 2>&1; then
|
||||
sel=$(whiptail --title "Profilo Connessioni" --menu \
|
||||
"Scegli il profilo server DB:\n- PRIVATO: singolo DB, poche connessioni\n- SHARED: multi-tenant, molte connessioni\n- ANALYTICS: carichi analitici pesanti (molta parallelizzazione)" \
|
||||
20 74 12 \
|
||||
"PRIVATO" "max_connections = 100 (conservativo)" \
|
||||
"SHARED" "max_connections = 10000 (multi-tenant)" \
|
||||
"ANALYTICS" "max_connections = 10000 + parallelismo aggressivo" 3>&1 1>&2 2>&3) || show_error "Operazione annullata."
|
||||
p="$sel"
|
||||
else
|
||||
echo "Profili: PRIVATO (100), SHARED (10000), ANALYTICS (10000)"
|
||||
read -r -p "Profilo [PRIVATO/SHARED/ANALYTICS]: " p
|
||||
fi
|
||||
case "$p" in PRIVATO|SHARED|ANALYTICS) echo "$p" ;; *) show_error "Profilo non valido" ;; esac
|
||||
}
|
||||
|
||||
# --- Calcolo parametri
|
||||
compute_tuning() {
|
||||
local RAM_MB="$1" MAX_CONN="$2" CORES="$3" PROFILE="$4"
|
||||
|
||||
local sb=$(( RAM_MB / 4 ))
|
||||
[ "$sb" -lt 512 ] && sb=512
|
||||
[ "$sb" -gt 65536 ] && sb=65536
|
||||
SHARED_BUFFERS_MB=$sb
|
||||
|
||||
local ecs=$(( (RAM_MB * 70) / 100 ))
|
||||
[ "$ecs" -lt 512 ] && ecs=512
|
||||
EFFECTIVE_CACHE_SIZE_MB=$ecs
|
||||
|
||||
local mwm=$(( (RAM_MB * 5) / 100 ))
|
||||
[ "$mwm" -lt 64 ] && mwm=64
|
||||
[ "$mwm" -gt 4096 ] && mwm=4096
|
||||
MAINTENANCE_WORK_MEM_MB=$mwm
|
||||
|
||||
local wm=$(( (RAM_MB / 4) / (MAX_CONN * 2) ))
|
||||
[ "$wm" -lt 1 ] && wm=1
|
||||
local wm_cap=64
|
||||
case "$PROFILE" in
|
||||
SHARED) wm_cap=8 ;;
|
||||
ANALYTICS) wm_cap=128 ;;
|
||||
esac
|
||||
[ "$wm" -gt "$wm_cap" ] && wm="$wm_cap"
|
||||
WORK_MEM_MB=$wm
|
||||
|
||||
local avw=3
|
||||
|
||||
if [ "$PROFILE" = "ANALYTICS" ]; then
|
||||
local mwp=$(( CORES * 3 ))
|
||||
[ "$mwp" -lt 12 ] && mwp=12
|
||||
[ "$mwp" -gt 96 ] && mwp=96
|
||||
MAX_WORKER_PROCESSES=$mwp
|
||||
|
||||
local mpw=$(( mwp - avw - 2 ))
|
||||
local mpw_alt=$(( CORES * 3 ))
|
||||
[ "$mpw" -gt "$mpw_alt" ] && mpw=$mpw_alt
|
||||
[ "$mpw" -lt 4 ] && mpw=4
|
||||
MAX_PARALLEL_WORKERS=$mpw
|
||||
|
||||
local mpwpg=$(( CORES / 2 ))
|
||||
[ "$mpwpg" -lt 3 ] && mpwpg=3
|
||||
[ "$mpwpg" -gt 6 ] && mpwpg=6
|
||||
MAX_PARALLEL_WORKERS_PER_GATHER=$mpwpg
|
||||
|
||||
local mpmw=$(( CORES / 2 ))
|
||||
[ "$mpmw" -lt 3 ] && mpmw=3
|
||||
[ "$mpmw" -gt 6 ] && mpmw=6
|
||||
MAX_PARALLEL_MAINT_WORKERS=$mpmw
|
||||
|
||||
PARALLEL_SETUP_COST="200"
|
||||
PARALLEL_TUPLE_COST="0.06"
|
||||
MIN_PARALLEL_TABLE_SCAN_SIZE="'16MB'"
|
||||
MIN_PARALLEL_INDEX_SCAN_SIZE="'32MB'"
|
||||
DEFAULT_STATISTICS_TARGET="2000"
|
||||
else
|
||||
local mwp=$(( CORES * 2 ))
|
||||
[ "$mwp" -lt 8 ] && mwp=8
|
||||
[ "$mwp" -gt 64 ] && mwp=64
|
||||
MAX_WORKER_PROCESSES=$mwp
|
||||
|
||||
local mpw=$(( mwp - avw - 2 ))
|
||||
local mpw_alt=$(( CORES * 2 ))
|
||||
[ "$mpw" -gt "$mpw_alt" ] && mpw=$mpw_alt
|
||||
[ "$mpw" -lt 2 ] && mpw=2
|
||||
MAX_PARALLEL_WORKERS=$mpw
|
||||
|
||||
local mpwpg=$(( CORES / 2 ))
|
||||
[ "$mpwpg" -lt 2 ] && mpwpg=2
|
||||
[ "$mpwpg" -gt 4 ] && mpwpg=4
|
||||
MAX_PARALLEL_WORKERS_PER_GATHER=$mpwpg
|
||||
|
||||
local mpmw=$(( CORES / 2 ))
|
||||
[ "$mpmw" -lt 2 ] && mpmw=2
|
||||
[ "$mpmw" -gt 4 ] && mpmw=4
|
||||
MAX_PARALLEL_MAINT_WORKERS=$mpmw
|
||||
|
||||
PARALLEL_SETUP_COST="500"
|
||||
PARALLEL_TUPLE_COST="0.08"
|
||||
MIN_PARALLEL_TABLE_SCAN_SIZE="'32MB'"
|
||||
MIN_PARALLEL_INDEX_SCAN_SIZE="'64MB'"
|
||||
DEFAULT_STATISTICS_TARGET="1000"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
PG_VERSION=$(get_postgres_version)
|
||||
PROFILE=$(get_connection_profile)
|
||||
|
||||
case "$PROFILE" in
|
||||
PRIVATO) MAX_CONN=100 ;;
|
||||
SHARED) MAX_CONN=10000 ;;
|
||||
ANALYTICS) MAX_CONN=10000 ;;
|
||||
esac
|
||||
|
||||
RAM_MB=$(detect_ram_mb)
|
||||
CPU_CORES=$(detect_cpu_cores)
|
||||
[ "$RAM_MB" -lt 1024 ] && RAM_MB=1024
|
||||
[ "$CPU_CORES" -lt 1 ] && CPU_CORES=1
|
||||
|
||||
CONF_DIR="/etc/postgresql/${PG_VERSION}/main"
|
||||
POSTGRESQL_CONF="${CONF_DIR}/postgresql.conf"
|
||||
[ -f "$POSTGRESQL_CONF" ] || show_error "File ${POSTGRESQL_CONF} non trovato."
|
||||
|
||||
compute_tuning "$RAM_MB" "$MAX_CONN" "$CPU_CORES" "$PROFILE"
|
||||
|
||||
# include_if_exists sempre
|
||||
if ! grep -Eiq "^[[:space:]]*include_if_exists[[:space:]]*=.*zucchetti\.conf" "$POSTGRESQL_CONF"; then
|
||||
printf "\ninclude_if_exists = 'zucchetti.conf'\n" >> "$POSTGRESQL_CONF"
|
||||
fi
|
||||
|
||||
ZUCC_CONF="${CONF_DIR}/zucchetti.conf"
|
||||
echo "Scrivo ${ZUCC_CONF}..."
|
||||
|
||||
cat > "$ZUCC_CONF" <<EOF
|
||||
# =========================
|
||||
# Tuning Zucchetti (PG${PG_VERSION})
|
||||
# Profilo: ${PROFILE} | max_connections=${MAX_CONN}
|
||||
# RAM totale: ${RAM_MB}MB | CPU cores: ${CPU_CORES}
|
||||
# =========================
|
||||
|
||||
# Connessioni
|
||||
max_connections = ${MAX_CONN}
|
||||
|
||||
# Memory & cache
|
||||
shared_buffers = ${SHARED_BUFFERS_MB}MB
|
||||
effective_cache_size = ${EFFECTIVE_CACHE_SIZE_MB}MB
|
||||
maintenance_work_mem = ${MAINTENANCE_WORK_MEM_MB}MB
|
||||
work_mem = ${WORK_MEM_MB}MB
|
||||
|
||||
# Checkpoint & WAL
|
||||
checkpoint_completion_target = 0.9
|
||||
wal_buffers = -1
|
||||
min_wal_size = 1GB
|
||||
max_wal_size = 4GB
|
||||
|
||||
# Statistiche & IO
|
||||
default_statistics_target = ${DEFAULT_STATISTICS_TARGET}
|
||||
random_page_cost = 1.1
|
||||
effective_io_concurrency = 200
|
||||
|
||||
# Lock/strings
|
||||
max_locks_per_transaction = 1024
|
||||
escape_string_warning = on
|
||||
standard_conforming_strings = off
|
||||
|
||||
# Logging/rotation
|
||||
log_destination = 'stderr'
|
||||
logging_collector = on
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 100MB
|
||||
|
||||
# Autovacuum (valori ragionevoli per ERP)
|
||||
autovacuum = on
|
||||
autovacuum_analyze_scale_factor = 0.1
|
||||
autovacuum_analyze_threshold = 500
|
||||
autovacuum_freeze_max_age = 200000000
|
||||
autovacuum_max_workers = 3
|
||||
autovacuum_multixact_freeze_max_age = 400000000
|
||||
autovacuum_naptime = 5s
|
||||
autovacuum_vacuum_cost_delay = 20ms
|
||||
autovacuum_vacuum_cost_limit = -1
|
||||
autovacuum_vacuum_scale_factor = 0.2
|
||||
autovacuum_vacuum_threshold = 1500
|
||||
autovacuum_work_mem = -1
|
||||
|
||||
# ===== Parallel Query =====
|
||||
max_worker_processes = ${MAX_WORKER_PROCESSES}
|
||||
max_parallel_workers = ${MAX_PARALLEL_WORKERS}
|
||||
max_parallel_workers_per_gather = ${MAX_PARALLEL_WORKERS_PER_GATHER}
|
||||
max_parallel_maintenance_workers = ${MAX_PARALLEL_MAINT_WORKERS}
|
||||
|
||||
# Soglie/costi del planner per parallelismo
|
||||
parallel_setup_cost = ${PARALLEL_SETUP_COST}
|
||||
parallel_tuple_cost = ${PARALLEL_TUPLE_COST}
|
||||
min_parallel_table_scan_size = ${MIN_PARALLEL_TABLE_SCAN_SIZE}
|
||||
min_parallel_index_scan_size = ${MIN_PARALLEL_INDEX_SCAN_SIZE}
|
||||
EOF
|
||||
|
||||
chown postgres:postgres "$ZUCC_CONF"
|
||||
chmod 640 "$ZUCC_CONF"
|
||||
|
||||
systemctl restart "postgresql@${PG_VERSION}-main" || show_error "Riavvio postgresql@${PG_VERSION}-main fallito."
|
||||
|
||||
show_msg "Completato" "Tuning completato per PostgreSQL ${PG_VERSION} (profilo ${PROFILE}).\nFile: ${ZUCC_CONF}"
|
||||
}
|
||||
|
||||
main
|
||||
Loading…
Reference in New Issue
Block a user