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