とりあえずブログ

とりあえずのブログ開設

俺の想像を遙かに超えるオーマイニュース(2)

2007-12-12 15:22:25 | Weblog
『俺の想像を遙かに超えるオーマイニュース(1)』
というか、根本的には積算処理が1つのアトミック処理(分割できない処理)になっていない点が俺には???なんだけどね。

と書いたので、これについて詳細に説明する。もはやオーマイニュースのデータベースのテーブルがどのような構成になっているのかは俺の想像できる範囲を遙かに超えているので、簡単な例で代用させてもらうことにする。<r>

次のような2つのテーブルがあるものとする。

記事データテーブル

項目名説明
author_id記者ID
article_id記事ID
article_access_amount記事毎アクセス数(これをアトミックデータとみなす)


記者データテーブル

項目名説明
author_id記者ID
author_access_amount記者毎アクセス数集計値


記事データテーブル中の記事毎アクセス数をアクセス数をアトミックデータとみなし、これを積算する方法を考える。
普通の手続き型のプログラム的な発想だと、全記者についてのループを行うよう実装するかもしれない。オーマイニュースは基本perlで作成されているものと思われるが、俺がperlをあまり知らないこともあるので、代わりにストアードプロシジャー風に記述する。
DECLARE cur1_eof INT DEFAULT 0;
DECLARE author_id, amount INT;
DECLARE cur1 CURSOR FOR SELECT author_table.author_id
                 FROM author_table;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET cur1_eof = 1;

OPEN cur1;

/* 集計対象の記者IDを取得する為に1件先読みする */
FETCH cur1 INTO author_id;

/* 記者データテーブルを全件処理するまで処理を行う */
WHILE cur1_eof = 1 DO
  /* 記事データテーブルから該当するアクセス数をサマリし、
     変数amountに取得する */
  SELECT SUM(article_table.article_access_amount) INTO amount
      FROM article_table WHERE article_table.author_id = author_id;

  /* 今処理を行っている記者データのアクセス数集計値を更新する */
  UPDATE author_table SET author_table.author_access_amount = amount
      WHERE author_table.author_id = author_id;

  /* 次の集計対象の記者IDを取得する */
  FETCH cur1 INTO author_id;
END WHILE;

CLOSE cur1;

リレーショナルデータベースを使う場合にこのようなロジックはかなり無駄である。無駄であるばかりでなく少なくとも1桁以上は実行速度が落ちる悪手。

『俺の想像を遙かに超えるオーマイニュース(1)』で示したように
4. 記者本人が見る事が可能な記事別のPV値は増えたり減ったりする。(減るのは深夜で朝には回復?)

という事実からすると、もっとひどくて
DECLARE cur1_eof INT DEFAULT 0;
DECLARE author_id, amount, access INT;
DECLARE cur1 CURSOR FOR SELECT author_table.author_id FROM author_table;
DECLARE cur2 CURSOR FOR SELECT article_table.article_access_amount
                 FROM article_table
                 WHERE article_table.author_id = author_id;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET cur1_eof = 1;

OPEN cur1;

FETCH cur1 INTO author_id;
WHILE cur1_eof = 0 DO

  /* 先の例のSELECT SUM・・・の部分をもっと悪い例に書き換えてみた */
  SET amount = 0;

  OPEN cur2;

  FETCH cur2 INTO  access;
  WHILE cur2_eof = 0 DO
    SET amount = amount + access;
    FETCH cur2 INTO  access;
  END WHILE;

  CLOSE cur2;

  UPDATE author_table SET author_table.author_access_amount = amount
      WHERE author_table.author_id = author_id;
  FETCH cur1 INTO author_id;
END WHILE;

CLOSE cur1;

みたいな処理かもしれない。

これらの処理は次の1発のSQL文で済ませることができる。
UPDATE author_table
    SET author_table.author_access_amount
          = (SELECT SUM(article_table.article_access_amount)
    FROM article_table
    WHERE article_table.author_id = author_table.author_id);


無駄な処理をしているのではないだろうか? とても気になる。

尚、上記例は実際に試した訳ではないので、細かい点は間違っているかもしれないことを断っておく。
また、適切なindexが設定されていない等の不備があれば最後の例にしても速度は低下する。
EXPLAINでちゃんと確認してね。

『俺の想像を遙かに超えるオーマイニュース(1)』に書いたように、根本的には再積算をしているならばその再積算を必要とする構造そのものに欠陥があるというのが俺様意見だが、同時にこうしたそれぞれの部分にも問題があるのではないかと思う。

必要なら見直してください><

最新の画像もっと見る