💬

ひとりMongoDB University (Shardingの章課題)

2021/02/25に公開

この記録は、アドベントカレンダー形式ではじめた、MongoDB Universityの学習コースの記録の続きになります!毎日ではありませんが、ある程度まとまったら上げています。

ただいまのコース

2020年12月中旬から、休み休みですが取り組んでいます。
最後のShardingの章の演習課題の回答をアップします。(2/3に完了しています!)

Chapter 3: Lab: Shard a Collection (演習問題)

やることを眺める

こんなことが書いてあります。

Import and shard a collection of data on your cluster:
クラスタにデータを投入し、シャーディングをさせましょう。

1.Use mongoimport to import the data in /dataset/products.json:

  • import this dataset onto m103.products
  • use mongos as the target for mongoimport - you can find the configuration - details for this process in mongos.conf
  • authenticate to mongos as m103-admin (with password m103-pass)
データの投入にはmongoimportを使いましょう。
データベース名はm103.productsを指定。
データベースへの接続先は、mongosを利用します。ホスト、ポートはmongos.confを参考に。
認証情報は、ユーザ名:m103-admin / パスワード:m103-pass を使いましょう。

2.Enable sharding on the m103 database.

シャーディングを有効化しましょう。

Two shards have already been added to your cluster, shard1 and shard2. For more information, run sh.status() on mongos.

2つのシャード用のノードがクラスタに追加済みです。(shard1, shard2)
sh.status() で確認しましょう。

3.Choose a shard key for the m103.products collection.

シャーディングを有効化の際、キーはm103.productsのフィールドを指定してください。

Review the qualities of a good shard key in the docs and the following information about the products collection:

シャードキーの指定にあたっては、ドキュメントのガイドにしたがって適切なものを使ってみましょう。
また、コレクションの各フィールドは以下の特性があるので、そこも踏まえて選択しましょう。

  • _id is a serial number for each product in this collection, rarely used in queries but important for internal MongoDB usage
  • sku (Stock Keeping Unit) is a randomly generated integer unique to each product - this is commonly used to refer to specific products when updating stock quantities
  • name is the name of the product as it appears in the store and on the website
  • type is the type of product, with the possible values "Bundle", "Movie", "Music" and "Software"
  • regularPrice is the regular price of the product, when there is no sale this price changes every season
  • salePrice is the price of a product during a sale - this price changes arbitrarily based on when sales occur
  • shippingWeight is the weight of the product in kilograms, ranging between 0.01 and 1.00 - this value is not known for every product in the collection
_id は各プロダクトのシリアル番号になります。滅多にクエリの検索条件として直接利用されることはありませんが、MongoDB上でデータを一意に特定するために重要なキーになります。

- sku (Stock Keeping Unit) is a randomly generated integer unique to each product - this is commonly used to refer to specific products when updating stock quantities

skuはランダムに生成されます。また、各プロダクトで一意の値が割り当てられます。
この値は、在庫数を更新する際にプロダクトを特定するために使います。
(在庫数は別のフィールドで更新。検索のためのもの)

- name is the name of the product as it appears in the store and on the website
nameはプロダクト名です。Webサイト上で表示します。

- type is the type of product, with the possible values "Bundle", "Movie", "Music" and "Software"
typeはプロダクトの種類になります。("Bundle", "Movie", "Music" and "Software"のいずれか)

- regularPrice is the regular price of the product, when there is no sale this price changes every season
regularPriceはプロダクトの定価情報です。(セール期間中には別のフィールドで価格を設定します)

- salePrice is the price of a product during a sale - this price changes arbitrarily based on when sales occur
salesPriceはセール期間中の価格になります。この価格は期間中に任意で変更されます。

- shippingWeight is the weight of the product in kilograms, ranging between 0.01 and 1.00 - this value is not known for every product in the collection
shippingWeightはプロダクトの重量(kg)です。0.01から1.00までのレンジがあります。
この値が無いものもあります。

