mixi engineer blog

ミクシィ・グループで、実際に開発に携わっているエンジニア達が執筆している公式ブログです。様々なサービスの開発や運用を行っていく際に得た技術情報から採用情報まで、有益な情報を幅広く取り扱っています。

技術書典4に行ってきました

人事部のきたこうじ(@koji.kita)です。先月までminimoのSEOやweb予約システムの開発エンジニアをしていました。今月から人事部に異動し、社内の盛り上げ隊長兼ミクシィグループ全体の技術発信力強化活動を行っています。

 

今回は4月22日に秋葉原UDXで開催された技術書典4の参加報告です。ミクシィグループではスポンサーもさせていただきました!

techbookfest.org

 

技術書典とは

技術をテーマにした同人誌即売会です。参加者数が数千人と技術系のイベントでは最大規模の会になります。

 

当日の様子

日曜日なので、お昼に起きてゆっくり向かいました。

 14時ごろに秋葉原着!3千人くらい人が来ると聞いてたので予想通りUDX前には人だかりが!

f:id:mixi_engineers:20180423140641j:plain

整理券が配られているようなので、受け取ると・・・!

f:id:mixi_engineers:20180423143146j:plain

あれ・・・4590番・・・?3千人じゃないの・・・?

知り合いの運営スタッフに話を聞くと、前回までは雨で天候が悪い時が多かったけど、今回は天気が良くて来場者が多いとのこと。

公式発表によると最終的な来場者は6380人・・・すごい!

※実は整理券は5千枚しか用意されておらず、それ以降は手書きの整理券を配ってました。運営スタッフの皆様お疲れ様でしたmm

 

入場自体はスムーズで、30分ほど待って無事入場。

中は予想通りすごい人で、みなさん楽しみながら面白い本を探していました。

f:id:mixi_engineers:20180423141247j:plain

残念ながら私が入った時には持ち帰り用トートバッグなど公式グッズはすべて配布終了&完売しておりました。

 

f:id:mixi_engineers:20180423141657j:plain

技術書2割引

 

f:id:mixi_engineers:20180423141907j:plain

こちらは技術季報 vol.3にも寄稿されていた、ラズパイで作ったデジタルサイネージの展示

 

f:id:mixi_engineers:20180423142134j:plain

偶然会場で会った弊社エンジニアはたくさん購入してました

USBドライバの自作入門書がイチオシとのこと

 

そんなこんなで私のイチオシはこちら

f:id:mixi_engineers:20180423142308j:plain

「肉を創る」

純肉(人工的に培養した肉)をDIYで自宅作る手順つきのかなりぶっ飛んだ本ですが、食糧問題に向き合ったり、国際会議のレポートが載っていたりとかなりクオリティの高い本でした

 

まとめ

・晴れると人が増えるので、朝一で並ぶのがおすすめ

・さまざまな技術やニッチな情報にたくさん出会えて楽しい

re:Invent 2017: Tuesday Night Live の裏番組で

行ってきました re:Invent。 ミクシィグループで他の方のレポート ^1 ^2 も出ていますが今回は日本語でも英語でもほとんど情報がでていないセッション、 ARC214 - Addressing Your Business Needs with AWS をご紹介します。

ラスベガス入りして 3 日目、とにかく会場が広くて疲れ気味だったことから Tuesday Night Live は体力的に厳しいと判断、裏番組のこのセッションへの参加を決めました。

謎のセッション

そもそも、このセッションはスライド ^3 以外に全く情報がないのですが、これには以下のような理由があると思われます。

  • AWS の利用が浅い (もしくはまだ利用していない) 方が多く参加していた。このような参加者は twitter や blog でセッションの内容を報告しにくい
  • 新機能発表の場ではない。拡散したくなる情報がない
  • Tuesday Night Live の裏番組の時間であり、参加者の絶対数が少ない (30 人程度)
  • ずっとディスカッションしていて twitter やる暇がない

セッションの目的は自身の経験をシェアし、他の AWS 利用者とのコラボレーションを深めましょうといったところです。

unconference とは

まず冒頭、このセッションは unconference スタイルで進めますよという説明があります。 unconference とは、

  • 参加者相互のディスカッションが中心
  • 話題に制限を設けない

前提でのグループディスカッションのことです。 re:Invent の他のワークショップがほとんど ハンズオン形式で進むのに対し、このセッションは参加者相互のディスカッションによって成立するものです。つまり、このセッションの約 2 時間、議論を理解した上で発言が求められます。理解できなくても時間が過ぎていく他のセッションとは一味違います。

議論のはじまり

ファシリテーターとなった講師の進行で、各自の自己紹介からセッションがはじまります。SNS mixi を AWS に移行した経験があったので、「おれたちはでかくて古い perl のプログラムをオンプレから AWS に移したんだぜ」で自己紹介を乗り切りました。同じグループには、世界的に有名なハードウェアベンダのセキュリティアナリストもいました。 その後各テーブルで各々が直面している課題や興味のあるトピックを出していきます。「オンプレをフォークリフトアプローチ (構成をなるべく変えずにそのまま AWS に持っていく) で移してしまったので、マネージドサービスへの移行に課題感がある」という感じ。

ひととおりテーブルごとに課題を出し終わった段階で、各テーブルでディスカッションした内容を報告しあい、問題となっているテーマを大きなくくりでまとめます。ここで出たおおきなテーマのくくりごとに、テーブルを移動してさらに議論を深めます。

マネージドサービスへの移行を課題として出していたので、「 migration/integration 」のテーブルに移動。ここで驚いたのは、「まだ AWS への移行をはじめていない」というユーザが多数派だったことです。 AWS のヘビーユーザが多くを占める re:Invent でこのような人々が集まることは、珍しいことだといえるでしょう。

あるあるネタ

各参加者が報告した課題は以下のようなものでした。

  • B2B でユーザ企業がクラウドへの移行を許してくれない。また、メリットが説明できない
  • データ移動に規制がある。EU 、中国と複数の地域の参加者から
  • コストが問題

クラウド利用にあたっては一般的に意識される問題でしょう。 これに対して出た意見は以下のようなものでした。

  • たしかに、クラウドにあるかどうかって顧客にとってはどうでもいいことだよね
  • コスト、仕方ないよねー、おれも高いと思う (「GCP にしたらやすくなるかもよ」、はぐっとこらえた)
  • EU や 中国でも、各地域のリージョン使えばデータ移動規制は遵守できるんじゃないの?

など。

我々はどうしたのか

今回、特に データセンタから AWS への移行をどうしようかという方が複数おられたので、mixi の AWS 移行で実施した以下の経験を話しました。

  • AWS Direct Connect でデータセンタと VPC をつないで移動させやすいアプリケーションサーバから検証、移動をはじめた
  • 仮に AWS への移行が頓挫したとしても、オンプレでの構成を維持できる状態で検証を進めた
  • アプリケーションサーバに比べて動かしにくい DB の移行は最後に回した。ここで、Aurora や RDS も検証したが、結局は mysql を EC2 で動かすことを選択した

特にデータストアの移行を後回しにし、AWS への移行が失敗した場合のリスクを最小限にするアプローチについては interesting というコメントをいただけました。また、「それって遅延とかだいじょうぶだったの?」 という質問をいただき、 memcached を VPC 側にも作って read のトラフィックは VPC と DC の中で完結するようにしたよという回答をしています。

まとめ

このセッションでは新機能の発表や AWS 実装の裏側に関する新しい知識を得ることはありませんでした。 しかしながら、他の参加者と本音ベースの知識をシェアできたという経験、なにより自身の経験を振り返って自信を持てたという点においては、他のセッションでは得がたい充足感がありました。

re:Invent 2016 でも 同じタイトルで unconference スタイルのセッションがあったようです ^4 。今回は 2016 年から続けて 2 回目。他の参加者との交流のきっかけを掴みたい方に、 unconference はぜひおすすめしたいセッションです。

AWS DeepLens はしっかりゲット

Vienetian の食堂。めちゃくちゃ広い。世界最大級のカンファレンスを捌くには効率的に飯を食わせるノウハウが不可欠

re:Play Party の熱気

Char2Vec で文字の特性について調べてみた

ミクシィ Vantage スタジオのAI・ロボットチームで自然言語処理関連の研究開発に関わっている原(@toohsk)です.

Vantage スタジオでは人の感情に寄り添った会話ができるAIの研究開発を通じて,新しいコミュニケーションサービスを生み出そうとしています. 今回, Char2Vec を用いた,文字毎の特性について実験を行いましたので,紹介したいと思います.

Word2Vec とは

Word2Vec は単語をベクトル表現に変換する方法です. これまでは自然言語処理の分野では単語を扱う場合, one-hot の形式で文章内の単語を表現することが多かったです.
しかし,自然言語を機械学習で扱う場合や論文では,最近では必ずといっていいほど Embedding された状態,すなわち単語をベクトルに変換してから機械学習のアルゴリズムに与えています.
ではなぜ one-hot の形式ではなく, Embedding された状態に変換するのでしょうか? それは one-hot の形式では,単語同士の関係性や意味が考慮されていないためです. そこで単語同士の関係性や意味を学習するために Mikolov らにより, Word2Vec の手法が提案されました.
Word2Vec には大きく2つの手法があり, cbow と skipgram の2つが提案 1 されています. この2つの手法での大きな違いは, cbow は着目している単語の周辺語を入力に与え, skipgram は着目している単語を入力に与えるといったものがあります. これらの違いにより, cbow はたくさんの分布情報をなだらかにする効果があり,小さなデータセットでよく使われます. 一方, skipgram は着目する単語と文脈を新しい観点で扱うため,大きなデータセットに有効であると一般的に言われています.
また,Word2Vec 以降も GloVe 2 や FastText 3 というアルゴリズムが提案されています.

Char2Vecとは

Word2Vecを学習させる対象言語が英語の場合,各単語がスペースで区切られているため,スペースやピリオドなどで単語区切りのデータセットを作成します. 一方,日本語の場合は文章中に区切り文字がないため,一般的には分かち書きや形態素解析を行い,必要な品詞だけを抽出して学習させます. しかし,Word2Vec にはアルゴリズムの違いだけでなく,言語モデルに与えるデータセットの種類を変えた Doc2Vec や Paragraph2Vec, Char2Vec があります.
今回,私達は対話文を文字単位に分割し,文字のベクトル表現を学習させる Char2Vec に取り組みました.また,学習は Gensim4 の Word2Vec ライブラリを利用し, cbow として学習させました.

学習結果

学習した Char2Vec の結果を文字分布図とアナロジータスクの2つで見てみたいと思います. 今回の実験では,全・半角や数字,アルファベット,平仮名,カタカナ,漢字など3,731種類の文字を対象としています.

文字分布図

文字分布図はある文字に近い文字を可視化する分布図です.
今回は,'1','a','あ','悲'の4文字についてそれぞれに近い上位50個の文字を表示しました. すこしウォーリーを探せ感があるのですが,頑張って探してみてください.笑

まずは,数字をベクトル表現にした場合です.

f:id:mixi_engineers:20171005183859p:plain

数字が近くに集まっているのがわかるかと思います.図だとわかりにくいのですが,図の中に同じ数字が出てきています.その理由は全角と半角の違いによるものです. しかし,全角は全角同士,半角は半角同士で集まっているので,区別して学習していることが分かります.

次に,アルファベットをベクトル表現にした場合です.

f:id:mixi_engineers:20171005183920p:plain

アルファベットは単体で使われることもありますが,文章においてはアルファベット同士が組み合わさって意味が表現されます.したがって,アルファベットはアルファベットとして集まって表現されているのだと分かります.

そして,平仮名をベクトル表現にした場合です.

f:id:mixi_engineers:20171005183937p:plain

'あ' の近くには平仮名は集まっていないことがわかります.平仮名は単体では意味を表さず,文章中においては漢字と組み合わせて意味を表すので,分布図として漢字の中に紛れているのだと思われます.

最後に,漢字をベクトル表現にした場合です.

f:id:mixi_engineers:20171005183951p:plain

今回は'悲'という漢字を選択しました.目的として,感情や形容する言葉に使われる漢字なので,その近くには同様の漢字がある場合,漢字の意味をベクトルとして表現できているのではないかと考えられます.実際に図を見ると,'悲'という漢字の近くには,'楽'や'優','寂'など似た使われ方をする漢字が近くに集まることが分かりました.

アナロジータスク

アナロジータスクとは,単語の性質を演算として計算できるタスクのことです.
Word2Vec の魅力はたくさんありますが,単語をベクトル表現にできたことで演算が可能になったことは,その一つだと思います. このアナロジータスクの例としてよくあげられるのが, king - man + woman = queen というものです. 今回の Char2Vec でも同様のものができるか確認してみます.
Gensim でアナロジータスクを解く場合, most_similar メソッドを利用します. most_similar メソッドの引数に positive として与えているものは加算, negative として与えているものは減算するものです.
それでは,実験結果を見てみましょう.

まずは,数字でのアナロジータスクを見てみましょう. 今回の例として, 5 + 1 -2 を計算してみました.

model.most_similar(positive=["5", "1"],["2"]) 

[('4', 0.9669662117958069),
 ('3', 0.9577144980430603),
 ('6', 0.9576835036277771),
 ('7', 0.952031672000885),
 ('8', 0.9488403797149658),
 ('0', 0.9165981411933899),
 ('9', 0.8589234352111816),
 ('5', 0.8185327053070068),
 ('7', 0.7915964722633362),
 ('8', 0.7892774939537048)]

驚くことに,正しく計算されています.
数字の組み合わせということもあり,以下の条件でありうる440件分のデータセットを作成し,正答率がどの程度か計算しました.

  • "positive" と "negative" の中の数字は一致しない
  • Char2Vec のため "positive" と "negative" の計算結果は 0~9 までの値域とする
def create_analogy_sets():
    
    analogy_sets = []
    answer_sets = []
    
    for p1 in range(0, 10):
        for p2 in range(0, 10):
            for n1 in range(0, 10):
                
                # p1, p2, n1 が同じ数字になる場合 skip する
                if p1 == p2 or p1 == n1 or n1 == p2:
                    continue
                    
                # p1+p2-n1 が負の整数の場合 skip する
                answer = p1+p2-n1
                if answer < 0:
                    continue
                
                # p1+p2-n1 が10より大きい正の整数の場合 skip する
                if answer > 9:
                    continue
                    
                answer_sets.append(answer)
                analogy_sets.append([p1,p2,n1])
        
    return analogy_sets, answer_sets

def precision(analogy_sets, answer_sets, topn=1):
    correct = 0
    not_num = 0

    for i in range(len(analogy_sets)):
        p1, p2, n1 = analogy_sets[i]
        for predict in model.most_similar([str(p1), str(p2)], [str(n1)], topn=topn):
            try:
                if int(predict[0]) == answer_sets[i]:
                    correct += 1
            except:
                print(predict)
                not_num += 1
                continue

    return correct, not_num

モデルの戻り値を top1 と top3 の2パターンで評価しました.
まず,戻り値の種類についてですが, top1 と top3 の両方において,半角数字が返された率は100\%でした.
次に,計算結果についてですが, top1 での計算結果では,26.8\%の正答率となり, top3 での計算結果では,56.8\%の正答率となりました.
ちなみにですが,数字に限定した上でランダムに回答した場合, top1 では10\%, top3 では 1 - 9/10 * 8/9 * 7/8 = 0.3,すなわち30\%となるはずなので,ランダムで回答するよりも良い結果になっていることが分かります.
学習データが対話データなのでほとんど数字の関係性を教えられておらず, また,文字として数を与え,候補として3,000文字以上ある中から正解できていることに驚きを感じます.

ちなみに,漢数字の場合は,

model.most_similar_cosmul(["五", "一"],["二"]) 

[('四', 0.7392055988311768),
 ('染', 0.7125757932662964),
 ('1', 0.7093852758407593),
 ('先', 0.709115743637085),
 ('三', 0.7078965306282043),
 ('4', 0.706843912601471),
 ('3', 0.7037045955657959),
 ('6', 0.7036187052726746),
 ('九', 0.7021587491035461),
 ('敏', 0.6988415718078613)]

これも回答できています.近しいアナロジーとして漢数字や全角の数字が挙がっていることは驚くべき結果です.ちなみに先程の計算用のデータセットの場合,漢数字が返答されている確率は55.9\%,計算結果の正答率は 9.5\% でした.

最後に,漢字同士によるアナロジータスクです.
漢字は偏や旁などの部首とそれ以外の部分から構成されます. 今回は,その知識を活かしてアナロジータスクをしてみたいと思います.

model.most_similar(["木", "林"])

[('森', 0.6414894461631775),
 ('谷', 0.6174930334091187),
 ('雲', 0.6121721267700195),
 ('川', 0.6072961688041687),
 ('松', 0.6014201045036316),
 ('崎', 0.5943500995635986),
 ('熊', 0.5926244258880615),
 ('村', 0.5882901549339294),
 ('峰', 0.5817473530769348),
 ('冠', 0.5709230303764343)]
model.most_similar(["山", "石"])

[('岩', 0.6268459558486938),
 ('熊', 0.6220624446868896),
 ('松', 0.6124925017356873),
 ('芹', 0.610385000705719),
 ('藍', 0.5979625582695007),
 ('木', 0.5863777995109558),
 ('滝', 0.5861614346504211),
 ('韮', 0.5789919495582581),
 ('井', 0.5788655281066895),
 ('森', 0.5647031664848328)]

驚くことに漢字の構成を組み合わせると意図する漢字が返されました. しかし,必ずしもそうなるわけではありません.

model.most_similar(positive=["草", "楽"])

[('悲', 0.5380357503890991),
 ('憎', 0.47937360405921936),
 ('惜', 0.4682950973510742),
 ('苦', 0.4587554335594177),
 ('休', 0.44919806718826294),
 ('寂', 0.44706428050994873),
 ('忙', 0.4312727451324463),
 ('欲', 0.4239286184310913),
 ('鼻', 0.42341917753219604),
 ('嬉', 0.4128988981246948)]

残念なことに,'草'+'楽'='薬'を期待したのですが, '悲','憎','苦'などの漢字が抽出されました. (薬で辛い経験をしたニューラルネットワークなのでしょうか...)

まとめ

今回は, Char2Vec についてと学習させて得られた興味深い知見を紹介させていただきました.
全体として,概ね数字やアルファベットなどの種類や漢字が持つ意味はある程度近い距離にまとまっており,うまく Enbedding されているようでした. 今回のデータセットは,対話データをベースとしていましたが,データセットを変えることでまた異なる知見が得られるかもしれません.

最後に,弊社では一緒に人の感情に寄り添った会話ができる AI を創出したい自然言語処理 エンジニアを募集しています.ご応募お待ちしております.


新卒研修の受講レポート~git編~

はじめに

はじめまして、2017年新卒エンジニアの親川と玄馬です。 本記事では、git研修でおこなった内容や得た学びについて紹介したいと思います。

そもそもgitとは何なのか、という方は以下のサイトを参考にしてください。

Gitを使ったバージョン管理【Gitの基本】 | サルでもわかるGit入門 〜バージョン管理を使いこなそう〜 | どこでもプロジェクト管理バックログ

研修の様子

前半は、先輩社員による座学形式の研修でした。gitを楽しく学ぼう、ということで内容は

  • gitの使い方

  • commitとbranchについての解説

  • 歴史の取り込み方(merge, rebase)

となっていました。 研修で使用した資料は以下のページで見ることができます。

すごいGit楽しく学ぼう // Speaker Deck

gitの使い方ではリポジトリの作り方から、変更のステージング方法、commit・pushの操作、branchの操作を学びました。
操作だけでなく、commitやbranchはどういったデータ構造で表現されているかを知ることができ、この後に学ぶmerge操作の理解における大きな助けとなりました。
歴史の取り込み方の学習として、merge操作でのオプションによる動作の違いを学びました。
後半は演習として、gitを使う上で起こるトラブルを解決する「git challenge」の過去問に挑戦しました。問題は難易度別に分けて出題されるので、自分のレベルにあったものから解答することができました。
前半で学んだ操作を使いつつ、理解不足な情報は調べたり先輩社員や同期に質問して各自のペースで進めていきました。

f:id:mixi_engineers:20170828132541j:plain

ちなみに実際のgit challengeは、gitに関する問題にチームで挑戦し、時間内にいくつ解けるか!…という弊社主催の学生向けの技術イベントです。(第5回大会では4時間で18問に挑戦したそうです。)

問題の解き進め方

git challengeは、1問ごとに、以下のような流れで進めていきます。

  1. 問題のリポジトリをclone

  2. 問題の指示通りの状態になるようにリポジトリを修正

  3. push

すると、採点サーバで自動的に問題の正誤が判定され、全体のランキングページで結果を閲覧できるようになります。
「pushが出来ない」という同僚を助けるようなシチュエーションや、そもそもcloneすらできないリポジトリもあり、様々な問題に対して解決案を模索していきます。
最後まで解けないような難しい問題もありましたが、終了後に解説をしてもらえたので非常に勉強になりました。

ちなみに、git challengeの問題の解き方だけでなく、自動採点などのインフラの話が気になる方は、以下のブログを是非読んでみてください。

git challengeの自動採点高速化に向けたインフラのハナシ - mixi engineer blog

得た学び

今回の研修でcommitについての理解を深めることができました。ここでは、commitについて学んだことを紹介したいと思います。

皆さんはgitの仕組みについて、どのように理解しているでしょうか?
私は、「変更があった差分情報を時系列に保存し、いつでも過去の状況に戻れる」といった大まかな理解しかしていませんでした。
git研修が始まり、commitが持つ「Revision」という値が、gitの仕組みを理解する上で重要だということに気づきました。
commitの大まかなデータ構造は、以下の通りです。

commitのデータ構造

Revision commitのSHA-1ハッシュ
Tree ファイルのスナップショット
Parent ひとつ前のcommitのRevision
Author commitを作成した人
Committer commitを適用した人

Revisionとは、commitに対応する以下のようなハッシュ値です。
6fe9db43763ded8bbfd0b428894baa9bfc0b7d42
gitの操作をする上で、このRevisionは多くの場所で登場します。

commitのデータ構造の中にも、Parentという値にひとつ前のcommitのRevisionが入っています。
ひとつ前のcommit、そのひとつ前のcommit・・・と辿って行くことで、一番最初のcommitまで見ることができます。

branchのデータ構造にもRevisionが登場します。
私はbranchについて、枝を伸ばす・枝を分けるといったイメージを持っていました。しかしbranchの正体は、あるcommitのRevisionを指すポインタのようなものでした。
branchが指しているRevisionを新しいcommitのRevisionに変更することで、枝が伸びる・枝が分かれるような処理を実現しています。
意外と単純な仕組みだと思いませんか?これを知って、私はgitの仕組みの理解がしやすくなりました。

普段gitを使う際に、私はこれらのことをそれほど意識せずに使っていました。
しかし、データ構造や実際に行なわれる処理といった仕様を知ることで、branchやmergeについて曖昧だった部分も理解しやすくなります。
そういった理解ができていることで、gitで困った時の対応力が大きく改善されると実感しました。

おわりに

今まではgitのコマンドの機能や実行結果を把握している程度でしたが、改めて詳細な仕様を知り、実際に行われている処理を考えながらgitを使えるようになりました。
この記事を見て、gitに興味を持った方は、ぜひgit challengeにご参加下さい!
また、git challengeの概要や今までの様子などがまとまっている以下のページもぜひご覧ください。

mixi GROUP presents「git challenge」

問題の一部は以下に公開されていますので、興味がある学生の方は挑戦してみてはいかがでしょうか?
第1回git challengeの出題内容を一部公開します - mixi engineer blog

新卒研修の受講レポート~AWS編~

AWS研修

みなさま、はじめまして!ミクシィ2017年新卒エンジニアの田村と金銅です。

今回は5月10日から3日間に渡っておこなった新卒エンジニア対象のAWS研修について書きたいと思います。 研修はミクシィのオフィスにAWSの外部講師をお招きし3日間みっちりとAWSの基礎から、実例を踏まえた応用まで教わりました。

まず、「AWSとは?」という方もいらっしゃると思いますので軽く説明しますと、AWSはウェブサービス、モバイルアプリケーション、ゲームなど様々なサービスの基盤となるインフラです。従来、大規模なインターネットサービスを運営しようとするとサーバ用に専用のハードウェアを購入し、データセンターに場所を借りて設置しに行く必要がありました。しかし、このAWSはいわゆるクラウドサービスなので実際のハードウェアを意識せず開発者がアプリケーションの開発や運用に集中できるというメリットがあります。

研修を大きく分けると講義と実際に習ったことを構築する実践形式のラボ、そしてチームで行ったインフラ設計のディスカッションといった内容でした。

初日

講義では可用性について学びました。

アプリケーションのインフラでは可用性がとても重要です。可用性とはシステムがいかにダウンせずに利用できる状態を保てるかという指標で、これが高ければ高いほどインフラの質が良いことを示します。

これには、急激なアクセスの増加によって負荷が増大した際にも耐えられるスケーラブルなインフラ構成をとる必要があり、まさにクラウド型のサービスであるAWSはこのような要求にうってつけです。

初日のラボではAWSのサービス群の中からVPC, EC2, DynamoDB, S3, IAMを使用し、障害を見据えたスケーラブルなウェブアプリケーション環境を構築しました。

f:id:mixi_engineers:20170822142839j:plain

二日目

二日目は、すでに自社が運用しているインフラ環境を想定し、それをクラウド上に持って行くフォークリフトという作業をチームでディスカッションし、その内容をホワイトボードに書いて発表しました。

ここで同期や講師の上原さんから鋭いツッコミが入り、実運用を考えた上で改善すべき点を知ることができました。

また、講義ではLambdaを使ったイベント駆動のオートスケールするインフラ設計を学び、ラボでは実践でその構築方法を身につけました。これがクラウドの特徴であり、必要な時に必要な分だけの処理能力を利用することでコストを最低限に保つことができるのです。

最終日

ここまで二日間を通して可用性の重要さについて学びました。 例えば、災害でデータセンターに火災や停電が起こったらどうでしょうか。最終日の講義では、そのような滅多に起こらない場合についても冗長化を図り、サービスの提供を継続できる強固なインフラ設計を学びました。

AWSは世界中にリージョンと呼ばれる地理的に分離したデータセンターを持っており、東京リージョンで障害が発生した際には北カリフォルニアリージョンにあらかじめ作っている同じ構成のインフラに処理を引き継ぐことができます。

最後に行ったハンズオンでは、サーバレスというアーキテクチャに沿って掲示板のウェブアプリケーションを作りました。サーバレスといってもサーバが存在しないわけではなく、AWSに管理された(フルマネージドな)サービスを使うことで全くコードを書くことなくアプリケーションの実装ができる構成のことを指します。しかし、これにはデメリットもあり、フルマネージドサービスは容易に利用できる反面カスタマイズ性が低く、細かい要求には応えられないケースがあります。そのため講師の方もおっしゃっていましたが、バランスが重要なのです。 f:id:mixi_engineers:20170822142832j:plain

まとめ

今回AWS研修を通じて、インフラは障害が起こることを前提に設計することが大切だと感じました。また、急激な変化に耐えられるようにスケーラブルな構成をとることで絶やさずサービスを提供し続けることができ、事業者もユーザもハッピーになります。

以上、AWS研修三日間のレポートでした! f:id:mixi_engineers:20170822143337j:plain

新卒研修の受講レポート~データベース編~

17新卒エンジニアデータベース研修

今回は、XFLAG事業本部 SREグループの清水さん(@isaoshimizu)によるデータベース研修で学んだことについて、新卒エンジニアの左野と坂本がレポートしていきます。

f:id:mixi_engineers:20170824103326j:plain

↑研修中の様子です

研修内容

講義は以下の内容で進んでいきました! - MySQLの基本的な話 - データベースの基本的な話 - インデックス - 負荷対策 - 運用の話 - 演習

今回は研修内容については深く掘り下げませんが、研修を受けて得られた学びと感想について私たちが感じたことを書いていきます!

今年の新卒が使ったことのあるフレームワーク

1位. Ruby on Rails

2位. Sinatra

3位. FuelPHP

いきなり蛇足ですが、研修前に扱ったことがあるフレームワークについて事前にアンケートがありまして、Ruby on Railsが人気のようでした。ORMはActive Recordが人気でした。

照合順序と寿司とビールの話

前半は、MySQLを題材にデータベースの基礎的な部分について学習しました。ACID特性やトランザクション、正規化、インデックスなどです。聞いた話の中で印象に残ったのは、寿司ビール問題と言われているもので、例えばMySQLで設定しているUnicodeの照合順序によっては🍣や🍺などの絵文字が同一扱いになったりならなかったりする問題です。詳しくは以下の記事が参考になります。

MySQL と寿司ビール問題 - かみぽわーる http://blog.kamipo.net/entry/2015/03/23/093052

インデックスの話

わたくし坂本は、過去にMySQLを利用したサービスの開発やISUCONに参加したことがあるのですが、その際、速度向上を狙いインデックスを貼ることがありました。しかし実際にインデックスがどのように作用して速度向上するのか、ということは全く理解せずにインデックスを追加していました。 今回の研修では改めてインデックスの挙動について学ぶことが出来ました。 例えば、「関数や式、否定構文、LIKEではインデックスは効かない(LIKEの前方一致では効く)」「LIMIT OFFSETはとても遅いのでWHEREで絞り込むと良い」などの注意すべき点を教わりました。これまでは、こういった点を知らずにクエリを発行していましたが、今後はどのようなクエリを発行するのか、学んだことを踏まえたインデックス設計を行いたいと思いました。

演習してみた話

最後に、演習として実際に効率の良いクエリ発行ができるか、の確認をしました。 データセットとしては、 https://github.com/datacharmer/test_db 内の employees.sql を利用しました。

演習1

USE employees;
SELECT SQL_NO_CACHE * FROM employees WHERE hire_date = '1985-02-01' AND birth_date = '1963-08-02';

このクエリを実行すると全件スキャンがおこなわれ、検索に時間がかかります。 最も短い時間で検索が行われるように工夫してください。

まずは、このまま実行をするとどうなるのかを確認します。

mysql> SELECT SQL_NO_CACHE * FROM employees WHERE hire_date = '1985-02-01' AND birth_date = '1963-08-02';
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  20539 | 1963-08-02 | Poorav     | Gecsei    | M      | 1985-02-01 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.13 sec)

この時点で0.13secかかっており既に遅いということが分かります。実際にどの程度スキャンされているのかを explain を用いて確認します。

mysql> explain SELECT SQL_NO_CACHE * FROM employees WHERE hire_date = '1985-02-01' AND birth_date = '1963-08-02';
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 299290 |     1.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

rows の項目を見ると分かるように、30万件近いスキャンが走っていることが分かります。

この場合は、インデックスを追加することでスキャンする数を減らすことができます。 今回はインデックスを hire_date に追加します。

mysql> alter table employees add index hire_date_index(hire_date);
Query OK, 0 rows affected (0.48 sec)
Records: 0  Duplicates: 0  Warnings: 0

その後、同じクエリを実行すると以下のようになります。

mysql> SELECT SQL_NO_CACHE * FROM employees WHERE hire_date = '1985-02-01' AND birth_date = '1963-08-02';
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  20539 | 1963-08-02 | Poorav     | Gecsei    | M      | 1985-02-01 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.01 sec)

最初0.13secかかっていた時間が、0.01secまで短縮できました!explainでスキャン数も確認してみます。

mysql> explain SELECT SQL_NO_CACHE * FROM employees WHERE hire_date = '1985-02-01' AND birth_date = '1963-08-02';
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ref  | hire_date_index | hire_date_index | 3       | const |   15 |    10.00 | Using where |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

30万件近くスキャンしていたのが、15件まで減らすことができました! key を見ると先程設定した hire_date_index が利用されていることも確認できました。

演習2

次の演習は以下のような課題です

USE employees;
SELECT SQL_NO_CACHE * FROM employees WHERE birth_date > '1959-01-01' ORDER BY hire_date LIMIT 5;

上のクエリを実行すると、ファイルソートが発生して検索速度が遅いことが分かります。ファイルソートが起きないように工夫をしてみましょう。

mysql> SELECT SQL_NO_CACHE * FROM employees WHERE birth_date > '1959-01-01' ORDER BY hire_date LIMIT 5;
+--------+------------+-------------+-----------+--------+------------+
| emp_no | birth_date | first_name  | last_name | gender | hire_date  |
+--------+------------+-------------+-----------+--------+------------+
| 111400 | 1959-11-09 | Arie        | Staelin   | M      | 1985-01-01 |
| 110725 | 1961-03-14 | Peternela   | Onuegbe   | F      | 1985-01-01 |
| 111035 | 1962-02-24 | Przemyslawa | Kaelbling | M      | 1985-01-01 |
| 110085 | 1959-10-28 | Ebru        | Alpin     | M      | 1985-01-01 |
|  87761 | 1960-08-19 | Shir        | Munck     | F      | 1985-02-01 |
+--------+------------+-------------+-----------+--------+------------+
5 rows in set (0.14 sec)

実行結果です。0.14 sec とクエリが遅いことが確認できました。EXPLAINを使ってクエリを確認してみましょう。

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM employees WHERE birth_date > '1959-01-01' ORDER BY hire_date LIMIT 5;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra                       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 299157 |    33.33 | Using where; Using filesort |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)

ここで Extra という項目に注目してみましょう。Using filesort からファイルソートが発生していることが確認できます。これはテーブルにインデックスが存在しないために ORDER BY で指定されたソートを行うためのメモリ使用量が増加しており、一時的な記憶領域としてファイルを利用するためクエリが遅くなります。1つ目の演習と同じように hire_date にインデックスを追加してみましょう。

mysql> alter table employees add index index_name(hire_date);
Query OK, 0 rows affected (0.40 sec)
Records: 0  Duplicates: 0  Warnings: 0

再びクエリを実行してみます。

mysql> SELECT SQL_NO_CACHE * FROM employees WHERE birth_date > '1959-01-01' ORDER BY hire_date LIMIT 5;
+--------+------------+-------------+-----------+--------+------------+
| emp_no | birth_date | first_name  | last_name | gender | hire_date  |
+--------+------------+-------------+-----------+--------+------------+
| 110085 | 1959-10-28 | Ebru        | Alpin     | M      | 1985-01-01 |
| 110725 | 1961-03-14 | Peternela   | Onuegbe   | F      | 1985-01-01 |
| 111035 | 1962-02-24 | Przemyslawa | Kaelbling | M      | 1985-01-01 |
| 111400 | 1959-11-09 | Arie        | Staelin   | M      | 1985-01-01 |
|  20539 | 1963-08-02 | Poorav      | Gecsei    | M      | 1985-02-01 |
+--------+------------+-------------+-----------+--------+------------+
5 rows in set (0.00 sec)

0.00 sec まで改善できました。EXPLAINでファイルソートが起きていないか確認してみましょう。

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM employees WHERE birth_date > '1959-01-01' ORDER BY hire_date LIMIT 5;
+----+-------------+-----------+------------+-------+---------------+------------+---------+------+------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | index | NULL          | index_name | 3       | NULL |    5 |    33.33 | Using where |
+----+-------------+-----------+------------+-------+---------------+------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

Using filesort が消えてファイルソートが起きなくなったことを確認できました。

感想

坂本: 今までデータベースについていろいろなアンチパターンや高速化について勉強をしてきましたが、今回の研修を通じて体系的に学べたおかげで点と点が線になり、今後につながる学びが得られたと思います。今後は、業務で役に立つようにさらに勉強を重ねて自分の武器となるようにしていきたいです。

左野: 今回の研修ではデータベースについて知らないことばかりだと痛感しました。データベースの知識は業務で欠かせないものになると思うので、講義資料を読み込んで勉強していきます。

git challengeの自動採点高速化に向けたインフラのハナシ

git challengeのインフラを担当している2016年度新卒エンジニアの轟 (@tdrk18) と、2017年度新卒エンジニアでSREの見習いをやっております佐藤 (@jtwp470) です。今回は、git challengeという技術競技イベントの自動採点の高速化に向けたインフラのお話です。

続きを読む