Sharding-ul MongoDB folosind strategia Range

  • 6 minute de citit
  • 16 exemple de cod
  • 2 imagini

Disponibil și în English

Pe această pagină

Salut 👋👋

În acest articol voi explora subiectul sharding-ului unei baze de date Mongo care rulează pe Kubernetes. Înainte să începem, dacă vrei să urmărești pas cu pas, te rog instalează uneltele enumerate în secțiunea cu cerințe, iar dacă vrei să afli mai multe despre sharding, aruncă o privire la acest articol excelent Sharding Pattern.

Cerințe

Introducere

Să instalăm o instanță MongoDB pe clusterul Kubernetes folosind helm.

helm repo add bitnami https://charts.bitnami.com/bitnamihelm install my-mongo bitnami/mongodb-sharded

După ce instalarea se termină, salvează parola de root a bazei de date și cheia replica set-ului. Când am făcut asta prima dată, am greșit și nu le-am salvat corect.

Rulează următoarele comenzi pentru a afișa parola și cheia replica set-ului în linia de comandă. Dacă ești pe Windows, am pregătit o funcție Powershell pentru base64, iar dacă ești pe Unix, nu uita să adaugi –decode la base64.

kubectl get secret --namespace default my-release-mongodb-sharded -o jsonpath="{.data.mongodb-root-password}" | base64kubectl get secret --namespace default my-release-mongodb-sharded -o jsonpath="{.data.mongodb-replica-set-key}" | base64

Sharding-ul bazei de date

Verifică dacă toate pod-urile rulează și pornește o conexiune de shell către serverul mongos.

	@denis ➜ ~ kubectl get pods	NAME                                              READY   STATUS    RESTARTS   AGE	my-mongo-mongodb-sharded-configsvr-0              1/1     Running   0          3m8s	my-mongo-mongodb-sharded-configsvr-1              1/1     Running   0          116s	my-mongo-mongodb-sharded-mongos-c4dd66768-dqlbv   1/1     Running   0          3m8s	my-mongo-mongodb-sharded-shard0-data-0            1/1     Running   0          3m8s	my-mongo-mongodb-sharded-shard0-data-1            1/1     Running   0          103s	my-mongo-mongodb-sharded-shard1-data-0            1/1     Running   0          3m8smy-mongo-mongodb-sharded-shard1-data-1            1/1     Running   0          93s
kubectl port-forward --namespace default svc/my-mongo-mongodb-sharded 27017:27017# și într-un alt terminal:mongosh --host 127.0.0.1 --authenticationDatabase admin -u root -p $MONGODB_ROOT_PASSWORD

Rulând sh.status() ar trebui să primești un rezultat care conține două shard-uri Mongo:

shards[  {    _id: 'my-mongo-mongodb-sharded-shard-0',    host: 'my-mongo-mongodb-sharded-shard-0/my-mongo-mongodb-sharded-shard0-data-0.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017,my-mongo-mongodb-sharded-shard0-data-1.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017',    state: 1  },  {    _id: 'my-mongo-mongodb-sharded-shard-1',    host: 'my-mongo-mongodb-sharded-shard-1/my-mongo-mongodb-sharded-shard1-data-0.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017,my-mongo-mongodb-sharded-shard1-data-1.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017',    state: 1  }]

Pentru a activa sharding-ul pe baza de date și pe colecție, voi insera niște date de test în baza de date my_data și în colecția my_users. Scriptul folosit pentru inserarea datelor este atașat la finalul acestui articol.

[direct: mongos]> sh.enableSharding("my_data"){  ok: 1,  operationTime: Timestamp(3, 1628345449),  '$clusterTime': {    clusterTime: Timestamp(3, 1628345449),    signature: {      hash: Binary(Buffer.from("e57c8c37047f7aa170fb59f6b11e22aa65159a30", "hex"), 0),      keyId: Long("6993682727694237708")    }  }}[direct: mongos]> db.my_users.createIndex({"t": 1})[direct: mongos]> sh.shardCollection("my_data.my_users", { "t": 1 })sh.addShardToZone("my-mongo-mongodb-sharded-shard-1", "TSR1")sh.addShardToZone("my-mongo-mongodb-sharded-shard-0", "TSR2")

Dacă ai ajuns până aici, felicitări, ai activat sharding-ul, acum să definim câteva reguli.

Deoarece vom folosi o strategie de sharding pe interval (range) bazată pe cheia t, iar am două shard-uri disponibile, aș vrea ca datele mele să fie distribuite în felul următor:

 sh.updateZoneKeyRange("my_data.my_users", {t: 46}, {t: MaxKey()}, "TSR2") sh.updateZoneKeyRange("my_data.my_users", {t: MinKey()}, {t: 46}, "TSR1")

Notă: Eticheta de pe zona TSR2 este greșită, valoarea corectă este: 46 ≤ t < 1000

Rularea sh.status() ar trebui acum să producă următorul rezultat.

    collections: {      'my_data.my_users': {        shardKey: { t: 1 },        unique: false,        balancing: true,        chunkMetadata: { shard: 'my-mongo-mongodb-sharded-shard-1', nChunks: 3 },        chunks: [          {            min: { t: MinKey() },            max: { t: 45 },            'on shard': 'my-mongo-mongodb-sharded-shard-1',            'last modified': Timestamp(2, 1)          },          {            min: { t: 46 },            max: { t: MaxKey() },            'on shard': 'my-mongo-mongodb-sharded-shard-0',            'last modified': Timestamp(0, 2)          }        ],        tags: [          { tag: 'TSR1', min: { t: MinKey() }, max: { t: 46} },          { tag: 'TSR2', min: { t: 46 }, max: { t: MaxKey() } }        ]      }

Pentru a testa regulile, folosește scriptul Python atașat, modifică variabila times și rulează-l cu diverse valori.

Poți rula db.my_users.getShardDistribution() pentru a vedea distribuția datelor pe shard-uri.

[direct: mongos]> db.my_users.getShardDistribution()Shard my-mongo-mongodb-sharded-shard-0 at my-mongo-mongodb-sharded-shard-0/my-mongo-mongodb-sharded-shard0-data-0.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017,my-mongo-mongodb-sharded-shard0-data-1.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017{  data: '144KiB',  docs: 1667,  chunks: 1,  'estimated data per chunk': '144KiB',  'estimated docs per chunk': 1667}Shard my-mongo-mongodb-sharded-shard-1 at my-mongo-mongodb-sharded-shard-1/my-mongo-mongodb-sharded-shard1-data-0.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017,my-mongo-mongodb-sharded-shard1-data-1.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017{  data: '195KiB',  docs: 2336,  chunks: 3,  'estimated data per chunk': '65KiB',  'estimated docs per chunk': 778}

Adăugarea de shard-uri noi

Pentru a adăuga mai multe shard-uri în cluster, tot ce trebuie să facem este să rulăm helm upgrade. Dacă nu greșești cheia replica set-ului așa cum am greșit eu, ar trebui să funcționeze din prima.

helm upgrade my-mongo bitnami/mongodb-sharded --set shards=3,configsvr.replicas=2,shardsvr.dataNode.replicas=2,mongodbRootPassword=tcDMM5sqNC,replicaSetKey=D6BGM2ixd3

Dacă greșești cheia 😅, atunci, pentru a rezolva problema și a readuce clusterul online, urmează acești pași.

  1. revino la cluster cu 2 shard-uri
  2. Conectează-te prin SSH la un shard vechi care funcționează, shard1 sau shard0, și ia credențialele din variabilele de mediu.

Secretul Kubernetes și credențialele pod-ului mongos au fost suprascrise de upgrade și sunt greșite!

MONGODB_ROOT_PASSWORD=tcDMM5sqNC
MONGODB_ENABLE_DIRECTORY_PER_DB=no
MONGODB_SYSTEM_LOG_VERBOSITY=0
MY_MONGO_MONGODB_SHARDED_SERVICE_PORT=27017
KUBERNETES_SERVICE_HOST=10.245.0.1
MONGODB_REPLICA_SET_KEY=D6BGM2ixd3

După ce salvezi parola și cheia replica set-ului corecte, caută volumele care aparțin shard-urilor care au cheia replica set-ului greșită și șterge-le. În cazul meu am șters doar volumele care aparțin celui de-al 3-lea shard pe care l-am adăugat; deoarece numărătoarea începe de la 0, caut shard2 în nume.

@denis ➜ Downloads kubectl get persistentvolumeclaimsNAME                                               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGEdatadir-my-mongo-mongodb-sharded-configsvr-0       Bound    pvc-8e7fa303-9198-419e-a6c1-8de3e6d89962   8Gi        RWO            do-block-storage   132mdatadir-my-mongo-mongodb-sharded-configsvr-1       Bound    pvc-6e3bc70f-83a8-4e80-b856-c44a4295be35   8Gi        RWO            do-block-storage   131mdatadir-my-mongo-mongodb-sharded-shard0-data-0     Bound    pvc-f66647bc-ee3b-4820-b466-a11b197fde74   8Gi        RWO            do-block-storage   132mdatadir-my-mongo-mongodb-sharded-shard0-data-1     Bound    pvc-62257e91-d461-4ddb-af37-4876d2431703   8Gi        RWO            do-block-storage   131mdatadir-my-mongo-mongodb-sharded-shard1-data-0     Bound    pvc-9a062ba5-f320-49c9-ae15-d75e8e5f2cf8   8Gi        RWO            do-block-storage   132mdatadir-my-mongo-mongodb-sharded-shard1-data-1     Bound    pvc-068b04bd-8875-40d7-b47c-40092ceb7973   8Gi        RWO            do-block-storage   130mdatadir-my-mongo-mongodb-sharded-shard2-data-0     Bound    pvc-93d9a238-ae36-49e1-b0b6-f320baf89373   8Gi        RWO            do-block-storage   73mdatadir-my-mongo-mongodb-sharded-shard2-data-1     Bound    pvc-b09a8d0d-5012-4f23-8096-a713f3025521   8Gi        RWO            do-block-storage   50m@denis ➜ Downloads kubectl get persistentvolumesNAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                                      STORAGECLASS       REASON   AGEpvc-068b04bd-8875-40d7-b47c-40092ceb7973   8Gi        RWO            Delete           Bound    default/datadir-my-mongo-mongodb-sharded-shard1-data-1     do-block-storage            131mpvc-321136d8-8a27-45cb-8ed1-8d636c530859   8Gi        RWO            Delete           Bound    default/datadir-my-release-mongodb-sharded-shard2-data-1   do-block-storage            143mpvc-42dd7167-5836-4e94-bf42-473c6cea49a4   8Gi        RWO            Delete           Bound    default/datadir-my-release-mongodb-sharded-shard2-data-0   do-block-storage            145mpvc-48714777-97b3-4acc-8562-7b69a8e3b488   8Gi        RWO            Delete           Bound    default/datadir-my-release-mongodb-sharded-shard1-data-1   do-block-storage            143mpvc-499797e9-a5df-4c7b-a1fb-482c3dca36a6   8Gi        RWO            Delete           Bound    default/datadir-my-release-mongodb-sharded-shard3-data-1   do-block-storage            143mpvc-61ec9e04-1bad-4312-ba16-fb24c12efb4b   8Gi        RWO            Delete           Bound    default/datadir-my-release-...

După ce ai terminat, rulează din nou comanda helm upgrade și, dacă totul funcționează, obține o conexiune mongosh 😀.

Rulând sh.status() se va vedea acum al 3-lea shard.

[  {    _id: 'my-mongo-mongodb-sharded-shard-0',    host: 'my-mongo-mongodb-sharded-shard-0/my-mongo-mongodb-sharded-shard0-data-0.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017,my-mongo-mongodb-sharded-shard0-data-1.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017',    state: 1,    tags: [ 'TSR2' ]  },  {    _id: 'my-mongo-mongodb-sharded-shard-1',    host: 'my-mongo-mongodb-sharded-shard-1/my-mongo-mongodb-sharded-shard1-data-0.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017,my-mongo-mongodb-sharded-shard1-data-1.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017',    state: 1,    tags: [ 'TSR1' ]  },  {    _id: 'my-mongo-mongodb-sharded-shard-2',    host: 'my-mongo-mongodb-sharded-shard-2/my-mongo-mongodb-sharded-shard2-data-0.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017,my-mongo-mongodb-sharded-shard2-data-1.my-mongo-mongodb-sharded-headless.default.svc.cluster.local:27017',    state: 1  }]

Apoi, actualizează regulile de sharding și totul va funcționa ca în diagramă.

sh.addShardToZone("my-mongo-mongodb-sharded-shard-2", "TSR3")sh.removeRangeFromZone("my_data.my_users", {t: 46}, {t: MaxKey()}, "TSR2")sh.updateZoneKeyRange("my_data.my_users", {t: 46}, {t 1000}, "TSR2")sh.updateZoneKeyRange("my_data.my_users", {t: 1000}, {t: MaxKey()}, "TSR3")

sh.status() ar trebui să arate ceva de genul

        chunks: [          {            min: { t: MinKey() },            max: { t: 46 },            'on shard': 'my-mongo-mongodb-sharded-shard-1',            'last modified': Timestamp(0, 5)          },          {            min: { t: 46 },            max: { t: 1000 },            'on shard': 'my-mongo-mongodb-sharded-shard-0',            'last modified': Timestamp(3, 4)          },          {            min: { t: 1000 },            max: { t: MaxKey() },            'on shard': 'my-mongo-mongodb-sharded-shard-2',            'last modified': Timestamp(1, 5)          }        ],        tags: [          { tag: 'TSR1', min: { t: MinKey() }, max: { t: 46 } },          { tag: 'TSR2', min: { t: 46 }, max: { t: 1000 } },          { tag: 'TSR3', min: { t: 1000 }, max: { t: MaxKey() } }        ]      }

