なにやっとるん?あんたのお母ちゃんよ。

主人がオオアリクイに殺されて1年が過ぎました。に続く名作スパムの予感。(笑)

Subject: なにやっとるん?あんたのお母ちゃんよ。

久しぶりにメールしてきたと思ったら、この子は。
まぁ、ええわ。
ええ機会やからなあんたに前から話さんといけんこと思っとった話があるんよ。
カミングアウトするから落ち着いて聞きや。
あのな・・・
あんた、実は貴族の血を引いとるんよ。
プレイボーイ貴族ドン・ファン家、そこの血があんたの体の中には流れとるんよ。
いや・・・お母ちゃんがドン・ファンとやったんちゃうよ。
そんなん無理やん?
細かい事は今度帰ってきたときに話すけどな、お母ちゃんが本当に話したいのはそんな事じゃないんよ。
あんたはヤレる男の血筋を引いとるってことや。
なのにあんた・・・いまだにセコセコ働いてるやろ?
その事なんよ。
あんなしょうもない仕事やめてしまい。な?
働く必要ないんや、あんたは貴族やからな。
あんたには生まれた時から女をたらしこむ素質が備わってるんよ。
これからはヒモになれる女探して、それで食うていけばいいんよ。
一人や二人じゃあかんで、せめて八人くらいは囲うんやで。
そんでな、お母ちゃんいいところ見つけといたんよ。
ここや。
http://www.**************/
あんたの為にえらい探したで。
あんたはお母ちゃんの言う通りやれば大丈夫や。
お父ちゃん来年で定年退職やろ、うちの世話してくれる分も囲うんや。ええな?

それでな、お母ちゃん今の日本の社会情勢を調べたんよ。
そしたらビックリや。
今の女はよう働いてお金稼いどるから結婚する気がないんよ。
そりゃ今の収入のあんたと結婚して養ってもらおうなんて思うはずないわな。
結婚してあんた共稼ぎしようもんなら、あんたより女の方がいいお給料もろうとるなんて恥ずかしい結果になるんよ。
キャリアウーマンいうのは恐ろしいやろ?
だからあんたは最初っからそんな事で張り合っちゃあかんのよ。
その代わりな、金持ってる女は淋しい人生送っとるんよ。
「結婚する気ないけど淋しい」らしいんよ。
あんたはな、そういう女のヒモになればいいんよ。
あとは、その女の部屋に転がり込めばあんたがおらんと淋しくて何でも言うこと聞くようになるがな。
どこの女もそんなもんなんよ。
お母ちゃんもお父ちゃんをそうやって養ってきたんや。
ええな、あんたはヒモの存在の大きさを教え込むんやで。
誰もヒモなしでバンジージャンプなんかやらんやろ?
そんくらい大事なんやヒモは。

ええな、ここやで!
貴族なんやからな、気高いヒモになるんやで。
http://www.**************/
他の平民に先越されんようにせんとあかんで。
わかったらはよ行き。

追伸:
来年の正月は帰ってくるんか?

お盆は無理だけど、正月は帰るよ、母ちゃん!

「政治資金の領収書1円以上は提出」しかし報告書のコピーも撮影も禁止というカラクリ

要約。

  1. 某大臣が、水道代と光熱費タダのはずの議員会館でのことなのに光熱費で数千万円を費用計上してたのがバレたとか、他の大臣も似たようなもんだったという件。
  2. 例によって「透明化!」とかいって善人ヅラした議員さんがすったもんだ
  3. でもそれってすべてパフォーマンス。単なる小芝居。
  4. 賭けてもいい。政治資金がらみで今やってる議論は、「1円以上すべて領収書を添付だ!」「いや、それじゃ事務的な経費がかかりすぎて無駄遣いだ」「んじゃ5万円から1万円に引き下げるくらいで手を打とうか」でシャンシャンとなり、「国民の皆様!もっと透明性を高めるという結論が出ました」と高々と報告されることで「決着」とかいう見出しになるだろう。
  5. それって良い事に見えて、実はある部分が巧妙に隠されたトリック。
  6. なぜなら最終的な「公開」の部分にカラクリがあるから。 政治資金収支報告書の「公開」は、ほんとうに「閲覧=目視で見る」だけ。コピーもできない。撮影もできない。ExcelやPDFファイルで配布なんてもってのほか。印刷もできない。 コピーアンドペーストすら許さない。いや、ホントにそうだから。
  7. 書類上の情報がどれだけ詳細化されようと、そこさえ守っておけば=詳細に調べにくくしておけば=痛いところを見つけられてしまう確率は意外に低い!という腹黒政治家さんたちの目論見はここ数十年そして今後も脈々と受け継がれるのが現実。

