Migration de Time Series Insights (TSI) Gen2 vers Azure Data Explorer

Remarque

Le service Time Series Insights (TSI) ne sera plus pris en charge après le mois de mars 2025. Pensez à migrer dès que possible les environnements TSI existants vers d’autres solutions. Pour plus d’informations sur la dépréciation et la migration, consultez notre documentation.

Vue d’ensemble

Recommandations de migration générales.

Fonctionnalité État Gen2 Migration recommandée
Ingestion de JSON à partir d’un hub avec aplanissement et échappement Ingestion de TSI ADX - ingestion / Assistant OneClick
Ouvrir un magasin froid Compte de stockage client Exportation continue de données vers une table externe spécifiée par le client dans ADLS.
Connecteur PBI Préversion privée Utiliser le connecteur PBI d’ADX. Réécrire TSQ sur KQL manuellement.
Spark Connector Préversion privée. Interroger des données de télémétrie. Interroger des données de modèle. Migrer des données vers ADX. Utiliser le connecteur Spark d’ADX pour les données de télémétrie + le modèle d’exportation vers JSON, et charger dans Spark. Réécrire des requêtes dans KQL.
Chargement en bloc Préversion privée Utiliser ADX OneClick Ingest et Lightingest. Éventuellement, configurer le partitionnement dans ADX.
Modèle de série chronologique Peut être exporté en tant que fichier JSON. Peut être importé dans ADX pour effectuer des jointures dans KQL.
Explorateur TSI Basculement entre chaud et froid Tableaux de bord d’ADX
Langage de requête Requêtes de série chronologique (TSQ) Réécrire des requêtes dans KQL. Utiliser des kits de développement logiciel (SDK) Kusto plutôt que TSI.

Migration de télémétrie

Utiliser le dossier PT=Time dans le compte de stockage pour récupérer la copie de toutes les données de télémétrie dans l’environnement. Pour plus d’informations, consultez Stockage de données.

Étape de migration 1 – Obtenir des statistiques sur les données de télémétrie

Données

  1. Vue d’ensemble d’environnement
    • Enregistrer l’ID d’environnement de la première partie du nom de domaine complet (FQDN) d’accès aux données (par exemple, d390b0b0-1445-4c0c-8365-68d6382c1c2a à partir de .env.crystal-dev.windows-int.net)
  2. Vue d’ensemble d’environnement -> Configuration du stockage -> Compte de stockage
  3. Utiliser l’Explorateur Stockage pour récupérer les statistiques de dossiers
    • Enregistrer la taille et nombre de blobs du dossier PT=Time. Pour les clients en préversion privée d’Importer en bloc, enregistrer également la taille et le nombre de blobs de PT=Import.

Étape de migration 2 – Migrer la télémétrie vers ADX