Concluzii

Sharding-ul unei baze MongoDB poate părea intimidant la început, dar cu puțină practică în avans poți să o faci! Dacă sharding-ul nu îți merge, poți Convert Sharded Cluster to Replica Set, dar fii pregătit cu niște backup-uri.

Mulțumesc pentru lectură 📚 și succes la programat! 🔩🔨

Funcția Powershell pentru Base64
function global:Convert-From-Base64 {  [CmdletBinding()]  [Alias('base64')]  param (    [parameter(ValueFromPipeline,Mandatory=$True,Position=0)]    [string] $EncodedText  )  process {    [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($EncodedText))  }}

Script Python

import randomimport pymongodef do_stuff():    client = pymongo.MongoClient("mongodb://root:[email protected]:27017/?directConnection=true&serverSelectionTimeoutMS=2000")    col = client.my_data.my_users    usernames = ["dovahkiin", "rey", "dey", "see", "mee", "rollin", "they", "hating"]    hobbies = ["coding", "recording", "streaming", "batman", "footbal", "sports", "mathematics"]    ages = [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]    # times = [12, 14, 15, 23, 45, 32, 20]    times = [47, 80, 93, 49, 96, 43]    buffer = []    for _ in range(1_000):        first = random.choice(usernames).capitalize()        mid = random.choice(usernames).capitalize()        last = random.choice(usernames).capitalize()        buffer.append(pymongo.InsertOne({            "name": f"{first} '{mid}' {last}",            "age": random.choice(ages),            "hobbies": random.choice(hobbies),            "t": random.choice(times)        }))    col.bulk_write(buffer)if __name__ == '__main__':    do_stuff()

Referințe

Blue vector created by starline – www.freepik.com