と結論を先に書いたところで、話を進めよう。

数字のチカラ

「食い逃げされてもバイトは雇うな」という本は面白かった。 そこではこんな話が載っていた。

ビジネスの数字がうまくなるためには?
  • 数字は言い換え可能(1勝2分けは3戦無敗に言い換える)
  • いちばん有利な数字を使う
  • ゼロに勝る数字ナシ
領収書の話なので「ゼロ円」は無理でも「1円」ならインパクトがある。「1円以上」と最初に言った政治家さんは最小単位という数字のチカラを見事に活用している。と同時に、核心の部分から注目をそらすことに成功している(まあもちろんそこまで狙ったわけじゃないだろうが)。

実はQ,C,Dで言うとQとCだけ論点にされて、Dの話題はあえて避けられている

そこそこ社会人やってる人なら、「ビジネスのQCD」くらいなんとなくでも知っているだろう。

  1. Q=クオリティ。品質。
  2. C=コスト。値段。費用。
  3. D=デリバリー。納期。物流。要するに「目的のものがちゃんと届けられること」。
めちゃくちゃうまいラーメンでも1万円は払わない=QとCのバランスが取れてない。
ピザ屋に電話したら3分で持ってきた、でもまずかった=QとDのバランスが悪い。
オークションでいい出物があるのに送料を考えると躊躇せざるをえない=CとDのバランスがいまいち。

というように、どんなビジネスであれQ,C,Dの3点すべてにまたがるバランスが重要である。 どれかひとつ欠けても目的は達せられない。

ではQCDの観点で見てみよう。

だから無駄だって。
その領収書を整理するための事務員を雇ったりで、税金が大量に使われるだけでしょ。
税金は、返ってきません。 (以下略)
Yahoo!みんなの政治 - 第166回国会 衆法 166回39号 政治資金規正法の一部を改正する法律案 - みんなの評価 詳細より。
なるほど、この人はQ=情報の詳細度=を向上させようとすればC=事務的な経費=も増大しそうだというバランスの悪化を懸念している。いい着眼点だと思う。

だが、ほうぼうの情報ソースをいくら探しても、D=デリバリー=の問題を挙げているものがまったく見当たらない。

たとえば、こんなことがあった。例の「光熱水費の何千万円というのはナントカ還元水を飲んでたんだ」とのたまった大臣(故)がすったもんだのとき、某野党の悪人顔で損してる人が自分の事務所費をパフォーマンス的に先陣切って公開したのだが、

=さあ、その1週間後、小沢氏が領収書などを公開したとして、朝日は小沢氏を絶賛しています。小沢氏の行為が、歴史的意義のある英断であるかのように持ち上げています。まあ、どう評価しようと自由ですが、それほどのものだったのか。同じく2月21日の日経新聞は《小沢一郎代表は、事務所費の詳細を公表するにあたり、領収書などのコピーや撮影を認めないなどの条件をつけた。公開時間も報道機関1社あたり30分で、閲覧人数も3人に限定》と報じています。この日、「公開」された資料はかなり膨大なもので、どの社も全部目を通したり、メモをとったりはできなかったと思うのですが。
朝日の「政治とカネ」社説と領収書へのこだわり:イザ!
という感じで、要するに事務所費やその領収書などの書類を
  1. 詳細に=クオリティ観点ではGOODかも?
  2. 公開。=デリバリー観点ではすごい縛りが入ってる
ということである。

つまりQCDのうちのデリバリーの部分が実は極悪。

んじゃこの議員さんが特に悪人なのかというと、そうではない。本人は「法律にそって公開している」と言い張っているし実際違法じゃない。

何人も、前条第一項の規定により報告書の要旨が公表された日から三年間、総務大臣の場合にあつては総務省令の定めるところにより、都道府県の選挙管理委員会の場合にあつては当該選挙管理委員会の定めるところにより、当該報告書又は書面の閲覧を請求することができる。

