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-shardedDupă 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}" | base64Sharding-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 93skubectl 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_PASSWORDRulâ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=D6BGM2ixd3Dacă greșești cheia 😅, atunci, pentru a rezolva problema și a readuce clusterul online, urmează acești pași.
- revino la cluster cu 2 shard-uri
- 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()