奇特なブログ

「殊勝に値する行いや心掛け」を意味する、奇特な人になる為のブログです

2016年7月16日のダイジェスト

長いので、こちらにまとめて書くことにしました。
短いなら、TwitterとFBで良いんですけどね。


1.プログラマが知っておくべき、メモリ/ディスク/ネットワークの速度まとめ
qiita.com
おお~アプリ「全体」でチューニングをする事の大切さが伺える記事ですね。
・・・ネットワークの速度が気になる所。10Base-Tとか100Base-Txとかのアレ。
あと、モバイルはやっぱり、クライアントとサーバーの間が遅いようで。


2.プログラミング言語の水準について
http://qiita.com/kantomi/items/747aef968d37b5ebeced
「技術者なのに自分の考えを持たず、惰性で使っている」っていうのがポイントなんでしょうと。
対象技術に対して深く考察し、メリットだけではなくデメリットも認識して、
それで、それを使い続けるなら、「道具に振り回される事もなくなる」と思いますから。


3. 日本デジタルゲーム学会2016年夏季研究発表大会|DiGRA JAPAN 2016 Summer Conference
あぁ、今回は頑張って行きたい所。


4.選挙権がメルカリで出品出来た?
・・・典型的な「それをお金で買いますか(本の名前)」的な事案っぽい。


5. Expired
韓国以前に、最初に見た時に「これはヤバイ」って事で、完全にガン無視状態ですね。LINEは。
もう、記憶も曖昧だし、今はどうなってるか知らないですけど、
最初は、自分がアドレス帳に登録している友達の住所も、他の友達に公開されるとかだった様な?


6. http://www.jp.square-enix.com/info/images/image_technical_seminar2014_06/pdf/SQEX_DevCon_sugimoto.pdf
リソース管理の所・・・あぁ、まだまだ詰められますね自分のプログラム。
ただ、やり過ぎると使いにくくはなるとは思うので、一定以上の熟練が要求される話には思いましたけど。


7. PHPのround関数とは一体なんだったのか - hnwの日記
あぁ、期待値計算する所でround関数使ってるなあ。
ここは・・・GMP


8. https://www.amazon.co.jp/dp/B017W1U2CU?tag=bookquote-22
Kindleのみか・・・いい加減に導入しようかとも思う時はあるんですけどね。


9.



シミュレーションゲーム 戦闘予測」で、普通にありましたよ(笑)
こちら http://www.vector.co.jp/soft/winnt/game/se483057.html が。
むしろあった方が、イメージしやすいし良いのでは。
実際、ゲームが違うから全然違うけど、よく似てますし。
あと、「性欲 コントロール」も勿論沢山ヒットしますけど、
オナ禁・・・あぁそっちか、確かに年間500回以上抜くのも、それはそれでどうよと(笑)
・・・3日間、我慢してみましょう。過去にやったことあるから想像は付きますけど(笑)


10. http://headlines.yahoo.co.jp/hl?a=20160623-00004810-bengocom-soci
女優に飯食わせてもらっているんじゃないのかの一言で終わりかなと。


11.オフショアで気になった点
元債権取り立て屋からすると、捕まえるのが難しそうだなぁと。
都合悪くなったので母国に帰ったその後不明、リモートワークなので連絡付かなくなってどこにいったか分からなくなった、
とりあえず二つ思い付きましたね。


12.わかりやすさの技術 - やしお
おお、これは・・・じっくり読む事に。


13. http://alfalfalfa.com/articles/134817.html
デフレはあるんでしょうけど・・・1円なら分かるんですけどね。
まだ、「お金が動く」から。
この辺で一番気になるのは、デフレ脱却政策をしているにも関わらずお金を動かさなかった時ですね。


14.


セキュリティと同じでイタチごっこですから(笑)
実際、パンチラ対策(だと思うが)でのペチパンも「ペチパンツ エロ」でググったら分かる通りの状況になってる様ですし(一部では)(笑)
あとは、個人的には「パズル技」ってのもありますね。


15.


何の技術を使っているかは、クライアントには「関係無い」話なんですけどね。
あと、使っている技術を面談時などに聞くことは勿論あるけど、それは重要度としてそう高くはないですね。
趣味ならともかく、ビジネスなら「上手くいく(継続的なら尚良い)」って事の方が重要かと。
極論、PHP4でも場合によってはアリだと思いますし。


16.


あぁ、そういえば・・・僕の場合はそもそも見れなくなったので、魚拓を取ろうにも取れない状況ですが(苦笑)
有料ユーザーだし、魚拓ももう少し有効活用しませんとね。


17.言葉を使う意味
・・・あぁ、なるほど。
椎名林檎の本能って曲の歌詞を思い出しましたね(笑)


18.消極的支持は暴走も許す?
2/3って結構暴走出来ると思うので、消極的支持なら避けた方が良いんじゃないかって思って避けたんですが。
事前に1/2はほぼ確実に取る事は分かっていましたし。


19.性犯罪の抑止力
http://barpeachpit.blog86.fc2.com/blog-entry-409.html
そういえば、これもありましたね。
あと、こっちはデータですが http://skymouse.hatenablog.com/entry/20140430/1398800010
あるいは、もっと日常的な話として、
「性犯罪をやって”一時的”に性欲が満たされるメリットと、捕まって仕事や家族などの"今の生活"失ったりするデメリットとを天秤にかけたら"割に合わない"よな」
とか。
あるいは、「女性が好きなんでしょ?で、"好きな相手が不幸になるのが良い"ってどういう趣味?幸せになってほしいんじゃないの?」とか。
あるいは・・・今回はここまでで。


20.生産性アップや効率化のデメリット
供給が需要を上回っている時に、生産性アップや効率化をすると、
供給量が更に増え、そして増えた供給量をさばく為に、価格を安くせざるを得なくなる。
そして、価格が安くなるということは、入ってくる収入も減るので、
入ってくる収入を回復させようと、更に生産性アップや効率化を図る・・・以下、無限ループ。
で、今の「供給と需要の割合はどうなっているか」。
「働いたら負け」とか「だらだら仕事した方が良い」だと、
逆に供給が足りない時に困るので、生産性アップや効率化自体が出来た方が良いのは確か。
ただ、状況によっては、「自分で自分の首を絞める行為」だと思いますね。


21.書いてることに意味がある
・・・ちょっとこれは想定外な良い発見でしたけど。
あまり、詳しくは書けないですけどね。残念ながら。
ということで、ペースはさておき今後も書いていく事にはしましょう。

第三回雑談の会開催(4/18~4/24)

そういえば、しばらくやってないなって事で。
先月、以下の記事も見かけましたしね。

wadap.hatenablog.com

あと、今回は用事とか考えて、期間を1週間にしました。
といっても、仮に応募が殺到(間違いなくしないと思うが)したら、
キャンセルさせていただく形にはなると思いますけど。

内容は、以下の第一回と基本変わりません。

kitoku-magic.hatenablog.com

また、連絡は、
Twitter(DMも全体公開中)・Facebook・HP内のコチラ
http://www.kitoku-magic.net/inquiry.html 等からいただければと思います。

システム開発における自動化の一例(あくまで一例)

こちらでは、すっかりご無沙汰しております。
まず、前回 2015年の振り返りと2016年の展望 - 奇特なブログ の年末年始に予告した記事は、
別に忘れてはいないんですけど、
内容があまりにも壮大になってしまいそうなのと、
どう書くかで悩んでいる(&調査中)ので、
時期未定に変更します。すいません。
対象になっている本を読んだ方が早いと思いますね、正直。


さて、本題なんですが、
最近「自動化」について考える事があったので、
これまでに見聞きした自動化手法なんぞをメモしておこうかと思いまして。
なので、「一例」なんですけど、
とりあえず以下の様な感じです。
あと、実装が簡単と思われる順番に書いてます。


1.この時間からこの時間まではイベント(別にソシャゲに限らず)を開催中にして、
時間外だったら、非開催中に自動で切り替わるようにして欲しい

以下が、方法(の一例)です。


方法:
DB(CSVとかでも良いけど)に、
イベント情報のテーブルを作り、その中に開始日時と終了日時を持たせる。
そして、イベントページ(仕様によってはTOPページとかも)にアクセスしたら、
アクセスした「サーバー(クライアントはダメ)」の現在日時が、
イベント開始日時と終了日時の範囲に入っているかチェックする。
入っていたら開催中、逆なら非開催中。


まぁ、これは簡単かなぁと。


2.指定時刻になったら、指定したファイルを自動で本番に反映してほしい

ここ(に限らない気もするが)は、やり方が以下の様に複数考えられますね。
あと、ここの(1)~(3)は、この後も出てくるので注意です。

(1)crontabに指定時刻を登録しておく場合
(2)DB(何度も言うが、別にDBじゃなくても可)のテーブルに指定時刻を登録しておく場合
(3)crontabに定期巡回をさせる場合

で、この場合は(1)が一番自然で簡単かなぁと思われます。
次が(2)で、(3)は出来るというよりやっちゃダメ(高確率で事故りそう)と思われます。
で、(1)だと以下の様な感じで。


方法:
crontabにファイルの反映(デプロイ)を開始したい時刻と、
デプロイするシェルスクリプト(ある現場、多いでしょ)を登録しておく。
デプロイするシェルスクリプトは、反映したいファイルパスが書かれているファイル名リストを参照し、
そのファイルのみを、本番に反映させる。
本番反映が終了したら、crontabの該当行はコメントアウト(しなくても良い気もするが)しておく。


ここは、「ファイル名リスト」って所が若干手間かなぁ・・・
コミットしたら、フックスクリプトで、そのファイル名リストを書き換えれば良いか(笑)
指定時刻も書き換え?出来るとは思いますけど、「こういう系って、あんまりやると事故ります」よ。
なので、この辺までかなぁと。


3.指定時刻になったら、自動でテストツールを実行して欲しい

ここは、例えば毎日何時って決まってるんだったら、
上記の(1)が一番かなぁと。
crontabの編集NGなら、(2)もありでしょうけど。
(3)は「頻度による」としか。


方法:
crontabにテストツールの実行を開始したい時刻と、
テストツールの実行コマンドを登録しておく。


で、ここから( or ここで)、カバレッジを取るだの何だのは、現場の方針次第ということで。
あと、上記2と合わせると、Jenkins不要説(まともに使った事無いので、これだけじゃないのかもしれないけど)も(笑)
いや、だって僕の口癖は「面倒だなぁ」ですから、
「覚えなくて良いなら、覚えたくない」んですよね(苦笑)


4.指定時刻になったら、DBのテーブル内の参照するレコードを変えてほしい

これは、ちょっとマニアック(別に、そう難しくはないと思われますが)かも。
あと、トランザクションデータだと、この辺はどうかなと思いますので、
マスタと履歴データ(さすがに怖いが、こっちは消す方でも使えるかも)が対象かなと思いますね。
あと、上記の(1)だと管理大変そうなので、
(2)か(3)かなぁと。


方法:
DB(何度もry)に、スケジュールを管理するテーブルを作り、
指定時刻になったら、
「このテーブル(テーブルAとする)の、この辺を参照する様にする」というレコードを「開始日時と終了日時」も合わせて登録する。
テーブルA側には、上記テーブルの「この辺を」と一致する列を作り、
列が一致しているレコードのみを参照する様にする。
そして、テーブルAからデータを取得する時には、
事前にサーバーの現在時刻に一致するレコードを、スケジュール管理テーブルから取得(この時、絶対にキャッシュから取ってはいけない)し、
「どの辺を参照するか」を決めてから、テーブルAのデータを取得する。


これ、「どの辺を参照するかを変えるじゃなくて、書き換えたら?」って意見もありそうで、
まぁ、確かにそっちでも全然良いと思いますね(書き換え後のキャッシュ消し忘れに注意)。
なので、ココは議論かなぁと。


5.指定時刻になったら、目覚まし時計代わりに、自分のPC内のアダルトDVDの再生が始まる(笑)自動化版Iot?(笑)

wwwwwwwwwwwwwww
で、ココは、実装は全く思い浮かばないですけど、趣味目的で作ってみるのもアリかも(笑)


ということで以上です。
あとは「番外編」として、
自動化とは全然関係無い、以下の様なモノを考えてみました。
・・・多分、以下の本の「キチガイ(では無いという人も、結構いそうなのが怖いですが)要求」辺りから、連想したと思われます。


www.amazon.co.jp


番外編:ファイルのバージョン管理を、バージョン管理ツールを使わないで行いたい

まず、以下の様なテーブル(書式などは若干適当)を作る。


CREATE TABLE file_version_history (
revision INT NOT NULL COMMENT 'リビジョン番号',
no INT NOT NULL COMMENT '識別番号',
file_name TEXT NOT NULL COMMENT '更新されたファイルのパス情報',
change_type INT NOT NULL COMMENT '追加や削除などの更新種別',
file_bosy TEXT NOT NULL COMMENT 'ファイルの中身の内容',
insert_date DATETIME NOT NULL COMMENT '追加日時',
PRIMARY KEY (revision, no)
) COMMENT 'ファイルのバージョン管理テーブル';


次に、クライアントツールやWebでの管理ツールを作り、
そこに、「コミットする」という項目(Webならボタンとか)を作る。
コミットする機能を使用すると、
上記テーブルにレコードが登録される。
差分を取りたい場合は、
上記テーブルの情報と現在の情報を比較する。
リバートしたい場合は、
上記テーブルの対象リビジョンのレコードをDELETEし、
DELETEしたレコードは、同じ様な構造のバックアップテーブルに入れる。
リバートした内容を更にリバートしたい場合は、
バックアップテーブルから、レコードを取ってきて入れ直す。
フックスクリプト?→あくまで番外編なので、一旦ここまで。

こんな感じですかね。

例によって、ご感想などをお待ちしております。

2015年の振り返りと2016年の展望

さて、毎年恒例となりましたが、今年も書きましょう。
で、書く前に「ネタが凄い多くなるかも」と思っていたんですけど、
全部覚えている(特にメモも取ってないので)わけでもないので、
思い付いた内容から書いていきたいと思います。
まずは、2015年の振り返りから。


1.お金を持っていると、出来ること(選択肢)が増える


これが、まぁ2014年もそうだったし、
考えてみたら昔パチプロをやっていた時もそうだったんですが、
特に実感した所ですね。
だって、「働いた月数」が、
2014年は「8ヶ月」、
2015年は「4ヶ月」、
パチプロ(パチプロの定義によっては仕事かもしれないですけど、少なくとも僕のやり方は外れていたかな)の時は「0ヶ月」でしたから。
というか、2015年酷いなコレ(苦笑)
あ、講師の仕事は月数に含めていないですが。


で、上記の場合だと、「仕事をしないという選択肢が増える」ですかね。
まぁ、勿論他にも色々ありまして、


・より美味しい料理を食べることが出来る
・色々な所に旅行に行くことが出来る
・色々な所に寄付する事が出来る
・自分の好きな相手に、プレゼントをすることが出来る


とか、書きだしたらキリないでしょうけど。
で、上記の項目の接頭辞に「お金を持っていると」を付けてみるとピンと来るんじゃないかなと思うんですけどね。


ちなみに、仕事に対してこの事実を悪用すると、
「給料を多くすると辞められる可能性が高くなるので、給料も高くせずに且つ、
転職活動をさせない為に仕事の量を大量に押し付けて、プライベートな時間を減らす。
で、それでも止める様なら、同業(競合)他社への転職禁止的な誓約書なりに同意させたりしてプレッシャーをかける」
という様な、結構ざっくりですが、ブラック企業の経営者が考えそうなロジックにもなったり(苦笑)
ちなみに、最後の項目については、
「機密と定義されている情報を持ち出して、他社でのビジネス展開に活かそうとした」ならNGかも(専門分野じゃないので要確認を)ですが、
単純に、同業他社への転職禁止なら、憲法職業選択の自由の条文の方で引っ掛かると思いますね。


で、まぁ、上記の件については「気を付けましょう(僕自身もですが)」としか言いようがないですけど、
話を戻して、とにかく「お金を持っていることに伴うメリット」というのを再実感出来たココ2年間だったなと。


なので、「お金を幾ら頂けるのか」は拘った方が良い(勿論、拘れば拘る程仕事減るので、そこは注意ですが)と思いますし、
「収入アップに繋がりやすい努力(それが全てではないのは知ってます、というか元々その傾向強い)」の比率を高めるのが大事だろうなぁと。


ちなみに、こういう話するとよく、
「お金を持っていたって幸せになれるとは限らないでしょ?」的な感想を持つ方もいらっしゃる様ですけど、
えぇ、まぁこれは特に否定もしません。
ただ、幸せって「お金で買う」系の幸せもあるんですよね。で、貧乏だとそれが出来ない。
例えば、「お金が無いからゲーム(ココは自分の好きな趣味でOK)買えないので、幸せを感じにくい」とか。


なので、幸せと一言でいっても色々とあるので(お金で買えない幸せも当然ありますし)、
一概には言えないかなと。


長くなりましたけど、ひとまず1番はここまでにします。


2.禁煙や健康面


これは、つい最近の話ですけど、
先日、気管支炎(正確にジャッジするのは難しいみたいですし、とりあえず肺炎では無かった)にかかりまして、
2週間ぐらい、咳が止まらなかった(具体的には、痰が喉に溜まっている状態)と。
今も、まぁ「完治」はしてないですかね。なので、カラオケには若干悪影響はありそう(笑)


で、これだけだと単に「ただ、風邪引いただけでしょ」なんですけど、
僕の場合、生まれてから「ずっと」、「風邪を引いたとしても、せいぜい3日もあれば完治する」のが特徴でして、
なので、これは珍しいと。まず、これが一つ。


で、もう一つは、同業の知り合いのフリーのエンジニア(スキルは僕より10倍以上、上かな)が倒れて1か月以上入院し、
生死の狭間を彷徨ったらしいと。
で、どうやらコレ、そう大げさな話でも無かったみたいなんですね。で、これが二つ目。


で、上記二つの話を合わせると、
まぁ、元々どのくらい健康体質なのかってのはあるかと思うんですが、
それでも、多分「働き過ぎによる疲労の蓄積」とかはあるのではと。
かといって、じゃあ働かなければ良いという選択肢も、
今後10~20年という様なスパンで見た場合には難しいだろうなぁとも思うので、
結論として、多分「長生きは難しいなぁ」と。
僕個人に関しては、このままだと60歳になる前に死んでる気がしますね。
上記の気管支炎は、ヒヤリハットでいう「ヒヤリ」じゃないかと思ってますし。
だから、未婚の女性からすると、僕と結婚するのはリスクが高いかもしれないですね。


ただまぁ、気管支炎がきっかけで禁煙も出来る事が分かった(喫煙歴20年ぐらいだったんですが)ので、
良い事もあった(というか作った)んですけどね(笑)


で、根本的な改善については、この後の3と4も若干絡むんですけど、
基本的にはかなり難しいのではとも思いますね。
少なくとも、エンジニア全員が取れる選択肢でも無いでしょうし。


あと、健康に関しては、そういうば以下の記事もありましたね。


gallu.hatenablog.com


3.クビになった件


そういえば、これはまだ公にはしてなかったんですけど、
振り返るとこれは重要だなぁと思った出来事を一つ。
今年の5月に講師の仕事をしていた時だったんですけど、
「研修初日で、もう次の日からは来なくて良い(その日の講師料は頂きましたが)」と言われたんですね。
で、聞いた時には「え~、そんな予兆全く感じなかったし、気になる点あるなら言ってくれれば良いのに。あと、たった一日で判断されるの?」という感想だったんですね。
なので、2~3日、ショックで寝込んだ記憶が(苦笑)
ただまぁ、高給(少なくとも、時給換算で今までの開発案件より高かったのは事実)を払うクライアントからすると、
「こっちは高いお金を払っているわけだし、提供内容がコストに見合わなければ切らざるを得ない」というロジックは当然あるだろうなぁと。
あと、「改善しますって口で言われても、本当に改善出来るのか分からないし」という話も。
で、前者は僕が逆の立場だったら(なった事無いけど)そう思いますし、
あと後者に関しては、開発の案件で役職が上の人が「私の責任です。今後、改善していきます」って言ってるけど、
ちっとも改善してないし、改善する気が本気であるのかも分からないしって辺りを考えると、「妥当かもなぁ」とも思いますね。
何というか、意外と日本(といっても、前述の通り場所によるわけですが)ってビジネスに対して「甘い」のかもなぁと、この辺見ると。
いやだって、「結果が出ないなら、降格させて担当変えるなり、最悪クビ切れば良い」じゃないですか。
特に、外注なら尚更。
他の人はどうか知らないですけど、少なくとも僕は「いつクビになるか分からない」と思って仕事してますし。
前述のクビになった出来事の後は、特にそうですね。
で、こう思えた事を考えると、前述の出来事も良かったかもね。


で、この辺が4にも出てくる「雇用の流動性」にも関連あるなぁと。
多分、「雇用の流動性」が無いと、仕事に「緊張感が生まれない」って一面はあるのではと。


4.海外について


この辺は確か、今年の4月に、「シアトルで日本人エンジニア募集」みたいな話をTwitterで知って(スキルがミスマッチでダメでしたが)、
あと、以下の本も最近出て読んで(っていうのと、勿論上記の国内の状況もありますが)視界に入る様になりましたね。


www.amazon.co.jp


いや、話はかなり単純でして、
「日本でフリーでエンジニアやっていけないなら、海外(特にアメリカに限らず)行くしかないよな」という(苦笑)
あと、上記の本については、かなり思う所ありましたので、
出来れば、正月休み中に、もう一つの方のブログ http://d.hatena.ne.jp/kitoku_magic/ に、
書きたいと思います。
今の所、この本の他の人の書評を読んでる限り、あまり踏み込んだ内容を書いてる人がいなかったとも思いましたしね。



では、2015年の振り返りはここまでで、
ここからは、2016年の展望を。


1.開発案件


出来ればゲーム以外の案件(ゲーム以外だと単価下がるとか聞きますが、ホントかソレ?)が良いなぁと。
いや、ゲームだけは、どうも異常に拘りが強いせいか、
以下の記事の症状がねっていう(苦笑)


gallu.hatenablog.com


あと、この辺は例のエロの方も拘りが強い(下半身専門で、キモイ系)気がするんですけど、
最近、「上半身もアリじゃないか?(上半身だと、女性の"顔"が見えるw)」とも思う様にもなってきており、
DMMとかで、アダルト関連の案件をやってみたい気も(笑)
あ、ただ上半身でも下半身でも、キモイ系だけは共通しているかもしれないですね(苦笑)


2.講師案件


専業では考えていないですが、
「口ベタ」を改善したいかなぁという気持ちもあり。
あと、単純に上記のクビになった件のリベンジも(笑)


3.英語


上記の海外云々の話も勿論ありますけど、
別に国内にいても「英語のドキュメント(最近、よく見かけるので)」を読むのに困るので。
ちなみにこの辺、期間(何ヶ月が妥当かは別途考えるとして)を区切って、一気にTOEIC800点(じゃなくても良いかもしれないが)とかを狙った方が良いんじゃないかと思っています。
「期間を長く取って、コツコツと覚えていく」ってのは、あまりピンと来ない(頻繁に使う場合には、左記の方法で良いと思うんだけど)ので。


4.雑談会の再開催


そういえば、すっかり忘れてましたけど、以下の記事の事ですね。
「基本理念」的な話は、第一回の方に書いてます。


kitoku-magic.hatenablog.com


ただまぁ、これ系って別に、何時でも良いっちゃ何時でも良いんですけどね。
ただ、イベントを立てた場合には、「他の用事は絶対に入れない」ってだけなので、
気軽にお問合せ下さいということで。


5.ゲームから学ぶ系


いや、別に今でも興味は全然あるんですけど、何しろ「お金に繋がりにくい」ので。
ちなみに、夏にこれと同じ様な内容の以下のイベントに行ってきましたけど、
そこでも、「予算(ここでは研究の事)不足」については聞きましたね。


2015年夏季研究発表大会 | DiGRA JAPAN


で、予算が付かない原因は色々とあると思うんですけど、
仮に、政府がOK出したとしても、
国民側の猛反発もそれなりにありそうに見える(僕がまともだと思った人でも、この分野はどうも意見が割れるみたい)ので、
言う程簡単じゃないなというのが、個人的な印象ですね(苦笑)
なので、ココ2年は、この分野にかなりの時間を割いて(仕事やってない時期は、殆どコレですし)きましたが、
少し、トーンダウンすると思います。
いや、何よりもお金の問題なんですけどね。


6.自作アプリなど


まず、ホームページはリニューアル予定(特に、書籍ページ。個人的に使いにくくなったので)ですと。
で、これは1年もあれば、さすがに終わると思います。
あと、以下にも書いたエロアプリの件。


kitoku-magic.hatenablog.com


う~ん、ひとまず「可愛くて恋愛要素が強い」系と、
「エロ要素が強い系」で二つありまして。
で、前者だと、設問ネタが少ない・当たり障りがあまりないって所から、多分「売れない」んですね。
ただ、逆に後者だと、仮に売れた場合(といっても、売るのすら難しいですが)に「批判を浴びやすい」というのもあり。
先日の、おっぱい募金反対の声とか見ても。
ただまぁ、個人的には批判についてはあまり気にしてなくて、
どっちかというと、以下の刑法175条の方が引っ掛かってるんですけどね。
「文章」も、対象に入ってますので。


刑法第175条 - Wikibooks


あとは、まぁ類似としては、以下の様なのも見つけた(ホントかどうかは知りませんけどね)ので、
どっちかというと、一般人の批判とかよりは、警察の動きの方が気になるなぁと。


d.hatena.ne.jp


ただまぁ、ここで問題なのは、
文章とは言うものの、「パンツ」とか「お尻」とか「おっぱい」と書くだけでNGなのかといったら、
そんな事はない(もしあったら、それこそ社会がおかしいわ)わけで、
「線引きのライン」があるわけですが、これがどうも分かりにくいと。
なので、この辺がある程度見えないと、ビジネス展開は難しいですね。


ちなみに、この分野、おそらく海外での展開は難しい(日本より、この辺うるさい様に見える。宗教もあるかな?)でしょうね。
そういう意味で、グローバル的にみたら「ダメ」なんでしょうけど、
「日本独自」というのを強みにするなら「アリ」って感じでしょうね。
で、どっちが良いかは、国の展望(目指す方向性とか)によるんじゃないかなと。
ひとまずは、ここまでにします。



という感じで、2016年も盛り沢山な感じですが、どうぞよろしくお願い致します。

Webアプリにおけるプログラムのパフォーマンスチューニング

さて、ちょっと予定には無かったんですけど、

最近この辺を考える事がやけに多く、

ということで、こんな記事を書いてみようかなぁと思います。


ちなみに、非エンジニアがエンジニアを評価(出来るのか?って話もありますが)するにあたって、

チューニングをした結果の「レスポンスが10秒→1秒になった」というのは、

スキルジャッジとしては分かりやすい指標だろうなぁとは思うので、

そういう意味でも「チューニング実績」というのは良いのかもと思いますね。


では、本題です。

が、急いでいる人?の為に、結論を先に。


1.JOINが出来るなら、まずはJOINを検討。あと、OFFSETとLIMITが使えるなら、よりベター。

2.JOINが出来ないなら、NoSQLを上手く使う。その際、出来るだけループ内でデータ取得処理を行わない様に工夫する

で、基本は、上記の流れで良いんじゃぁないかと。


というのが結論なので、どうしてこういう結論になったかは、以下を読んでくださいということで。

あと、最近読んでいる以下の本(かなり面白い)を見て思ったんですけど、

チューニングと一言でいっても色々あるわけでして。

で、今回はタイトルにもある通り、

プログラム(といっても大体SQLのSELECT関連)サイドのチューニングになります。


www.amazon.co.jp


あと、以前に以下の論理削除の記事でも若干触れた、

「マスタデータを何処に持つか」という話も若干絡みますね。


kitoku-magic.hatenablog.com


あと、地味かもしれないですが、

以下の記事もかなり役に立ってます。

というか、この記事見て完全にピンと来たんですけどね。


php-sql-gdgd.jugem.jp


では、「どういう時のボトルネックに対するチューニングなのか」ですが、

「ユーザーIDをキーにして、DBのトランザクションデータからデータを取得(データA)して、その後データAの中のマスタデータのIDをキーにしてマスタデータからデータを取得(データB)する」という様なプログラムです。

勿論、更に続きでデータC・データDと取得したりする事もザラではありますけど。

で、プログラム自体はよくあるプログラムだと思うので、

結構色々な環境で使えるんじゃないかとも思うんですよね。

じゃなきゃ、わざわざ書かないんですけどね(笑)


では、サンプルプログラム(合計9パターン)のご紹介です。

殆ど変わらないのもあるんで、5~6パターンでも良い気もするんですが、折角なので全部。


先にパターンと実行平均時間と高速度順の順位だけ紹介しておきますと、以下の通りです。


【1.トランザクションデータとマスタデータをJOINする】0.0067554741秒(1位)

【2.DBサーバーが別だとJOIN出来ないので、クエリを分ける】0.5855079975秒(8位)

【3.DBサーバーが別だとJOIN出来ないので、クエリを分ける & MySQL複数接続】0.6397109158秒(9位)

【4.マスタをMemcachedにキャッシュする】0.5397023411秒(7位)

【5.マスタをMemcachedにキャッシュする & キャッシュのキーが一つ】0.0144977431秒(2位)

【6.マスタをRedisにキャッシュする & キャッシュのキーが一つ】0.015953728秒(3位)

【7.先にマスタデータを全件取っておく】0.1823107789秒(6位)

【8.マスタデータを事前にAPCに突っ込んでおく】0.122105834秒(4位)

【9.マスタデータを事前にRedisに突っ込んでおく】0.180010453秒(5位)


まずは、DB関連から。


【0.テーブル定義とデータ投入】


// トランザクションデータが入るテーブル
CREATE TABLE `test` (
`user_id` int(11) NOT NULL,
`code` int(11) NOT NULL,
PRIMARY KEY (`user_id`, `code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


// マスタデータが入るテーブル
CREATE TABLE `code_master` (
`code` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

// データ投入シェルスクリプト
#!/bin/sh

file_name='insert_code_master.csv'

# マスタテーブルに10万件のデータを投入
for i in `seq 1 100000`
do
  echo "${i},${i}" >> $file_name
done

file_name='insert_test.csv'

# トランザクションテーブルに1億件のデータを投入
for i in `seq 1 100000`
do
  for j in `seq 1 1000`
  do
    echo "${i},${j}" >> $file_name
  done
done


LOAD DATA LOCAL INFILE 'insert_code_master.csv' INTO TABLE code_master FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';

LOAD DATA LOCAL INFILE 'insert_test.csv' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';


まず、ここまでです。

ちなみに、以下のインデックスも張ろうかと思いましたが、

結果的に誤差レベルの差で殆ど変わらなかった(今回のクエリではですが)ので、

張ってません。


CREATE INDEX test_code ON test (code);


次に、以下から実際のコードになりますが、

実行方法は以下の様な事をして、EXCELで平均秒を算出しています。


■実行方法
for i in `seq 1 1 1000`; do php {PHPファイル名}.php >> result_{PHPファイル名}.xls; done &


// apcuの時のみ以下
for i in `seq 1 1 1000`; do curl http://localhost/apcu.php >> result_apcu.xls; done &


【1.トランザクションデータとマスタデータをJOINする】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn) {
    die('データベース接続エラー: ' . mysql_error());
    exit();
}

$user_id = 859;
$start = microtime(true);
$result_array = array();
$sql = 'SELECT cm.name FROM test t LEFT JOIN code_master cm ON t.code = cm.code WHERE t.user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($name);

while (true === $stmt->fetch())
{
    //echo $name . "\n";
    $result_array[] = $name;
}

echo microtime(true) - $start . "\n";


1000回平均:0.0067554741秒


う~ん、速い?

というか、他のプログラムと比較しないと分からないので、次に。


【2.DBサーバーが別だとJOIN出来ないので、クエリを分ける】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn) {
    die('データベース接続エラー: ' . mysql_error());
    exit();
}

$user_id = 859;
$start = microtime(true);
$result_array = array();
$sql = 'SELECT code FROM test WHERE user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($code);

$code_array = array();
while (true === $stmt->fetch())
{
    $code_array[] = $code;
}

$stmt->close();
$sql = "SELECT name FROM code_master WHERE code = ?";
$stmt = $conn->prepare($sql);
foreach ($code_array as $code)
{
    //echo $code . "\n";
    $stmt->bind_param('i', $code);
    $stmt->execute();

    $stmt->bind_result($name);

    while (true === $stmt->fetch())
    {
        //echo $name . "\n";
        $result_array[] = $name;
    }
}

echo microtime(true) - $start . "\n";


1000回平均:0.5855079975秒


あら、JOINに比べてだいぶ遅くなりました。

ちなみに遅い原因は、「ループの中でSQL投げてるから」でしょう。

一回一回のSQLは「軽い」と思います(上記だとPKのインデックス効いてますし)けど、

でも「千回も一万回」も投げたら流石にねぇと。


ところが、つい先日までの僕自身の書き方もそうだったんですけど、

こういうプログラム、かなりの現場に潜んでいるんじゃなかろうか?と。

というか、これまでの経験だけでも、結構見覚えが・・・

いや、勿論この速度はデータ量にもよりますし、

そもそも0.6秒が問題にならないシステムなら良いんですけど、

案件によってはね・・・っていう。


ひとまず、次にいきましょう。


【3.DBサーバーが別だとJOIN出来ないので、クエリを分ける & MySQL複数接続】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn || $conn->connect_errno) {
    die('データベース接続エラー: ' . $conn->connect_error);
    exit();
}

$user_id = 859;
$start = microtime(true);
$result_array = array();
$sql = 'SELECT code FROM test WHERE user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($code);

$conn1 = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');
if (!$conn1  || $conn1->connect_errno) {
    die('データベース接続エラー: ' . $conn1->connect_error);
    exit();
}
$sql = "SELECT name FROM code_master WHERE code = ?";
$stmt1 = $conn1->prepare($sql);
while (true === $stmt->fetch())
{
    //echo $code . "\n";
    $stmt1->bind_param('i', $code);
    $stmt1->execute();

    $stmt1->bind_result($name);

    while (true === $stmt1->fetch())
    {
        //echo $name . "\n";
        $result_array[] = $name;
    }
}

echo microtime(true) - $start . "\n";


1000回平均:0.6397109158秒


こちらは、2番のプログラムの中の「$code_array」配列に要素を突っ込むのと、

その後に、$code_array配列内の要素を一つずつ取り出す部分の、

「繰り返し処理を一つにまとめた」パターンなんですが、

1ユーザーでDB接続数2以上って事で、逆に遅くなってますかね。

どっちにしても、1ユーザーでDB接続数2以上って時点で速くてもイヤですけど。

ちなみに、繰り返し処理を一つにまとめる他の方法として、

2番のプログラムの最初の$stmt->fetch()辺りの箇所を、

$stmt->get_result()とすれば、また同じ$stmt変数使えて繰り返し処理も一つに出来ます。

ただ、それをしても大体「0.59秒」ぐらいということで、特に変化無かったですね。

では、次。


【4.マスタをMemcachedにキャッシュする】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn) {
    die('データベース接続エラー: ' . mysql_error());
    exit();
}

$user_id = 859;
$start = microtime(true);
$result_array = array();
$sql = 'SELECT code FROM test WHERE user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($code);

$code_array = array();
while (true === $stmt->fetch())
{
    $code_array[] = $code;
}

$stmt->close();
$sql = "SELECT name FROM code_master WHERE code = ?";

$memcached = new Memcached();
$memcached->addServer('Memcachedホスト名', ポート番号);

foreach ($code_array as $code)
{
    //echo $code . "\n";
    $cache_key = $user_id . '_' . $code;
    $name = $memcached->get($cache_key);
    if (false === $name)
    {
        $stmt = $conn->prepare($sql);
        $stmt->bind_param('i', $code);
        $stmt->execute();

        $stmt->bind_result($name);

        while (true === $stmt->fetch())
        {
            //echo '$name1:' . $name . "\n";
            $result_array[] = $name;
            $memcached->set($cache_key, $name);
        }
    }
    else
    {
        //echo '$name2:' . $name . "\n";
        $result_array[] = $name;
    }
}

echo microtime(true) - $start . "\n";


1000回平均:0.5397023411秒


おお、流石Memcachedと言いたい所ですけど、

でも、それでも「JOINより遅い」んですよね。

・・・ふと思ったんですけど、

これ「ネットワークの通信(あるいは回線?)速度(あるいは回数?)」も、結構影響大きいのでは?(今回、Web・DB・KVS全てが別サーバ)とも思いますね。

いや、今回その辺までは調べてませんけど。

ひとまず、次に。


【5.マスタをMemcachedにキャッシュする & キャッシュのキーが一つ】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn) {
    die('データベース接続エラー: ' . mysql_error());
    exit();
}

$user_id = 859;
$start = microtime(true);
$result_array = array();
$sql = 'SELECT code FROM test WHERE user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($code);

$code_array = array();
while (true === $stmt->fetch())
{
    $code_array[] = $code;
}

$stmt->close();
$sql = "SELECT name FROM code_master WHERE code = ?";

$memcached = new Memcached();
$memcached->addServer('Memcachedホスト名', ポート番号);
$cache_key = 'code_master' . '_' . $user_id;
$name_array = array();
$result = $memcached->get($cache_key);

foreach ($code_array as $code)
{
    //echo $code . "\n";
    if (false === $result || false === isset($result[$code]))
    {
        $stmt = $conn->prepare($sql);
        $stmt->bind_param('i', $code);
        $stmt->execute();

        $stmt->bind_result($name);

        while (true === $stmt->fetch())
        {
            //echo '$name1:' . $name . "\n";
            $name_array[$code] = $name;
            $result_array[] = $name;
        }
    }
    else
    {
        //echo '$name2:' . $name . "\n";
        $name_array[$code] = $result[$code];
        $result_array[] = $result[$code];
    }
}
if (0 < count($name_array))
{
    $memcached->set($cache_key, $name_array);
}

echo microtime(true) - $start . "\n";


1000回平均:0.0144977431秒


あら~なんと、これはJOINとほぼ同じですね。

ただ、このプログラムの問題は「メモリ食う」ですね。

・・・あ、これ、$resultと$name_arrayは変数を一つにまとめても問題無さそうですね。

これを書いている途中で気付きました(苦笑)

ということで、まだ速くなりそうですし、別にメモリもさほど食わないかも(苦笑)

あと、ついでにもう一つ書いてる途中に気付いた事として、

ループ内で「$stmt = $conn->prepare($sql);」と書いてある場合は、

大抵はループ外に出せますね(元々が「準備された文」だから当然か)。

ひとまず、次行きましょう。


【6.マスタをRedisにキャッシュする & キャッシュのキーが一つ】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn) {
    die('データベース接続エラー: ' . mysql_error());
    exit();
}

$user_id = 859;
$start = microtime(true);
$result_array = array();
$sql = 'SELECT code FROM test WHERE user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($code);

$code_array = array();
while (true === $stmt->fetch())
{
    $code_array[] = $code;
}

$stmt->close();
$sql = "SELECT name FROM code_master WHERE code = ?";

$name_array = array();
$redis = new Redis();
$redis->connect('Redisホスト名', ポート番号);
$cache_key = 'code_master' . '_' . $user_id;
$result = $redis->get($cache_key);
if (false !== $result)
{
    $result = json_decode($result, true);
}

foreach ($code_array as $code)
{
    //echo $code . "\n";
    if (false === $result || false === isset($result[$code]))
    {
        $stmt = $conn->prepare($sql);
        $stmt->bind_param('i', $code);
        $stmt->execute();

        $stmt->bind_result($name);

        while (true === $stmt->fetch())
        {
            //echo '$name1:' . $name . "\n";
            $name_array[$code] = $name;
            $result_array[] = $name;
        }
    }
    else
    {
        //echo '$name2:' . $name . "\n";
        $name_array[$code] = $result[$code];
        $result_array[] = $result[$code];
    }
}
if (0 < count($name_array))
{
    $redis->set($cache_key, json_encode($name_array));
}

echo microtime(true) - $start . "\n";


1000回平均:0.015953728秒


Memcachedと殆ど同じという事で。

ただ、こっちはjson_encode & decode(Redisの他のデータ型なら不要かもしれないですけど)の分、

処理がメンドイと。

っていうのもあるし、Redisは無駄に多機能な事を考えても、

今回の様な用途なら、Memcachedの方が良いのではとは思いますね。

次。


【7.先にマスタデータを全件取っておく】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn) {
    die('データベース接続エラー: ' . mysql_error());
    exit();
}

$user_id = 859;
$start = microtime(true);
$result_array = array();
$sql = 'SELECT code FROM test WHERE user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($code);

$code_array = array();
while (true === $stmt->fetch())
{
    $code_array[$code] = $code;
}

$stmt->close();

$redis = new Redis();
$redis->connect('Redisホスト名', ポート番号);

$redis_key = 'code_master' . '_' . $user_id;

$all_master_data = $redis->get($redis_key);
if (false === $all_master_data)
{
    $all_master_data = array();
    $sql = "SELECT code, name FROM code_master";
    $stmt = $conn->prepare($sql);
    $stmt->execute();

    $stmt->bind_result($code, $name);

    while (true === $stmt->fetch())
    {
        $all_master_data[$code] = $name;
    }
    if (0 < count($all_master_data))
    {
        $redis->set($redis_key, json_encode($all_master_data));
    }
}
else
{
    // json_decodeは、第二引数をtrueにすると連想配列形式のデータを返す(デフォルトはstd_class)
    $all_master_data = json_decode($all_master_data, true);
}

foreach ($code_array as $code)
{
    // もし、ここで、$all_master_data[$code]にデータが無かったら、マスタに無いコードがtestテーブルに入ってる
    $result_array[] = $all_master_data[$code];
}

echo microtime(true) - $start . "\n";


1000回平均:0.1823107789秒


さて、こちらは前述の通り「ループ内でSQLを投げる」のが遅いということなので、

「ループ外でSQL1回投げて全件取って、ループ内でSQLは投げない様にする」という改良を入れたプログラムです。

で、上記の平均はRedisのキャッシュ効いている状態の平均ですが、

このプログラムをRedisだけ使わない様に修正しても、「0.35秒ぐらい」ということで、

以下の3つがAND条件で全てYesの場合には、

今回のパターン内では一番速い(ただ、マスタ全件を配列に格納するので、メモリ不足には注意)ですね。


・JOIN出来ない

・KVSも無い

・後述のAPCも無い


ということで、まぁまぁオススメかと。

では、次。


【8.マスタデータを事前にAPCに突っ込んでおく】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn) {
    die('データベース接続エラー: ' . mysql_error());
    exit();
}

$user_id = 859;
$apc_key = 'code_master' . '_' . $user_id;

$all_master_data = apc_fetch($apc_key, $success);
if (false === $all_master_data || false === $success)
{
    $all_master_data = array();
    $sql = "SELECT code, name FROM code_master";
    $stmt = $conn->prepare($sql);
    $stmt->execute();

    $stmt->bind_result($code, $name);

    while (true === $stmt->fetch())
    {
        $all_master_data[$code] = $name;
    }
    if (0 < count($all_master_data))
    {
        apc_store($apc_key, $all_master_data);
    }
    $stmt->close();
}

$start = microtime(true);
$result_array = array();
$sql = 'SELECT code FROM test WHERE user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($code);

while (true === $stmt->fetch())
{
    $result_array[] = $all_master_data[$code];
}

echo microtime(true) - $start . "\n";


1000回平均:0.122105834秒


まず、このプログラムのマスタから「事前に」って言ってる所についての補足ですが、

「事前に」っていうのは、この「個別」処理を行う前の「共通」処理で事前に行うって意味です。

なので、このプログラムは、「$start = microtime(true);」の位置が途中からになっています。

その点、ご注意を。

で、まぁ、速度的に悪くはないわけですけど、

「マスタ全件をキャッシュ」よりは、

「存在したマスタのID分のデータだけキャッシュ」の方が当然、効率は良いですよね。

ただ、このプログラム「だけ」ならそうなんですけど、「全体」で見た場合にはどうかなと。

「共有可能なデータ」は、共有した方が良いでしょうし。

まぁ、共有したけど使われない処理が殆どだったってケースも考えられるので、「共有するデータの選定」がポイントでしょうけど。

あと、APCに関しては、チラっとしか見てないですけど、

キャッシュがクリアされないだの何だのといったトラブル事例(回避策も同時に載っていた記憶もかすかにあるが)もチラホラ見かけましたので、

まぁ、あんまりじゃあないかという気もしますね。

あと、APCってWebサーバ毎にキャッシュ持つと思うんですけど、

そうすると、キャッシュのヒット率は低いのではってのもありますかね?

なので、特に理由無ければMemcachedなどの方が良いかなぁと。

じゃあ、次で最後です。


【9.マスタデータを事前にRedisに突っ込んでおく】

<?php

ini_set('memory_limit', -1);

$conn = new mysqli('DBホスト名', 'ユーザー名', 'パスワード', 'データベース名', 'ポート番号');

if (!$conn) {
    die('データベース接続エラー: ' . mysql_error());
    exit();
}

$user_id = 859;
$start = microtime(true);

$redis = new Redis();
$redis->connect('Redisホスト名', ポート番号);
$cache_key = 'code_master' . '_' . $user_id;

$all_master_data = $redis->get($cache_key);
if (false === $all_master_data)
{
    $all_master_data = array();
    $sql = "SELECT code, name FROM code_master";
    $stmt = $conn->prepare($sql);
    $stmt->execute();

    $stmt->bind_result($code, $name);

    while (true === $stmt->fetch())
    {
        $all_master_data[$code] = $name;
    }
    if (0 < count($all_master_data))
    {
        $redis->set($cache_key, json_encode($all_master_data));
    }
    $stmt->close();
}
else
{
    $all_master_data = json_decode($all_master_data, true);
}

$result_array = array();
$sql = 'SELECT code FROM test WHERE user_id = ?';
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $user_id);
$stmt->execute();

$stmt->bind_result($code);

while (true === $stmt->fetch())
{
    $result_array[] = $all_master_data[$code];
}

echo microtime(true) - $start . "\n";


1000回平均:0.180010453秒


APCをRedisに変えただけなので特に。

Redisの方が遅いのは意外でしたけど。

ということで、結論に。


【結論】


1.JOINが出来るなら、まずはJOINを検討。あと、OFFSETとLIMITが使えるなら、よりベター。

2.JOINが出来ないなら、NoSQLを上手く使う。その際、出来るだけループ内でデータ取得処理を行わない様に工夫する

基本は、上記の流れで良いんじゃぁないかと。

あと、とにかく重要なのが、「ループ内でデータ取得(SQLも、NoSQLのget()も)を出来るだけ控えろ」という点。

更に、画面設計的にページング(スマホだとスクロールが同義語?)処理にするとかも大事ですね、って基本ですけど。


あと最後に、チューニングにおける「鉄則中の鉄則」ですけど、

「推測するな、計測せよ」ってのは、

「JOINが遅いのも、ケースバイケース」ということからも感じた所ですので、

胸に刻み込んで、日々の業務に携わるのが良いんじゃないかと思いましたね。


いつもの事ですが、何かご意見やご感想などありましたら、遠慮無くいただければと思います。