政治資金規正法 第二十条の二より
そう。「閲覧できる」しか書いてない。30分の時間制限を課しても閲覧は閲覧である。 ましてや、コピーできるとか、撮影できるとか、PDFやExcelファイルをダウンロードできるとか、そういうことにはならないようになっている。そこがカラクリである。

つまり、今現在、こういうことになっている。

  • 印刷できない(と主張している)政治資金収支報告書、WEBで公開 (スラッシュドット ジャパン 2004/3)
  • 政治資金収支報告書及び政党交付金使途等報告書(総務省)
  • ↑Webで公開といいつつも、実際はわけのわからない専用アプリケーションをダウンロードしないと読めないようになっており、そのアプリには印刷機能が搭載されていない。コピーアンドペーストもできないように細工されている。
  • なんでまた税金を消費してわざわざそんな専用アプリを開発させてんの?単純なテキスト/HTMLあるいはExcelやPDFの形式で配布しないのはなぜ?
  • 「それはね、法律では『閲覧できる』としか規定されてないからだよ。アカンベー!」

これは本当に、2004年当時から今現在まで、総務省において運用されていることである。 今の政治資金規正法には「デリバリー」の観点で致命的な欠陥と、そこにつけこんだ巧妙な妨害があるのだが、国会でしゃべってる偉い人も主要なマスコミも誰もツッコまないこの不思議な『空気』は何?(それはね...)

さてところで、デリバリー観点がなぜそこまで重要なのだろうか?

少し話が飛ぶが、こんなことがあった。

要するに、
  1. 市が持っている農道や水道などの地図が電子データで存在する。
  2. もちろん誰にでも「公開」される
  3. ところが公開の方法は「紙」に限定されている。法律で。
  4. だから、データをCDにでも焼いて渡せば済むものをわざわざコストをかけて印刷。
これも、デリバリー=どんな形での「公開」が望ましいのか?=の観点がまるきり欠け落ちている法律が生んだバカバカしい展開のひとつである。

実はこの話の裏では「だってしょうがないじゃん」的な言い訳をしているお役所の中の人を見たことがある。法律論以外の観点で。

CD-ROMなどの電子データで渡したら、「原本との照合」が難しいじゃないか?
掲示板等で見た話なのでソースが無いのが残念だ。つまりこういうことである。
  • 例えば書類であれば、役所のほうに残っている原本と、配布したものとを見比べることで、流通過程での改ざんなどを抑止できる
  • でも電子データでは見た目でそれができないじゃないか。お役所としてはそういう危険は冒せない。
はい、IT関係の人なら今すかさず「ダウト!」とつぶやいたことだろう。md5チェックサム(最近だとSHA1)によるハッシュ計算といったそういうIT技術をまるで知らない(or助言する人がいない)人たちが中途半端にIT化を進めた結果生まれたエアポケットがそこにある。
このように、電子データの整合性を確認する技術はもう何十年も前から確立されており、実際にITの世界ではいまも常識的に利用されているというのに。

話を政治資金に戻そう。またちょっと「食い逃げされてもバイトは雇うな」から抜粋させていただく。

  • 決算書は「読む」のではなく「探す」
  • 知るべき「指標」を決め、決算書から数字を探していく
  • 「比較(過去比較、他社比較)」することで問題を浮き彫りにする
  • 割り算で「比率」を出す
(第4章 決算書の見方はトランプと同じ より)
そう。政治資金収支報告書の内容を詳細化=クオリティを上げる=しても、デリバリーの部分で縛っておけば、上のような分析がされてしまう心配が無いのである。コピーさせなければ数値を探すことも比較もしづらい。指標を定めることも難しい。そこがキモ。議員会館は光熱費タダのはずという、数値を探す側にとってもそれを報道する側にとっても報道を聞く側にとっても食いつきやすくわかりやすい指標をもって探すくらいがせいぜいだ。

数字のチカラはすごい。「7つの習慣」「SEがおさえるべき50のナントカ」「○○である10の理由」。タイトルに数字を入れるだけで、本でもブログでも売れ行きがだいぶ違う。 筆者はあえてやらないのだが、それでも少しテストしてみたことがある。 「たとえ個人の趣味のブログであっても独自ドメイン上でやるべき8つの理由」という記事がそれである。いやあ、このときは釣れた釣れた。フォースの暗黒面をチラ見した。