-> カーディナリティの高いもの、読み書きがうまく分散されるもの、単純な値の増加で偏りが発生してしまうものは避ける、というのがお約束。
skuが良さそう?
データを投入してから確認します。

4.Create an index on your shard key and shard the collection.

シャードキーにはインデックスが必要なので、ターゲットを選んだらまずはインデックスを作成します。

5.Once you've run the proper commands, click "Run Tests" to run a suite of tests that will check the configuration of your sharded cluster. The results of these tests will let you know which steps you've yet to complete.

適切なコマンドを実行したら、"Run Tests" で検証してみましょう。

If you chose the wrong shard key, clicking "Run Tests" will give you an error.

もし適切でないシャードキーを選んでしまっていたら、エラーになります。
その場合は、まずコレクションを削除して、再度データをインポートし、シャードキーを登録し直してみましょう。

However, if you already imported the dataset, you must drop the collection:

use m103
db.products.dropCollection()

Then reimport the dataset, and shard it using a different key.

実際にやってみる

クラウド上の実行環境を使います。
ある程度の設定が済んでいる状態なのですが、プロセスを確認してみます。

mongos.confとプロセスの確認

mongos (シャーディングの構成の場合のサービス)の設定は、以下の通り。

  • configDBはport: 27004で起動しているものを指している
    • こちらにシャーディングの設定、インデックスなどが格納されている
    • シャーディングの場合は必要
    • とても重要なので、configDB自体がレプリケーションで冗長化させるのが基本
  • mongosのportは26000
  • processManagement = fork: trueなのでバックグラウンドで起動
sharding:
  configDB: csrs/localhost:27004
security:
  keyFile: /var/mongodb/pki/m103-keyfile
net:
  bindIp: localhost
  port: 26000
systemLog:
  destination: file
  path: /var/mongodb/logs/mongos.log
  logAppend: true
processManagement:
  fork: true

この状態でプロセスをチェックします。

  • 26000: mongos
  • 27004: configDBに該当 (--configsvr オプションあり) / 3台でのレプリケーション (--replSet csrオプションあり)
  • 27005: configDBに該当 (--configsvr オプションあり) / 3台でのレプリケーション (--replSet csrオプションあり)
  • 27006: configDBに該当 (--configsvr オプションあり) / 3台でのレプリケーション (--replSet csrオプションあり)
  • 27001: シャーディングShard1のDB (--replSet shard1オプションあり)
  • 27002: シャーディングShard1のDB (--replSet shard1オプションあり)
  • 27003: シャーディングShard1のDB (--replSet shard1オプションあり)
  • 27007: シャーディングShard2のDB (--replSet shard2オプションあり)
  • 27008: シャーディングShard2のDB (--replSet shard2オプションあり)
  • 27009: シャーディングShard2のDB (--replSet shard2オプションあり)
bash-4.4# ps -elf | grep [m]ongo
  236 root      0:01 mongod --port 27007 --dbpath /var/mongodb/db/7 --auth --logpath /var/mongodb/logs/mongod7.log --logappend --fork --replSet shard2 --keyFile /var/mongodb/pki/m103-keyfile --shardsvr
  237 root      0:01 mongod --port 27004 --dbpath /var/mongodb/db/4 --auth --logpath /var/mongodb/logs/mongod4.log --logappend --fork --replSet csrs --keyFile /var/mongodb/pki/m103-keyfile --configsvr
  238 root      0:01 mongod --port 27001 --dbpath /var/mongodb/db/1 --auth --logpath /var/mongodb/logs/mongod1.log --logappend --fork --replSet shard1 --keyFile /var/mongodb/pki/m103-keyfile --shardsvr
  438 root      0:01 mongod --port 27002 --dbpath /var/mongodb/db/2 --auth --logpath /var/mongodb/logs/mongod2.log --logappend --fork --replSet shard1 --keyFile /var/mongodb/pki/m103-keyfile --shardsvr
  439 root      0:01 mongod --port 27003 --dbpath /var/mongodb/db/3 --auth --logpath /var/mongodb/logs/mongod3.log --logappend --fork --replSet shard1 --keyFile /var/mongodb/pki/m103-keyfile --shardsvr
  444 root      0:01 mongod --port 27008 --dbpath /var/mongodb/db/8 --auth --logpath /var/mongodb/logs/mongod8.log --logappend --fork --replSet shard2 --keyFile /var/mongodb/pki/m103-keyfile --shardsvr
  445 root      0:01 mongod --port 27005 --dbpath /var/mongodb/db/5 --auth --logpath /var/mongodb/logs/mongod5.log --logappend --fork --replSet csrs --keyFile /var/mongodb/pki/m103-keyfile --configsvr
  446 root      0:01 mongod --port 27009 --dbpath /var/mongodb/db/9 --auth --logpath /var/mongodb/logs/mongod9.log --logappend --fork --replSet shard2 --keyFile /var/mongodb/pki/m103-keyfile --shardsvr
  447 root      0:01 mongod --port 27006 --dbpath /var/mongodb/db/6 --auth --logpath /var/mongodb/logs/mongod6.log --logappend --fork --replSet csrs --keyFile /var/mongodb/pki/m103-keyfile --configsvr
  974 root      0:00 mongos --port 26000 --configdb csrs/localhost:27004 --logpath /var/mongodb/logs/mongos.log --logappend --fork --keyFile /var/mongodb/pki/m103-keyfile

シャーディングの手順の確認

上記のプロセス確認ができた状態ですが、ここまでの手順はこちら。

Config Server用のMongodの設定の一部(抜粋)

sharding:
  # configsvrというロールを割り当てます
  clusterRole: configsvr
replication:
  # Config Server自体もレプリケーションするので、識別用のレプリカセット名を設定します
  replSetName: m103-csrs
security:
  keyFile: /var/mongodb/pki/m103-keyfile
net:
  bindIp: localhost,192.168.103.100
  # ポートは最低3つ分になります
  port: 26001
systemLog:
  destination: file
  path: /var/mongodb/db/csrs1.log
  logAppend: true
processManagement:
  fork: true
storage:
  dbPath: /var/mongodb/db/csrs1

Config Serverの起動(3つ分)

mongod -f csrs_1.conf
mongod -f csrs_2.conf
mongod -f csrs_3.conf

# 起動したらどれか1つにmongo shellでアクセス
mongo --port 26001

# mongo shellの中で「レプリカセット」の初期化
rs.initiate()

# mongo shellの中でアカウントとパスワード設定
use admin
db.createUser({
  user: "m103-admin",
  pwd: "m103-pass",
  roles: [
    {role: "root", db: "admin"}
  ]
})

# アカウントの確認
db.auth("m103-admin", "m103-pass")

# Config Server用のレプリカセットにのこり2つのサーバを追加
rs.add("192.168.103.100:26002")
rs.add("192.168.103.100:26003")

ここまでがConfogDB用のレプリカセット一式の起動。
つぎにsharding対応をさせるため、mongosを起動。

# mongos用の設定
# configDBはレプリカセットで作成した分(3つ)を指定
sharding:
  configDB: m103-csrs/192.168.103.100:26001,192.168.103.100:26002,192.168.103.100:26003
security:
  keyFile: /var/mongodb/pki/m103-keyfile
net:
  # 自身のサービスは26000で受け付け
  bindIp: localhost,192.168.103.100
  port: 26000
systemLog:
  destination: file
  path: /var/mongodb/db/mongos.log
  logAppend: true
processManagement:
  fork: true

起動からShardingの設定まで。

mongos -f mongos.conf

# mongo shellでのmongosへの接続
$ mongo --port 26000 --username m103-admin --password m103-pass --authenticationDatabase admin

