Lorsque la charge de surveillance augmente, un seul collecteur peine souvent à absorber les écritures à haute fréquence, tandis que la latence des requêtes s'accroît sous charge concurrente. TairTS, un module de séries temporelles intégré à Tair (Enterprise Edition), résout ces deux problèmes en offrant des requêtes à latence milliseconde et une accumulation d'écritures sécurisée face à la concurrence. Cette rubrique explique comment utiliser TairTS pour mettre en place un système de surveillance à grain fin.
Concepts clés
TairTS organise les données de séries temporelles à l'aide de deux identifiants :
| Identifiant | Rôle | Exemple |
|---|---|---|
| pkey (clé primaire) | Regroupe les séries temporelles associées | cpu_load (nom d'une métrique) |
| skey (clé secondaire) | Identifie une série spécifique au sein du groupe | app1 (instance d'application) |
Autres fonctionnalités principales :
| Fonctionnalité | Description |
|---|---|
| Compression Gorilla | Réduit les coûts de stockage en appliquant l'algorithme Gorilla avec une disposition optimisée pour les données de séries temporelles |
| TTL par skey | Chaque skey possède son propre délai de conservation (TTL), permettant une rotation automatique des données selon des fenêtres temporelles |
| Agrégation via une seule commande | Exécutez des requêtes par lots avec sous-échantillonnage, filtrage basé sur les attributs et filtrage multiniveau en une seule commande afin de réduire les interactions réseau |
Architecture
Figure 1. Architecture de surveillance à grain fin 
Le système de surveillance fonctionne comme suit :
La console envoie les configurations de surveillance à grain fin à l'application.
L'application transmet les configurations aux collecteurs via MQTT (MQ Telemetry Transport).
Les collecteurs traitent les données entrantes et les écrivent dans les bases de données Tair à l'aide des commandes TairTS.
Gérez les requêtes à forte concurrence
TairTS prend en charge les opérations d'agrégation — sous-échantillonnage, filtrage basé sur les attributs, requête par lots et filtrage multiniveau à l'aide de fonctions numériques — au sein d'une unique requête. Cette approche réduit les interactions réseau et renvoie les résultats en quelques millisecondes, permettant ainsi une détection immédiate des anomalies.
Gérez les écritures à forte concurrence
À mesure que les applications montent en charge, un seul collecteur peut ne pas suivre le volume d'écriture entrant. TairTS résout ce problème grâce à extsrawincr, qui accumule les valeurs issues d'écritures concurrentes au lieu de les écraser. Lorsque plusieurs collecteurs écrivent sur la même skey avec le même horodatage, le résultat cumulatif reste correct, ce qui réduit l'utilisation de la mémoire sans aucune perte de données.
L'exemple suivant illustre des écritures concurrentes provenant de deux collecteurs vers la même pkey (cpu_load) et la même skey (app1) :
import com.aliyun.tair.tairts.TairTs;
import com.aliyun.tair.tairts.params.ExtsAggregationParams;
import com.aliyun.tair.tairts.params.ExtsAttributesParams;
import com.aliyun.tair.tairts.results.ExtsSkeyResult;
import redis.clients.jedis.Jedis;
public class test {
protected static final String HOST = "127.0.0.1";
protected static final int PORT = 6379;
public static void main(String[] args) {
try {
Jedis jedis = new Jedis(HOST, PORT, 2000 * 100);
if (!"PONG".equals(jedis.ping())) {
System.exit(-1);
}
TairTs tairTs = new TairTs(jedis);
// Use the following code if you want to work with a cluster instance:
//TairTsCluster tairTsCluster = new TairTsCluster(jedisCluster);
String pkey = "cpu_load";
String skey1 = "app1";
long startTs = (System.currentTimeMillis() - 100000) / 1000 * 1000;
long endTs = System.currentTimeMillis() / 1000 * 1000;
String startTsStr = String.valueOf(startTs);
String endTsStr = String.valueOf(endTs);
tairTs.extsdel(pkey, skey1);
long num = 5;
// Concurrently update data in Collector A.
for (int i = 0; i < num; i++) {
double val = i;
long ts = startTs + i*1000;
String tsStr = String.valueOf(ts);
ExtsAttributesParams params = new ExtsAttributesParams();
params.dataEt(1000000000);
String addRet = tairTs.extsrawincr(pkey, skey1, tsStr, val, params);
}
ExtsAggregationParams paramsAgg = new ExtsAggregationParams();
paramsAgg.maxCountSize(10);
paramsAgg.aggAvg(1000);
System.out.println("Updated result of Collector A:");
ExtsSkeyResult rangeByteRet = tairTs.extsrange(pkey, skey1, startTsStr, endTsStr, paramsAgg);
for (int i = 0; i < num; i++) {
System.out.println(" ts: " + rangeByteRet.getDataPoints().get(i).getTs() + ", value: " + rangeByteRet.getDataPoints().get(i).getDoubleValue());
}
// Concurrently update data in Collector B.
for (int i = 0; i < num; i++) {
double val = i;
long ts = startTs + i*1000;
String tsStr = String.valueOf(ts);
ExtsAttributesParams params = new ExtsAttributesParams();
params.dataEt(1000000000);
String addRet = tairTs.extsrawincr(pkey, skey1, tsStr, val, params);
}
System.out.println("Updated result of Collector B:");
rangeByteRet = tairTs.extsrange(pkey, skey1, startTsStr, endTsStr, paramsAgg);
for (int i = 0; i < num; i++) {
System.out.println(" ts: " + rangeByteRet.getDataPoints().get(i).getTs() + ", value: " + rangeByteRet.getDataPoints().get(i).getDoubleValue());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Les deux collecteurs appellent extsrawincr avec les mêmes horodatages et les mêmes valeurs. Après que le collecteur A a écrit 5 points de données, la requête renvoie :
Updated result of Collector A:
ts: 1597049266000, value: 0.0
ts: 1597049267000, value: 1.0
ts: 1597049268000, value: 2.0
ts: 1597049269000, value: 3.0
ts: 1597049270000, value: 4.0
Une fois que le collecteur B a écrit les mêmes valeurs, extsrawincr ajoute les nouvelles valeurs aux existantes :
Updated result of Collector B:
ts: 1597049266000, value: 0.0
ts: 1597049267000, value: 2.0
ts: 1597049268000, value: 4.0
ts: 1597049269000, value: 6.0
ts: 1597049270000, value: 8.0
Chaque valeur est doublée car les deux collecteurs ont contribué à parts égales. Ce comportement d'accumulation rend extsrawincr sûr pour les écritures concurrentes provenant de plusieurs collecteurs.