「領収書の提出は現状は5万円以上」「そこを1円以上に」といった数字のチカラを利用してクオリティ観点に議論をひきつけておき、 肝心なデリバリー観点には注目されないように細心の注意が払われているというのは決してうがった見方ではない。 頼むからビジネスのQCDの基本くらいおさえてくれよ>中の人。

つまり、いまおきていることは、自分で自分の首を絞めかねないような法律や規制を政治家自身に議論させてもまったくムダであるという過去百年繰り返されてきた日常がそこにあるというだけのことである。 そんなわけで先の選挙では自民にも民主にも入れなかった筆者。 ああ、美しい国、日本。

see also:

富士通エフ・アイ・ピーの中の人から興味深いコメントをいただきました

横幅を固定するな! − 後悔しないためのWebデザインという2004年に書いた非常に古い記事に対して、興味深いコメントをいただいた。 以下はこのブログで使っているMovableTypeが自動送信してきたコメントのメールである。

「横幅を固定するな! - 後悔しないためのWebデザイン (ブログ: Web屋のネタ帳、ID: 348)」への新しいコメントを受け付けました。

コメントを確認: http://neta.ywcafe.net/000348.html

IPアドレス: 202.219.37.117
名前: いまさらのコメント
メールアドレス: aaa@ddd.com
URL:
Comments:

あなたの感覚は極端ですね。固定サイズが気持ちいいという人もいるし、横スク
ロールしたからって、なんら不快に思わない。固定せずにテーブルレイアウトが崩
れるほうが気持ち悪い人だっている。これは好みの問題。それを最悪だ!と言って
いる人の方が問題。

なるほどなるほど。ところで、ちょっとした興味から、発信元のIPアドレスを逆引きしてみた。

C:\>nslookup 202.219.37.117
(途中略)
Name: fipbc01.fip.co.jp
Address: 202.219.37.117

どうやら富士通エフ・アイ・ピーというところのLANからのアクセスのようだ。

ところで、その会社のWebsサイトを見てみると、ほとんど全てのページに共通なフッタとして次のようなページへのリンクがある。

富士通のアクセシビリティ

なかなか充実していそうだ。勉強になるなあ。 さらにリンクをたどると、 富士通ウェブ・アクセシビリティ指針 日本語サイト向け 第2.01版 : 富士通というページにたどりついた。これはすごい。 さらに充実している。

そのなかに、こんな1行をみつけた。(色合いとかは省略)

21. 優先度 2 全体要件 横方向のスクロールが発生しないようにする。 -

大変参考になるので引用しておく。

21. 横方向のスクロールが発生しないようにする。

優先度2 仕様に関する指針 <全体要件> | 設計・制作・運営工程

解説

ブラウザで縦と横の両方にスクロールが表示されている場合、ページ全体の把握が困難になります。

特に、上肢に障害のある利用者は、スクロール操作が困難になる場合があります。
また、弱視の利用者の場合、拡大ツールで画面の一部を拡大していることがあり、拡大ツールのスクロールとブラウザのスクロールが二重になると、操作がより困難になります。

スクロールを縦方向だけにすることで、多くの利用者がページを参照しやすくなります。

事例と実装

* ウィンドウの横幅が800ピクセルのとき、横スクロールなしで表示可能とする(ページの横幅は固定にしなくてもよい)。

大変勉強になりました。どうもありがとうございました。

(正直言って、コメントあけておくとこんなコメントが意外に過半数を占めたりすることがあるので、いちいちつっこむのもめんどいので最近はコメントあけないのである。え?大人気ない?たしかにw。夜中の眠いときになんか書くもんじゃないなあ)

Google Analyticsの「ユーザー定義」レポートの設定方法

Google Analyticsにはいろんな機能がありすぎるので活用し切れていないことも多いだろう。 ユーザー分析セクションの「ユーザー定義」なんかそのひとつだ。
200708-googleanalytics01.png

使い方がよくわからんかったのでヘルプ見たら、こういうことらしい。

