NoSQLは速いのか?

生島勘富氏(以下、生島):では、NoSQLとSQLの話に入らせていただきます。この章では、MySQLと、memcachedと、MySQLをNoSQLのように使うHandler Socketというものがあるんですが、そのHandler Socketについて、少し説明します。

これはRDBの構造を説明したものです。

memcachedというものがバッファリングしていて、それがないときはストレージシステムに「ちょうだい」と言って、書き込み依頼があったら「書き込むよ」という構造です。(RDBサーバの構造の下半分を指して)ここのこれだけ一緒なんですよね。

RDBには、もうバッファリング機能があるんですよ。それで、たいがい大量のメモリを抱えていて、ほぼ一緒です。ここだけ使いたい人はmemcachedを使えばいいですよ、というようなかたちです。

ですから、NoSQLは、だいたいRDBのなんらかの機能を省いたもの、サブセットになるんです。ただ、たまに機能を足していることもあります。

RDBはストレージではないのですが、NoSQLまでくると、もうストレージと考えて……キーバリューストアというものがありますから、ストアなんですね。NoSQLといわれるものは、ストレージと考えてもいいものがたくさんありますが、RDBMSはストレージではありません。

RDBが一番複雑なので、これを十分わかっていただいたら、新たなNoSQLが出てきたときに、「これはどの機能を省いて、何を足したのか?」ということで、自分に必要なものが何か理解できるので、まずはRDBのことをきちんと理解してください。だいたいこれから派生して作られているので、これを理解しておけば派生も理解できます。

SQL・memcached・Handler Socketを比較する

これは少し古いですが、Handler Socketを作られたDeNAの松信さんが7、8年前に書かれたブログです。

そこでは「1ギガのNICを4ポート刺したサーバに、SQLとmemcachedとHandler Socketにそれぞれ1,000万回(のリクエストを)流したら、1秒間にこれだけ処理できました」というような実験をやられたみたいです。

SQLに流した場合、1秒間に10万回ですらなかなかのものです。結果として、memcachedで42万回、Handler Socketで75万回取れました。これはなかなかのスピードです。

おもしろいのは、実はSQLでやったときは、88パーセントしかCPUを使い切れていないんです。この4ポートといった条件では、CPUを使い切るのが難しいんですね。SQLでは、他の待ちが入っているんです。

(memcachedとHandler Socketを指して)このあたりだと簡単な処理になるので、ほぼ100パーセントに張り付いてますね。(SQLを指して)ここが88パーセント。だから、なかなかSQLでCPUを張り付かせるのは難しいな……まあ、それは余談です。けっこういろんなところでWAITが入って、実はSQLの方が張り付かせるのが難しいですよということです。

SQLを1としたら、4倍速い、7倍速いというのを図形化すると、だいたいこんなかたちです。

(実際のデータ処理の部分を指して)Handler Socketのここは、MySQLのここと一致します。なぜかというと、同じものを使っているからです。ここがものすごく小さいということは、SQLのオーバーヘッドが大きくないと計算が合わないんです(先ほど説明した通り、実際にオーバーヘッドは大きい)。

memcachedはそれより遅いので、データ処理で下手くそなことをやっているのがわかりますが、こんなかたちになります。

(リクエストとレスポンスの部分を指して)やはりここが大きいです。ネットワークは遅いデバイスを使うことになるため、ここが問題になります。

複数件同時に処理できれば逆転する

先ほどから「SQLを書きなさい」と言っていますが、SQLは複数同時に処理できます。

ドカンと処理してあげれば、オーバーヘッドは全部大きくなるけれども、これを繰り返して、数件同時に処理できれば、memcachedを超えることができます。

ですから、どれぐらい複数処理できるかわからないと、単純に比較することはできません。こんなに大きなオーバーヘッドを使っていても逆転は起こるんです。

私が初めてSQLを使ったとき、64Kbpsのネットワークを使ったシステムを組んでいました。ISDNのダイヤルアップでシステムを組んでいたんですね。必要なとき、勝手にダイヤルアップされるという設定にして、そんなことを5年ぐらい行っていました。

だから、SQLで処理するときは1回のリクエストでできるだけ大量の処理をしましょう。逆に、1件だけ取得するような処理が多いゲームでは、オーバーヘッドが大きいのでSQLは使ってはいけません。1件取って、1件返してという処理ばかりするのであれば、こんなに大きなオーバーヘッドのものを使ってはダメなんです。

結局、「SQLを使わない≠ORMを使う」なんですね。SQLを使わないというのは、(SQLのオーバヘッドを指して)ここを使わない、なんですね。ですから、Handler SocketやMySQLクラスタにはNoSQL APIというのがありますけれども、そういうものを使うのが、SQLを使わないということなんです。

これを勘違いして、「ORM使ってます。なので、SQLなんか書いてません」と言う人がけっこういらっしゃるんですが、それは少し違います。

SQLを使うなら、SQLのオーバーヘッドがものすごく大きいから、ここを小さくするために、人間が考えたSQLをきっちりと書いてあげないといけないね、というのが最終的な結論です。(Handler Socketを指して)ゲームだったらおそらく1件で取るのが非常に多いので、こっちのほうがいいんじゃないの、というのがオチです。

MySQLからの派生でMariaDBというものがありますが、Handler Socketは、MySQLで使うには、MySQLのソースとHandler Socketのソースを持ってきてコンパイルする必要があります。そうすると、RDSではコンパイルはできないから使えないんです。

RDSがHandler Socketに対応していたらいいんですが……きちんと調べていないのでMySQLのRDSは対応しているかどうかわからないのですが、MariaDBなら標準でできるようになっているため、MariaDBに乗り換えてもらえば、Handler SocketがRDSでも使えます。そうすると、1件取るならこっちにしちゃおう、ということができるようになります。

よって、インピーダンスミスマッチというのはなくならないです。ですので、ゲーム会社であればSQLを書くときとHandler Socketとの併用がおすすめです。

ORMで、JOINが入るときは、いままでどおりSQLを書きまして、それ以外のときはHandler Socketで流すように、ORMを改造したらどうでしょうか。私だったらそうしますね。まずはDBをHandler Socketに対応させなければいけませんが。

UNITIAでSQLを使うべきか考察する

今日は『UNITIA』でSQLを使うべきか考察をしたいと思います。ここはゲーム屋さんにツッコんでいただきたいところではありますが、DB屋さんから見て、やってみた考察です。

これは私がやっているUNITIAのデータです。けっこう使い込んでいることがわかると思います。さて、この画面を立ち上げるとき、私のマシンで5秒から7秒かかります。ただ、これは他のゲームでも、こういったスクロールがあるときは動作がすごく遅いんですよ。「スクロールがあるとき、遅いな」と、私はいつも思っていました。

スクロールがない画面では3秒ぐらいでできます。ということは、2秒から4秒ぐらいスクロールで使っているということです。

ここは、同じようにスクロールがある画面で、スクロールが3つあります。ランキングが3つあって、タブが3つあって、たぶん同時に書いてあるはずです。ここでは30件、50件のリストを書いているんですが、ここは速くて、3秒です。

DB屋の視点で改善策を考える

ここから考えると、ORMでこの部分のSQLを、私なりにテーブルを想像して書くと、「ORMでも書けるな」というぐらいの単純なSQLになります。

このギルドランキングやここのランキングも、簡単なSQLでできるため、もしかしたらORMでSQLを書けるかもしれません。

5秒から7秒かかっていたこの部分のSQLを想像すると、サブクエリが3つぐらい入るSQLなのではないかと思います。なんとなく、「ゲーム屋さんはこんなSQLは書かないな」と思いますが。

ですので、これは私の想像です。そういえばちょうど先日、100万ユーザーを超えられたということで、おめでとうございます。100万ユーザーいて、100万のなかから50件抜くというSQLを書くとすると、私のイメージでは「数十ミリセックで返せるな」と思います。でも、これを50件ループさせているとしたら、そこそこ遅くなります。

でも、わからないです。「これは描画が遅いのか、ぐるぐるやっているのか、どっちなのかな? 5秒から7秒、うーん……」というのが謎なんですが、もしかしたらこのタイプのものはグルグルやるしかないような気もします。

これは私のイメージですが、「SQLで表現しにくい条件だな」と思ったとき、「これ、ORMでチャチャッと書けるような条件じゃないな」となったときはたいがい遅いです(笑)。

だから、こういうところは生で書いたら速くなります。確か、前回か前々回のアップデートで、この画面が重いから、再挑戦かなにかのボタンを押したときに、この画面を出さずにいきなりバトルに進むようになって、うちのギルドでけっこう評判がよかったので(笑)、やっぱりみんな遅いと感じているんだなと思いました。

技術に新しいも古いもない

では、最後に「古い技術を見直そう!」というテーマについてお話していきたいと思います。これはCygamesの人が書かれている「データ指向設計」についての記事です。

私はびっくりしたんですが、こういったデータ指向設計では何をやっているかというと、こんなソースを書かれてます。これはソースです。オブジェクト指向で敵を書いていて、うんちゃらかんちゃらで、更新するときはこんな感じで、データ指向設計のソースはこういう感じになるよ、と書かれています。

(データ指向設計のソースを指して)これ、パッと見でわかりにくいです。

でも、私たちおじさんから見ると、「これ、C言語の時代のやつやん」「オブジェクト指向がなかったC言語時代のソースやん」ということで……データ指向設計は、C言語時代の設計なんですね。

オブジェクト指向で先ほどのように書くと、メモリ上にはこういうふうに載るんですよ。データ指向設計というのは、「これを最密充填するにはどうしたらいいか?」って考えるのがデータ指向なんです。「最密充填させるために、どういうふうに書くか?」というのを考えながら設計するためデータ指向設計というんですが、これで最密充填されました。

