Les serveurs NUMA Nodes créent localement les accès mémoire par socket et augmentent ainsi de manière mesurable l'efficacité des grands systèmes d'hébergement. Je montre comment cette architecture réduit la latence, augmente le débit et, par conséquent, réduit les coûts. Charges de travail sur les serveurs d'entreprise est plus évolutif.
Points centraux
- Localité de la mémoire diminue la latence et réduit les accès à distance.
- Évolutivité sur de nombreux cœurs sans goulots d'étranglement du bus mémoire.
- Sensibilisation à la NUMA dans le noyau, l'hyperviseur et les applications apporte de la vitesse.
- Planification de VMs/conteneurs par nœud empêche le thrashing.
- Suivi via numastat/perf révèle les hotspots.
Que sont les serveurs NUMA Nodes ?
Je mise sur une architecture dans laquelle chaque socket dispose de sa propre zone de mémoire locale en tant que Nœud NUMA est obtenue. Ainsi, un noyau accède en premier lieu à la RAM rapide et proche et évite la mémoire plus lente et éloignée. Les accès via des interconnexions comme Infinity Fabric ou UPI restent possibles, mais ils coûtent du temps supplémentaire.
Contrairement à l'UMA, le temps d'accès varie, ce qui a des conséquences directes sur la qualité des données. Latence et de la bande passante. Les grands systèmes regroupent autant de cœurs sans s'effondrer sur le bus mémoire. Le livre compact Architecture NUMA dans l'hébergement.
Localité de la mémoire dans l'hébergement
Je lie les processus et la mémoire au même nœud, afin que les chemins de données restent courts et que les données ne soient pas perdues. Cache-Les occurrences augmentent. Cette Memory Locality a un effet immédiatement perceptible sur les serveurs web, les PHP-FPM et les bases de données. Je repousse les accès à distance pour que davantage de requêtes passent par seconde.
Des binds CPU et mémoire bien planifiés empêchent les threads de se déplacer sur les nœuds et Thrashing déclencher des attaques. Pour les configurations dynamiques, j'étudie des approches d'équilibrage NUMA qui optimisent les accès dans le temps ; une introduction approfondie est disponible ici NUMA-Balancing. Cela me permet de maintenir une faible latence et d'utiliser les cœurs plus efficacement.
Pourquoi NUMA compte pour les grands systèmes d'hébergement
Les grandes plates-formes d'hébergement supportent de nombreux sites web en même temps et exigent des temps de réponse courts en cas de Peak-trafic de données. NUMA augmente les chances que les données se trouvent à proximité du noyau d'exécution et ne se déplacent pas sur l'interconnexion. C'est précisément là que les boutiques, les API et les CMS gagnent les millisecondes décisives.
J'assure ainsi une plus grande densité sur l'hôte sans sacrifier les performances, et je maintiens un niveau de sécurité élevé. Temps de fonctionnement-plus facilement les cibles. Même en cas de pics de trafic, les temps de réponse restent plus lisses, car il y a moins de charge à distance. Cela se traduit directement par une meilleure expérience utilisateur et moins d'abandons.
La technique dans la pratique
Je lis la topologie avec lscpu et numactl --hardware pour nœuds, Je vois clairement la disposition de la mémoire vive et des cœurs. Ensuite, je lie les charges de travail avec numactl --cpunodebind et --membind. Les hyperviseurs comme KVM et les noyaux Linux modernes reconnaissent la topologie et schedulent déjà avantageusement.
Sur les systèmes multi-socket, je fais attention à la bande passante d'interconnexion et au nombre de RAM-canaux par nœud. Je place les applications avec une forte empreinte de cache dans les nœuds. Pour les services avec des modèles mixtes, j'utilise la mémoire entrelacée si les tests en profitent de manière cohérente.
En outre, j'évalue avec numactl --hardware le distances entre les nœuds est désactivée : Des valeurs faibles entre nœuds voisins indiquent un accès à distance plus rapide, mais augmentent tout de même la latence par rapport à la RAM locale. Je note que --mempolicy=preferred peut se déplacer à distance en cas de pression de mémoire, alors que --membind est stricte et fait échouer les allocations en cas de doute. Je l'utilise de manière ciblée en fonction de la criticité des charges de travail.
Lorsque les processus créent des threads de manière dynamique, je mets avant le démarrage taskset- ou bien cset-pour que les nouveaux threads soient automatiquement placés dans le bon ordre. CPU-domaine de la base de données. Je planifie le chemin complet lors du déploiement : Les workers, les threads d'E/S, le garbage collector et, le cas échéant, les jobs d'arrière-plan reçoivent des affinités cohérentes afin qu'il n'y ait pas de chemins cachés entre les nœuds.
Comparaison des indicateurs de performance
J'évalue l'optimisation NUMA sur la latence, le débit, CPU-utilisation et mise à l'échelle. Chaque métrique montre si le local est efficace ou si les accès à distance dominent. Des tests constants sous charge fournissent une orientation claire pour les prochaines étapes de réglage.
Le tableau suivant montre les ordres de grandeur typiques des charges de travail d'hébergement pour les services et les bases de données proches du web ; il illustre l'effet des services locaux. Accès par rapport aux accès à distance.
| Métriques | Sans optimisation NUMA | Avec NUMA & Memory Locality |
|---|---|---|
| Latence (ns) | 200-500 | 50–100 |
| Débit (Req/s) | 10.000 | 25.000+ |
| Utilisation du CPU (%) | 90 | 60 |
| Évolutivité (noyaux) | jusqu'à 64 | 512+ |
Je mesure en continu et je compare Profils avant et après les ajustements. Des benchmarks reproductibles comptent pour que les effets ne soient pas dus au hasard. J'en déduis ainsi des mesures concrètes et solides pour l'exploitation productive.
Les percentiles tels que p95/p99 sont particulièrement significatifs, au lieu de simples valeurs moyennes. Si les percentiles élevés diminuent sensiblement après l'égalisation des accès à distance, la plateforme est plus stable sous charge. En complément, je vérifie les taux d'échec LLC, les changements de contexte et les run queue length par nœud, afin d'attribuer proprement les effets d'ordonnancement et de cache.
Défis et bonnes pratiques
Le thrashing NUMA se produit lorsque les threads se déplacent à travers les nœuds et qu'ils sont constamment soumis à des attaques étrangères. Mémoire demander de l'aide. Je réponds à cela par un placement fixe des threads, un memory binding cohérent et des limites par service. Une attribution claire réduit visiblement le trafic à distance.
Comme outils de contrôle, j'utilise numastat, parfait et les événements du noyau pour Points chauds de la même manière. Une surveillance régulière montre si un pool glisse vers le mauvais nœud ou si une VM est répartie de manière défavorable. En procédant par petites étapes planifiées, je limite les risques et assure des progrès constants.
Options du noyau et du BIOS/UEFI
Je vérifie les réglages BIOS/UEFI tels que le clustering sub-NUMA ou le partitionnement des nœuds par socket. Une répartition plus fine peut aiguiser la localité, mais nécessite des bindings plus stricts. En règle générale, je désactive l'entrelacement global de la mémoire afin de réduire les différences entre le local et le distant. Mémoire restent visibles et que l'ordonnanceur puisse prendre des décisions judicieuses.
Côté Linux, je passe kernel.numa_balancing en toute connaissance de cause. Pour les charges de travail HPC rigides ou à latence, je désactive l'équilibrage automatique (echo 0 > /proc/sys/kernel/numa_balancing), pour les charges de travail mixtes, je le teste en combinaison avec des affinités CPU claires. vm.zone_reclaim_mode je le définis de manière conservatrice, afin que les nœuds ne demandent pas trop agressivement le retour de leurs propres pages et ne déclenchent pas de rappels inutiles.
Pour les bases de données nécessitant beaucoup de mémoire, je prévois HugePages par nœud. Transparent Huge Pages (THP) peuvent varier ; je préfère utiliser des HugePages statiques et les lier localement à un nœud. Cela réduit les taux d'échec TLB et stabilise la latence. En outre, je contrôle le swapping avec vm.swappiness proche de 0, afin d'éviter que les hot-paths ne se retrouvent dans le déstockage.
Je règle les interruptions en fonction de la topologie : irqbalance je configure les interruptions NIC de manière à ce qu'elles se terminent sur les CPU du même nœud que celui sur lequel fonctionnent les workers correspondants. Piles réseau avec RPS/RFS distribuent les paquets en fonction des masques CPU ; je définis ces masques en fonction de la position du worker afin d'éviter les chemins cross-node dans le dataplane.
Pour les SSD NVMe, je distribue des files d'attente par nœud et je lie les threads d'E/S localement. Ainsi, les bases de données, les caches et les métadonnées du système de fichiers rencontrent des chaînes de latence aussi courtes que possible, du CPU au contrôleur de stockage en passant par la RAM. Pour les logs persistants ou les write-ahead logs, je veille particulièrement à ce que les affinités entre les nœuds soient propres, car elles ont une influence directe sur les temps de réponse.
Configuration dans des piles courantes
Je crée des pools PHP-FPM de manière à ce que les workers soient sur un Nœud et je dimensionne la taille du pool en fonction du nombre de noyaux. Pour NGINX ou Apache, je lie les processus à forte intensité d'E/S à la même localité que les caches. Les bases de données telles que PostgreSQL ou MySQL reçoivent des HugePages fixes par nœud.
Au niveau de la virtualisation, je crée des dispositions de vCPU cohérentes avec le système physique. Mise en page une seule fois. J'utilise l'affinité CPU de manière ciblée, une entrée en matière rapide se trouve ici Affinité du CPU. J'évite ainsi que les hot-paths ne surchargent inutilement l'interconnexion.
Modèles de charge de travail : web, cache et bases de données
Le serveur web et le PHP-FPM profitent lorsque les sockets d'écoute, les workers et les caches se trouvent dans le même domaine NUMA. Je fais évoluer chaque nœud de manière autonome : des groupes de processus séparés par nœud avec leur propre masque CPU et leur propre zone de mémoire partagée. Cela évite que les caches de session, l'OPCache ou les pipes FastCGI locales ne passent par l'interconnexion.
Dans les configurations Redis/Memcached, j'utilise plusieurs instances, une par nœud, au lieu d'une grande instance sur les deux sockets. Ainsi, les buckets de hachage et les slabs restent locaux. Pour Elasticsearch ou des moteurs de recherche similaires, j'attribue délibérément des shards à des nœuds et je garde les threads Query et Ingest du même côté que les zones de cache de fichiers et de pages correspondantes.
Pour PostgreSQL, je partage shared_buffers et les pools de travail en segments de nœuds, en séparant les instances ou les services par nœud. Je fais évoluer InnoDB via innodb_buffer_pool_instances et je veille à ce que les threads d'un pool restent à l'intérieur d'un nœud. Je surveille séparément les pointeurs de contrôle, les écrivains WAL et l'Autovacuum, car ils génèrent souvent des accès à distance non souhaités.
Pour les services stateful, je garde les tâches d'arrière-plan (compactage, analyse, réindexation) séparées temporellement et topologiquement des hot-paths. Si nécessaire, j'utilise numactl --preferred, pour permettre une répartition plus douce de la charge, sans pour autant imposer une rigueur totale de --membind de forcer le passage.
Planification des capacités et coûts
Je calcule le TDP, les canaux de RAM et le nombre d'ordinateurs souhaités. densité par hôte avant de déplacer les charges de travail. Un double socket avec un pourcentage élevé de RAM par nœud fournit souvent la meilleure valeur en euros par requête. Des économies apparaissent lorsqu'un hôte supporte plus de VMs avec le même temps de réponse.
Par exemple, le passage à un placement conscient de la NUMA peut réduire le nombre d'hôtes de plusieurs dizaines de milliers. Pourcentages de la consommation. Même avec un surcoût de quelques centaines d'euros par nœud en RAM, le bilan est positif. Le calcul est réussi si je compare les mesures aux coûts d'exploitation courants en €.
Je tiens également compte des coûts énergétiques : la localité réduit le temps CPU par requête, ce qui diminue sensiblement la consommation. Dans les ateliers Sizing, je n'évalue donc pas seulement les requêtes de pointe, mais aussi les kWh/1000 requêtes par topologie. Cette vue rend les décisions entre une densité plus élevée et des sockets supplémentaires plus tangibles.
vNUMA et la migration en direct dans la pratique
Dans les environnements virtualisés, je représente les topologies vNUMA en fonction de la structure physique. Je regroupe les vCPU d'une VM par vNode et je lie également la RAM attribuée. J'évite ainsi qu'une VM prétendument petite se disperse sur les deux sockets et génère des accès à distance.
J'épingle les processus QEMU et leurs threads d'E/S de manière cohérente, y compris iothread et vhost-des tâches de maintenance. Je dépose HugePages par nœud comme backend de mémoire, afin que la VM utilise la même mémoire locale à chaque démarrage. Je prévois sciemment des compromis : Des stratégies d'épinglage très strictes peuvent limiter la migration en direct ; dans ce cas, je choisis entre une stabilité maximale de la latence et une flexibilité opérationnelle.
En cas d'overcommit, je veille à des limites supérieures claires : Si la RAM par nœud devient insuffisante, je préfère des stratégies d'évitement au sein du même groupe de VM plutôt qu'un débordement sauvage des nœuds. Je connecte de préférence les vNIC et les vDisks au nœud sur lequel les VM-Worker calculent, afin que le chemin des données reste cohérent.
NUMA et orchestration de conteneurs
conteneurs en bénéficient lorsque les requêtes, le cache et les Données se trouvent en local. Dans Kubernetes, j'utilise des points de topologie pour que Scheduler alloue les cœurs et la mémoire dans le même nœud. Je sécurise les classes de qualité de service et les requêtes/limites de manière à ce que les pods ne se déplacent pas sans but.
Je teste des politiques pour le gestionnaire de CPU et HugePages jusqu'à ce que Latence et le débit. Les charges de travail stateful reçoivent des nœuds fixes, tandis que les services stateless s'adaptent plus étroitement à la périphérie. La plateforme reste ainsi agile sans perdre les avantages du local.
Avec la politique statique du gestionnaire de CPU, j'attribue des noyaux exclusifs et j'obtiens des affinités claires. Le gestionnaire de topologie donne la priorité single-numa-node, pour que les pods soient regroupés. Pour les passerelles et les contrôleurs Ingress, je distribue SO_REUSEPORT-par nœud, afin que le trafic se termine localement. Je planifie les caches, les sidecars et les segments de mémoire partagée par groupe de pods de manière à ce qu'ils atterrissent sur le même nœud NUMA.
Playbook de benchmarking et suivi
Je travaille avec une procédure fixe afin de mesurer et d'ajuster les effets du NUMA de manière fiable :
- Saisir la topologie :
lscpu,numactl --hardware, Vérifier les canaux d'interconnexion et de RAM. - Baseline sous charge : saisir les latences p95/p99, les Req/s, les profils d'échec CPU et LLC par nœud.
- Introduire le binding :
--cpunodebind/--membind, Les pools peuvent être étendus par nœud. - Re-run : même charge, mêmes données, attribuer les différences de manière logique.
- Ajustement fin : affinité avec les interruptions, HugePages, allocateur de mémoire, collection de garbage.
- Contrôles de régression dans le CI : répliquer régulièrement les scénarios pour éviter toute dérive.
Pour la profondeur, j'ai recours à statistique parfaite et perf record retour, observe les compteurs d'accès à distance, les échecs LLC et TLB et les proportions de temps dans le noyau vs. le pays utilisateur. numastat me fournit par nœud la répartition des allocations et le taux de défauts à distance. Cette vue permet de reproduire les étapes d'optimisation et de les prioriser.
Problèmes courants et dépannage
Je reconnais les anti-patterns typiques à des latences brusques et à une utilisation élevée du CPU sans gain de débit correspondant. Les causes les plus fréquentes sont des masques de CPU trop larges, un THP global sans HugePages fixes, un autoscailing agressif sans référence à la topologie ou un cache mal réparti.
Je vérifie d'abord si les fils de discussion avec ps -eLo pid,psr,psr,cmd et taskset -p fonctionnent là où ils doivent fonctionner. Ensuite, je contrôle les numastat-Je vérifie les compteurs d'accès à distance et les compare aux pics de trafic. Si nécessaire, j'active temporairement l'entrelacement pour démasquer les goulets d'étranglement, puis je repasse à la localité stricte.
Ce qui a également fait ses preuves, a vis de réglage l'une après l'autre : D'abord les bindings, puis l'affinité avec les interruptions, ensuite les HugePages et enfin le réglage fin de l'allocateur de mémoire. Ainsi, les effets restent compréhensibles et réversibles.
Développements futurs
Les nouveaux Interconnects et CXL élargissent la gamme de produits adressables. Mémoire et rendent la RAM découplée plus tangible. Les serveurs ARM avec de nombreux cœurs utilisent également des topologies de type NUMA et exigent le même regard sur la localité. La tendance est clairement à des stratégies de placement encore plus fines.
Je m'attends à ce que les planificateurs utilisent davantage les signaux NUMA dans les Temps réel évaluer la situation. Les piles d'hébergement intègrent alors automatiquement les bindings appropriés pour les charges de travail typiques. La localisation devient ainsi la norme plutôt qu'une mesure spéciale.
En bref
Les serveurs de nœuds NUMA regroupent les Ressources par socket et raccourcissent considérablement les trajets de données. Je lie les processus et la mémoire ensemble, je limite les accès à distance et j'en mesure les effets de manière cohérente. Il en résulte des gains sensibles en termes de latence, de débit et de densité.
Avec une reconnaissance propre de la topologie, des bindings intelligents et une Suivi les fournisseurs d'hébergement tirent davantage de leur matériel. En suivant ces étapes de manière conséquente, on obtient des pages plus rapides, une meilleure évolutivité et des coûts prévisibles. C'est ce qui fait la différence au quotidien.


