WordPress 效能最佳化:Apache Prefork 與 Event MPM 實測比較

前言

最近在接手一個舊的 WordPress 專案後,發現網站在使用上有些效能瓶頸。由於原本的專案是使用 Apache 作為 Web Server,為了確保最高的相容性,決定延續使用 Apache 架構,調整設定達到最大的效能

這篇文章記錄了過程中透過官方提供的 Docker 映像檔,測試 Apache 的 PreforkEvent 模式在 WordPress 上的表現,了解如何選擇適合的 Apache 設定

什麼是 Apache MPM?

Apache MPM 決定了 Web Server 如何處理多個使用者的連線請求,以下是兩種主要模式的差異:

Prefork MPM

  • 每個請求會產生一個獨立的 Apache 子行程(process)處理
  • 設定簡單
  • 穩定性與相容性高,但記憶體消耗較大、擴展性較差

適合:較小流量的 WordPress 網站或舊專案相容性需求高的情況

Event MPM

  • 採用非同步事件驅動(event-driven)模式處理請求
  • 使用少量執行緒同時處理多個連線
  • 效能高、記憶體使用效率佳,需搭配 PHP-FPM 使用
  • 設定較複雜,需拆分 Apache 與 PHP 處理邏輯

適合:中大型網站或追求高效能的 WordPress 部署

如何在 Docker 中切換與使用?

1. 使用 Prefork 的方式

Docker 官方映像:wordpress:6-php8.4
這個映像檔內建 Apache + PHP Module,預設使用 Prefork 模式。

1
2
3
4
5
6
7
8
docker-compose.yml:
wordpress-prefork:
image: wordpress:6-php8.4
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
...

設定上較簡單,只要用這個映像檔即可開始使用。

2. 使用 Event 的方式

Docker 官方映像需要調整為:

  • wordpress:6-php8.4-fpm(PHP-FPM)
  • httpd:2.4(Apache Event)

需自訂 Apache 設定檔,啟用 mpm_event_module 並設定 proxy:fcgi:// 將 PHP 請求轉送給 PHP-FPM。

1
2
3
4
5
6
7
docker-compose.yml:
wordpress-event:
image: wordpress:6-php8.4-fpm

apache-event:
image: httpd:2.4
...

這樣的架構效能高,但需要較多設定。

Prefork vs Event 效能比較(實測)

  • Apple Macbook M1 Pro
  • 透過 k6 壓力測試工具模擬 20 位使用者同時瀏覽網站,測試時間為 30 秒。
項目 Prefork Event
總請求數 1676 3241
平均回應時間 358.94 毫秒 185.7 毫秒
每秒請求數 55.59 req/s 107.36 req/s

結果:Event MPM 效能是 Prefork 的兩倍,回應速度也快一倍以上

小結

  • Prefork 適合穩定、安全、設定簡單的情況
  • Event MPM 搭配 PHP-FPM 適合追求效能情況
  • Docker 官方映像檔都有提供支援,選擇哪一種主要看對於專案需求與技術熟悉程度

Reference

補充

  • 完整 docker-compose.yaml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    services:
    # Apache prefork MPM with PHP module
    wordpress-prefork:
    image: wordpress:6-php8.4
    cpus: 2
    container_name: wordpress-prefork
    environment:
    WORDPRESS_DB_HOST: db-prefork
    WORDPRESS_DB_USER: wordpress
    WORDPRESS_DB_PASSWORD: wordpress
    WORDPRESS_DB_NAME: wordpress
    volumes:
    - wp_prefork_data:/var/www/html
    ports:
    - "8080:80"
    depends_on:
    - db-prefork

    # Database for prefork setup
    db-prefork:
    image: mysql:8.0
    container_name: mysql-prefork
    environment:
    MYSQL_ROOT_PASSWORD: rootpassword
    MYSQL_DATABASE: wordpress
    MYSQL_USER: wordpress
    MYSQL_PASSWORD: wordpress
    volumes:
    - db_prefork_data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password
    cap_add:
    - SYS_NICE

    # Apache event MPM with PHP-FPM
    wordpress-event:
    image: wordpress:6-php8.4-fpm
    cpus: 2
    container_name: wordpress-fpm
    environment:
    WORDPRESS_DB_HOST: db-event
    WORDPRESS_DB_USER: wordpress
    WORDPRESS_DB_PASSWORD: wordpress
    WORDPRESS_DB_NAME: wordpress
    volumes:
    - wp_event_data:/var/www/html
    depends_on:
    - db-event

    # Database for event setup
    db-event:
    image: mysql:8.0
    container_name: mysql-event
    environment:
    MYSQL_ROOT_PASSWORD: rootpassword
    MYSQL_DATABASE: wordpress
    MYSQL_USER: wordpress
    MYSQL_PASSWORD: wordpress
    volumes:
    - db_event_data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password
    cap_add:
    - SYS_NICE

    # Apache with event MPM for PHP-FPM
    apache-event:
    image: httpd:2.4
    container_name: apache-event
    ports:
    - "8081:80"
    volumes:
    - wp_event_data:/var/www/html
    depends_on:
    - wordpress-event
    command: >
    bash -c "cat > /usr/local/apache2/conf/httpd.conf << 'EOL'
    ServerRoot /usr/local/apache2
    Listen 80

    # 加載基本模塊
    LoadModule mpm_event_module modules/mod_mpm_event.so
    LoadModule authn_file_module modules/mod_authn_file.so
    LoadModule authn_core_module modules/mod_authn_core.so
    LoadModule authz_host_module modules/mod_authz_host.so
    LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
    LoadModule authz_user_module modules/mod_authz_user.so
    LoadModule authz_core_module modules/mod_authz_core.so
    LoadModule access_compat_module modules/mod_access_compat.so
    LoadModule auth_basic_module modules/mod_auth_basic.so
    LoadModule reqtimeout_module modules/mod_reqtimeout.so
    LoadModule filter_module modules/mod_filter.so
    LoadModule mime_module modules/mod_mime.so
    LoadModule log_config_module modules/mod_log_config.so
    LoadModule env_module modules/mod_env.so
    LoadModule headers_module modules/mod_headers.so
    LoadModule setenvif_module modules/mod_setenvif.so
    LoadModule version_module modules/mod_version.so
    LoadModule proxy_module modules/mod_proxy.so
    LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
    LoadModule unixd_module modules/mod_unixd.so
    LoadModule status_module modules/mod_status.so
    LoadModule autoindex_module modules/mod_autoindex.so
    LoadModule dir_module modules/mod_dir.so
    LoadModule alias_module modules/mod_alias.so
    LoadModule rewrite_module modules/mod_rewrite.so

    # 服務器配置
    ServerAdmin admin@localhost
    ServerName localhost

    User www-data
    Group www-data

    # 日誌配置
    LogLevel warn
    ErrorLog /proc/self/fd/2
    CustomLog /proc/self/fd/1 combined

    # 文檔根目錄設置
    DocumentRoot \"/var/www/html\"
    <Directory \"/var/www/html\">
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
    DirectoryIndex index.php
    </Directory>

    # PHP-FPM 代理設置
    <FilesMatch \\.php$>
    SetHandler \"proxy:fcgi://wordpress-event:9000\"
    </FilesMatch>

    # MIME 類型
    TypesConfig conf/mime.types
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz

    # 基本安全設置
    ServerTokens Prod
    ServerSignature Off
    EOL
    echo 'Checking loaded modules:'
    httpd -M | grep mpm
    echo 'Starting Apache with event MPM...'
    httpd-foreground"

    volumes:
    wp_prefork_data:
    wp_event_data:
    db_prefork_data:
    db_event_data: