English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

MongoDBの分片管理を深く理解する

前書き

MongoDB(バージョン 3.2.9)では、分片クラスタ(sharded cluster)は、データセットを異なる分片(shard)に分散的に保存することでデータベースシステムのパフォーマンスを水平に拡張する方法です。データセットは各分片にデータセットの一部のみを保存し、MongoDBは各分片間に重複するデータがないことを保証します。すべての分片で保存されているデータの合計が完全なデータセットです。分片クラスタはデータセットを分散的に保存し、負荷を複数の分片に分散することで、各分片がデータの一部のみを読み書きするようにし、各shardのシステムリソースを最大限に活用し、データベースシステムのスループットを向上させます。

データセットはデータブロック(chunk)に分割され、各データブロックには複数のdocが含まれています。データブロックは分散的にシャードクラスタに保存されます。MongoDBは、データブロックがシャード上での分布情報を追跡し、各シャードがどのデータブロックを保存しているかを保存する分片のメタデータ、config server上のデータベースconfigに保存します。一般的には、3config serverがあり、すべてのconfig server内のconfigデータベースは完全に同じでなければなりません。mongosを通じて直接configデータベースにアクセスし、シャードのメタデータを確認できます;mongo shellはsh補助関数を提供し、安全にシャードクラスタのメタデータ情報を確認できます。

shardのいずれかをクエリすると、collectionが現在のシャード上にあるデータのサブセットのみを取得し、データセット全体ではありません。Applicationは、mongosに接続するだけで、そこでの読み書き操作が自動的に読み書きリクエストが対応するシャードにルーティングされます。MongoDBは、mongosを通じて分片の低レベル実装をApplicationに透明にし、Applicationにとってはデータセット全体にアクセスしているように見えます。

、プライマリーシャード

分片クラスタ内では、各コレクションがデフォルトで分散的に保存されるわけではありません。sh.shardCollection()を使用して明示的にコレクションを分片化する必要があります。非分片集合(un-sharded collection)のデータは、デフォルトでは主分片(Primary shard)にのみ保存されます。主分片は、データベースが最初に作成されたシャードであり、非分片集合のデータを保存するために使用されます。各データベースにはプライマリーシャードがあります。

分片クラスタ内の各データベースには、すべての未分片化されたデータを保持するプライマリーシャードがあります。-そのデータベースの分片化されたコレクション。各データベースにはそれぞれのプライマリーシャードがあります。

例えば、分片クラスタには3つの分片があります:shard1、shard2、shard3、分片化されたshard1データベースblogを作成します。データベースbolgを分片化する場合、MongoDBは自動的にshard2、shard3上に構造が同じデータベースblogを作成し、データベースblogのプライマリーシャードはShard1。

図示、Collection2のプライマリーシャードはShardAです。

movePrimaryコマンドを使用してデータベースのデフォルトのプライマリーシャードを変更し、非分片集合は現在のシャードから新しいプライマリーシャードに移動します。

db.runCommand({ movePrimary : "test", to : "shard000"1})

movePrimaryコマンドを使用してデータベースの主分片を変更した後、config serverの設定情報は最新ですが、mongosのキャッシュの設定情報は古くなります。MongoDBはコマンド「flushRouterConfig」を提供しており、強制的にmongosがconfig serverから最新の設定情報を取得し、mongosのキャッシュをリフレッシュします。

db.adminCommand({"flushRouterConfig":}1})

二、スパーシングのメタデータ

直接configサーバーにアクセスしてスパーシングクラスタのメタデータ情報を確認することは推奨されません。これらのデータは非常に重要で、安全な方法はmongosを通じてconfigデータベースに接続するか、shアシスタント関数を使用することです。

shアシスタント関数を使用して確認します。

sh.status()

configデータベースのセットに接続します。

mongos> use config

1、shards セットはスパーシング情報を保存しています

db.shards.find()

shardのデータはhostで指定されたreplica setやstandalone mongodに保存されています。

{
 "_id" : "shard_name",
 "host" : "replica_set_name"/host:port",
 "tag":[shard_tag1,shard_tag2] 
}

2、databases セットはスパーシングクラスタ内のすべてのデータベースの情報を保存しています。データベースがスパーシングされているかどうかに関係ありません。

db.databases.find()

データベース上でsh.enableSharding(“db_name”)を実行した場合、フィールドpartitionedの値はtrueになります;primary フィールドはデータベースの主スパーシング(プライマリースパーシング)を指定します。

{
 "_id" : "test",
 "primary" : "rs0",
 "partitioned" : true
}

3、collections セットはすべての既にスパーシングされたコレクションの情報を保存していますが、非スパーシングコレクション(un-sharded collections)

keyは:スパーシングのシートキー

db.collections.find()
{
 "_id" : "test.foo",
 "lastmodEpoch" : ObjectId("57dcd4899bd7f7111ec15f16")
 "lastmod" : ISODate("1970-02-19T17:02:47.296Z"),
 "dropped" : false,
 "key" : {
  "_id" : 1
 },
 "unique" : true
}

4、chunks セットはデータブロックの情報を保存しています。

ns:スパーシングのセット、構造は:db_name.collection_name

min と max:シートキーの最小値と最大値

shard:ブロックが属するスパーシング

db.chunks.find()
{
 "_id" : "test.foo"-_id_MinKey",
 "lastmod" : Timestamp(1, 1),
 "lastmodEpoch" : ObjectId("57dcd4899bd7f7111ec15f16")
 "ns" : "test.foo",
 "min" : {
  "_id" : 1
 },
 "max" : {
  "_id" : 3087
 },
 "shard" : "rs0"
}

5changelogコレクションは分片クラスタの操作を記録し、chunkの分割や移行操作、シャードの追加や削除操作を含みます。

whatフィールド:操作の種類を示します。例えば:multi-splitはchunkの分割を表します。

"what" : "addShard",
"what" : "shardCollection.start",
"what" : "shardCollection.end", 
"what" : "multi",-split",

6タグズコレクションはシャードのタグと対応するキー範囲を記録します。

{
 "_id" : { "ns" : "records.users", "min" : { "zipcode" : "10001}
 "ns" : "records.users",
 "min" : { "zipcode" : "10001},
 "max" : { "zipcode" : "10281},
 "tag" : "NYC"
}

7セットティングスコレクションはバランス取る役割の状態とchunkの大きさを記録し、デフォルトのchunk sizeは64MB。

{"_id" : "chunksize", "value" :} 64 }
{"_id" : "balancer", "stopped" : false }

8ロックコレクションは分散ロック(distributed lock)を記録し、1つのmongosインスタンスが分片クラスタで管理タスクを実行できることを保証します。

mongosがバランスを取る役割を果たすときは、config.locksにドキュメントを1つ挿入して分布ロックを取得します。

ロックコレクションは分散ロックを保存しており、これにより、一度にクラスタ上で管理タスクを実行できるのは1つのmongosインスタンスのみになります。バランスを取る役割を果たすmongosは、以下のようなドキュメントをロックコレクションに挿入してロックを取得します。

{
 "_id" : "balancer"}}
 "process" : "example.net:40000:1350402818:16807",
 "state" : 2,
 "ts" : ObjectId("507daeedf40e1879df62e5f3")
 "when" : ISODate("2012-10-16T19:01:01.593Z"),
 "who" : "example.net:40000:1350402818:16807:Balancer:282475249",
 "why" : "doing balance round"
}

3、シードの削除

シードを削除する場合、そのシード上のデータが他のシードに移行されていることを確認する必要があります、シードされたコレクションの場合、バランサーを使用してデータブロックを移行し、シードされていないコレクションの場合、コレクションのメインシードを変更する必要があります。

1、既にシードされたコレクションのデータを削除します

step1、バランサーが有効であることを確保します

sh.setBalancerState(true);

step2、既にシードされたコレクションをすべて他のシードに移行します

use admin
db.adminCommand({"removeShard":"shard_name"})

removeShardコマンドはデータブロックを現在のシードから他のシードに移行します、シード上のデータブロックが多い場合、移行プロセスが長くかかることがあります。

step3、データブロックの移行状態を確認します

use admin
db.runCommand( { removeShard: "shard_name" } )

removeShardコマンドを使用してデータブロックの移行状態を確認できます、remainingフィールドは残りのデータブロックの数を示します

{
  "msg" : "draining ongoing",
 "state" : "ongoing",
 "remaining" : {
  "chunks" : 42,
  "dbs" : 1
 },
 "ok" : 1
}

step4、データブロックの移行が完了しました

use admin
db.runCommand( { removeShard: "shard_name" } )
{
 "msg" : "removeshard completed successfully",
 "state" : "completed",
 "shard" : "shard_name"
 "ok" : 1
}

2、未分片のデータベースを削除

step1、未分片のデータベースを確認

未分片のデータベースは、以下の二つの部分から成り立っています:

     1、データベースが未分片です、このデータはsh.enableSharding(“db_name”)を使用していないため、データベースconfig内でこのデータベースのpartitionedフィールドはfalseです

     2、データベース内に未分片のcollectionが存在します、すなわち現在の分片がこの集合の主分片です

use config
db.databases.find({$or:[{"partitioned":false},{"primary":"shard_name"}]})

partitioned=falseのデータベースの場合、データは現在のshardに全て保存されます;partitioned=true、primary="shard_name"のデータベースの場合、未分片(un-sharded collectionが保存されているデータベース内では、これらの集合の主分片を変更する必要があります。

step2、データベースの主分片を変更

db.runCommand( { movePrimary: "db_name", to: "new_shard" })

四、分片の追加

分片がデータセットの一部を保存しているため、データの高可用性を確保するために、Replica Setを使用することをお勧めします。Replica Setにはメンバーが1人でも構いません。mongosに接続し、sh辅助関数を使用して分片を追加します。

sh.addShard("replica_set_name/host:port)

standalone mongodをshardとして使用することは推奨されません

sh.addShard("host:port")

五、特大ブロック

一部のケースでは、chunkは持続的に増加し、chunk sizeの制限を超えて特大ブロック(jumbo chunk)となります。特大ブロックが発生する原因は、chunk内のすべてのdocが同じ片鍵(shard key)を使用しているため、MongoDBはこのchunkを分割することができません。このchunkが持続的に増加すると、chunkの分布が不均等になり、パフォーマンスのボトルネックとなります。

chunkの移行時には、以下の制限があります:各chunkのサイズは以下を超えてはなりません2.5万件のdoc、または1.3設定値の倍です。chunk sizeのデフォルトの設定値は64MB、制限を超えたchunkはMongoDBによって特大ブロック(jumbo chunk)とマークされ、MongoDBは特大ブロックを他のshardに移行することができません。

MongoDBは、ブロック内のドキュメント数がどちらか一方を超える場合でも、ブロックを移動することができません 250000 documentsまたは 1.3 、設定されたchunk sizeを平均ドキュメントサイズで割った結果の倍数

1、特大ブロックを確認

sh.status()を使用して、特大ブロックを発見し、特大ブロックの後ろにjumboマークがあります

 { "x" : 2 } -->> { "x" : 3 ) on : shard-Timestamp(2, 2) jumbo

2、特大ブロックを配布

特大ブロックは分割できず、バランサーを自動的に配布することもできません。手動で配布する必要があります。

step1、バランサーを停止

sh.setBalancerState(false)}

step2、Chunk Sizeの設定値を増やします

MongoDBはサイズが制限を超える特大ブロックを移動することを許可しないため、一時的にchunk sizeの設定値を増やし、特大ブロックをバランスよくシャードクラスタに分散する必要があります。

use config
db.settings.save({"_id":"chunksize","value":"1024"})

step3、特大ブロックを移動

sh.moveChunk("db_name.collection_name",{sharded_filed:"value_in_chunk"},"new_shard_name")

step4、バランサーを有効にします

sh.setBalancerState(true)

step5、mongosの設定キャッシュをリフレッシュ

強制的にmongosがconfig serverから設定情報を同期し、キャッシュをリフレッシュします。

use admin
db.adminCommand({ flushRouterConfig: 1 })

6. バランサー

バランサーはmongosから変換されます、つまり、mongosはクエリを適切なシャードにルーティングするだけでなく、データブロックのバランスを管理する責任もあります。通常、MongoDBはデータのバランスを自動的に処理し、config.settingsを使用してバランサーの状態を確認したり、sh補助関数を使用して確認できます

sh.getBalancerState()

trueを返すことで、バランサーが正しく動作していることを示し、システムがデータのバランスを自動的に処理し、sh補助関数を使用してバランサーを閉じることができます

sh.setBalancerState(false)}

balancerは、現在のblock迁移操作をすぐに停止することができません。mongosがbalancerに変換されたとき、balancerロックを要求し、config.locksコレクションを確認します。

use config
db.locks.find({"_id":"balancer"})
--または 
sh.isBalancerRunning()

state=2、これはbalancerがアクティブ状態にあることを示します。state=0の場合、balancerは閉じられていることを示します。

バランス調整プロセスは、データブロックを1つのshardから他のshardに移行することや、大きなchunkを小さなchunkに分割し、小さなchunkを他のshardに移行することです。ブロックの移行と分割は、システムのIO負荷を増加させます。したがって、バランス調整のアクティブ時間はシステムの空き時間に限定し、balancerのアクティブ時間ウィンドウを設定し、balancerが指定された時間範囲内でデータブロックの分割と移行操作を行うように制限します。

use config
db.settings.update(
  {"_id":"balancer"},
  "$set":{"activeWindow":{"start":"23:00","stop":"04:00"}}),
  true
)

バランス調整を分割し、移動するオブジェクトはchunkです。バランス調整は各shard上のchunkの数が均衡であることを保証しますが、各chunkに含まれるdocの数は必ずしも均衡ではありません。一部のchunkには多くのdocが含まれる場合があり、他のchunkにはほとんどdocが含まれない場合や、docが含まれない場合もあります。したがって、慎重に分片のインデックスキー、すなわち片キーを選択する必要があります。どのフィールドもほとんどのクエリの要件を満たし、docの数が均等に分布する場合、そのフィールドは片キーの最適な選択です。

まとめ

これがこの記事の全ての内容です。皆さんの学習や仕事に少しでも役立つことを願っています。何か疑問があれば、コメントを残してください。

おすすめ