WordPress のチューニングポイントを探す 2

前回 WordPress のチューニングポイントを探す の続編。MySQL のクエリーキャッシュと、 memcached による分散キャッシュサーバを導入して、WordPress の高速化を目指す。

MySQL のクエリーキャッシュ

デフォルト状態では、クエリーキャッシュが有効になっていないため、有効にする。動作を確認する為に実行されたクエリーをファイルに保存する。クエリーキャッシュを有効にした直後のアクセスと、2 回目のアクセスでどのような変化があるかを調べる。

CODE:
  1. # mysqladmin -u root variables | grep query_cache_
  2.  
  3.     | query_cache_limit               | 1048576 |
  4.     | query_cache_min_res_unit        | 4096    |
  5.     | query_cache_size                | 0       |
  6.     | query_cache_type                | ON      |
  7.     | query_cache_wlock_invalidate    | OFF     |
  8.  
  9. # vi /etc/my.cnf
  10.  
  11.     [mysqld]
  12.     datadir=/var/lib/mysql              # 初期設定
  13.     socket=/var/lib/mysql/mysql.sock    # 初期設定
  14.     user=mysql                          # 初期設定
  15.     old_passwords=1                     # 初期設定
  16.     query_cache_limit=1M                # 1MB より大きい結果はキャッシュしない
  17.     query_cache_min_res_unit=4k         # 4K が推奨値
  18.     query_cache_size=24M                # クエリキャッシュにメモリを 24M (bytes) 割り当てる
  19.     query_cache_type=1                  # 0: OFF 1: ON 2: DEMAND
  20.     log=/var/log/mysql-all.log          # クエリーを全文保存
  21.                                            
  22.     [mysqld_safe]                       # 初期設定
  23.     log-error=/var/log/mysqld.log       # 初期設定
  24.     pid-file=/var/run/mysqld/mysqld.pid # 初期設定
  25.  
  26. # echo ""> /var/log/mysql-all.log
  27. # chown mysql. /var/log/mysql-all.log
  28. # service mysqld restart
  29. # mysqladmin -u root variables | grep query_cache_
  30.  
  31.     | query_cache_limit               | 1048576  |
  32.     | query_cache_min_res_unit        | 4096     |
  33.     | query_cache_size                | 25165824 |
  34.     | query_cache_type                | ON       |
  35.     | query_cache_wlock_invalidate    | OFF      |
  36.  
  37. # mysqladmin -u root extended-status | grep Qcache
  38.  
  39.     | Qcache_free_blocks              | 1        |
  40.     | Qcache_free_memory              | 25157000 |
  41.     | Qcache_hits                     | 0        |
  42.     | Qcache_inserts                  | 0        |
  43.     | Qcache_lowmem_prunes            | 0        |
  44.     | Qcache_not_cached               | 1        |
  45.     | Qcache_queries_in_cache         | 0        |
  46.     | Qcache_total_blocks             | 1        |
  47.  
  48. # echo ""> /var/log/mysql-all.log
  49. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=1 --num-calls=1 --rate=1
  50. # cat /var/log/mysql-all.log | wc -l
  51.  
  52.     22
  53.  
  54. # mysqladmin -u root extended-status | grep Qcache
  55.  
  56.     | Qcache_free_blocks              | 1        |
  57.     | Qcache_free_memory              | 25115576 |
  58.     | Qcache_hits                     | 0        |
  59.     | Qcache_inserts                  | 16       |
  60.     | Qcache_lowmem_prunes            | 0        |
  61.     | Qcache_not_cached               | 3        |
  62.     | Qcache_queries_in_cache         | 16       |
  63.     | Qcache_total_blocks             | 42       |
  64.  
  65. # echo ""> /var/log/mysql-all.log
  66. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=1 --num-calls=1 --rate=1
  67. # cat /var/log/mysql-all.log | wc -l
  68.  
  69.     22
  70.  
  71. # mysqladmin -u root extended-status | grep Qcache
  72.  
  73.     | Qcache_free_blocks              | 1        |
  74.     | Qcache_free_memory              | 25115576 |
  75.     | Qcache_hits                     | 16       |
  76.     | Qcache_inserts                  | 16       |
  77.     | Qcache_lowmem_prunes            | 0        |
  78.     | Qcache_not_cached               | 5        |
  79.     | Qcache_queries_in_cache         | 16       |
  80.     | Qcache_total_blocks             | 42       |

1 回目のアクセスで 22 回のクエリーが実行された事が分かった。その内、16 クエリーがキャッシュに保存された。(クエリーに保存されるのは、SELECT 文のみ) 2 回目のアクセスでは、同じページへのアクセスなので、前回同様に 22 回のクエリーが実行された。その内、16 回のクエリーがキャッシュに保存された内容と一致し、キャッシュから返された。クエリーを有効にすることで、多少の高速化が見込めそうだ。

WordPress 標準のキャッシュ機能

WordPress には標準でキャッシュ機能がある。

CODE:
  1. # cd $wordpress_root_dir
  2. # vi wp-config
  3.  
  4.     define('ENABLE_CACHE',true); // 先頭の方に追加
  5.  
  6. # mkdir ./wp-content/cache
  7. # chmod 777 ./wp-content/cache

WordPress 2.5 より、この機能が削除された。

memcached による分散キャッシュ

memcached は高性能な分散メモリキャッシュサーバで、データベースへのクエリー結果を一時的にキャッシュできる。MySQL のクエリーキャッシュの場合は、実際に問い合わせが発生するが、memcached の場合は問い合わせが発生しない。memcached は分散キャッシュサーバなので、複数のサーバで構築する事も出来るが、今回はlocalhost で memcached にアクセスできるように WordPress をインストールしたサーバに memcached を同居させる。memcached を有効にした直後のアクセスと、2 回目のアクセスでどのような変化があるかを調べる。

CODE:
  1. # cd $wordpress_root_dir
  2. # wget http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
  3. # rpm -ihv rpmforge-release-0.3.6-1.el5.rf.i386.rpm
  4. # yum install php-pecl-memcache memcached
  5. # service memcached start
  6. # chkconfig memcached on

次に WordPress に取り込むために object-cache.php をダウンロードし、./wp-content/object-cache.php として設置する。

http://ryan.boren.me/2005/12/23/memcached-backend/
http://plugins.trac.wordpress.org/browser/memcached/trunk/object-cache.php

WordPress と memcached が同じサーバーの場合は特に設定は必要ないが、異なるサーバーの場合は、wp-config.php に追記する必要がある。

CODE:
  1. # echo "$memcached_servers = array('192.168.1.1:11211', '192.168.1.2:11211');">> ./wp-config.php

必要ではないが、便利なツールとして memcache.php がある。これを設置すると、memcache でキャッシュしているサイズを表示する事ができる。

http://livebookmark.net/journal/2008/05/21/memcachephp-stats-like-apcphp/
http://livebookmark.net/memcachephp/memcachephp.zip

memcached が動作しているサーバと、接続するのに必要な認証情報を編集する必要がある。

CODE:
  1. # cat memcache.php |grep -e "define('ADMIN_" -e "add more as an array"
  2.  
  3.     define('ADMIN_USERNAME','memcache');     // Admin Username
  4.     define('ADMIN_PASSWORD','password');     // Admin Password
  5.     $MEMCACHE_SERVERS[] = 'localhost:11211'; // add more as an array

memcache.php を開くと、現在のキャッシュ容量が Used: 0 KBytes (0.0%) と表示されていれば正常。準備は整ったので、実際に memcached を動作させ、アクセスする。

CODE:
  1. # service mysqld restart
  2. # echo ""> /var/log/mysql-all.log
  3. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=1 --num-calls=1 --rate=1
  4. # cat /var/log/mysql-all.log | wc -l
  5.  
  6.     22
  7.  
  8. # mysqladmin -u root extended-status | grep Qcache
  9.  
  10.     | Qcache_free_blocks              | 1        |
  11.     | Qcache_free_memory              | 25115576 |
  12.     | Qcache_hits                     | 0        |
  13.     | Qcache_inserts                  | 16       |
  14.     | Qcache_lowmem_prunes            | 0        |
  15.     | Qcache_not_cached               | 2        |
  16.     | Qcache_queries_in_cache         | 16       |
  17.     | Qcache_total_blocks             | 42       |

memcache.php を確認すると、17 クエリー (Current Items 17) がキャッシュに保存されており Used: 18.7 KBytes (0.0%) となっている。MySQL のクエリーキャッシュには 16 クエリー保存されている。

CODE:
  1. # echo ""> /var/log/mysql-all.log
  2. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=1 --num-calls=1 --rate=1
  3. # cat /var/log/mysql-all.log | wc -l
  4.  
  5.     11
  6.  
  7. # mysqladmin -u root extended-status | grep Qcache
  8.  
  9.     | Qcache_free_blocks              | 1        |
  10.     | Qcache_free_memory              | 25115576 |
  11.     | Qcache_hits                     | 5        |
  12.     | Qcache_inserts                  | 16       |
  13.     | Qcache_lowmem_prunes            | 0        |
  14.     | Qcache_not_cached               | 4        |
  15.     | Qcache_queries_in_cache         | 16       |
  16.     | Qcache_total_blocks             | 42       |

2 回目のアクセスでは、memcached のキャッシュに保存された内容と一致し、最終的に 11 回のクエリーが実行された。その内、5 クエリーが MySQL キャッシュに保存された内容と一致し、キャッシュから返された。大幅に減少しており、かなりの高速化が見込めそうだ。検証する為に設定したクエリーの全文保存は、負荷が増加する為、通常運営時は無効にする。

CODE:
  1. # sed -i 's/log=/#log=/g' /etc/my.cnf
  2. # service mysqld restart

パフォーマンス測定

httperf を使って、チューニング後のパフォーマンスを測定する。生成する負荷は、WordPress の 1 ページ (?p=3) に X={1,5,10.20.50} 人 (--rate=X) が同時にアクセスして、合計 100 回 (--num-conns=100) 表示されたらベンチマークを終了する。Apache の設定はデフォルト値とする。

CODE:
  1. # ssh 10.0.0.1 "service memcached restart"
  2. # ssh 10.0.0.2 "service httpd restart"
  3. # ssh 10.0.0.2 "service mysqld restart"
  4. # sleep 3
  5. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=100 --num-calls=1 --rate=1
  6.  
  7.     httperf --hog --client=0/1 --server=10.0.0.1 --port=80 --uri=/wordpress/?p=3 --rate=1 --send-buffer=4096 --recv-buffer=16384 --num-conns=100 --num-calls=1
  8.     Total: connections 100 requests 100 replies 100 test-duration 99.488 s
  9.  
  10.     Connection rate: 1.0 conn/s (994.9 ms/conn, <=1 concurrent connections)
  11.     Connection time [ms]: min 459.9 avg 479.0 max 559.8 median 466.5 stddev 24.2
  12.     Connection time [ms]: connect 0.2
  13.     Connection length [replies/conn]: 1.000
  14.  
  15.     Request rate: 1.0 req/s (994.9 ms/req)
  16.     Request size [B]: 75.0
  17.  
  18.     Reply rate [replies/s]: min 1.0 avg 1.0 max 1.0 stddev 0.0 (19 samples)
  19.     Reply time [ms]: response 448.6 transfer 30.2
  20.     Reply size [B]: header 230.0 content 9911.0 footer 2.0 (total 10143.0)
  21.     Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0
  22.  
  23.     CPU time [s]: user 27.90 system 71.59 (user 28.0% system 72.0% total 100.0%)
  24.     Net I/O: 10.0 KB/s (0.1*10^6 bps)
  25.  
  26.     Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
  27.     Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
  28.  
  29. # ssh 10.0.0.2 "mysqladmin -u root extended-status | grep Max_used_connections"
  30.  
  31.     | Max_used_connections              | 1        |
  32.  
  33. # ssh 10.0.0.1 "service memcached restart"
  34. # ssh 10.0.0.2 "service httpd restart"
  35. # ssh 10.0.0.2 "service mysqld restart"
  36. # sleep 3
  37. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=100 --num-calls=1 --rate=5
  38.  
  39.     httperf --hog --client=0/1 --server=10.0.0.1 --port=80 --uri=/wordpress/?p=3 --rate=5 --send-buffer=4096 --recv-buffer=16384 --num-conns=100 --num-calls=1
  40.     Total: connections 100 requests 100 replies 100 test-duration 21.629 s
  41.  
  42.     Connection rate: 4.6 conn/s (216.3 ms/conn, <=17 concurrent connections)
  43.     Connection time [ms]: min 528.4 avg 1948.2 max 8628.6 median 1789.5 stddev 1026.5
  44.     Connection time [ms]: connect 0.2
  45.     Connection length [replies/conn]: 1.000
  46.  
  47.     Request rate: 4.6 req/s (216.3 ms/req)
  48.     Request size [B]: 75.0
  49.  
  50.     Reply rate [replies/s]: min 3.6 avg 4.2 max 4.8 stddev 0.5 (4 samples)
  51.     Reply time [ms]: response 1897.1 transfer 50.9
  52.     Reply size [B]: header 230.0 content 9911.0 footer 2.0 (total 10143.0)
  53.     Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0
  54.  
  55.     CPU time [s]: user 4.68 system 16.95 (user 21.6% system 78.4% total 100.0%)
  56.     Net I/O: 46.1 KB/s (0.4*10^6 bps)
  57.  
  58.     Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
  59.     Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
  60.  
  61. # ssh 10.0.0.2 "mysqladmin -u root extended-status | grep Max_used_connections"
  62.  
  63.     | Max_used_connections              | 16       |
  64.  
  65. # ssh 10.0.0.1 "service memcached restart"
  66. # ssh 10.0.0.2 "service httpd restart"
  67. # ssh 10.0.0.2 "service mysqld restart"
  68. # sleep 3
  69. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=100 --num-calls=1 --rate=10
  70.  
  71.     httperf --hog --client=0/1 --server=10.0.0.1 --port=80 --uri=/wordpress/?p=3 --rate=10 --send-buffer=4096 --recv-buffer=16384 --num-conns=100 --num-calls=1
  72.     Total: connections 100 requests 100 replies 100 test-duration 22.186 s
  73.  
  74.     Connection rate: 4.5 conn/s (221.9 ms/conn, <=65 concurrent connections)
  75.     Connection time [ms]: min 1423.8 avg 8411.0 max 18285.9 median 8540.5 stddev 3848.5
  76.     Connection time [ms]: connect 0.2
  77.     Connection length [replies/conn]: 1.000
  78.  
  79.     Request rate: 4.5 req/s (221.9 ms/req)
  80.     Request size [B]: 75.0
  81.  
  82.     Reply rate [replies/s]: min 2.4 avg 4.0 max 4.8 stddev 1.1 (4 samples)
  83.     Reply time [ms]: response 8321.4 transfer 89.4
  84.     Reply size [B]: header 230.0 content 9911.0 footer 2.0 (total 10143.0)
  85.     Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0
  86.  
  87.     CPU time [s]: user 2.61 system 19.58 (user 11.8% system 88.2% total 100.0%)
  88.     Net I/O: 45.0 KB/s (0.4*10^6 bps)
  89.  
  90.     Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
  91.     Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
  92.  
  93. # ssh 10.0.0.2 "mysqladmin -u root extended-status | grep Max_used_connections"
  94.  
  95.     | Max_used_connections              | 22       |
  96.  
  97. # ssh 10.0.0.1 "service memcached restart"
  98. # ssh 10.0.0.2 "service httpd restart"
  99. # ssh 10.0.0.2 "service mysqld restart"
  100. # sleep 3
  101. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=100 --num-calls=1 --rate=20
  102.  
  103.     httperf --hog --client=0/1 --server=10.0.0.1 --port=80 --uri=/wordpress/?p=3 --rate=20 --send-buffer=4096 --recv-buffer=16384 --num-conns=100 --num-calls=1
  104.     Maximum connect burst length: 1
  105.  
  106.     Total: connections 100 requests 100 replies 100 test-duration 21.534 s
  107.  
  108.     Connection rate: 4.6 conn/s (215.3 ms/conn, <=86 concurrent connections)
  109.     Connection time [ms]: min 1024.4 avg 10203.0 max 21183.5 median 10542.5 stddev 5459.2
  110.     Connection time [ms]: connect 0.2
  111.     Connection length [replies/conn]: 1.000
  112.  
  113.     Request rate: 4.6 req/s (215.3 ms/req)
  114.     Request size [B]: 75.0
  115.  
  116.     Reply rate [replies/s]: min 2.8 avg 4.2 max 5.0 stddev 1.0 (4 samples)
  117.     Reply time [ms]: response 10091.4 transfer 111.4
  118.     Reply size [B]: header 230.0 content 9911.0 footer 2.0 (total 10143.0)
  119.     Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0
  120.  
  121.     CPU time [s]: user 1.81 system 19.73 (user 8.4% system 91.6% total 100.0%)
  122.     Net I/O: 46.3 KB/s (0.4*10^6 bps)
  123.  
  124.     Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
  125.     Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
  126.  
  127. # ssh 10.0.0.2 "mysqladmin -u root extended-status | grep Max_used_connections"
  128.  
  129.     | Max_used_connections              | 22       |
  130.  
  131. # ssh 10.0.0.1 "service memcached restart"
  132. # ssh 10.0.0.2 "service httpd restart"
  133. # ssh 10.0.0.2 "service mysqld restart"
  134. # sleep 3
  135. # httperf --hog --server=10.0.0.1 --uri=/wordpress/?p=3 --num-conns=100 --num-calls=1 --rate=50
  136.  
  137.     httperf --hog --client=0/1 --server=10.0.0.1 --port=80 --uri=/wordpress/?p=3 --rate=50 --send-buffer=4096 --recv-buffer=16384 --num-conns=100 --num-calls=1
  138.     Maximum connect burst length: 1
  139.  
  140.     Total: connections 100 requests 100 replies 100 test-duration 21.443 s
  141.  
  142.     Connection rate: 4.7 conn/s (214.4 ms/conn, <=98 concurrent connections)
  143.     Connection time [ms]: min 851.2 avg 11621.5 max 20695.9 median 11338.5 stddev 5828.4
  144.     Connection time [ms]: connect 0.2
  145.     Connection length [replies/conn]: 1.000
  146.  
  147.     Request rate: 4.7 req/s (214.4 ms/req)
  148.     Request size [B]: 75.0
  149.  
  150.     Reply rate [replies/s]: min 3.0 avg 4.3 max 5.8 stddev 1.2 (4 samples)
  151.     Reply time [ms]: response 11573.4 transfer 47.9
  152.     Reply size [B]: header 230.0 content 9911.0 footer 2.0 (total 10143.0)
  153.     Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0
  154.  
  155.     CPU time [s]: user 1.98 system 19.47 (user 9.3% system 90.8% total 100.0%)
  156.     Net I/O: 46.5 KB/s (0.4*10^6 bps)
  157.  
  158.     Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
  159.     Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
  160.  
  161. # ssh 10.0.0.2 "mysqladmin -u root extended-status | grep Max_used_connections"
  162.  
  163.     | Max_used_connections              | 25       |

チューニング後の測定結果のポイントとなる点を以下に示す。

--rate=1 Request rate: 1.0 req/s
Reply time [ms]: response 448.6
Errors: total 0
Max_used_connections 1
--rate=5 Request rate: 4.6 req/
Reply time [ms]: response 1897.1
Errors: total 0
Max_used_connections 16
--rate=10 Request rate: 4.5 req/s
Reply time [ms]: response 8321.4
Errors: total 0
Max_used_connections 22
--rate=20 Request rate: 4.6 req/s
Reply time [ms]: response 10091.4
Errors: total 0
Max_used_connections 22
--rate=50 Request rate: 4.7 req/s
Reply time [ms]: response 11573.4
Errors: total 0
Max_used_connections 25

--rate=1 のときは、1 リクエストあたりの平均 response 448.6 [ms] で、まだまだサーバに余裕がある。--rate=5~50 をみると、Request rate が 4.5~4.7 req/s となっているため、サーバに余裕が無い状態を示している。MySQL への同時コネクション数は、初期設定では最大 100 だが、測定結果の Max_used_connections をみると、最大 25 となっている。このため、まだまだ MySQL 側には余裕があるがサーバからのリクエストが届いていない状態を表しており、MySQL 側がボトルネックになっている可能性は低いと考えられる。

Leave a Reply