Créer un cluster ADX

  1. Définir la taille du cluster en fonction de la taille des données en utilisant l’estimateur de coût d’ADX.

    1. À partir des métriques d’Event Hubs (ou d’IoT Hub), récupérer le taux d’ingestion de données par jour. À partir du compte de stockage connecté à l’environnement TSI, récupérer la quantité de données présentes dans le conteneur d’objets blob que TSI utilise. Ces informations seront utilisées pour calculer la taille idéale d’un cluster ADX pour votre environnement.
    2. Ouvrir l’estimateur de coût d’Azure Data Explorer et renseigner les champs existants avec les informations trouvées. Définir « Type de charge de travail » sur « Stockage optimisé » et « Données chaudes » avec la quantité totale de données interrogées activement.
    3. Une fois toutes les informations fournies, l’explorateur de coût d’Azure Data Explorer suggère une taille de machine virtuelle et un nombre d’instances pour votre cluster. Analyser si la taille des données activement interrogées tient dans le cache chaud. Multiplier le nombre d’instances suggéré par la taille de cache de la taille de machine virtuelle, par exemple :
      • Suggestion de l’estimateur de coût : 9x DS14 + 4 To (cache)
      • Cache chaud total suggéré : 36 To = [9x (instances) x 4 To (de cache chaud par nœud)]
    4. Autres facteurs à prendre en considération :
      • Croissance de l’environnement : lors de la planification de la taille de cluster ADX, tenez compte de la croissance des données au fil du temps.
      • Alimentation et partitionnement : lors de la définition du nombre d’instances dans un cluster ADX, tenez compte des nœuds supplémentaires (2-3x) pour accélérer l’alimentation et le partitionnement.
      • Pour plus d’informations sur la sélection de calcul, consultez Sélectionner la bonne référence SKU de calcul pour votre cluster Azure Data Explorer.
  2. Pour surveiller votre cluster et l’ingestion de données de façon optimale, vous devez activer les Paramètres de diagnostic et envoyer les données à un espace de travail Log Analytics.

    1. Dans le panneau d’Azure Data Explorer, accédez à « Surveillance | Paramètres de diagnostic », puis cliquez sur « Ajouter un paramètre de diagnostic »

      Capture d’écran du panneau Azure Data Explorer - Surveillance | Paramètres de diagnostic

    2. Entrer les informations suivantes

      1. Nom du paramètre de diagnostic : nom d’affichage de cette configuration
      2. Journaux : sélectionner au minimum SucceededIngestion, FailedIngestion, IngestionBatching
      3. Sélectionner l’espace de travail Log Analytics auquel envoyer les données (si vous n’en avez pas, vous devez en approvisionner un avant cette étape)

      Capture d’écran de l’espace de travail Log Analytics Azure Data Explorer

  3. Partitionnement des données.

    1. Pour la plupart des jeux de données, le partitionnement ADX par défaut est suffisant.
    2. Le partitionnement des données convient à un ensemble très spécifique de scénarios et ne doit pas être appliqué dans les autres cas :
      1. Amélioration de la latence des requêtes dans les jeux de Big Data où la plupart des requêtes sont filtrées sur une colonne de chaîne de cardinalité élevée, par exemple un ID série-chronologique.
      2. Lors de l’ingestion de données dans le désordre, par exemple, lorsque les événements du passé peuvent être ingérés des jours ou des semaines après leur génération dans l’origine.
    3. Pour plus d’informations, consultez Stratégie de partitionnement de données ADX.

Préparer l’ingestion de données

  1. Accédez à https://dataexplorer.azure.com.

    Capture d’écran de la page d’accueil Azure Data Explorer

  2. Accédez à l’onglet Données et sélectionnez « Ingérer à partir d’un conteneur d’objets blob »

    Capture d’écran de l’ingestion Azure Data Explorer à partir d’un conteneur d’objets blob

  3. Sélectionner Cluster, Base de données, puis créer une table avec le nom que vous choisissez pour les données TSI

    Capture d’écran de la sélection de l’ingestion Azure Data Explorer de cluster, de base de données et de table

  4. Sélectionnez Suivant : Source.

  5. Sous l’onglet Source, sélectionner :

    1. Données historiques
    2. « Sélectionner un conteneur »
    3. Choisir l’abonnement et le compte de stockage pour vos données TSI
    4. Choisir le conteneur correspondant à votre environnement TSI

    Capture d’écran de la sélection de l’ingestion Azure Data Explorer du conteneur

  6. Sélectionner Paramètres avancés

    1. Modèle d’heure de création : '/'aaaaMMjjHHmmssfff'_'
    2. Modèle de nom de blob : *. parquet
    3. Sélectionnez « Ne pas attendre la fin de l’ingestion »

    Capture d’écran de la sélection de l’ingestion Azure Data Explorer des paramètres avancés

  7. Sous Filtres de fichiers, ajouter le chemin d’accès au dossier V=1/PT=Time

    Capture d’écran de la sélection de l’ingestion Azure Data Explorer du chemin de dossier

  8. Sélectionner Suivant : Schéma

    Remarque

    TSI applique un aplanissement et un échappement lors de la persistance de colonnes dans des fichiers Parquet. Pour plus d’informations, consultez les liens suivants : Règles d’aplanissement et d’échappement, Mises à jour des règles d’ingestion.

  • Si un schéma est inconnu ou variable

    1. Supprimer toutes les colonnes rarement interrogées, en conservant au moins une ou plusieurs colonnes timestamp et TSID.

      Capture d’écran de la sélection de l’ingestion Azure Data Explorer du schéma

    2. Ajouter une nouvelle colonne de type dynamique et la mapper à l’enregistrement entier en utilisant $ Path.

      Capture d’écran de l’ingestion Azure Data Explorer pour le type dynamique

      Exemple :

      Capture d’écran de l’ingestion Azure Data Explorer pour l’exemple de type dynamique

  • Si le schéma est connu ou fixe

    1. Vérifier que l’aspect des données est correct. Corrigez certains types si nécessaire.
    2. Sélectionner Suivant : Résumé

Copier la commande LightIngest et la stocker quelque part afin de pouvoir l’utiliser à l’étape suivante.

Capture d’écran de l’ingestion Azure Data Explorer pour la commande Lightingest

Ingestion de données

Avant d’ingérer des données, vous devez installer l’outil LightIngest. La commande générée à partir de l’outil One-Click inclut un jeton SAS. L’idéal est d’en générer une nouvelle afin de pouvoir contrôler l’heure d’expiration. Dans le portail, accéder au conteneur d’objets blob pour l’environnement TSI et sélectionner « Jeton d’accès partagé ».

Capture d’écran de l’ingestion Azure Data Explorer pour le jeton SAS

Remarque

Il est également recommandé d’effectuer un scale-up de votre cluster avant de lancer une ingestion importante. Par exemple, D14 ou D32 avec plus de 8 instances.

  1. Définir ce qui suit

    1. Autorisations : Lecture et Liste
    2. Expiration : définir une période à l’issue de laquelle vous êtes certain que la migration des données sera terminée.

    Capture d’écran de l’ingestion Azure Data Explorer pour l’expiration d’autorisations

  2. Sélectionner « Générer un jeton et une URL SAS », puis copier l’« URL SAS de blob ».

    Capture d’écran de l’ingestion Azure Data Explorer pour l’URL de blob SAS

  3. Accéder à la commande LightIngest copiée précédemment. Remplacer le paramètre -source dans la commande par cette « URL SAS de blob »

  4. Option 1 : Ingérer toutes les données. Pour des environnements plus petits, vous pouvez ingérer toutes les données à l’aide d’une seule commande.

    1. Ouvrir une invite de commandes et accéder au répertoire dans lequel l’outil LightIngest a été extrait. Ensuite, coller la commande LightIngest et l’exécuter.

    Capture d’écran de l’ingestion Azure Data Explorer pour l’invite de commandes

  5. Option 2 : Ingérer les données par année ou par mois. Pour des environnements plus grands ou pour effectuer un test sur un jeu de données plus petit, vous pouvez filtrer davantage la commande Lightingest.

    1. Par année : modifier le paramètre -prefix

      • Avant : -prefix:"V=1/PT=Time"
      • Après : -prefix:"V=1/PT=Time/Y=<Year>"
      • Exemple : -prefix:"V=1/PT=Time/Y=2021"
    2. Par mois : modifier le paramètre -prefix

      • Avant : -prefix:"V=1/PT=Time"
      • Après : -prefix:"V=1/PT=Time/Y=<Year>/M=<month #>"
      • Exemple : -prefix:"V=1/PT=Time/Y=2021/M=03"

Après avoir modifié la commande, l’exécuter comme ci-dessus. Une fois l’ingestion terminée (à l’aide de l’option de surveillance ci-dessous), modifier la commande pour l’année et le mois suivants à ingérer.

Surveillance de l’ingestion

La commande LightIngest incluait l’indicateur -dontWait pour ne pas attendre la fin de l’ingestion. La meilleure façon de surveiller la progression consiste à utiliser l’onglet « Insights » dans le portail. Ouvrir la section du cluster Azure Data Explorer dans le portail et accéder à « Surveillance | Insights »

Capture d’écran de l’ingestion Azure Data Explorer pour Surveillance, Insights

Vous pouvez utiliser la section « ingestion (préversion) » avec les paramètres ci-dessous pour surveiller l’ingestion en cours

  • Intervalle de temps : 30 dernières minutes
  • Examiner Réussi et par Table
  • S’il y a des échecs, examiner Échec et par Table

Capture d’écran de l’ingestion Azure Data Explorer pour les résultats de surveillance

Vous saurez que l’ingestion est terminée lorsque les métriques afficheront 0 pour votre table. Pour afficher plus de détails, vous pouvez utiliser Log Analytics. Dans la section du cluster Azure Data Explorer, sélectionnez l’onglet « Journal » :

Capture d’écran de l’ingestion Azure Data Explorer pour les journaux de surveillance

Requêtes utiles

Comprendre le schéma si un schéma dynamique est utilisé

| project p=treepath(fullrecord)
| mv-expand p 
| summarize by tostring(p)

Accès aux valeurs d’un tableau

| where id_string == "a"
| summarize avg(todouble(fullrecord.['nestedArray_v_double'])) by bin(timestamp, 1s)  
| render timechart 

Migration de Modèle Time Series (TSM) vers Azure Data Explorer

Le modèle peut être téléchargé au format JSON à partir de l’environnement TSI à l’aide de l’interface utilisateur de l’Explorateur TSI ou de l’API Batch TSM. Le modèle peut ensuite être importé dans un autre système comme Azure Data Explorer.

  1. Télécharger TSM à partir de l’interface utilisateur de TSI.

  2. Supprimer les trois premières lignes à l’aide de VSCode ou d’un autre éditeur.

    Capture d’écran de la migration TSM vers Azure Data Explorer - Supprimer les 3 premières lignes

  3. À l’aide de VSCode ou d’un autre éditeur, rechercher et remplacer l’expression régulière \},\n \{ par }{

    Capture d’écran de la migration TSM vers Azure Data Explorer - Rechercher et remplacer

  4. Ingérer au format JSON dans ADX en tant que table distincte à l’aide de la fonctionnalité Charger à partir d’un fichier.

    Capture d’écran de la migration TSM vers Azure Data Explorer - Ingérer en tant que JSON

Traduire les requêtes de séries chronologiques (TSQ) en KQL

GetEvents

{
  "getEvents": {
    "timeSeriesId": [
      "assest1",
      "siteId1",
      "dataId1"
    ],
    "searchSpan": {
      "from": "2021-11-01T00:00:0.0000000Z",
      "to": "2021-11-05T00:00:00.000000Z"
    },
    "inlineVariables": {},
  }
}
events
| where timestamp >= datetime(2021-11-01T00:00:0.0000000Z) and timestamp < datetime(2021-11-05T00:00:00.000000Z)
| where assetId_string == "assest1" and siteId_string == "siteId1" and dataid_string == "dataId1"
| take 10000

GetEvents avec filtre

{
  "getEvents": {
    "timeSeriesId": [
      "deviceId1",
      "siteId1",
      "dataId1"
    ],
    "searchSpan": {
      "from": "2021-11-01T00:00:0.0000000Z",
      "to": "2021-11-05T00:00:00.000000Z"
    },
    "filter": {
      "tsx": "$event.sensors.sensor.String = 'status' AND $event.sensors.unit.String = 'ONLINE"
    }
  }
} 
events
| where timestamp >= datetime(2021-11-01T00:00:0.0000000Z) and timestamp < datetime(2021-11-05T00:00:00.000000Z)
| where deviceId_string== "deviceId1" and siteId_string == "siteId1" and dataId_string == "dataId1"
| where ['sensors.sensor_string'] == "status" and ['sensors.unit_string'] == "ONLINE"
| take 10000

GetEvents avec variable projetée

{
  "getEvents": {
    "timeSeriesId": [
      "deviceId1",
      "siteId1",
      "dataId1"
    ],
    "searchSpan": {
      "from": "2021-11-01T00:00:0.0000000Z",
      "to": "2021-11-05T00:00:00.000000Z"
    },
    "inlineVariables": {},
    "projectedVariables": [],
    "projectedProperties": [
      {
        "name": "sensors.value",
        "type": "String"
      },
      {
        "name": "sensors.value",
        "type": "bool"
      },
      {
        "name": "sensors.value",
        "type": "Double"
      }
    ]
  }
}	 
events
| where timestamp >= datetime(2021-11-01T00:00:0.0000000Z) and timestamp < datetime(2021-11-05T00:00:00.000000Z)
| where deviceId_string== "deviceId1" and siteId_string == "siteId1" and dataId_string == "dataId1"
| take 10000
| project timestamp, sensorStringValue= ['sensors.value_string'], sensorBoolValue= ['sensors.value_bool'], sensorDoublelValue= ['sensors.value_double']

AggregateSeries

{
  "aggregateSeries": {
    "timeSeriesId": [
      "deviceId1"
    ],
    "searchSpan": {
      "from": "2021-11-01T00:00:00.0000000Z",
      "to": "2021-11-05T00:00:00.0000000Z"
    },
    "interval": "PT1M",
    "inlineVariables": {
      "sensor": {
        "kind": "numeric",
        "value": {
          "tsx": "coalesce($event.sensors.value.Double, todouble($event.sensors.value.Long))"
        },
        "aggregation": {
          "tsx": "avg($value)"
        }
      }
    },
    "projectedVariables": [
      "sensor"
    ]
  }	
events
| where timestamp >= datetime(2021-11-01T00:00:00.0000000Z) and timestamp < datetime(2021-11-05T00:00:00.0000000Z)
| where  deviceId_string == "deviceId1"
| summarize avgSensorValue= avg(coalesce(['sensors.value_double'], todouble(['sensors.value_long']))) by bin(IntervalTs = timestamp, 1m)
| project IntervalTs, avgSensorValue

AggregateSeries avec filtre

{
  "aggregateSeries": {
    "timeSeriesId": [
      "deviceId1"
    ],
    "searchSpan": {
      "from": "2021-11-01T00:00:00.0000000Z",
      "to": "2021-11-05T00:00:00.0000000Z"
    },
    "filter": {
      "tsx": "$event.sensors.sensor.String = 'heater' AND $event.sensors.location.String = 'floor1room12'"
    },
    "interval": "PT1M",
    "inlineVariables": {
      "sensor": {
        "kind": "numeric",
        "value": {
          "tsx": "coalesce($event.sensors.value.Double, todouble($event.sensors.value.Long))"
        },
        "aggregation": {
          "tsx": "avg($value)"
        }
      }
    },
    "projectedVariables": [
      "sensor"
    ]
  }
}	
events
| where timestamp >= datetime(2021-11-01T00:00:00.0000000Z) and timestamp < datetime(2021-11-05T00:00:00.0000000Z)
| where  deviceId_string == "deviceId1"
| where ['sensors.sensor_string'] == "heater" and ['sensors.location_string'] == "floor1room12"
| summarize avgSensorValue= avg(coalesce(['sensors.value_double'], todouble(['sensors.value_long']))) by bin(IntervalTs = timestamp, 1m)
| project IntervalTs, avgSensorValue

Migration du connecteur Power BI TSI vers le connecteur Power BI ADX

Les étapes manuelles de cette migration sont les suivantes

  1. Convertir une requête Power BI en TSQ
  2. Convertir une requête Power BI TSQ à KQL en TSQ : la requête Power BI copiée à partir de l’explorateur de l’interface utilisateur de TSI ressemble à ceci

Pour des données brutes (API GetEvents)

{"storeType":"ColdStore","isSearchSpanRelative":false,"clientDataType":"RDX_20200713_Q","environmentFqdn":"6988946f-2b5c-4f84-9921-530501fbab45.env.timeseries.azure.com", "queries":[{"getEvents":{"searchSpan":{"from":"2019-10-31T23:59:39.590Z","to":"2019-11-01T05:22:18.926Z"},"timeSeriesId":["Arctic Ocean",null],"take":250000}}]}
  • Pour convertir en TSQ, générer un JSON à partir de la charge utile ci-dessus. La documentation de l’API GetEvents contient également des exemples qui vous aideront à mieux comprendre. Requête - Exécuter - API REST (Azure Time Series Insights) | Microsoft Docs
  • Le TSQ converti ressemble à ce qui est présenté ci-dessous. Il s’agit de la charge utile JSON à l’intérieur des « requêtes »
{
  "getEvents": {
    "timeSeriesId": [
      "Arctic Ocean",
      "null"
    ],
    "searchSpan": {
      "from": "2019-10-31T23:59:39.590Z",
      "to": "2019-11-01T05:22:18.926Z"
    },
    "take": 250000
  }
}

Pour des données agrégées (API AggregateSeries)

  • Pour une variable incluse unique, la requête PowerBI à partir de l’Explorateur de l’interface utilisateur de TSI se présente comme suit :
{"storeType":"ColdStore","isSearchSpanRelative":false,"clientDataType":"RDX_20200713_Q","environmentFqdn":"6988946f-2b5c-4f84-9921-530501fbab45.env.timeseries.azure.com", "queries":[{"aggregateSeries":{"searchSpan":{"from":"2019-10-31T23:59:39.590Z","to":"2019-11-01T05:22:18.926Z"},"timeSeriesId":["Arctic Ocean",null],"interval":"PT1M", 		"inlineVariables":{"EventCount":{"kind":"aggregate","aggregation":{"tsx":"count()"}}},"projectedVariables":["EventCount"]}}]}
  • Pour convertir en TSQ, générer un JSON à partir de la charge utile ci-dessus. La documentation de l’API AggregateSeries contient également des exemples qui vous aideront à mieux comprendre. Requête - Exécuter - API REST (Azure Time Series Insights) | Microsoft Docs
  • Le TSQ converti ressemble à ce qui est présenté ci-dessous. Il s’agit de la charge utile JSON à l’intérieur des « requêtes »
{
  "aggregateSeries": {
    "timeSeriesId": [
      "Arctic Ocean",
      "null"
    ],
    "searchSpan": {
      "from": "2019-10-31T23:59:39.590Z",
      "to": "2019-11-01T05:22:18.926Z"
    },
    "interval": "PT1M",
    "inlineVariables": {
      "EventCount": {
        "kind": "aggregate",
        "aggregation": {
          "tsx": "count()"
        }
      }
    },
    "projectedVariables": [
      "EventCount",
    ]
  }
}
  • Pour plusieurs variables incluses, ajouter le json à « inlineVariables », comme dans l’exemple ci-dessous. La requête Power BI pour plusieurs variables incluses se présente comme suit :
{"storeType":"ColdStore","isSearchSpanRelative":false,"clientDataType":"RDX_20200713_Q","environmentFqdn":"6988946f-2b5c-4f84-9921-530501fbab45.env.timeseries.azure.com","queries":[{"aggregateSeries":{"searchSpan":{"from":"2019-10-31T23:59:39.590Z","to":"2019-11-01T05:22:18.926Z"},"timeSeriesId":["Arctic Ocean",null],"interval":"PT1M", "inlineVariables":{"EventCount":{"kind":"aggregate","aggregation":{"tsx":"count()"}}},"projectedVariables":["EventCount"]}}, {"aggregateSeries":{"searchSpan":{"from":"2019-10-31T23:59:39.590Z","to":"2019-11-01T05:22:18.926Z"},"timeSeriesId":["Arctic Ocean",null],"interval":"PT1M", "inlineVariables":{"Magnitude":{"kind":"numeric","value":{"tsx":"$event['mag'].Double"},"aggregation":{"tsx":"max($value)"}}},"projectedVariables":["Magnitude"]}}]}

{
  "aggregateSeries": {
    "timeSeriesId": [
      "Arctic Ocean",
      "null"
    ],
    "searchSpan": {
      "from": "2019-10-31T23:59:39.590Z",
      "to": "2019-11-01T05:22:18.926Z"
    },
    "interval": "PT1M",
    "inlineVariables": {
      "EventCount": {
        "kind": "aggregate",
        "aggregation": {
          "tsx": "count()"
        }
      },
      "Magnitude": {
        "kind": "numeric",
        "value": {
          "tsx": "$event['mag'].Double"
        },
        "aggregation": {
          "tsx": "max($value)"
        }
      }
    },
    "projectedVariables": [
      "EventCount",
      "Magnitude",
    ]
  }
}
  • Si vous souhaitez interroger les données les plus récentes (« isSearchSpanRelative » : true), calculez manuellement le searchSpan comme ci-dessous
    • Rechercher la différence entre les valeurs « from » et « to » dans la charge utile Power BI. Appelons cette différence « D », où « D » = « from » - « to »
    • Prenez le timestamp actuel (« T ») et soustrayez la différence obtenue à la première étape. Il s’agit d’une nouvelle valeur « from » (F) de searchSpan où « F » = « T » - « D »
    • Désormais, la nouvelle valeur « from » est la valeur « F » obtenue à l’étape 2, et la nouvelle valeur « to » est la valeur « T » (timestamp actuel)