Je doute que la maximisation de l'utilisation du processeur de Redis profite à la conception de votre backend. La bonne question est plutôt de savoir si Redis est suffisamment efficace pour maintenir votre débit à une latence donnée. Redis est un serveur à thread unique :à 80 % de consommation du processeur, la latence sera probablement très mauvaise.
Je vous suggère de mesurer la latence pendant que redis-benchmark fonctionne pour voir si elle est acceptable pour vos besoins avant d'essayer d'augmenter la consommation du processeur Redis. L'option --latency de redis-cli peut être utilisée pour cela :
- lancer redis-server
- essayez redis-cli --latency, notez la valeur moyenne, arrêtez-la
- dans une autre fenêtre, démarrez le benchmark et assurez-vous qu'il s'exécute pendant un certain temps
- essayez redis-cli --latency, notez la valeur moyenne, arrêtez-la
- arrêter le benchmark
- comparer les deux valeurs moyennes
Maintenant, si vous voulez vraiment augmenter la consommation du processeur Redis, vous avez besoin soit d'un programme client efficace (comme redis-benchmark), capable de gérer plusieurs connexions simultanément, soit de plusieurs instances de votre programme client.
Lua est un langage interprété rapidement, mais c'est toujours un langage interprété. Ce sera un ou deux ordres de grandeur plus lent que le code C. Redis est beaucoup plus rapide à analyser/générer son protocole que lua-redis, vous ne pourrez donc pas saturer Redis avec un client Lua unique (sauf si vous utilisez les commandes O(n) Redis - voir plus loin).
webdis est implémenté en C, avec une bibliothèque client efficace, mais doit parser les protocoles http/json qui se trouvent être plus verbeux et complexes que le protocole Redis. Il consomme probablement plus de CPU que Redis lui-même pour la plupart des opérations. Donc encore une fois, vous ne saturerez pas Redis avec une seule instance webdis.
Voici quelques exemples pour saturer Redis avec plusieurs clients Lua.
Si ce n'est pas déjà fait, je vous suggère de jeter d'abord un coup d'œil à la page de référence de Redis.
Si vous exécutez votre benchmark sur le même boîtier que Redis :
Le point clé est de dédier un cœur à Redis et d'exécuter les programmes clients sur les autres cœurs. Sous Linux, vous pouvez utiliser la commande taskset pour cela.
# Start Redis on core 0
taskset -c 0 redis-server redis.conf
# Start Lua programs on the other cores
for x in `seq 1 10` ; do taskset -c 1,2,3 luajit example.lua & done
Le programme Lua doit utiliser le pipelining pour maximiser le débit et réduire l'activité du système.
local redis = require 'redis'
local client = redis.connect('127.0.0.1', 6379)
for i=1,1000000 do
local replies = client:pipeline(function(p)
for j=1,1000 do
local key = 'counter:'..tostring(j)
p:incrby(key,1)
end
end)
end
Sur mon système, le programme Lua prend plus de 4 fois le CPU de Redis, il faut donc plus de 4 cœurs pour saturer Redis avec cette méthode (une boîte à 6 cœurs devrait suffire).
Si vous exécutez votre benchmark sur une boîte différente de Redis :
Sauf si vous exécutez sur des machines virtuelles gourmandes en CPU, le goulot d'étranglement sera probablement le réseau dans ce cas. Je ne pense pas que vous puissiez saturer Redis avec autre chose qu'un lien 1 GbE.
Assurez-vous de canaliser vos requêtes aussi loin que vous le pouvez (voir le programme Lua précédent) pour éviter le goulot d'étranglement de la latence du réseau et réduire le coût des interruptions du réseau sur le CPU (remplissage des paquets Ethernet). Essayez d'exécuter Redis sur un cœur qui n'est pas lié à la carte réseau (et traite les interruptions réseau). Vous pouvez utiliser des outils comme htop pour vérifier ce dernier point.
Essayez d'exécuter vos clients Lua sur diverses autres machines du réseau si vous le pouvez. Encore une fois, vous aurez besoin d'un bon nombre de clients Lua pour saturer Redis (6-10 devraient suffire).
Dans certains cas, un processus Lua unique suffit :
Désormais, il est possible de saturer Redis avec un seul client Lua si chaque requête est suffisamment coûteuse. Voici un exemple :
local redis = require 'redis'
local client = redis.connect('127.0.0.1', 6379)
for i=1,1000 do
local replies = client:pipeline(function(p)
for j=1,1000 do
p:rpush("toto",i*1000+j)
end
end)
end
N = 500000
for i=1,100000 do
local replies = client:pipeline(function(p)
for j=1,10 do
p:lrange("toto",N, N+10)
end
end)
end
Ce programme remplit une liste avec 1 million d'éléments, puis utilise les commandes lrange pour récupérer 10 éléments au milieu de la liste (pire cas pour Redis). Ainsi, chaque fois qu'une requête est exécutée, 500 000 éléments sont analysés par le serveur. Comme seuls 10 éléments sont renvoyés, ils sont rapides à analyser par lua-redis, ce qui ne consommera pas de CPU. Dans cette situation, toute la consommation CPU sera du côté serveur.
Derniers mots
Il existe probablement des clients Redis plus rapides que redis-lua :
- https://github.com/agladysh/lua-hiredis (basé sur embauchés)
- https://github.com/agladysh/ljffi-hiredis (basé sur l'embauche, en utilisant luajit FFI)
Vous voudrez peut-être les essayer.