Google Analytics ヘルプ センター - ユーザー セクションではどのような種類のレポートを利用できますか。
ユーザー定義: 定義した独自のセグメント別に訪問ユーザーを比較できます。セグメントを設定するには、ウェブサイトのコードの utm_setvar 関数を呼び出します。 たとえば、訪問ユーザーがサイトのフォームで役職 ("マネージャ"、"専門技術者"、"マーケティング担当者"など) を選択した場合に、utm_setvar を呼び出して、ユーザー定義変数に選択項目を保存することができます。 このレポートでは、設定したユーザー セグメントを比較できます。

なるほど、utm_setvarか、と思ってさらに検索しても、コードのサンプルなどがさっぱり見つからない。 なんでだろと思ってさらに調べたら、

アクセスしたページや問い合わせフォームの内容によってユーザーを分類するにはどうすればよいですか。
ユーザー セグメントを設定するには、Javascript の __utmSetVar 関数を呼び出します。 たとえば、ウェブページのトラッキング コードより下のセクションで下記のように設定します。

<script type="text/javascript">__utmSetVar('Marketing/PR');</script>

utm_setvarじゃなくて、__utmSetVarなのか?どっちが正なのか知らんが、とりあえず __utmSetVar('hogehoge')で試してみたら確かにユーザー定義レポートに「hogehoge」が出るようになった。

たとえばPHPやJSPなどでなんらかのユーザー情報が取れるという場合には、

<script src="http://www.google-analytics.com/urchin.js" 
type="text/javascript"></script>
<script type="text/javascript">
_uacct = "UA-999999-1";
__utmSetVar('<?php echo $なんか好きなように変数とか; ?>');
urchinTracker();
</script>

とかやっとくのもいいかもしれない。

画像もDBに格納して管理する -扱いがめんどうなLOB(ラージオブジェクト)は使わない方法も含め

結論:

  • ブログにせよECサイトにせよ、およそWebサイトを構成しているデータはテキストだけでなく画像もある。
  • しかしデータベースに格納するのはテキストデータだけで、画像データまでDBに入れるということは考えられていない場合がほとんど。
  • でもよくよく考えると、データはデータ層=DB=に格納する、という一元管理の基本にのっとったほうがやっぱりいいんじゃないだろうか?
  • 容量食いすぎ?いや、Youtubeみたいなサイトならともかく、そもそも普通のWebサイトに載ってる画像なんて容量小さいし、最近ストレージ安いし。
  • ラージオブジェクト型は扱いづらい?たしかに。でもだったら画像をbase64エンコードしてテキスト化して文字列型カラムにつっこんどくのもいいのでは。
  • DB層やアプリ層に負荷がかかる?そこはmod_rewriteでキャッシュもどき作戦をとればいい。
というお話。なお以降は初心者でもなんとかわかるように書くために詳しい人にとってはまわりくどくなることをご了承ください。

「Web層-AP層-DB層」
誰もが知ってる基本3層構造である。ちなみに、「プレゼンテーション層-アプリケーション層-データ層」という書き方がされたりもする。残念ながらここで紹介されてた基礎知識には無かったけど。

Web層に使われているのがたいていApacheかIIS。AP層はperl/PHP/Java等でつくられたプログラムコードとそれを実行するためのAPサーバ。JavaだとTomcatが多い。perl/PHPを使っている場合はmod_php5、mod_perlのような形でApacheに寄生する感じで動くのでWeb層とAP層の区別がつかなかったりもする。

そしてDB層。Oracle、MySQL、PostgreSQLといったデータベースソフトが稼動する。ブログサイトなら記事のテキストとか、ECサイトなら商品説明とか注文データとかそういうのが格納される。

で、ここで問題になるのが画像ファイルのとりあつかい。典型的な例で言うと、ECサイトの商品の画像は概念的に言ってどの層に格納されるべきなのか?という問題である。

出来合いのECサイト構築ソフトであれ手作りなWeb-DBシステムであれ、たいていの場合は画像ファイルはファイルのままでファイルシステム上のどこかに格納しておき、ファイル名やその形式等の情報だけをDBに格納しているというケースがほとんどである。

なぜならDBは画像のようなバイナリデータの格納にはいろんな面で弱いから。 そもそもデータベースソフトのほとんどが数値あるいは文字のデータを格納、整理する目的で開発されたという歴史的事情もある。

