月間10億pvを支えるmongo db
DESCRIPTION
TRANSCRIPT
月間10億PVを支えるMongoDB
speaker: “Yuji Isobe”
name: “Yuji Isobe”
attributes: [ “engineer” “consultant” “security specialist” “startup member \ of ”]
-> 2年間で月間10億PVを支える まで成長した の 「泥臭い」負荷対策をお話します-> あるある紹介-> 今はまだ問題点を洗い出している段階 ベストプラクティスがあれば教えて欲しい
goals:
大量データ
大量データトラフィック急増
-> 合計月間PV10億-> 秒間平均400PV-> 平均同時接続数5万-> 月間保存データ量10TB
db. .stats()
back_ends: [ “Node.js” “CoffeeScript” “MongoDB” “Redis” “AWS”]
front_ends: [ “Nginx” “Ruby on Rails” “MySQL” “memcached” “AWS”]
architecture:
back_ends: [ “Node.js” “CoffeeScript” “MongoDB” “Redis” “AWS”]
front_ends: [ “Nginx” “Ruby on Rails” “MySQL” “memcached” “AWS”]
architecture:
we.use(“ ”).explain()
-> スキーマレス-> 安定した書き込み-> 柔軟なクエリ-> 容易なスケールアウト-> との相性の良さ
月間10億PVへの道のり
-> ~3000万PV-> 3000万~1億PV-> 1億~3億PV-> 3億~10億PV-> 継続的な監視
~3000万PVいいから
インデックスだ
!!!
-> メモリ上に乗れば高速-> 1クエリにつき1インデックス-> B-Treeのバランスが重要
インデックスを理解する
-> メモリ上に乗れば高速-> 1クエリにつき1インデックス-> B-Treeのバランスが重要
インデックスを理解する
From 2.6.0
メモリ上に乗れば高速
user = new mongoose.Schema first: {type: String, required: true, index: true} last: {type: String, required: true, index: true} city: {type: String, required: true, index: true} ... created_at: {type: Date, default: Date.now, index: true}
書き込みが犠牲になっても読み込みが早ければいいじゃん!
Before
user = new mongoose.Schema first: {type: String, required: true} last: {type: String, required: true} city: {type: String, required: true} ... created_at: {type: Date, default: Date.now, index: true}
書き込みが犠牲になっても読み込みが早ければいいじゃん!
After
メモリ上に乗らなければ遅くなる
プログラムからフィールドを削除したが、インデックスを消し忘れ、無駄なインデックスが残る…
スキーマレスあるある
1クエリにつき1インデックス
From 2.6.0
db.users.ensureIndex({first:1});db.users.ensureIndex({last:1});
db.users.ensureIndex({ first:1, last:1});
indexintersection
compoundindex vs.
-> 複数の検索条件を 組み合わせたインデックス
-> 2つのインデックスを使った 検索結果の共通部分を返す
{first: “Yuji”, last: “Isobe”, city: “Osaka”}
From 2.6.0
db.users.ensureIndex({first:1});db.users.ensureIndex({last:1});
db.users.ensureIndex({ first:1, last:1});
indexintersection
compoundindex vs.
-> 複数の検索条件を 組み合わせたインデックス
-> 2つのインデックスを使った 検索結果の共通部分を返す
{first: “Yuji”, last: “Isobe”, city: “Osaka”}
From 2.6.0
このクエリを早くしたい
db.users.ensureIndex({first:1});db.users.ensureIndex({last:1});db.users.ensureIndex({ first:1, last:1});
indexintersection
compoundindex vs.
メリット-> 同時にいくつでも
デメリット-> 用途は限定的
メリット-> 柔軟な用途
デメリット-> 現在は同時に2つまで
2.6.0以降は消してしまってもいいかも知れません
From 2.6.0
B-Treeのバランスが重要
-> インデックスにB-Treeを採用-> B-Treeは常に平衡を保っている
インデックスの構造
文字列のインデックス
-> Hotデータが分散-> メモリ効率が悪い
ObjectId・時間のインデックス
-> Hotデータが集中-> メモリ効率が良い
できるだけObjectIdを使いましょう後からの変更は大変です(経験談)
教訓
Schema Design at Scale
http://www.mongodb.com/presentations/schema-design-scale-1
3000万~1億PV
書込みは
一度だけ
-> にバッファする-> Bulk Inserts/Operationsを使う
書き込みの回数を減らす
-> にバッファする-> Bulk Inserts/Operationsを使う
書き込みの回数を減らす
From 2.6.0
にバッファする
-> オンメモリ型のKVS-> データ型が豊富 -> リスト・セット・ハッシュ
-> の苦手分野を補う
-> は肥大化するデータの Updateは遅い-> はオンメモリ型のため リストの操作が早い
の苦手分野
without
db.col.update({_id: 1}, {$push: {data: {x: 151, y: 100, ...}});db.col.update({_id: 1}, {$push: {data: {x: 151, y: 179, ...}});db.col.update({_id: 1}, {$push: {data: {x: 151, y: 266, ...}});db.col.update({_id: 1}, {$push: {data: {x: 151, y: 295, ...}});db.col.update({_id: 1}, {$push: {data: {x: 151, y: 322, ...}});db.col.update({_id: 1}, {$push: {data: {x: 151, y: 333, ...}});db.col.update({_id: 1}, {$push: {data: {x: 151, y: 340, ...}});...
db.col.insert({data: [ {x: 151, y: 100, ...}, {x: 151, y: 179, ...}, {x: 151, y: 266, ...}, {x: 151, y: 295, ...}, {x: 151, y: 322, ...}, {x: 151, y: 333, ...}, {x: 151, y: 340, ...}, ...]});
with
Bulk Inserts/Operationsを使うFrom 2.6.0
db.col.insert([ {first: “Yuji”, last: “Isobe”, city: “Osaka”}, {first: “Yuji”, last: “Isobe”, city: “Tokyo”}]);
使い方
var bulk = db.col.intializeOrderedBulkOp()bulk.find({first: “Yuji”}) .updateOne({$set: {city: “Tokyo”}});
Bulk Inserts
Bulk Operations From 2.6.0
MongoDBは開発スピードが早く、パフォーマンス向上機能が次々と出るので、積極的に使いましょう
豆知識
1億~3億PV
mongodmongodmongodmongodmongodmongodmongodmongodmongodmongod
-> 用途ごとにDBを分ける-> Hotデータ/Coldデータを分ける-> 古くなったデータを削除する
DB設計
-> Reader-Writerロックが使われている-> 2.2.0以降はDB単位でロックする-> マシンを分ければメモリが増える
なぜDBを分けるのか?
-> メインデータ(Hot)-> メインデータ(Cold)-> 生データ-> 統計データ-> サイトデータ
におけるDBの用途
-> Hotデータのロックを避ける-> Hotデータ用に大容量メモリ-> Coldデータ用に大容量ストレージ
Hotデータ/Coldデータを分ける
-> S3にバックアップ-> 統計データを保存-> 定期的な自動削除
古いデータを削除する
クエリの条件をタイプミスしてインデックスを使うことができず
MongoDB死亡…
スキーマレスあるある
3億~10億PV
メモリが
欲しいです
-> メモリは積めば積む程早くなる-> ディスクアクセスも早い方が良い -> SSDを使いましょう
チューニング
EC2インスタンスの選び方
http://blog.mongodirector.com/mongodb-on-aws-how-to-choose-the-right-ec2-instance-type-for-your-mongodb-server/
一般的な目的(m3)-> 最初に選ぶインスタンス
メモリの最適化(r3)-> メモリとCPUのバランスが良い
ストレージの最適化(i2/hs)-> SSDを使用。高負荷に耐えられる
EC2インスタンスの選び方
r3.2xlarge-> vCPU: 8-> Memory: 61GiB-> SSD: 160GB-> Cost: $0.840 per Hour
i2.2xlarge-> vCPU: 8-> Memory: 61GiB-> SSD: 800GB x 2-> Cost: $2.001 per Hour
主に使用しているインスタンス
継続的な監視
監視を止めたら
そこで異常終了
ですよ?
-> ホスティングサービスの ダッシュボード-> 毎日の健康チェック用-> 過去の情報は参照できない-> メリット・デメリットは下記参照ホスティングサービスで始めるMongoDBhttp://www.slideshare.net/yujiosaka/starting-mongo-db-on-hosting-services
-> 定期的な健康診断用-> リアルタイム監視はMongoHQ 過去の状態チェックはMMS-> アラートとバックアップもあり-> 導入が簡単なので、 とりあえず入れておくべき
-> explain(true)-> system.profile.find()-> MongoDB professor-> Dex-> MongoHQ Slow Queries
遅いクエリの監視
ObjectIdを使ったクエリの実行に100ms以上かかったら危険信号です
豆知識
-> インデックスの仕組みを理解する-> 書き込みの回数を減らす-> DBを用途別に分ける-> メモリを増やす-> 継続的に監視する
my.presenation.aggregate()
my.presentation.end “thank you ;)”
any?.questions?