消え去った後に経験値も入らないという事は遭遇した事実自体が消え去っているのだろうか。僕を消したあの子は僕の事なんて覚えちゃいないというのかい?
DevOpsの話題が先週熱かった。ECナビはDevとOpsの壁が割と高い感じがする。何かやろうとする度に「ああ、Opsと確認したり説得しなきゃいけないのか…」みたいな感情になるのでよくないですね。Opsの方々も似たような事を思っていたりするのでしょう。改善していかないとダメですね。よろこびの最大化。よろこび組結成。
DevOpsの文化となるキーワード。
- 尊敬
- 信頼
- 失敗を許容する
- 責めない
話が変わるけど、1年ほど所属している事業部が上手くいかない感じだったので事業縮小してしまった。残念だけどお金が稼げなかったならしょうがないですね。お金もちになりたいなー。お金もちになってシャレオツなバーとか経営したいなー。お金持ちになるとバーとかカフェとか経営してもむずがゆくなくなるのかなー。僕はむずがゆくなる感性は失いたくないのでやっぱ平民でいいやと思いました。
最近、去る人が多くて、
をわり。
ニフラムされて光の彼方へ消え去ろうとしている人に触れているとどうなるのだろう。
急にランキング集計することになってしまったデータの作成を極力MySQLだけでする。
とつぜん「この機能にユーザランキングが欲しいよ。なるはやで。もちろん更新間隔も短めなやつ。当日のランキングとデイリーランキングあるといいな。」みたいな依頼をされる事が多い業務をしております。
そんな時はとりあえず既存の資源だけで何とかしなきゃいけないので、頑張ってSQLとわずかなプログラムで対応したりしております。
とりあえずランキングで出したいデータ例
順位, 点数 1, 100 2, 98 3, 97 3, 97 5, 95
てな感じで同位がいたら、ちゃんと何か何これ、ほら、説明できないけど、アレっぽくする。
hogeをn個取得する度に insert でガンガン追加していく謎の user_hoge_nums テーブル
desc user_hoge_nums; +------------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+---------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | user_id | int(10) unsigned | NO | MUL | NULL | | | num | int(10) unsigned | NO | | 0 | | | created | datetime | NO | MUL | NULL | | | updated | datetime | NO | | NULL | | +------------+---------------------+------+-----+---------+----------------+
とりあえず指定した期間(当日分とか前日分とか)を指定してスコアをユーザ毎に計算するSQL。
これくらいのSQLなら3000万レコードくらいでも30秒ほどで処理できました。
select user_id, sum(num) total_score from user_hoge_nums where created between ? and ? group by user_id;
上記SQLで取得したデータを一時的に格納する tmp_hoge_scores さん。
desc tmp_hoge_scores; +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | user_id | int(10) unsigned | NO | UNI | NULL | | | score | int(10) unsigned | NO | | NULL | | +-------------+------------------+------+-----+---------+----------------+
前回の処理データが残ってるので、皆殺しする。
truncate tmp_hoge_scores;
ひたすら普通に insert する。10件〜100件単位くらいで commit するのがいいと思います。全件まとめて insert して commit はヤバいかも。
insert into tmp_hoge_scores(user_id, score) values(?, ?);
一時的に集計したランキングを格納する tmp_hoge_rankings さん。
desc tmp_hoge_rankings; +-------------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+---------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | user_id | int(10) unsigned | NO | UNI | NULL | | | score | int(10) unsigned | NO | | NULL | | | rank | int(10) unsigned | NO | MUL | NULL | | | created | datetime | NO | | NULL | | | updated | datetime | NO | | NULL | | +-------------+---------------------+------+-----+---------+----------------+
これも前回の処理データが残ってるので皆殺し。
truncate tmp_hoge_rankings;
ユーザ定義変数ってやつを使う。
set @rank:=0, @rownum:=0, @prevalue:=0; insert into tmp_hoge_rankings(user_id, score, rank) select user_id, score, rank from ( select user_id, score, @rank:=if((@rownum:=@rownum+1) and (@prevalue <=> score), @rank, @rownum) as rank, @prevalue:=score as preval from tmp_hoge_score order by score desc ) a;
不思議な感じの部分だけ分解して解説すると、@prevalue(前のレコードのスコア)が score(現在のレコードのスコア)と等しければ @rank を、違えば @rownum に 1 足したものを @rank にセット。
@rank:=if((@rownum:=@rownum+1) and (@prevalue <=> score), @rank, @rownum)
こうする事で同位が続いた後に、@rownum にレコード数がカウントアップされているので、なんかアレな順位を取得できる。
@prevalue:=score as preval
↑現在のレコードのスコアを @prevalue にセット
最後に tmp_hoge_rankings を hoge_rankings に、 hoge_rankings を tmp_hoge_rankings に rename してをわり。
rename table hoge_rankings to hoge_rankings_backup, tmp_hoge_rankings to hoge_rankings, hoge_rankings_backup to tmp_hoge_rankings;
とまあ、こんな感じの方法で集計するデータによって若干変更をしつつやっております。集計にかかる時間は3000万レコードで1分前後ですね。バッチで5分おきや10分おきくらいでなら運用できるレベルです。
もうちょい早くしたければ集計部分は前回の集計時からのデータをとってきて、前回の集計データと足してやるなどすればよいかと。
をわり。
攻撃をくらいながら唱えたニフラムをマホカンタではね返されると、詠唱時より弱っているので光の彼方へ行ってしまうかもしれない。気をつけましょう。
TSUTAYAに延滞していた漫画を返した。とてもつらい体験だったので多くは語りません。
ピグライフ、順調に何かのレベルがあがっており、色んな農作物や料理がつくれるようになった。ユーザが増えたせいか何らかのアクションをした後の反応がちょっとにぶくなってきた。毎日のようにメンテしていて運用が大変そうだなと思う。
お金的なものや物資を気軽に個人間で送らせようという仕組みがWebmoneyやPaypalやAmazonにあるけど、こういう機能はSNS的なサービスにあった方がいいんだろうなあと思う。送金系は法律がとてもやっかいだろうけど。
昨日はECナビのシステムライトニングトークというイベントがあり、飛び入りでLTしたら新卒エンジニアにdisられて生きるのが辛くなった。ECナビには最近ぞくぞくとエンジニアが入社してきているので顔を覚えてもらう目的で発表したけど、
をわり。
光の彼方に消えてる途中でレベルアップして詠唱者より強くなった場合はどうなるのだろうか
先週末は積ん読が目に余りはじめたので主に読書をした。
並列に読むとよい的なのを昔どっかで見た気がしたので、試しに6冊ほどを並列に読んでみたら、それぞれが中々読みすすめなくてチャプター1くらいまでしかどの本も読めなかったので技術書には向かない読書法だと思いました。
読書以外の時間は scala を試していた。まだ何も作っていない。型推論がいまいちな気がする。
TSUTAYAで借りた漫画を日曜日までに返さなきゃいけなかったのだけど、返すのを忘れていた。20冊1000円で1週間借りる事が出来て漫画喫茶とくらべると圧倒的にコストパフォーマンスがよい素敵サービスなんだけど、以前3日くらい延滞しただけで4000円近くの延滞料を払った記憶がある。今回はいくら延滞料を取られてしまうのか怖くてまだ返せないでいる。
今日はちょっと予定があって、
をわり。
光の彼方には何があるのだろう?ニフラム…(自分に手をかざしながら)
2ヶ月ちょい前にMySQLの不思議なbugに遭遇したので報告させて頂きます。
- MySQLのバージョン: 5.1.42-community-log
- OS: CentOS5 Final
- 現象: order by PRIMARY_KEY desc limit N で該当するレコードが取得できない
どのような事が起こるかは、http://stackoverflow.com/questions/2844699/mysql-order-by-and-limit-gives-wrong-resultからコピペ。
Works correctly: mysql> SELECT id, created_at FROM billing_invoices WHERE (billing_invoices.account_id = 5) ORDER BY id DESC ; +------+---------------------+ | id | created_at | +------+---------------------+ | 1336 | 2010-05-14 08:05:25 | | 1334 | 2010-05-06 08:05:25 | | 1331 | 2010-05-05 23:18:11 | +------+---------------------+ 3 rows in set (0.00 sec) WRONG result when limit added! Should be the first row, id - 1336 mysql> SELECT id, created_at FROM billing_invoices WHERE (billing_invoices.account_id = 5) ORDER BY id DESC limit 1; +------+---------------------+ | id | created_at | +------+---------------------+ | 1331 | 2010-05-05 23:18:11 | +------+---------------------+ 1 row in set (0.00 sec) Works correctly: mysql> SELECT id, created_at FROM billing_invoices WHERE (billing_invoices.account_id = 5) ORDER BY created_at DESC ; +------+---------------------+ | id | created_at | +------+---------------------+ | 1336 | 2010-05-14 08:05:25 | | 1334 | 2010-05-06 08:05:25 | | 1331 | 2010-05-05 23:18:11 | +------+---------------------+ 3 rows in set (0.01 sec) Works correctly with limit: mysql> SELECT id, created_at FROM billing_invoices WHERE (billing_invoices.account_id = 5) ORDER BY created_at DESC limit 1; +------+---------------------+ | id | created_at | +------+---------------------+ | 1336 | 2010-05-14 08:05:25 | +------+---------------------+ 1 row in set (0.01 sec) Additional info: explain SELECT id, created_at FROM billing_invoices WHERE (billing_invoices.account_id = 5) ORDER BY id DESC limit 1; +----+-------------+------------------+-------+--------------------------------------+--------------------------------------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------------+-------+--------------------------------------+--------------------------------------+---------+------+------+-------------+ | 1 | SIMPLE | billing_invoices | range | index_billing_invoices_on_account_id | index_billing_invoices_on_account_id | 4 | NULL | 3 | Using where | +----+-------------+------------------+-------+--------------------------------------+--------------------------------------+---------+------+------+-------------+
ORDER BY だけなら問題ないが LIMIT を指定すると type が index から range にかわりなんかうんこ。
対策は force index するか created_at に index はっておいて created_at でソートすればよいと思います。
http://bugs.mysql.com/bug.php?id=37830 をみると2008年頃に fix したのに今年また大復活を遂げたみたいなのでMySQL5.1系つかってる人は注意したほうがよいでしょう。
話がかわりますが、僕は
をわり。