CRONでの処理について教えてください。

CRONでPHPを使ってデータベース(MySQL)の処理を計画しています。
処理内容は、1日1回、MySQL内の特定の値に対しランキングを行います。
■主な条件
 ・サーバー:さくらレンタルサーバー・スタンダード
 ・データ数:まだ不明ですが、とりあえず想定として1万件
■参考までに、CRONの使用に関する注意事項(一部抜粋)
 ・メモリやCPUに著しく負荷をかける処理は他のお客様にご迷惑がかかりますのでおやめください。 サーバ運用に支障をきたす場合はやむを得ない場合には、予告無く設定解除、機能制限することがあります。
 ・実行頻度が1時間以内に連続する場合や、CPU処理時間が60秒以上連続で利用される場合、予告なく設定解除される場合があります。
■質問
 (1)上記の様な場合『CPU処理時間が60秒以上連続で利用』されるでしょうか?
 (2)何かCPUに負荷をかけず、処理をボチボチやる方法があれば教えてください。
     例えば朝3~5時まではデータメンテナンス中として
     新たな書き込みなどのアクセスは停止して、ゆっくり処理して良いです。

以上 よろしくお願いします。

回答の条件
  • 1人1回まで
  • 13歳以上
  • 登録:2015/04/14 12:08:40
  • 終了:2015/04/21 12:10:04

回答(6件)

id:pogpi No.1

pogpi回答回数368ベストアンサー獲得回数492015/04/14 12:54:46

ポイント50pt

ランキングを作るだけなら、60秒もかからないと思いますよ。
CRONを使う必要もない気がします。

他1件のコメントを見る
id:tsuka115

申し訳ありませんが、素人なので良く理解できてません。

(1)1日1回バッチ処理で一括して順位を求めて保存しておいて、必要な時に必要な人の順位を検索して取得する。

(2)必要な時に必要な人の順位を毎回count+1で取得する。

サーバー負荷的に考えて(1)の方が良いと思っていたのですが、(2)の方が良いと理解してよろしいのでしょうか?

2015/04/19 08:42:55
id:pogpi

順位を更新するメンテナンスが大変なので、その方がいいと思います。
サーバー負荷を心配されているようですが、それほどかからないと思いますよ。

2015/04/19 09:50:35
id:rafting No.2

ラフティング回答回数2652ベストアンサー獲得回数1762015/04/14 13:06:30

ポイント50pt

cronを設定する時刻は、0分丁度や30分丁度は避けた方がいいです。

id:nmori No.3

morinatsu回答回数72ベストアンサー獲得回数82015/04/14 13:22:56

ポイント50pt

同じくさくらレンタルサーバーのスタンダードプランで、twitterのbotを運用していたことがあります。
 
処理の頻度は1日1回ということなので、cron自体の制限にはかからないと思います。
 
処理の負荷はランキング処理の内容と量次第という感じです。
自分の経験としては、もともとインストールされていないソフトをビルドしたら、処理を途中で中断されました。(コンパイルは青天井にCPU使ってしまうので……)
手作業、自動実行にかかわらず、CPU利用率が一定を超えたJOBを強制終了するようになっているものと推測します。
その際は、niceコマンドを使って優先度下げたら、中断されずに完了しました。時間がかかっても良いなら、お薦めの手段です。

他1件のコメントを見る
id:tsuka115

ちなみに、最初 nice -n 10 cd /home/・・・ としてましたが
ちゃんと動かなかったので nice -n 10; cd /home/・・・ と
「;」を入れてみました。

2015/04/14 15:08:01
id:nmori

実際に試してみれる環境はもう持っていないので、気になったところだけ。
 
cdがちゃんと動かないのは、シェルの組込みコマンドだからです。(niceで実行できるのは外部コマンドだけ)
挙げられている例のように複数コマンドをまとめて実行するなら、スクリプトを組んで、それを実行させる方がよいでしょう。
 
