Sharding 詳解
〜機能と仕組みの理解〜doroykujin
自己紹介• doryokujin (25 歳 )
• 諭吉大学院 数学科 卒業できました
• MongoDB JP 主催者
• 芸者東京エンターテインメント (GTE) データマイニングエンジニア
発表の目的• Mongo Sharding への理解を深めてほしい– 1. Mongo Sharding の特徴を知ってほしい– 2. なかなか知り得ない機能詳細や意外な振る
舞いがあることを知って欲しい– 3. 使用上における問題点をきちんと把握して
おいて欲しい– 4. Mongo Sharding を試すきっかけになって
欲しい
発表の内容• 「 Scaling MongoDB」
の内容を 7 割方網羅– Mongo Sharding に焦点
を絞って書かれた本
– この本を読むこと Sharding への理解がかなり深まる
• 併せてドキュメントの内容も盛り込んでいる
アジェンダ• 1. Mongo Sharding の 3 つのコンセプ
トを紹介→ 概要の理解
• 2. Mongo Sharding の機能・振る舞いを4 つのキーワードから紹介→ 個々の機能の深い理解
最終的に言いたいこと• Shard Key の設定は非常に重要、慎重に
– Shard の偏りを極力減らす事は重要
– Shard Key によって偏り具合が大きく異なる
• Sharding 環境におけるクエリは振る舞いが怪しい部分があるので要注意(重複問題)
– 正しい count() が行われない
– unique のはずのキーが重複して存在
Mongo Sharding 3 つのコンセプト
Sharding とは?• 定義: DB 上のデータを複数のサーバーに
分割して運用すること• メリット:個々のサーバーの– 負荷の軽減– 使用領域を軽減– パフォーマンス向上– 扱えるデータサイズの向上
Mongo Sharding Concept
• MongoDB の掲げる 3 つのコンセプト
– 「 Make the cluster “invisible” 」
– 「 Make the cluster always available for reads and writes 」
– 「 Let the cluster grow easily 」
Mongo Sharding Concept 1
• Make the cluster “invisible”
– 全ての接続を mongos サーバーが仲介することによってクライアントはクラスタ全体の情報を意識することなく扱える
– クライアントは Sharding していない状況と同じようにクエリを発行し、結果を得ることができるより高速に)
Mongo Sharding Concept 2
• Make the cluster always available for reads and writes
– Auto-Failover で MongoDB のダウン時間を減らせ、常にデータにアクセスできる状態
– データの自動分割や Auto-balancing によって常に安定した状態を保ち続けられる
Mongo Sharding Concept 3
• Let the cluster grow easily
– Shard の追加・撤退が容易にできる– 新しい Shard の追加に対して自動的にデータ
の移行・均一化が図れる( Migration, Auto-Balancing )
Mongo Sharding を理解するための 4つのキーワード
Mongo Sharding を理解するための 4つのキーワード
• Shard Key:
– コレクションを分割する際のキー
• Balancing, Migration:
– Shard 間のデータの均等な分散を維持する仕組み
• mongos, config, mongod:
– Sharding を構成する 3 種類のサーバー
• Sharding クエリ :
– Sharding 環境におけるクエリの振舞と問題点
1. Shard Key
Shard Key :概略• データの分割は何らかのキーに従って行う• それを Shard Key と呼ぶ• Shard Key は Collection 内のどれか 1 つのキー
から選択• 各 Shard 内のデータは Chunk と呼ばれるデータ
単位で分割・移動・削除などが行われる• Shard 間の偏りとは、 Chunk の数が一部に偏る事• Shard が偏るのはよろしくない
Chunk に関して• Chunk は、特定のコレクションの連続した範囲
のデータ集合• データがどの Chunk に属するかは、 Shard
Key の値によって決まる• Chunk はデータサイズが 200MB (デフォル
ト)を超えると自動で等分割が行われる• 分割されるごとに Shard 内の Chunk 数が増
加• Shard Key は型の混在が可能。MongoDB は
全ての型を通して順序が決まっている
http://www.10gen.com/static/presentations/scaling-jp.pdf
データが挿入されていく度にChunk が分割されていくとしてその様子をみてみる
これが Chunk と呼ばれるデータの集合単位。連続した範囲のデータ集合。新しいデータは Shard Keyの値が Chunk の範囲内にある所に入る。 200MB を超える度に分割が起こる。(デフォルト)
Chunk に関する注意点
• 各々の Chunk の持つ範囲は絶対に重複しない。1 つのデータに対してただ 1 つの Chunk が対応。範囲は [ a, b ) で、左を含み右を含まない
• Shard Key は変更できない。やり直したい場合は対象の Collection を削除してから
• ShardKey の値を持たないドキュメントは保存できない。ただし null は可能
2. Balancing, Migration
Auto-balancing, Migration• Auto-balancing
– ある特定の Shard に Chunk が集中した場合、自動で他の Shard への移行( =Migration )
– トリガーは Shard 内における最大 Chunk 数と最小 Chunk 数の差が 10以上になったとき
• デフォルトで最大 200MB/Chunk なのでデータサイズの差が 2GB以上になったとき
– 最大 Shard からランダムに 5 つの Chunk が選ばれ、最小 Shard への Migration が行われる
Migration における問題• ※ Migration には多大なコストがかかる– config サーバーへの負担–ネットワーク帯域の圧迫– メモリ領域の圧迫( Chunk をメモリにコピーするので)
– パフォーマンスの低下– 正しいクエリの結果を返さない(後述)–低速。完了に非常に時間がかかる
1. (再) Shard Key
Shard Key 選択の重要性• Migration は極力起こらないように
• つまり Shard 間に Chunk の偏りがない
• Shard 間に偏り少なくなる Shard Key を選択
• 一度選択した ShardKey は変更できないので慎重に
Shard Key: 悪い例①• Low-Cardinality Shard Key
– 「密度」の低い Shard Key
– 主にカテゴリデータなどの”離散”データ– 例:大陸名
• (-∞, America], (America, Asia], (Asia, Australia],…
– 理由:• Chunk の分割ができない
• N 個のカテゴリ数なら N 個の Chunk しかできない
• N 個以上の Shard サーバーは意味がない
Shard Key: 悪い例②• Ascending Shard Key
– Shard Key の値が増えていくのみのデータ– Time, ObjectId, Incremental Unique Id
– 例:• [-∞, +∞ )
• → [ -∞, 12945 ), [12945, +∞ )
• → [ -∞, 12945 ), [12945, 12948 ), [ 12948, +∞ )
– 理由:• ( $CurrentMax, +∞ ] の Chunk だけがひたすら分割• 明らかに Shard 間で不均一がおこる
ShardKey: 悪い例③• Random Shard Key
– Shard Key の値が Random に決まる場合–例: MD5 hash
– 理由:• Chunk は均等分散するが非常に大きな範囲を
持った Chunk が存在する可能性
• その Chunk の Migration は RAM を圧迫する
• Random Shard Key に対する Index も非効率
Shard Key: 良い例• Coarsely Ascending Key + Search Key
– 緩く増加していくキー + 検索でよく使われるキー– 例:アクセス解析 { month:1, user: 1}
• ( (“2011-04”,”doryokujin”), (“2011-04”, “gohan”) ),
( (“2011-04”,”gohan”), (“2011-04”, “taberu”) ),…
– 理由:• 左のキーは一定期間固定で、右のキーはほぼ均等に分布し
てくれる
• 時間がたつと左のキーの値が増え、元の(解析対象としない、古い)値の Chunk は分割されなくなり、 Migration が起こりにくくなる
良い Shard Key のイメージ• 長い時間が経って Shard の偏りが起き始
める頃に、片方のキーが増加して Shard をリフレッシュしてくれる
shard1 shard2 shard3 shard4 shard5 shard6
2011/04 2011/05
name
month
3. mongos, config, mongod
Sharding を構成するサーバー群
config Servers(Shard Configration)
mongos Servers (Routers)
Shard Servers (Data)
[ a, f )
[ f, k )
[ k, n)
[ n, o )
[ o, t )
[ t, } )
shard1 shard2 shard3
Chunk
Cluster
サーバー間の関係
http://www.mongodb.org/display/DOCS/Sharding+Introduction
mongos サーバー
クライアントのアクセスは必ず mongos に対して行う
該当データのある Shard にのみクエリの送出・データの取得を行う
cinfig サーバーから Shard に関する情報を取得する
mongos サーバーの役割• クライアントは全ての読み書きを
mongos に対して行う• mongos はどの Chunk がどの Shard
にあるかを把握している• ただし永続的な情報を保存するわけでは
ない。 config サーバーがそれを持つ• [ ダウン ] : Shard にアクセス不能にな
るので、完全に機能停止することに
config サーバー
“ 特殊な” mongod サーバー
shard に関する (永続的な )メタ情報を保持
change chunkSizedisable Balancing
config サーバーの役割
• shards, mongos process, sysadmin に関するメタ情報を保持
• 複数起動可能。ただしテスト環境は 1 つ、本番環境でも 3 つで十分( 3 で最適化)
• 独自のプロトコルで同期を行う。手動でReplication の設定をしてはいけない
• [ ダウン ] : Shard の設定変更が不可能になる。読み書きは継続
config Collection> use configswitched to db config
> show collections changelog # 過去の Migration に関する詳細なログ chunks # 全ての Chunk の情報 collections # Sharding している全ての Collection の情報 databases # 全 DB に関して Sharding の有無などの情報 lockpingslocksmongos # mongos に関する情報settings # chunkSize の設定情報、 balancer 機能の切替shards # Shard に関する情報system.indexesversion
> db.settings.update({"_id" : "balancer"}, {"$set" : {"stopped" : true }}, true)
4. Sharding クエリ
mongos へのクエリタイプ• Targeted– Shard Key が指定されることによって、必要
なデータがある shard にのみアクセス。最小限。
• Global– Shard Key が指定されず、 mongos プロセ
スはシステム内の ( ほぼ ) すべての Shard にアクセス
Shard Key を用いての find()
{“name”: “inoue” }
[ k, n)
[ n, o )
shard2mongos
[ a, f )
[ f, k )
shard1
{“name”: “tanaka” }
[ o, t )
[ t, } )
shard3
指定された Shard Key (“name” key) を含む Chunk のある Shard にのみクエリの発行・データの取得を行う
db.adminCommand({"shardCollection" : ”mongo.users", key : {”name" : 1}}
db.shardCollection.find({“shardKey”: “xxx”})
targeted
db.shardCollection.find({“nonShardKey”: “xxx”})
[ k, n)
[ n, o )
shard2mongos
[ a, f )
[ f, k )
shard1
{“pref”: “Tokyo” }
[ o, t )
[ t, } )
shard3
mongos は Shard Key 以外 (“pref” key) の情報を知り得ないので全ての Shard にアクセスする。
ただしインデックスを利用できる。
db.adminCommand({"shardCollection" : ”mongo.users", key : {”name" : 1}}
Non Shard Key を用いての find()global
db.shardCollection.update({“shardKey”: “x”},{$set{key:value}})
{“name”: “inoue” }
[ k, n)
[ n, o )
shard2mongos
[ a, f )
[ f, k )
shard1
{“name”: “tanaka” }
[ o, t )
[ t, } )
shard3
指定された shard key(“name” key) を含む chunk のあるshard にのみクエリの送出・データの取得を行う
db.adminCommand({"shardCollection" : ”mongo.users", key : {”name" : 1}}
update
update
Shard Key を用いての single-update()
targeted
db.shardCollection.find().sort({“nonShardKey”: 1})
[ k, n)
[ n, o )
shard2
[ a, f )
[ f, k )
shard1
[ o, t )
[ t, } )
shard3
sort() が呼ばれた場合は、各 Shard における検索結果のドキュメント集合に対して sort を行い、さらに mongos が全 Shard で merge sort
shard1 内で sort
shard2 内で sort
shard3 内で sort
shard1,2,3 の結果を merge sort
{“pref”: “Tokyo” } mongos
Non Shard Key を用いての sort()
global
db.shardCollection.count ({“nonShardKey”: 1})
[ k, n)
[ n, o )
shard2
[ a, f )
[ f, k )
shard1
[ o, t )
[ t, } )
shard3
count() が呼ばれた場合は、各 Shard における検索結果のドキュメント集合に対して count を
行い、さらに mongos が全 Shard の結果で total
shard1 内で count
shard2 内で count
shard3 内で count
shard1,2,3 の結果で count
{“pref”: “Tokyo” } mongos
Non Shard Key を用いての count()
global
db.runCommand ({mapreduce: shardedCollection,…})
[ k, n)
[ n, o )
shard2mongos
[ a, f )
[ f, k )
shard1
[ o, t )
[ t, } )
shard3
mapreduce
mapreduce
shard1,2,3 の結果で ” re”-reduce
mapreduce
Non Shard Key を用いての mapreduce()
emit( “pref”: 1 )
mapreduce() が呼ばれた場合は、各 Shard における検索結果のドキュメント集合に対して mapreduce を行い、さらに mongos が全 Shard の結果で reduce
global
{ x : 1 } Shard Key と仮定。targeted では Shard へのアクセスは最小限global は、システム内の ( ほぼ ) すべての Shard にアクセス
Shard Key を使用しない場合に問題のあるクエリ
• Sharding 環境で Shard Key を使用しないクエリは、必ずしも正しく実行されるとは限らない– 「 Chunk Migration 中の count() 」– 「同時書き込み発生時の single-update() 」– 「同時書き込み発生時の unique index 」– 「 tmp_collection が削除されない
mapreduce() 」
Chunk Migration 中の count()
• Chunk Migration 中は処理が完了するまで、移動元・移動先に同じ Chunk が存在する
• 完全にコピーが完了し、 config サーバーが更新されて完了となる
• その間に count() コマンドが実行された場合、この Chunk のデータは重複してカウントしてしまう可能性がある。
• 結果:真値より大きくなる
mongos
shard2shard1
X
shard2shard1
X
shard2shard1
X
X
移行中…
count X
count X
Chunk Migration 中の count()
重複カウント
同時書き込み発生時のunique Index
• MongoDB は書き込みに対してロックを行わない(複数の書き込みに対してアトミックでない)
• unique な Index として設定したキー(Shard Key では無い ) が異なる Shard 間で存在する可能性がある
• “_id” でさえも重複する可能性がある• 結果: Shard Key 以外のユニーク性が保証されない
mongos
shardKey={“email”:1} とは別に unique Index={“name”:1} である Key での update が同時に行われた場合、 unique
キーなのに重複する可能性がある
shard 1
X
shard2
shard3
Y
Z
mongos
{"_id" : ObjectId("4d2a2f7c56d1bb09196fe7d0"), ”name" : ”doryokujin", "email" : “[email protected]” }
{"_id" : ObjectId("4d2a2e9f74de15b8306fe7d0"), ”name" : ”doryokujin", "email" : ”[email protected]"
update
update
同時書き込み発生時のunique Index
同時書き込み発生時のsingle-update
• 同様に nonShard なキーで同時に single- update を行う際にも問題が
• アトミックな操作が保証されないのでエラーとなる
• Non Shard Key に対しては必ず multi-update を行う
• 結果: single-update に対してエラー
「 tmp_collection が削除されないmapreduce() 」
• Shard の中から 1 つの「代表」が選ばれ、各Shard で mapreduce が実行された結果に対してもう一度 reduce を行う
• 計算はオプションを指定しない限り、 tmp_collection が作られる
• Sharding 環境では計算終了後に一時的に作成した tmp_collection が自動で削除されない
• 結果:不要な Collection が増えていく• v.1.8 で改善。手動で削除して対処
Sharding クエリは要注意
• このように Sharding 環境におけるクエリには十分な注意が必要
• 同時書き込みに対してアトミックでないことが影響
• Chunk Migration 中の Chunk は重複して数えられる可能性がある
Mongo Sharding を理解するための 4つのキーワード
• Shard Key:
– コレクションを分割する際のキー
• Balancing, Migration:
– Shard 間のデータの均等な分散を維持する仕組み
• mongos, config, mongod:
– Sharding を構成する 3 種類のサーバー
• Sharding クエリ :
– Sharding 環境におけるクエリの振舞と問題点
最終的に言いたいこと• Shard Key の設定は非常に重要、慎重に
– Shard の偏りを極力減らす事は重要
– Shard Key によって偏り具合が大きく異なる
• Sharding 環境におけるクエリは振る舞いが怪しい部分があるので要注意
– 正しい count() が行われない
– unique のはずのキーが重複して存在
ありがとうございました…