CPUが処理するときに、この64バイト……たった64バイトなんですが、一気に動く処理があるんですね。一気に読んで、2次キャッシュに突っ込む処理があるんです。2次キャッシュに突っ込んで、次のループがきたときにまたこれを見るんですが、2次キャッシュにすでに次のデータが載っているんですよ。ヒットするから、2次キャッシュのヒット率が高くなって速くなる。

これだと、だいたい3倍速くなります。このように「メモリ上にどう載るか?」というところまで考えて書きましょうという考え方なんですが、そんなものは……我々は大昔、C言語を使っていた時代に「メモリにどうレイアウトするか?」というところから考えていたので、当たり前のことなんです。

おそらくこれはAIでガーッと動くゲームなんですが、AIで細かいキャラが動いてしまうゲームというのは、こういう処理をしてあげなければいけません。

『UNITIA』でこの書き方をしたら速くなるということは、おそらくありません。だから、もし私が『UNITIA』を設計するなら「(オブジェクト指向を指して)絶対こっちでやってくれ」と言います。だから、なにがなんでも速いということを目指すわけではないんですが、(データ指向設計を指して)必要に応じてこっちもやりましょう。

ユーザー指向で技術を選ぶ

結局、SQLもそうですが、すごく古い技術ですよね。古い技術だからこそ、嫌う人が多いんです。少しショックだったのが、先日、「新しい言語を使ってるというのがモチベーションになる」というようなお話がありました。

それはまあ、モチベーションにはなるでしょうけれども、私のモチベーションは「あいつより3倍速いソース書いた」といったことだったり、SQLとSQLではないことだったら、20倍とか100倍の違いが簡単に出せるんですね。

先ほどの『UNITIA』の例でも5秒から7秒かかっていたところが2秒ぐらい速くなったらけっこううれしいなというのが、私のモチベーションになるんですね。

新しい技術かどうかではなく、ユーザーさんが喜んでくれるというところにモチベーションを持っていけば、「古いからイヤ」とか、そういうことはたぶんなくなるだろうなと思います。

データ指向設計は「2009年ぐらいに言われ出した」と書いていらっしゃいましたので、10年ぐらい経っています。

最近、たぶんUnityのエンジンを作る人は、(オブジェクト指向を指して)こっちでは3倍遅いということにもなりかねません。エンジンを作る人の技術でもあるんだとは思いますが、この手のゲームでも、たぶん効くイメージです。

我々がC言語でやっていた頃の話としては、「まあ、それは効くやろね」というところです。そして「できれば、古い技術もちょっとは勉強してね」というところです(笑)。たいていの新しい技術は古い技術から派生したり、それを拡張したりして作られているわけですから、古い技術でも悪いことはないと思います。

すいません。少し時間をオーバーしてしまいましたが、ご清聴ありがとうございました。

(会場拍手)

適切なページサイズは?

司会者:ありがとうございました。それでは、少し質疑応答のお時間が取れればと思っているんですが、質疑のある方はいらっしゃいますか?

(会場挙手)

質問者1:最初、パフォーマンスのところで、「ページサイズを変えるのもいいんじゃないか」というお話があったんですけれど、その発想が私にはまったくありませんでした。

それは、どちらかというと小さくする方向で書いていらっしゃいましたが、例えばレイテンシよりもスループットを高めたいときなどに、ページサイズを大きくしたりするのは効果があったりしますか?

生島:そうですね。というか、我々がよくやることで、データウェアハウスというような仕事をするとき、実は32Kとかを使うんですよ。

質問者1:そうなんですか。

生島:MySQLでは32Kは使えませんが、Oracleなどでは使えるので、32Kぐらいを使うこともけっこう多いです。ゲームは本当に細かいアクセスが多く、それがメインになるため、それなら4Kのほうがいいんじゃないの? という感じですね。

質問者1:ログ分析などで、大量のデータを取りたいと考えていたので、そういうのは……。

生島:そういったケースは16Kでいいと思います。

質問者1:あるいは、もっと大きくするみたいなことも?

生島:MySQLはできないんですよ。

質問者1:できないですか。

生島:いまのところ16Kが最大なので、ログ解析は、確かに大きいほうが有利です。

質問者1:ありがとうございました。

不整合にならない方法はあるか?

司会者:ありがとうございます。他に質問がある方はいらっしゃいますでしょうか?

(会場挙手)

質問者2:少し勉強不足なので、おうかがいしたい部分があります。メモリとハードディスクのデータの書き込み不整合の話があったかと思いますが、設定次第で不整合にならないようにする方法はあったりしますか?

生島:ないですね。基本的に不整合です。だから、ダーティページといわれるものがあるわけですよ。まだ書き込んでいないページがすでにあって、これが抜けるときか、ログファイルがいっぱいになったときに書き込みます。そうなると、当然抜けるときありますよね。使っていなかったら抜けますから。抜けたときも書き込みにいきますし、ログファイルがいっぱいになったときにも書き込みにいくという作りになっています。

質問者2:ありがとうございます。

司会者:ありがとうございます。生島さま、ありがとうございました。

(会場拍手)