もちろん、最近のモダンなDBのほとんどがラージオブジェクト型とよばれるバイナリデータの取り扱い機能を備えている。がしかし、方言っぽくて面倒な特殊SQL文、少ない技術情報、意外な性能劣化、バックアップ時の思わぬトラブルなどなどいろいろあって個人的にはあまり好きではない。筆者の周辺でもラージオブジェクト型の利用には否定的な意見が多い。(oracleが多いからかなあ)

結局、画像はファイルのままで置いといて、ファイル名だけDBで管理するということになる。 がしかし、例えばECサイトでは商品の画像は商品のページで見えさえすればよいというわけではない。 商品管理画面のような、いわゆる店の中の人だけが見れるページにおいても、商品画像は見えて、かつ、メンテのために追加したり削除したりも可能でなければならない。

これがデータベースであれば、データを取り扱う画面が複数にわたろうともSQL文を流すだけで見るも書くも消すも自由自在なのだが、データがファイルの形で格納されているとなるとそうも行かない。追加や削除にはファイルの所有権とWeb層、AP層での実行ユーザーの権限とのかねあいが出てくる。そもそもWeb層/AP層が複数台サーバーで構成されていると、NFSサーバをつくってそこに置くとかまでやらなければならなくなる。DB内のデータ(ファイル名とか)との同期にも気を使わなければならない。

というのが、最近常々、疑問というか不満に感じていたことである(前フリ長っ!)。 どうにかして画像のようなバイナリデータもDBに格納して文字/数値情報もろとも一元管理できないものか? 当たり前とかしょーがないとかで済ますのは簡単だけどそこで思考停止もなあ。

で、考えた。

ラージオブジェクト型じゃなくて、text型でやればいい

例えばPostgreSQLではtext型という、大きな文字列(少なくとも2Gbyteはいける)を格納できるデータ型がある。普通によく使われているので、これで画像データを格納すればいい。格納のときには画像をbase64エンコードで文字列に変換しておく。base64はメールに添付ファイルをつけるときに使われているごく一般的な方式である。PHPでもJavaでもperlでもどんな言語でも共通にbase64形式を取り扱うことができる。

いややっぱり画像データはでかいよ、という話もあるが、よくよく調べてみると5000の商品があるECサイト上の画像データを全部数えてみたら600Mbyte以下でした、なんてことだったりする。 base64エンコードすることでデータサイズが3割増しになり、DBに格納することでさらに内部的な付加情報がついて膨れ上がるが、それでも数ギガに収まってしまう。何年か前ならいざしらず現代のストレージの性能と価格から考えれば微々たる物である。

通常の文字/数値データを格納したテーブルと一緒にしておくとディスクI/O負荷がやばくね?という問題については、別のストレージ上で別のTABLESPACEを切っておいてそこに画像格納用テーブルを置くといったことで対応できるだろう。

DB上の画像データをファイルシステム上にキャッシュしておくためのmod_rewrite活用

画像がDB上にあるということは、例えばHTML上にはこんな感じでimgタグを書くのか?

<img src="getimage.php?imageid=123456" />
いやいや、こんなのをECサイトの商品一覧画面上にたくさん埋め込んでいたら、それこそAP層もDB層も過負荷で悲鳴をあげてしまうだろう。そこで、こうする。

商品一覧画面や商品詳細画面のページ内部に埋め込まれた、商品画像を表示するタグ:

<img src="imgdir/dbimage_123456.jpg" />
Apacheのmod_rewriteの設定:
RewriteCond %{REQUEST_FILENAME} ^dbimage_\d+\.(jpg|gif|png)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*dbimage_(\d+)\.(jpg|gif|png)$ /getimgfromdb_and_makefile.php?id=$1&type=$2 [R]
このカラクリはこうである。
  1. 最初の訪問者が商品番号123456のページにアクセスする。http://ec.example.com/product.php?id=123456 とか。
  2. ブラウザはそのHTML内部に書かれたimgタグにそって、http://ec.example.com/imgdir/dbimage_123456.jpg へ開アクセスする
  3. ここでmod_rewriteが働く。ファイル名が「dbimage_数字.jpg(またはgif/png)」で、かつ、そのファイルが存在しない場合に、404エラーを返すのではなく302リダイレクトで http://ec.example.com/getimgfromdb_and_makefile.php?id=123456&type=jpg というページに誘導する
  4. ブラウザは素直にアクセスしなおす。(HTML内部の画像のことなのでユーザーの見た目にはわからない)
  5. getimgfromdb_and_makefile.php というわざとらしい名前のスクリプトは、その名の通りDBから画像データを取得しbase64デコードして通常の画像データに戻し、typeを判別してContent-Type: image/jpgヘッダを出して画像データをそのままブラウザに返しつつ、裏では imgdir/dbimage_123456.jpg というパスで画像データをファイルに保存する
  6. 二人目以降の訪問者が同じページを訪れるときには、すでに画像ファイルが生成、保存されており、http://ec.example.com/imgdir/dbimage_123456.jpg にアクセスするだけで素直にその画像ファイルを返すので、phpスクリプトが稼動することも無く当然DBアクセスも発生しない。
mod_rewriteというと最近ではSEO対策のためのURLの簡素化の目的で使われるケースが多いが、こんな風なキャッシュもどきにも使える。

これでDBの負荷問題はクリアできる。キャッシュとして残るファイルの後始末だが、ファイルの更新日時を見て、一定期間以上古いファイルを消す、といった大雑把なバッチ処理をcron実行するだけでもいいだろう。それこそ1行コマンドでもできる。
/usr/bin/find /hogehoge/imgdir -type f -mtime +30 | /usr/bin/xargs -r /bin/rm
とか。(上の場合は更新日が30日以上前のファイルを消す)
ファイルを消してしまっても、もし必要ならまた自動的に作成されるのだからあまり慎重に考える必要も無い。

もしもバックヤード画面(商品管理とか)がエンドユーザー用画面とは違うWeb/APサーバー上で稼動していても、同様のキャッシュもどき手法を使えばいい。エンドユーザー向け画面から見るのと同じ画像ファイルを指向させるように気を使う必要が無いのがこの方法のいいところである。「web/apサーバが複数あるからNFS張るとかrsyncで配るとか、、、」といったことも考えなくて済む。「どの機能もあるひとつのDB層を見に行く」というごくごく基本をおさえるだけで済むようになる。

なお念のため書いておくが、Webサイトのロゴ画像とかデザイン調整用の1x1ドットの透過gifとかまでDBに格納する必要はまったくない。そういうのはソースコードと同様にAP層に所属されるものと思っていいはず。ここで言っている画像とはあくまでも「データ」の分類に属するべき=DB層に格納されるべき=画像情報を意味している。

その他細かいTIPS

わかりやすくするために上の話では省いたことその1。画像の命名方法。

例えばECサイトの画像だとすると、商品番号をそのままファイル名に当てようと考えるのが普通である。 productimg_0000123.jpg とか。がしかし、 商品ひとつにひとつの画像とは限らない。だから productimg_0000123-001、productimg_0000123-002 というふうに枝番をつけたりする。 また複数の異なる商品のページで、共通の画像が使われるというケースもあるだろう。 そういうのだと商品番号に紐付けるわけにもいかず、結局は命名規則は有名無実となり、 画像保存ディレクトリの中はシッチャカメッチャカになってゆく。 まったく同じ中身の画像が異なる名前で沢山存在してもうどれがどれやら、みたいな状況もめずらしくない。

だったらもう商品データと画像データの直接的な紐付けはあきらめたほうがいいだろう。 そこで、画像データのハッシュ値(MD5やSHA1)を画像の名称に割り当てるという規則にする。 「31ec79b6ad821e7c5568170151e65d97」とか。 画像を格納するテーブルの構造はこんな感じ。(PostgreSQLを想定)

CREATE TABLE IMGDATA_TBL (
IMGID VARCHAR(32) , -- 画像データのmd5値を想定しているので32バイト
IMGTYPE VARCHAR(3) ,-- 画像の種類、img,gif,pngなど。ファイル化してキャッシュ保存するときの拡張子に使う
IMGDATA TEXT, -- 画像データをbase64エンコードで文字列化した値を入れる
PRIMARY KEY (IMGID) -- プライマリキー
);
(その他、必要に応じて画像の縦横サイズや容量のカラムを用意してもいいかも)
こんな風にしておくと、まったく同じ画像データが異なる名前で複数あるといった状況は避けられる。 あるいは同じファイル名だが中の画像は異なるファイルが、あっちのディレクトリとこっちのディレクトリで別々にある、といったややこしい状況も避けられる。

一方で商品データと画像データの紐付けはそれようのテーブルを用意して間接的にヒモづければいい。

CREATE TABLE PRODUCT_IMG_REL_TBL (
PRODUCTID VARCHAR(16), -- 商品番号
IMGID VARCHAR(32), -- 画像番号
PRIMARY KEY (PRODUCTID, IMGID) -- 複合プライマリキー
);
これで、ひとつの商品ページに多数の画像を使用していても、逆にひとつの画像が複数の商品ページで使われていても、対応できる。(必要に応じて表示順位付けのカラムとかも。)

わかりやすくするために上の話では省いたことその2。画像が大量な場合には格納するディレクトリを多階層に分ける。

上の例で行くと、キャッシュとして生成されるファイルは 「プレフィクス+画像自体のハッシュ値+拡張子」といった命名規則が考えられる。 たとえば「dbimage_31ec79b6ad821e7c5568170151e65d97.jpg」。

ところが、調子に乗ってひとつのディレクトリ上に多数のファイルを作成すると、ファイルシステム自体の性能が劣化することがある。筆者の経験上、Linux等で広く使用されているext3ファイルシステムではひとつのディレクトリ内のファイル数が3000とか5000とかを超えるあたりで、読み書きに著しく時間がかかるようになる。lsコマンド叩くだけでも結果出力までしばらく待つような状況はちょっと寒い。

もちろんext3じゃない今風のファイルシステム=xfsとかReiserfsとか=ならこうした心配は少ないが、あっちのストレージはext3でこっちのストレージはxfs、とか分けて管理するのも意外と面倒である。

手軽な策は、ディレクトリを分けること。たとえば
dbimage_31ec79b6ad821e7c5568170151e65d97.jpg
というファイルは
imgdir/3/1/dbimage_31ec79b6ad821e7c5568170151e65d97.jpg
というディレクトリに格納する。ハッシュ値はたいてい16進数つまり0-9とa-fの16種類で表現されるため、その16種でディレクトリを掘り、それを2階層にすると16x16=256個のディレクトリに分けることができる。

たとえば10万アイテムの商品があるECサイトで1アイテムあたり画像が3つあるとすると、 10万x3÷256≒1171であり、ディレクトリひとつあたりのファイル数を2000以下くらいには抑えることができる。もっとファイル数が増えそうだということであれば3階層、4階層に増やせば余裕だろう。

ここまでの2つの話をまとめると、こういうことになる。

商品一覧画面や商品詳細画面のページ内部に埋め込まれた、商品画像を表示するタグ:

<img src="imgdir/3/1/dbimage_31ec79b6ad821e7c5568170151e65d97.jpg" />
Apacheのmod_rewriteの設定:
RewriteCond %{REQUEST_FILENAME} ^dbimage_[0-9a-z]{32}+\.(jpg|gif|png)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*/dbimage_([0-9a-z]{32}+)\.(jpg|gif|png)$ /getimgfromdb_and_makefile.php?id=$1&type=$2 [R]

とりあえずいまわかっている制限事項

実はOracle9iには、「ひとつのSQL文の長さは64Kbyteまで」という制限がある。ということは、「insert into hogehoge_tbl values (id, '画像データをbase64エンコードしたもの......')」というSQL文において、画像自体が64Kbyteを超えていたら間違いなくこの制限にひっかかる。64Mbyteならともかく64kでは小さすぎる。つまり画像データをエンコードして文字列としてDBに格納してしまおう作戦はDB層がOracle9iの場合は事実上不可能である。ちなみにOracle10gではこの制限は無くなったらしい。

PostgreSQLではもともとこうした制限は無い(もしあったら巨大な文字列を入れることができるというtext型カラムの存在意義がない)。MySQLだと、SQL文の制限というよりは通信上のパケットサイズかなにかだったか忘れたがそんな制限があったような気がする。

また、DB層での制限だけでなく、AP層とDB層とが通信する領域=ODBC/JDBCドライバなど=でのなんらかのデータサイズ制限にひっかかる可能性も考えられる。いずれにせよ自分が使おうとしているハード/ソフト構成のなかで「巨大なSQL文がちゃんとやりとりされるか?」くらいは軽くテストしておいたほうがいいだろう。

長々と書いたがとりあえずここまで。 こういうのも一種の「富豪プログラミング」なのかな。

see also: