phpのキャッシュを導入してデータベースの負荷を下げる

Shiga
4 min readJul 17, 2017

--

運営しているサイトにキャッシュをいれたのでその時のメモ。

対象のサイト https://38qa.net/

1日7000PVくらいなので、負荷も大したことないが、SNSでのシェアなどで一時的にデータベースに負荷がかかって稀に落ちることがある。落ちるのはサーバではなく、データベースのMySQLが多い。そこで、高速化も兼ねて、簡単にできる初歩的なデータベースでの負荷対策を行う。このあたりの知識は詳しくはないので、もしこれを見て同じことをやる場合は、個別に調べてもらった方が良い。

クエリの診断

まず、やばそうなクエリがないかを調べる。SHOW STATUSコマンドで調べたところ、Select_full_join、Select_full_range_joinなどのまずい値は0。サイトがオープンソースのシステムを使っているので、さすがにindexの貼り忘れなどの初歩的な問題はなさそうだ。

MySQLのキャッシュ

閲覧メインで更新クエリの少ないサイトであれば、MySQLのキャッシュを設定するだけで効果がある。MySQLキャッシュが未設定になっていたので設定した。mysqldに以下を追加しMysqlを再起動する。AWSのRDSを利用しているので、RDSのダッシュボード上で設定できる。query_cache_type=1なので、SQL文に明示的にキャッシュしないことを書かない限り、キャッシュされることになる。

query_cache_size = 16M
query_cache_type=1

テーブルが更新されるまでキャッシュが保持されるので、閲覧メインで更新が少ないサイトであれば、かなり高い割合でキャッシュがヒットするはずだ。しかし、実際には、キャッシュのヒット率はわずか46%。これは、投稿テーブルに閲覧数をカウントするカラムがあるからのようだ。誰かがページを開くたびに投稿テーブルが更新されるので、キャッシュが作り直される。

このため、関連質問の算出のように、閲覧数は必要としないクエリでも、投稿テーブルを参照していればキャッシュが破棄されてしまう。設計上、閲覧数を投稿テーブルに入れるのは良くないが、オープンソースを利用しているのでテーブルの構造は変更できない。残念ながら、MySQLキャッシュの設定はほとんど効果がない可能性が高い。むしろ、更新が頻繁に発生するので負荷がかかり、逆効果かもしれない。

PHPでのキャッシュ

残念ながら、MySQLのキャッシュは期待できないので、PHPでMySQLからの取得した値をキャッシュすることにする。

質問詳細ページの下部に表示される関連質問の算出は、質問詳細ページを開くたびに行われているが、短い間で見ると結果は同じになる。質問が新たに投稿された場合や、質問のタイトルや本文が変更された場合には、結果が異なってしまうが、それも頻繁には起こらない。そもそも関連質問算出の精度がわずかに下がったところで問題はない。この処理はキャッシュが当てやすいこと、クエリが他と比べると重いこと、質問数が増すと処理も重くなっていくことから、キャッシュの導入の効果が出やすい。

閲覧するたびに関連質問が計算し直される(結果はまず変わらない)

phpFastCacheというライブラリを使う。以下のサンプルのようにごくシンプルに記述ができる。

use phpFastCache\Helper\Psr16Adapter;

$Psr16Adapter = new Psr16Adapter($defaultDriver);

if(!$Psr16Adapter->has('test-key')){
// Setter action
$data = 'lorem ipsum';
$Psr16Adapter->set('test-key', 'lorem ipsum', 300);// 5 minutes
}else{
// Getter action
$data = $Psr16Adapter->get('test-key');
}

本当は根本的に直したいが、付け焼き刃的にはまずまずの対策になると思う。

--

--