あと、個人的には、実行ログを /dev/null に捨ててしまうのは危険だと思っています。私も一時期やりましたが、障害解析が全くできないので、ちゃんとログをファイルに書き出す方法に切替えました。

2015/04/14 17:20:41
id:taknt No.4

きゃづみぃ回答回数13537ベストアンサー獲得回数11982015/04/14 16:03:33

ポイント50pt

実際に動かして時間を測定し、それにより判断したほうがいいと思います。

id:syamaoka No.6

syamaoka回答回数19ベストアンサー獲得回数82015/04/18 12:42:39

ポイント50pt

ランキングの対象となるテーブルに適切にインデックスが張られていて、ランキングの結果が全件でなければ一瞬でランキングの作成が終わると見積れます。
全件に対してランキングを作成する場合、おそらく SELECT がファイルソートになるので、レコード数が多いと時間がかかります。
以下参考例です。

MariaDB [hatena_question]> SHOW CREATE TABLE item;
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                  |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| item  | CREATE TABLE `item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL,
  `count` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `count` (`count`)
) ENGINE=InnoDB AUTO_INCREMENT=204443 DEFAULT CHARSET=utf8 |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

MariaDB [hatena_question]> SELECT COUNT(*) FROM item;
+----------+
| COUNT(*) |
+----------+
|   204442 |
+----------+
1 row in set (0.05 sec)

MariaDB [hatena_question]> SELECT * FROM item ORDER BY count DESC LIMIT 10;
+--------+------------+-----------+
| id     | name       | count     |
+--------+------------+-----------+
| 102711 | item102711 | 399298036 |
|  34929 | item34929  | 399297693 |
|   5962 | item5962   | 399292859 |
| 180578 | item180578 | 399286130 |
|  40471 | item40471  | 399285684 |
|  41353 | item41353  | 399282738 |
| 134448 | item134448 | 399281769 |
| 101184 | item101184 | 399280862 |
| 202852 | item202852 | 399280606 |
| 170867 | item170867 | 399277978 |
+--------+------------+-----------+
10 rows in set (0.00 sec)

MariaDB [hatena_question]> explain SELECT * FROM item ORDER BY count DESC LIMIT 100;
+------+-------------+-------+-------+---------------+-------+---------+------+------+-------+
| id   | select_type | table | type  | possible_keys | key   | key_len | ref  | rows | Extra |
+------+-------------+-------+-------+---------------+-------+---------+------+------+-------+
|    1 | SIMPLE      | item  | index | NULL          | count | 4       | NULL |  100 |       |
+------+-------------+-------+-------+---------------+-------+---------+------+------+-------+
1 row in set (0.00 sec)

MariaDB [hatena_question]> explain SELECT * FROM item ORDER BY count DESC;
+------+-------------+-------+------+---------------+------+---------+------+--------+----------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra          |
+------+-------------+-------+------+---------------+------+---------+------+--------+----------------+
|    1 | SIMPLE      | item  | ALL  | NULL          | NULL | NULL    | NULL | 204835 | Using filesort |
+------+-------------+-------+------+---------------+------+---------+------+--------+----------------+
1 row in set (0.00 sec)

[サブクエリを使ったランキング]
参考サイトで挙げられている SQL によるランキング集計は、DEPENDENT SUBQUERY になるのでスケールしません。100 件のランキングを作るのでも 3秒 もかかります。全件でランキングを作成しようとするとおそらく終わりません。
PHP でランキング計算をして、データを更新するのがよいでしょう。

MariaDB [hatena_question]> explain SELECT id, count, (SELECT COUNT(*) FROM item sub WHERE sub.count > main.count) + 1 AS rank FROM item main ORDER BY count DESC LIMIT 10000;
+------+--------------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
| id   | select_type        | table | type  | possible_keys | key   | key_len | ref  | rows   | Extra                    |
+------+--------------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
|    1 | PRIMARY            | main  | index | NULL          | count | 4       | NULL |  10000 | Using index              |
|    2 | DEPENDENT SUBQUERY | sub   | index | count         | count | 4       | NULL | 204109 | Using where; Using index |
+------+--------------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
2 rows in set (0.00 sec)

MariaDB [hatena_question]> SELECT id, count, (SELECT COUNT(*) FROM item sub WHERE sub.count > main.count) + 1 AS rank FROM item main ORDER BY count DESC LIMIT 100;

100 rows in set (2.94 sec)
他1件のコメントを見る
id:syamaoka

> サブクエリを使うよりも、PHPの方が処理が早いと理解してよろしいのでしょうか?

データ件数に依存しますが、ランク計算は PHP でやった方がトータルでは早そうです。「MySQL DEPENDENT SUBQUERY 遅い」などで検索してもらえば仮に理解はできなかったとしても、サブクエリでパフォーマンスが出ないのはよくあることだということは感じで頂けるかと思います。

> また、PHP内で以下を実行すれば良いと理解して良いのでしょうか?

はい。
(1) は MySQL で ORDER BY を指定すればソートまでできます。試して早い方を選べばよいと思います。きっと MySQL でソートしておく方が結果がよいはずです。

2015/04/19 11:01:37
id:syamaoka

まぁ1万件程度でなら一気にやるでまったく問題ないと思いますけれど、cron に登録するバッチプログラムで 1000 件ずつ処理をするなどすれば、夜中の間に更新できると思われます。
MySQL のテーブルを更新する時は、UPDATE を繰り返さずに一度の UPDATE で複数のレコードを更新するとパフォーマンスを改善できます。他のテクニックとしては、一時的に MySQL のテーブルから index を drop してしまって、データを全部更新し終えたら再度 index をはり直す手法もあります。今回はここまでする必要はまずないと思いますが、レコード数が増えて大規模なデータを処理することになったときの参考までに。
ちなみに、PHP で大量のデータを処理する時は、php.ini で設定できる memory limit に気を付けてください。レコードを一気に取得しようとすると設定によってはプログラムが途中で止まります。そういう時は1件ずつ取得して処理するか、memory limit を上げるかします。

2015/04/19 11:45:19
id:tsuka115

状況説明を補足させていただきます

データベース(DB)の項目は
 ・ユニークID
 ・ユーザー名
 ・パスワード
 ・愛称
 ・ポイント
 ・ランキング順位

となっていて、1日1回CRONでポイントの大きい順にランキング順位を決めて上書きします。

通常は(CRON処理中以外)は、DBにアクセスして、自分のポイントを変更したり
自分の順位(最新のランキング順位)を見たりします。


現在参考にしようかと思っているのは下記です。
http://qiita.com/hmuronaka/items/1afc132ddf400363efc2
http://www.system-ido.com/risouken/index.php?page=tech&num=7
http://q.hatena.ne.jp/1373303099
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11102689666

  • id:syamaoka
    pogpi さんの提案されている方法は、データ件数が少ない場合は有効な方法です(サーバーの性能や MySQL の設定が不明なので、どれぐらいのレコード数が今回のケースでは「少ない」と言えるのかは分かりませんが…。)。
  • id:tsuka115
    多数のご意見、ご回答、アドバイス、ヒントをいただき、どうもありがとうございました。

    システムをよく理解してなくて、自動終了&ポイント等分になってしまいました。
    (終了後に判定するものだとばかり思ってました。)

    なお、多くのご意見の中に様々なヒントがあり、これから試行錯誤しようと思います。
    ですので、今後このQ&Aを見た人が、これが唯一の回答だ!と言えるものは無いと思い
    あえてベストアンサーは選びませんので、ご理解、ご了承ください。
  • id:syamaoka
    最初から万人に当てはまる唯一の回答なんて存在しません。
    私の方からは説明の中でただし書きを致したつもりですが、ベストな解決策はデータ規模やハードウェアスペックなど環境に依存します。それらを質問者が最後まで明らかにしなかったため、回答者は想像で回答するしかなく、最適な選択を提示できませんでした。それだけです。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません