Twitterのトレンドランキングサイトの「ツイータン」にPHPのfatal errorがでておりました。

表示されたエラー内容を見ると、「Allowed memory size of *** bytes exhausted」とあり、どうやらPHPのメモリエラーが発生しているようです。久々に見た気がします。

被リンク稼ぎにディレクトリ型検索エンジンが重宝されていた2000年初頭は、ディレクトリ型検索エンジンdtnに猛烈なアクセスが訪れて、PHPがエラーを吐くといったことがチョクチョクありましたが、この記事に辿り着いた方も存在すら知らない「ツイータン」ですので、アクセス数で落ちるといったことがあるはずもないので、何かしらPHPのバグだろうとは感じました。

エラーログから原因を調べてみると、DBに貯めているトレンドハッシュタグ、キーワードのレコード数を出していたあたりでエラーがでていたので、DBからPDOで全権取得するあたりでmemory_limitの容量を超えたということのようでした。レコード数が300万件を超えるほどたまっておりましたので、PHP側での処理には無理があるということのようです。

ということで、わざわざPHP側に持ってきてrowCount()をするなんてことはやめて、素直にSQL側でCOUNTするということで解決をいたしましたので、この記事ではその流れをご紹介したいと思います。

PHPのメモリ不足エラーが発生

Twitterのトレンドランキングを月別にご紹介しているサイト「tweetan」にPHPのfatal errorがでていました。

エラーを見るとAllowed memory size of *** bytes exhaustedとあるので、どうやらPHPのメモリエラーが発生しているようです。久々に見た気がします。エラーはtwitter APIから取得したログの全件数をカウントしている箇所ででていたので、DBからPDOで取得したデータ処理でmemory_limitの容量を超えたということのようです。

TwitterトレンドランキングサイトでPHPのメモリ不足エラーが表示されている
Twitterトレンドランキングサイトのデータ件数取得でエラー発生

memory_limitの容量

phpinfoで今のmemory_limitを確認してみたところ128MB設定になっていました。128MBあっても足りないというのも凄い感じですが、fatal errorを回避するために取り急ぎ倍に増やすことにしました。

DirectiveValue
memory_limit128M
レンタルサーバの規定値のまま

php.iniで容量アップしておく

VPSサーバでもないレンタルサーバですので、php.iniは管理画面からの設定となります。256MBに増やしたところ無事PHPが動くようにはなりました。

DirectiveValue
memory_limit256M
php.iniでmemory_limitを256Mに変更

データ件数取得処理でエラー

PDOのrowCount()でレコード数取得

エラーが発生していた全レコード件数のカウント処理を確認してみたところ、SELECT文で全件取得後にPDOのメソッドrowCount()を使ってPHP側でカウント処理をかけておりました。

一昨年末に10分おきにtwitter APIからトレンドデータを収集し始めたものの、当時はこれを年単位で継続するとどうなるかも全く想像せず作っていたので、こんな感じでやっつけてしまっていました。さすがに300万レコードを超えるとこれでは無理があるようです。

$SQL = 'SELECT id FROM log';
$prepare = $db->prepare($SQL);
$prepare->execute();
$count_log = $prepare->rowCount();

レコード数はSQLのcountで取得に変更

件数カウントをするためだけに300万件以上の結果セットをfetchしてPHP側で配列保持するのは意味がないので、おとなしくMySQL側でカウントをすることにしました。当然ながらレスポンスもこの方が劇的に良くなったので早く変えておけばよかったです。

$SQL = 'SELECT count(id) AS cnt FROM log';
$prepare = $db->prepare($SQL);
$prepare->execute();
$rows = $prepare->fetch();
$count_log = $rows["cnt"];

おわりに

先日の記事でもご紹介したように、2020年の一年間にtwitter APIで収集したデータ件数は2,500,000レコード程になりましたが、このレコード数程度であればMySQLは全く問題ないものの、使い方には気を付けないとダメということでした。

dtn.jp検索クエリログや泉アンテナの記事リストなど、長年やってきているだけに100万レコード超えのテーブルがいくつか他にもありますので、同じようにレコード数が増える想定もないままPHPで処理しているところがないか早めに見直しをしようと思いました。