Performance Tuning
Performance tuning is a systematic process: first identify the bottleneck, then optimize accordingly. Blindly adjusting parameters often does more harm than good. This article covers common tuning techniques and their applicable scenarios, helping you improve server performance step by step from kernel parameters to the application layer.
Pre-Tuning Preparation
Section titled “Pre-Tuning Preparation”Establishing a Baseline
Section titled “Establishing a Baseline”Before tuning, you must record the current performance baseline; otherwise, there is no way to measure the effect of optimizations.
# Record basic system informationuname -acat /etc/os-releaselscpufree -hlsblk
# Install performance analysis toolssudo dnf install sysstat perf iotop htop -y
# Enable sysstat data collectionsudo systemctl enable --now sysstat
# View historical performance datasar -u 1 5 # CPU usagesar -r 1 5 # Memory usagesar -d 1 5 # Disk I/Osar -n DEV 1 5 # Network throughputIdentifying the Bottleneck
Section titled “Identifying the Bottleneck”# Quickly determine the bottleneck type# CPU bottleneck: load average consistently higher than the number of CPU coresuptime
# Memory bottleneck: low available memory with active swapfree -hvmstat 1 5
# I/O bottleneck: high iowaitiostat -xz 1 5
# Network bottleneck: packet drops or saturated bandwidthss -ssar -n DEV 1 5Kernel Parameter Tuning (sysctl)
Section titled “Kernel Parameter Tuning (sysctl)”sysctl is used to modify kernel parameters at runtime.
Viewing and Modifying Parameters
Section titled “Viewing and Modifying Parameters”# View all parameterssysctl -a
# View a specific parametersysctl net.core.somaxconnsysctl vm.swappiness
# Temporary change (lost after reboot)sudo sysctl -w net.core.somaxconn=65535
# Permanent changesudo tee /etc/sysctl.d/99-tuning.conf > /dev/null <<'EOF'# Custom kernel parameter tuningEOF
# Apply the configurationsudo sysctl --systemNetwork Parameter Tuning
Section titled “Network Parameter Tuning”sudo tee /etc/sysctl.d/99-network.conf > /dev/null <<'EOF'# ==============================# Network Performance Tuning# ==============================
# --- TCP Buffers ---# Default and maximum TCP send/receive buffers (bytes)# Format: min default maxnet.core.rmem_default = 262144net.core.rmem_max = 16777216net.core.wmem_default = 262144net.core.wmem_max = 16777216net.ipv4.tcp_rmem = 4096 87380 16777216net.ipv4.tcp_wmem = 4096 65536 16777216
# --- Connection Queues ---# Maximum listen queue length (affects connection establishment under high concurrency)net.core.somaxconn = 65535
# Half-open connection queue sizenet.ipv4.tcp_max_syn_backlog = 65535
# NIC receive queue lengthnet.core.netdev_max_backlog = 65535
# --- TCP Connection Optimization ---# Enable TCP Fast Open (reduces handshake latency)net.ipv4.tcp_fastopen = 3
# TIME_WAIT relatednet.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_fin_timeout = 15net.ipv4.tcp_max_tw_buckets = 262144
# Keepalive parametersnet.ipv4.tcp_keepalive_time = 600net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 5
# --- Congestion Control ---# Use the BBR congestion control algorithm (recommended)net.core.default_qdisc = fqnet.ipv4.tcp_congestion_control = bbr
# --- Local Port Range ---net.ipv4.ip_local_port_range = 1024 65535
# --- Other ---# Allow more orphan socketsnet.ipv4.tcp_max_orphans = 262144
# SYN Flood protectionnet.ipv4.tcp_syncookies = 1EOF
sudo sysctl --systemVerify that BBR is active:
sysctl net.ipv4.tcp_congestion_control# Output should be: net.ipv4.tcp_congestion_control = bbr
lsmod | grep bbrMemory Parameter Tuning
Section titled “Memory Parameter Tuning”sudo tee /etc/sysctl.d/99-memory.conf > /dev/null <<'EOF'# ==============================# Memory Tuning# ==============================
# Swappiness: controls the kernel's tendency to swap memory pages to swap# 0 = avoid swap as much as possible (suitable for memory-rich database servers)# 10 = low swap tendency (recommended for most server scenarios)# 60 = default valuevm.swappiness = 10
# Dirty page flush policy# Background flush starts when dirty pages exceed this percentage of memoryvm.dirty_background_ratio = 5# Foreground processes must wait for flush when dirty pages exceed this ratiovm.dirty_ratio = 15# Dirty page lifetime (centiseconds, 500 = 5 seconds)vm.dirty_expire_centisecs = 500# Flush thread wakeup interval (centiseconds)vm.dirty_writeback_centisecs = 100
# VFS cache pressure (default 100)# Below 100 = prefer keeping dentry/inode cache# Above 100 = prefer reclaiming cachevm.vfs_cache_pressure = 50
# Behavior when memory is exhausted# 0 = heuristic OOM (default)# 1 = allow overcommit# 2 = disallow overcommitvm.overcommit_memory = 0EOF
sudo sysctl --systemHuge Pages
Section titled “Huge Pages”Huge pages reduce TLB misses and improve performance for memory-intensive applications (such as databases).
# View current huge page configurationgrep -i huge /proc/meminfo
# Calculate the number of huge pages needed# For example, to allocate 4GB for the database: 4096MB / 2MB (default huge page size) = 2048 pagesecho "Huge pages needed: $((4096 / 2))"
# Set the number of huge pagessudo sysctl -w vm.nr_hugepages=2048
# Permanent configurationecho 'vm.nr_hugepages = 2048' | sudo tee -a /etc/sysctl.d/99-memory.confsudo sysctl --system
# Verifygrep -i huge /proc/meminfo# HugePages_Total: 2048# HugePages_Free: 2048# Hugepagesize: 2048 kBTransparent Huge Pages (THP)
Section titled “Transparent Huge Pages (THP)”THP is managed automatically by the kernel. It is beneficial for most server applications, but some databases (such as MongoDB and Redis) recommend disabling THP to avoid latency jitter.
# Check current statuscat /sys/kernel/mm/transparent_hugepage/enabled
# Temporarily disableecho never | sudo tee /sys/kernel/mm/transparent_hugepage/enabledecho never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
# Permanently disable (via systemd)sudo tee /etc/systemd/system/disable-thp.service > /dev/null <<'EOF'[Unit]Description=Disable Transparent Huge PagesDefaultDependencies=noAfter=sysinit.target local-fs.targetBefore=basic.target
[Service]Type=oneshotExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled'ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/defrag'
[Install]WantedBy=basic.targetEOF
sudo systemctl daemon-reloadsudo systemctl enable disable-thpulimit Resource Limits
Section titled “ulimit Resource Limits”ulimit controls the maximum system resources available to user processes. Default values are typically conservative and may need adjusting for high-concurrency scenarios.
Viewing Current Limits
Section titled “Viewing Current Limits”# View all limits for the current userulimit -a
# Key parameters:# -n Maximum open files (open files)# -u Maximum processes (max user processes)# -l Maximum locked memory (max locked memory)Modifying Limits
Section titled “Modifying Limits”# Modify system-level limitssudo tee /etc/security/limits.d/99-tuning.conf > /dev/null <<'EOF'# Format: <domain> <type> <item> <value>
# All users* soft nofile 65535* hard nofile 131072* soft nproc 65535* hard nproc 131072
# Specific user (e.g., nginx)nginx soft nofile 131072nginx hard nofile 262144
# Root userroot soft nofile 131072root hard nofile 262144EOFFor systemd-managed services, you also need to set limits in the service file:
# Method 1: Modify systemd default limitssudo mkdir -p /etc/systemd/system.conf.dsudo tee /etc/systemd/system.conf.d/limits.conf > /dev/null <<'EOF'[Manager]DefaultLimitNOFILE=131072DefaultLimitNPROC=65535EOF
# Method 2: Set limits for a specific service (recommended)sudo systemctl edit nginx# Add in the editor:# [Service]# LimitNOFILE=131072# LimitNPROC=65535
sudo systemctl daemon-reloadsudo systemctl restart nginx
# Verify the actual limits for the servicecat /proc/$(pidof nginx | awk '{print $1}')/limitstuned Tuning Profiles
Section titled “tuned Tuning Profiles”tuned is a dynamic tuning daemon provided by RHEL-based distributions, with multiple built-in predefined tuning profiles.
Installation and Usage
Section titled “Installation and Usage”sudo dnf install tuned -ysudo systemctl enable --now tuned
# List all available tuning profilestuned-adm list
# Common profiles:# throughput-performance - High throughput optimization# latency-performance - Low latency optimization# network-latency - Network low latency# network-throughput - Network high throughput# virtual-guest - Virtual machine guest# virtual-host - Virtualization host# postgresql - PostgreSQL optimization
# View current profiletuned-adm active
# Switch profilessudo tuned-adm profile throughput-performance
# Get the recommended profiletuned-adm recommend
# View profile detailstuned-adm profile_info throughput-performanceCustom tuned Profile
Section titled “Custom tuned Profile”# Create a custom profile based on an existing onesudo mkdir -p /etc/tuned/my-web-server
sudo tee /etc/tuned/my-web-server/tuned.conf > /dev/null <<'EOF'[main]summary=Custom tuning for web serverinclude=throughput-performance
[sysctl]net.core.somaxconn = 65535net.ipv4.tcp_max_syn_backlog = 65535net.ipv4.tcp_tw_reuse = 1net.core.default_qdisc = fqnet.ipv4.tcp_congestion_control = bbrvm.swappiness = 10
[vm]transparent_hugepages = never
[disk]readahead = 4096EOF
# Apply the custom profilesudo tuned-adm profile my-web-serversudo tuned-adm activeI/O Scheduler
Section titled “I/O Scheduler”The I/O scheduler determines how disk read/write requests are ordered and merged.
Viewing and Modifying
Section titled “Viewing and Modifying”# View the current I/O schedulercat /sys/block/sda/queue/scheduler# Example output: [mq-deadline] kyber bfq none
# Temporary changeecho "kyber" | sudo tee /sys/block/sda/queue/scheduler
# Permanent change (via udev rules)sudo tee /etc/udev/rules.d/60-io-scheduler.rules > /dev/null <<'EOF'# SSD: use none (noop) or mq-deadlineACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
# HDD: use bfqACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"EOFScheduler Selection Guide
Section titled “Scheduler Selection Guide”| Scheduler | Suitable Scenario |
|---|---|
none (noop) | NVMe SSDs, virtual machines (host already handles scheduling) |
mq-deadline | General SSDs, database scenarios (guarantees deadlines) |
bfq | HDDs, desktop/interactive scenarios (fair scheduling) |
kyber | High-speed SSDs, high throughput scenarios |
I/O Read-Ahead Tuning
Section titled “I/O Read-Ahead Tuning”# View the current read-ahead value (in 512-byte sectors)cat /sys/block/sda/queue/read_ahead_kb
# For sequential read-intensive scenarios, increase read-aheadecho 2048 | sudo tee /sys/block/sda/queue/read_ahead_kb
# For random read-intensive scenarios (e.g., databases), reduce read-aheadecho 128 | sudo tee /sys/block/sda/queue/read_ahead_kbAdvanced Network Performance Tuning
Section titled “Advanced Network Performance Tuning”Interrupt Affinity
Section titled “Interrupt Affinity”Bind NIC interrupts to specific CPU cores to reduce context switching:
# View NIC interrupt distributioncat /proc/interrupts | grep eth0
# Automatic setup (using the irqbalance service)sudo systemctl enable --now irqbalance
# Or manually bind (for multi-queue NICs)# View NIC queuesls /sys/class/net/eth0/queues/
# Installing the tuned network plugin can auto-optimize thisNIC Parameter Tuning
Section titled “NIC Parameter Tuning”# Install ethtoolsudo dnf install ethtool -y
# View NIC informationethtool eth0
# View and modify ring buffer sizeethtool -g eth0 # Viewsudo ethtool -G eth0 rx 4096 tx 4096 # Modify
# View and enable NIC featuresethtool -k eth0 # Viewsudo ethtool -K eth0 tso on # Enable TCP Segmentation Offloadsudo ethtool -K eth0 gro on # Enable Generic Receive Offloadsudo ethtool -K eth0 gso on # Enable Generic Segmentation OffloadConnection Tracking Table Optimization
Section titled “Connection Tracking Table Optimization”High-concurrency proxy/NAT servers need a larger connection tracking table:
sudo tee /etc/sysctl.d/99-conntrack.conf > /dev/null <<'EOF'# Maximum connection tracking table entriesnet.netfilter.nf_conntrack_max = 1048576
# Connection tracking hash table bucket size# Typically set to 1/4 of nf_conntrack_maxnet.netfilter.nf_conntrack_buckets = 262144
# Shorten timeout for various statesnet.netfilter.nf_conntrack_tcp_timeout_established = 3600net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30net.netfilter.nf_conntrack_tcp_timeout_close_wait = 15net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30EOF
sudo sysctl --systemFile System Tuning
Section titled “File System Tuning”XFS Tuning
Section titled “XFS Tuning”# View XFS mount optionsmount | grep xfs
# Optimize mount options (edit /etc/fstab)# noatime - Don't update access time, reduces writes# nodiratime - Don't update directory access time# logbufs=8 - Increase log buffer count# Example:# /dev/sda1 /data xfs defaults,noatime,nodiratime,logbufs=8 0 0
# Remount to apply without rebootingsudo mount -o remount,noatime,nodiratime /dataext4 Tuning
Section titled “ext4 Tuning”# Optimize mount options# noatime - Don't update access time# commit=60 - Data commit interval (seconds); increasing improves performance but increases data loss risk# Example:# /dev/sdb1 /data ext4 defaults,noatime,commit=60 0 2
sudo mount -o remount,noatime /dataPerformance Tuning Check Script
Section titled “Performance Tuning Check Script”#!/bin/bash# Check current system performance parameter configuration
echo "============================================"echo " Performance Parameter Check - $(date)"echo "============================================"echo ""
echo "--- CPU ---"echo "Cores: $(nproc)"echo "Current Load: $(uptime | awk -F'load average:' '{print $2}')"echo "Tuned Profile: $(tuned-adm active 2>/dev/null || echo 'tuned not installed')"echo ""
echo "--- Memory ---"free -hecho "swappiness: $(sysctl -n vm.swappiness)"echo "dirty_ratio: $(sysctl -n vm.dirty_ratio)"echo "dirty_background_ratio: $(sysctl -n vm.dirty_background_ratio)"echo "THP: $(cat /sys/kernel/mm/transparent_hugepage/enabled)"echo "HugePages: $(grep HugePages_Total /proc/meminfo)"echo ""
echo "--- Network ---"echo "somaxconn: $(sysctl -n net.core.somaxconn)"echo "tcp_max_syn_backlog: $(sysctl -n net.ipv4.tcp_max_syn_backlog)"echo "tcp_congestion: $(sysctl -n net.ipv4.tcp_congestion_control)"echo "ip_local_port_range: $(sysctl -n net.ipv4.ip_local_port_range)"echo "tcp_tw_reuse: $(sysctl -n net.ipv4.tcp_tw_reuse)"echo ""
echo "--- File Descriptors ---"echo "System limit: $(sysctl -n fs.file-max)"echo "Current usage: $(cat /proc/sys/fs/file-nr)"echo "ulimit -n (current user): $(ulimit -n)"echo ""
echo "--- I/O ---"for disk in $(lsblk -dn -o NAME); do scheduler=$(cat /sys/block/$disk/queue/scheduler 2>/dev/null) readahead=$(cat /sys/block/$disk/queue/read_ahead_kb 2>/dev/null) rotational=$(cat /sys/block/$disk/queue/rotational 2>/dev/null) type="HDD" [ "$rotational" = "0" ] && type="SSD" echo "${disk} (${type}): scheduler=${scheduler}, readahead=${readahead}KB"doneecho ""
echo "--- Connection Tracking ---"if [ -f /proc/sys/net/netfilter/nf_conntrack_max ]; then echo "Max connections: $(sysctl -n net.netfilter.nf_conntrack_max)" echo "Current connections: $(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null || echo N/A)"else echo "nf_conntrack module not loaded"fichmod +x /usr/local/bin/perf_check.shTuning Principles
Section titled “Tuning Principles”- Measure before optimizing — Use
sar,vmstat,iostat,perfto identify the real bottleneck - Change one parameter at a time — This is the only way to confirm which change produced the effect
- Record every change — Makes it easy to roll back and review
- Validate with load testing — Use tools like
ab,wrk,fio,sysbenchto verify optimization results - Watch for side effects — Optimizing one parameter may introduce issues in other areas
# Common benchmarking toolssudo dnf install httpd-tools -y # Provides ab# ab -n 10000 -c 100 http://localhost/
# fio disk performance testsudo dnf install fio -yfio --name=randread --ioengine=libaio --rw=randread --bs=4k \ --size=1G --numjobs=4 --runtime=60 --group_reporting
# sysbench CPU testsudo dnf install sysbench -ysysbench cpu --threads=$(nproc) run