Nous avons passé 8 ans à rendre les bases de données vectorielles plus rapides. Puis nous avons arrêté.
Le coût compte. Il a toujours compté. Mais il y a un ordre : on ne peut réduire les coûts qu’après avoir atteint le niveau de performance requis. Un système bon marché mais qui renvoie de mauvais résultats n’est pas utile. Pas plus qu’un système qui ne peut pas maintenir la latence sous charge.
Milvus a été open-sourcé en 2019 avec une conviction simple : les bases de données vectorielles allaient devenir une infrastructure de données essentielle, et non une fonctionnalité cachée dans une application. Pendant plus de huit ans, cette conviction nous a guidés dans une seule direction : rendre la recherche vectorielle plus rapide et plus prévisible. Compression d’index, planification des segments, réglage HNSW, stratégies de préchargement — presque toutes les optimisations majeures visaient la même chose : faire entrer les données dans le cache local et accélérer la recherche.
Ce travail reste le socle. Le service always-on est la bonne architecture pour les charges de travail de recherche vectorielle à QPS élevé et faible latence. Si une collection est interrogée en permanence, garder les index résidents en mémoire n’est pas du gaspillage — c’est le coût nécessaire pour servir l’expérience produit.
Puis nous nous sommes attaqués au coût. Le stockage hiérarchisé a aidé — segments chauds en mémoire, données froides sur disque et stockage objet, économies réelles. Mais les nœuds ne s’éteignaient jamais. Pour une charge de travail qui s’exécute cinq heures par mois, vous payiez tout de même les 715 autres.
Cet écart est l’un des problèmes que le nouveau Zilliz Vector Lakebase est conçu pour résoudre. Le changement le plus important ne consiste pas simplement à « rendre la recherche vectorielle moins chère ». Il s’agit de permettre à des données sémantiques persistantes de prendre en charge plus d’un cycle de vie de calcul : un service always-on lorsque la latence et le débit comptent, et du calcul à la demande lorsque les données doivent rester interrogeables sans nécessiter de machines dédiées en fonctionnement tout le mois.
La physique derrière le modèle de service always-on
La latence de lecture de S3 est de 20 à 50 ms par requête. Le parcours d’un graphe HNSW touche des centaines de nœuds par requête. Mettez ces deux chiffres ensemble et la conclusion est évidente : les index vectoriels doivent résider en mémoire locale pour servir les requêtes. Ce n’est pas un défaut de conception — c’est de la physique.
Pour rendre cela concret : 100 M de vecteurs, 768 dimensions, float32. Les données vectorielles brutes représentent ~286 Go ; le graphe HNSW (M=48) ajoute encore ~55 Go en liens de voisinage — soit environ 340 Go au total.
Modèle traditionnel Milvus QueryNode :
┌──────────────────────────────────────────────────────────────┐
│ Traditional Milvus architecture │
│ │
│ 100M × 768-dim float32 → ~340 GB split across 3 QueryNodes │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ QueryNode 1 │ │ QueryNode 2 │ │ QueryNode 3 │ │
│ │ 128GB RAM │ │ 128GB RAM │ │ 128GB RAM │ │
│ │ + NVMe │ │ + NVMe │ │ + NVMe │ │
│ │ seg 0-99 │ │ seg 100-199 │ │ seg 200-299 │ │
│ │ (~113 GB) │ │ (~113 GB) │ │ (~113 GB) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ load() │ load() │ load() │
│ └─────────────────┼─────────────────┘ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ S3 (source of truth) │ │
│ │ 340 GB full dataset │ │
│ └───────────────────────┘ │
│ Collection queryable only when all 340 GB are loaded │
│ Node fails → its segments go dark → reload from S3 │
└──────────────────────────────────────────────────────────────┘
Chaque segment a besoin d’un nœud résident avant que la collection puisse être interrogée. 340 Go de données, trois machines de 128 Go, en fonctionnement 24/7. Pour les collections fréquemment interrogées, cela fonctionne bien. Puis l’IA a changé le schéma de la demande.
Les équipes produit exécutent des expériences A/B de deux semaines, après quoi ces embeddings ne sont plus jamais interrogés. Dans les produits SaaS, 90 % des utilisateurs ne se sont pas connectés la semaine dernière. Dans les bases de connaissances RAG, 80 % des documents n’ont pas été récupérés au cours du dernier mois. Les données ne sont pas inutiles — elles peuvent être interrogées à tout moment — mais elles le sont rarement. Les bases de données traditionnelles gèrent cela avec la hiérarchisation : les données chaudes en mémoire, les données froides sur disque, et le chargement des pages à la demande. Les bases de données vectorielles n’avaient pas ce concept. Soit vous chargiez toute la collection, soit elle n’était pas interrogeable.
Avant que les embeddings générés par l’IA ne se généralisent, cette binarité n’était pas un problème. La plupart des charges de travail vectorielles étaient soit clairement des systèmes de service en ligne, où garder les index résidents en mémoire avait du sens, soit des expériences hors ligne qui pouvaient tolérer des pipelines sur mesure. L’IA a changé cet entre-deux.
Nous avons commencé à observer ce changement dans nos conversations avec les clients. Les embeddings ne servaient plus seulement à alimenter des chatbots RAG en production. Un leader mondial des GPU encodait des données de conduite autonome — images de caméra, sessions de conduite, météo, localisation, horodatages et autres métadonnées — afin que les ingénieurs puissent extraire des scénarios de conduite rares parmi des dizaines de milliards de vecteurs. Une entreprise de technologies éducatives utilisait la recherche sémantique pour la détection de plagiat multilingue, avec des charges de travail pouvant passer d’une poignée de documents à plus de 10 000 documents dans un lot pendant les périodes d’examens.
C’est le contexte de Vector Lakebase. Les équipes IA accumulent des données non structurées qui doivent rester persistantes et découvrables, mais le modèle d’accès est inégal. Certains chemins nécessitent un service continu. D’autres nécessitent une recherche occasionnelle, de l’exploration ou de la découverte par lots sur les mêmes données sous-jacentes. Traiter tous ces chemins comme du service toujours actif laisse trop d’infrastructure inutilisée.
Un utilisateur a publié dans notre Slack communautaire :
"Mes embeddings sont déjà dans S3. Vous me dites que je dois passer trois heures à les importer, garder trois machines avec 128 Go de RAM en fonctionnement 24/7, et payer $24 000 par an — juste pour exécuter des requêtes occasionnelles ?"
Il avait raison. Le problème n’était pas l’endroit où les données vivaient ni la vitesse suffisante de l’index. Il payait des prix de données chaudes pour un modèle d’accès à données froides : 0,7 % actif, 100 % facturé.
Le marché avait déjà commencé à prouver que l’économie d’un stockage objet d’abord comptait pour les charges de travail vectorielles. Et garder un calcul sans état sur du stockage objet était une direction que de nombreux utilisateurs souhaitaient. Mais la question la plus difficile pour nous était de savoir comment intégrer ce modèle de coûts dans une base de données vectorielle complète : avec recherche filtrée, sémantique de base de données, isolation opérationnelle, et une trajectoire qui reste reliée au service toujours actif lorsque les charges de travail deviennent chaudes.
Voici notre thèse Vector Lakebase : garder les données sémantiques persistantes, et laisser la couche de calcul correspondre à la charge de travail. La recherche à la demande est une expression de cette architecture. La réussir a nécessité de lever quatre obstacles techniques.
Quatre obstacles à la recherche à la demande Lakebase
Dans le modèle de recherche à la demande Lakebase, les QueryNodes démarrent à la demande, servent les requêtes, puis sont libérés. Les données restent dans le stockage objet comme source de vérité. Le calcul se met à l’échelle jusqu’à zéro entre les sessions de requêtes. Cela paraît simple, mais le rendre utilisable a nécessité de traiter la latence de démarrage à froid, le volume de scan, l’amplification des E/S pendant la récupération, et les coûts fixes du plan de contrôle.
Le démarrage à froid était trop lent
340 Go d’index HNSW. Chargement depuis S3 : plus de quatre minutes. Quatre minutes de démarrage à froid tuent tout cas d’usage à la demande. Un utilisateur lance une requête et attend quatre minutes — ce n’est pas un délai, c’est un produit cassé.
La solution consistait à compresser l’index tout en le gardant utilisable. Nous avons construit une quantification matryoshka 1+3 bits basée sur RabitQ (Gao & Long, 2024). Deux couches, imbriquées comme des poupées matryoshka.
La couche 1 bit se charge d’abord — 13 Go au lieu de 340 Go. La recherche s’exécute immédiatement dessus : RabitQ fournit une borne d’erreur prouvable sur les distances 1 bit, ce qui vous permet d’élaguer les candidats en toute sécurité et de garantir que rien dans le vrai top-k n’est éliminé. 85 à 90 % de rappel dès la première requête.
La couche 3 bits se télécharge en arrière-plan pendant que la recherche 1 bit s’exécute. Une fois prête, elle est utilisée comme passe de raffinement — les survivants de l’étape 1 bit sont réévalués avec une précision complète de 1+3 bits. Le rappel passe à 95 %+ . Les deux couches ne sont pas des alternatives ; la couche interne effectue le filtrage, et la couche externe améliore les résultats.
Le débit de quantification brute est un goulot d’étranglement à grande échelle. La construction d’index accélérée par GPU et les noyaux de requête AVX512 / ARM SVE portent le débit de calcul de distance à un niveau où le surcoût de quantification devient négligeable. Deux améliorations supplémentaires augmentent le rappel : la mise à l’échelle optimale par vecteur, où l’erreur de quantification de chaque vecteur est minimisée individuellement au lieu de partager un facteur global ; et l’allocation non uniforme des bits entre les dimensions en fonction de la variance, afin que les dimensions riches en information reçoivent davantage de bits. Les deux réduisent directement l’erreur de quantification sans augmenter la taille de l’index.
Premier obstacle franchi. Mais même avec une quantification complète, parcourir 100 M de vecteurs reste coûteux.
Parcourir 100 M de vecteurs
L’index 1 bit est petit, mais le calcul de distance sur 100 M de vecteurs reste linéaire. Dans un modèle à la demande, cela s’aggrave : un temps de calcul plus long signifie que le QueryNode reste résident plus longtemps, ce qui réduit la fenêtre de libération élastique.
Clustering IVF avec élagage d’index global (le nombre de buckets augmente avec le volume de données) :
┌──────────────────────────────────────────────────────────────┐
│ Index global + élagage IVF │
│ │
│ 100M vecteurs → clustering IVF (N buckets, N augmente avec │
│ le volume de données) │
│ │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┬─── ··· ───┬───┐ │
│ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ │ N │ │
│ └───┴───┴───┴───┴───┴───┴───┴───┴─── ··· ───┴───┘ │
│ ▲ ▲ ▲ │
│ █ █ █ ← scanner seulement ~3%│
│ │
│ Requête q → trouver les centroïdes les plus proches → │
│ chercher dans ces buckets │
│ │
│ Données scannées : ~3% du total │
│ I/O S3 : récupérer ~3% des données │
│ Calcul : calcul de distance sur ~3% seulement │
└──────────────────────────────────────────────────────────────┘
IVF n’est pas nouveau, mais deux choses rendent le nôtre différent.
D’abord, l’échelle. La plupart des implémentations IVF s’effondrent à l’échelle du milliard de vecteurs parce que la construction de l’index nécessite de tout charger en mémoire en une seule fois. Nous avons construit une construction d’index distribuée qui répartit le travail de clustering entre les nœuds — IVF à n’importe quelle échelle, y compris des milliards de vecteurs.
Ensuite, l’interaction avec Lakebase. Au moment de la requête, seuls les buckets pertinents sont récupérés depuis S3. Sondez ~3 % des clusters, récupérez ~3 % des données, gardez ~3 % en mémoire QueryNode. Un nœud qui n’a chargé que 3 % du jeu de données peut être récupéré presque immédiatement après la fin de la requête.
Avec la quantification 1 bit, les deux barrières se combinent : 340 Go → 13 Go (quantification) → ~400 Mo par requête (élagage IVF). Le démarrage à froid ne charge que les centroïdes de clusters et les métadonnées de l’index 1 bit — 5 à 10 secondes. Chaque requête suivante ne récupère que les buckets pertinents, pas les 13 Go complets.
Deuxième obstacle franchi.
Retrieve amplifiait les I/O S3
La recherche vectorielle renvoie des ID, pas les données brutes. Obtenir les vecteurs d’origine ou les champs scalaires implique une deuxième série de lectures, et dans un chemin de requête natif du stockage, chacune est une lecture ponctuelle S3.
Le problème venait du format de stockage. Les fichiers Parquet standard utilisent des row groups de 64 Mo. Un seul enregistrement vectoriel fait environ 3 Ko. Le lire signifie télécharger tout le row group : 3 Ko de données utiles, 64 Mo d’I/O réelles — soit une amplification d’environ 20 000x. Tolérable sur disque local. Brutal sur S3.
Storage V2 en a résolu la moitié : séparation des colonnes larges et étroites, avec des vecteurs et des champs scalaires stockés indépendamment, et des groupes de lignes réduits à 1 Mo — 64x moins d’amplification. Le hic : la compression au niveau des blocs de Parquet repose sur de grands groupes de lignes. Réduisez-les, et la compression se dégrade ; les fichiers grossissent. Dans Parquet, petits groupes de lignes et bonne compression sont mutuellement exclusifs. C’est là que Vortex entre en jeu.
Vortex, développé par Spiral et hébergé par la Linux Foundation, dispose d’une disposition entièrement configurable sans structure de groupe de lignes imposée ; de requêtes ponctuelles directes sur des données compressées via un encodage imbriqué Delta → RLE → BitPacking, sans décompression requise ; et d’une sélection automatique de l’encodage, basée sur l’algorithme BtrBlocks, qui équilibre le taux de compression, la vitesse d’encodage et la vitesse de décodage.
Benchmarks : 3 M de lignes, vecteurs de 128 dimensions, S3, 256 lecteurs concurrents, lot de 10 lignes par lecture.
| Métrique | Parquet | Lance | Vortex |
|---|---|---|---|
| Débit de lecture ponctuelle (lectures/s) | 162 | 464 | 620 |
| Octets S3 par lecture (Mo) | 9.44 | 0.006 | 0.07 |
| GET S3 par ligne | ~2 | ~5 | ~2 |
| Débit de scan complet (Mo/s) | 638 | 730 | 1,548 |
| Débit d’écriture (Mo/s) | 216 | 247 | 244 |
Parquet télécharge 9,44 Mo par lecture — l’intégralité du groupe de lignes. Lance ramène cela à 0,006 Mo en lisant avec une granularité de 512 octets, mais le paie en IOPS : ~5 GET S3 par ligne contre ~2 pour les autres. Vortex arrive à 0,07 Mo avec ~2 GET par ligne — 135x moins de trafic que Parquet, sans la pénalité d’IOPS. Le débit de scan complet est 2,4x supérieur à celui de Parquet ; les écritures sont comparables.
Troisième obstacle levé.
Les coûts du plan de contrôle ne descendaient pas à zéro
Les trois premiers changements concernaient le chemin de requête. Le quatrième était caché dans le plan de contrôle.
Même lorsque tous les QueryNodes sont inactifs, chaque instance Milvus garde son Coordinator et etcd actifs. N locataires signifie N ensembles. Les QueryNodes pouvaient descendre à zéro ; ces deux composants ne le pouvaient pas — ils sont avec état et doivent rester résidents. À un million de locataires, la surcharge du plan de contrôle dépasse le coût des QueryNodes.
Le plan de contrôle Lakebase fait passer cela de O(N) à O(1) :
Milvus traditionnel : coût du plan de contrôle O(N)
┌──────────────────────────────────────────────────────────────┐
│ Shared infrastructure │
│ Kafka / Pulsar (shared) Index Pool (shared) │
└──────────────────────────────────────────────────────────────┘
| | |
Tenant A Tenant B Tenant C
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Coordinator │ │ Coordinator │ │ Coordinator │
│ etcd │ │ etcd │ │ etcd │
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
│ QueryNode │ │ QueryNode │ │ QueryNode │
│ (dedicated) │ │ (dedicated) │ │ (dedicated) │
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘
└─────────────────────┼─────────────────────┘
↓
┌──────┐
│ S3 │
└──────┘
Lakebase : coût du plan de contrôle O(1)
┌───────────────────────────────────────────────────────────────┐
│ Plan de contrôle partagé (par région) │
│ │
│ ┌──────────────────┐ ┌──────────┐ ┌───────────────────┐ │
│ │ Coordinateur │ │Catalogue │ │ Service WAL │ │
│ │ partagé │ │ ≠ etcd │ │ → S3, ≠ Kafka │ │
│ │ │ │ │ │ │ │
│ └──────────────────┘ └──────────┘ └───────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Service d’index (pool de build GPU) │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────┬─────────────────────────────────┘
┌────────────────────┼─────────────────────┐
NS locataire A NS locataire B NS locataire C
┌────────────┐ ┌────────────┐ ┌───────────┐
│ QueryNode │ │ (inactif) │ │ QueryNode │
│ QueryNode │ │ échelle = 0│ └────┬──────┘
└──────┬─────┘ └────────────┘ │
└─────────────────────┬─────────────────────┘
↓
┌──────┐
│ S3 │
└──────┘
Lakebase remplace chaque élément de l’ancien modèle par locataire. Le coordinateur est partagé entre les locataires, remplaçant les coordinateurs par locataire. Catalog remplace etcd par instance et supprime la limite de stockage de 2 Go. WAL Service écrit directement dans S3 sans disque local — débit mesuré de 750 Mo/s, 5,8x Kafka — remplaçant Kafka/Pulsar. Index Service est un pool de build GPU partagé entre les locataires, remplaçant l’allocation GPU par instance.
« Scale to zero » ne signifie plus « les QueryNodes peuvent être libérés », mais « l’instance entière ne coûte presque rien lorsqu’elle est inactive ».
┌──────────────────────────────────────────────────────────────┐
│ Multi-locataire × Lakebase On-demand Search │
│ │
│ Couche de stockage S3 calcul (à la demande) │
│ ┌──────────────┐ │
│ │Données loc. A│ ◄──── requête ── QueryNode A (actif) │
│ ├──────────────┤ │
│ │Données loc. B│ (inactif, pas de QueryNode)│
│ ├──────────────┤ │
│ │Données loc. C│ (inactif, pas de QueryNode)│
│ ├──────────────┤ │
│ │Données loc. N│ ◄──── requête ── QueryNode N (actif) │
│ └──────────────┘ │
│ │
│ 1M locataires, 1 % actifs → 99 % des données sans coût de calcul │
└──────────────────────────────────────────────────────────────┘
Traditionnellement, le multi-locataire signifiait partager un cluster entre les locataires via des collections ou partitions distinctes — mais ce cluster avait des plafonds stricts : la limite de métadonnées de 2 Go d’etcd, le débit du coordinateur et la capacité fixe des QueryNodes. Passer au-delà de ces limites signifiait davantage de clusters, donc davantage de surcharge.
Lakebase change le plafond. Catalog remplace etcd par un magasin de métadonnées scalable, et le coordinateur partagé gère beaucoup plus de locataires sans surcharge par locataire. S3 fournit l’élasticité du stockage. Il en résulte un cluster unique capable de servir beaucoup plus de locataires isolés — et seuls les locataires recevant activement des requêtes consomment du calcul. Les autres ne paient que le stockage.
Retour à cet utilisateur Slack
Même scénario : 100M de vecteurs, float32 à 768 dimensions, 10 requêtes par jour, une minute chacune. Actif environ 5 heures par mois.
Pour cette charge de travail, la différence importante ne réside pas seulement dans l’endroit où vivent les octets. Il s’agit de savoir si le calcul doit rester attaché à ces octets alors que personne ne les interroge.
Les temps de démarrage à froid de Milvus auto-hébergé et du modèle de stockage hiérarchisé de Zilliz Cloud sont tous deux des coûts de chargement ponctuels — une fois à chaud, les requêtes sont rapides. Le démarrage à froid à la demande de Lakebase se produit au début de chaque session après que le nœud est revenu à zéro, ce qui, pour cette charge de travail, revient essentiellement à chaque fois. 5 à 10 secondes par session constituent le compromis pour ne rien payer entre les sessions.
Le coût de l’auto-hébergement est principalement lié à EC2 toujours actif, avec 3 × r6g.4xlarge à la demande à environ $2,073/mois, plus Kafka. Le modèle de stockage hiérarchisé de Zilliz Cloud supprime la charge opérationnelle, mais le modèle de facturation reste le même. La recherche à la demande de Lakebase change le modèle : vous ne payez que les cinq heures que vous utilisez réellement.
| Milvus auto-hébergé | Modèle de stockage hiérarchisé de Zilliz Cloud | Recherche à la demande de Lakebase | |
|---|---|---|---|
| Cycle de vie du calcul | Toujours actif | Toujours actif | À la demande |
| Coût du calcul inactif | Plein tarif | Plein tarif | $0 |
| Schéma de démarrage à froid | Chargement ponctuel, puis à chaud | Chargement ponctuel, puis à chaud | 5–10 s au début de la session |
| Cas d’usage idéal | Charges de travail de service à chaud | Hiérarchisation chaud/froid gérée | Données sémantiques rarement interrogées |
~$240/an. Coût de calcul nul 99 % du temps. Quatre obstacles, quatre niveaux de changement.
La physique n’a pas changé. S3 est toujours à 20–50 ms par lecture.
Ce qui a changé, c’est le modèle de calcul autour de cette physique : le stockage hiérarchisé a réduit le coût de stockage des données plus froides, mais la recherche à la demande de Lakebase supprime le plancher de calcul toujours actif pour les charges de travail qui sont principalement inactives.
Cet écart compte plus que les économies. L’utilisateur Slack qui ne pouvait pas justifier $24,000/an n’a pas seulement économisé de l’argent lorsqu’il a migré — il a commencé à indexer davantage de données parce que la recherche était assez bon marché pour en faire plus. Prix plus bas, demande accrue.
C’est l’histoire plus large de Vector Lakebase. Une fois que les données sémantiques peuvent persister indépendamment d’un unique cluster de service toujours actif, les équipes peuvent choisir la forme de calcul qui correspond à la charge de travail : service continu pour les chemins chauds, recherche à la demande pour les données rarement interrogées, et calcul par lots pour les tâches de découverte ou de traitement.
Zilliz Vector Lakebase est disponible en aperçu public
Nous avons lancé l’aperçu public de Zilliz Vector Lakebase— une évolution majeure de Zilliz Cloud, qui passe d’une base de données vectorielle gérée à une plateforme unifiée de données sémantiques, combinant le service vectoriel à faible latence avec l’ouverture, la scalabilité et l’économie d’un data lake.
Capacités principales de Vector Lakebase :
- Service hiérarchisé optimisé pour différents compromis performance-coût en temps réel
- Recherche à la demande pour des charges de travail à grande échelle ou exploratoires sans calcul toujours actif
- Recherche dans des data lakes externes — indexez et recherchez directement sur vos données de lac existantes
- Recherche sur tout le spectre à travers les vecteurs, le texte, JSON et les données géospatiales avec récupération hybride et reranking
- Stockage unifié natif du lake construit sur Vortex, un format ouvert avec des lectures aléatoires plus rapides et moins chères que Lance ou Parquet
Si votre pile actuelle sépare le service et la découverte dans des systèmes distincts, Vector Lakebase pourrait valoir le coup d’œil. Essayez-le sur Zilliz Cloud — les nouvelles inscriptions avec une adresse e-mail professionnelle obtiennent $100 de crédits gratuits — ou parlez-nous de votre cas d’usage.
Continuer à lire

Vector Lakebase: End the AI Data Silo
Learn how Vector Lakebase unifies vector search, data lakes, and AI data operations so teams can serve RAG and agents without copy-and-sync pipelines.

Zilliz Cloud Update: Tiered Storage, Business Critical Plan, Cross-Region Backup, and Pricing Changes
This release offers a rebuilt tiered storage with lower costs, a new Business Critical plan for enhanced security, and pricing updates, among other features.

Expanding Our Global Reach: Zilliz Cloud Launches in Azure Central India
Zilliz Cloud expands to Azure Central India. This new region helps customers meet compliance, reduce latency, and optimize cloud costs when building AI applications.