# 中に入ったら"sh" prefixで確認(シャーディング用)
MongoDB Enterprise mongos> sh.status()

いったんここまで


改めてシャーディングの処理

https://university.mongodb.com/mercury/M103/2020_December_1/chapter/Chapter_3_Sharding/lesson/5e55718c014e1efdcc5390ac/problem

上記の設定をみつつ、mongosにアクセス。
csrs/localhost:27004に指定してあるので、すでにConfig serverは稼働。
bindIp: localhost:26000 での起動なので、その情報で接続します。


# mongosに接続
$ mongo --port 26000 --username m103-admin --password m103-pass --authenticationDatabase admin


# シャーディングの設定確認
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "minCompatibleVersion" : 5,
        "currentVersion" : 6,
        "clusterId" : ObjectId("6027645030ef0e73617d6673")
  }
  shards:
        {  "_id" : "shard1",  "host" : "shard1/localhost:27001,localhost:27002,localhost:27003",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/localhost:27007,localhost:27008,localhost:27009",  "state" : 1 }
  active mongoses:
        "4.0.5" : 1
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  0
        Migration Results for the last 24 hours:
                No recent migrations
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }

実際の操作(データ投入から)

Use mongoimport to import the data in /dataset/products.json:

シャーディングの構成になっているので、データを投入するところから。
宛先はmongos.confの設定で確認する。
対象はm103 database (存在しない....)

mongos> show dbs
admin   0.000GB
config  0.001GB

MongoDBは、データベースが存在しない場合、use DATABASE_NAME と宣言すればデータベースが作られます。
厳密には、そのネームスペースに切り替える形。
コレクションをその下に追加していく形になります。

MongoDB use DATABASE_NAME is used to create database.

データをインポートする場合の手順はこちら。

  • https://docs.mongodb.com/guides/server/import/
  • By default, mongoimport will import data into an instance of MongoDB on localhost, port 27017.
  • To import data into a MongoDB instance running on a different host or port, specify the hostname or port by including the --host and --port options.
    • mongoimportはデフォルトではlocalhostで起動しているport: 27017 のMongoDBインスタンスに対しデータをインポートします
    • 別なホストやポートで稼働しているMongoDBインスタンスに対しては、--host and --port を利用します

bash-4.4# ls /dataset/products.json
/dataset/products.json

# 宛先はmongosを指定
mongoimport --db m103 --collection products \
          --authenticationDatabase admin --username m103-admin --password m103-pass \
          --host localhost --port 26000 --drop --file /dataset/products.json

2021-02-13T15:59:22.560+0000    connected to: mongodb://localhost:26000/
2021-02-13T15:59:22.560+0000    dropping: m103.products
2021-02-13T15:59:24.699+0000    9966 document(s) imported successfully. 0 document(s) failed to import.

# データ確認
mongo --port 26000 --username m103-admin --password m103-pass --authenticationDatabase admin

# mongo shellに入る
mongos> show dbs
admin   0.000GB
config  0.001GB
m103    0.001GB  # m103 ができている

mongos> use m103
switched to db m103
mongos> show collections
products
mongos> db.products.find().count()
9966

Enable sharding on the m103 database

m103データベースのシャーディングを有効化します。
sh.status() で確認。

{ "_id" : "m103", "primary" : "shard2", "partitioned" : false, "version" : { "uuid" : UUID("bfe99b99-13cf-43c0-b1cb-f64d8a19849f"), "lastMod" : 1 } }

このエントリが追加されているので、m103も対象になっています。

mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "minCompatibleVersion" : 5,
        "currentVersion" : 6,
        "clusterId" : ObjectId("6027f55922ff619929c7df0b")
  }
  shards:
        {  "_id" : "shard1",  "host" : "shard1/localhost:27001,localhost:27002,localhost:27003",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/localhost:27007,localhost:27008,localhost:27009",  "state" : 1 }
  active mongoses:
        "4.0.5" : 1
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  0
        Migration Results for the last 24 hours:
                No recent migrations
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                shard1  1
                        { "_id" : { "$minKey" : 1 } } -->> { "_id" : { "$maxKey" : 1 } } on : shard1 Timestamp(1, 0)
        {  "_id" : "m103",  "primary" : "shard2",  "partitioned" : false,  "version" : {  "uuid" : UUID("bfe99b99-13cf-43c0-b1cb-f64d8a19849f"),  "lastMod" : 1 } }

The Databases section lists information on the database(s). For each database, the section displays the name, whether the database has sharding enabled, and the primary shard for the database.

シャーディングの前に、データベースのシャーディングを有効にします。


# 有効化
mongos> sh.enableSharding('m103')
{
        "ok" : 1,
        "operationTime" : Timestamp(1613234356, 3),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1613234356, 3),
                "signature" : {
                        "hash" : BinData(0,"3iwhqy1UlzIsniT+hFZNE+lMR6o="),
                        "keyId" : NumberLong("6928776314333691933")
                }
        }
}

# sh.status() で確認すると、あらためてdatabase: のところでm103が含まれているのを確認

correctionのシャーディングの有効化には、まずインデックスを作成し、それからシャードキーを指定して設定します。
今回のケースは sku (Stock Keeping Unit) が良さそう。

sku (Stock Keeping Unit) is a randomly generated integer unique to each product - this is commonly used to refer to specific products when updating stock quantities
skuは各プロダクトに対しランダムな整数で採番されるもの。


# sparse: true でインデックス作成の際に対象のキーだけのインデックスを作成(データ量を減らせる)
# デフォルトはfalse
db.products.createIndex(
  {
      "sku": 1
  }
)

# 上記で使ったインデックスでシャーディングを設定する
sh.shardCollection("m103.products", { sku : 1 } )

# 結果
{
        "raw" : {
                "shard1/localhost:27001,localhost:27002,localhost:27003" : {
                        "createdCollectionAutomatically" : false,
                        "numIndexesBefore" : 1,
                        "numIndexesAfter" : 2,
                        "ok" : 1
                }
        },
        "ok" : 1,
        "operationTime" : Timestamp(1614239249, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1614239249, 1),
                "signature" : {
                        "hash" : BinData(0,"vLDOlvM/Kmn6EV6uWgh/8OjUA6U="),
                        "keyId" : NumberLong("6933103433754869789")
                }
        }
}
mongos> db.adminCommand({ "shardCollection": "m103.products", "key": { "sku": 1 } })
{
        "collectionsharded" : "m103.products",
        "collectionUUID" : UUID("a1907cd5-7b85-42f5-ad90-9cfbde1578f7"),
        "ok" : 1,
        "operationTime" : Timestamp(1614239255, 10),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1614239255, 10),
                "signature" : {
                        "hash" : BinData(0,"/A8EB4/iyUrsi3jHxwXtj5zYcVM="),
                        "keyId" : NumberLong("6933103433754869789")
                }
        }
}

# 検索してみる
mongos> db.products.findOne()
{
        "_id" : ObjectId("573f7197f29313caab89b21b"),
        "sku" : 20000008,
        "name" : "Come Into The World - CD",
        "type" : "Music",
        "regularPrice" : 14.99,
        "salePrice" : 14.99,
        "shippingWeight" : "0.25"
}
mongos> db.products.find({"sku" : 20000008})
{ "_id" : ObjectId("573f7197f29313caab89b21b"), "sku" : 20000008, "name" : "Come Into The World - CD", "type" : "Music", "regularPrice" : 14.99, "salePrice" : 14.99, "shippingWeight" : "0.25" }

テストが通りました!

ということで、この演習は完了!
このコースのまとめの課題も、別途追記しようと思います!

Discussion