Business Intelligence en russe - Quintettes

Dans l'une de nos notes de blog, nous avons couvert l' approche du stockage et du traitement des données , à propos de laquelle nous avons reçu plusieurs questions très attendues d'un tel plan: «En général, nous attendons la mise en œuvre, maintenant il vole ...». Sur la base des résultats de plusieurs implémentations, je parlerai des avantages et des inconvénients de cette approche en utilisant un de nos développements comme exemple.


Nous offrons généralement à nos clients un outil de BI assez puissant et flexible qui peut résoudre tous leurs problèmes, mais il s'agit d'un produit commercial étranger, et les clients sont de plus en plus intéressés par le thème de la substitution des importations. Dans le cadre de l'exploration de nos perspectives à cet égard, nous avons commencé à tester notre propre ensemble d'outils de BI à l'aide de solutions open source et d'une plate-forme de développement basée sur un quintet.




Nous avons pris notre produit commercial existant comme référence, nous effectuerons donc une comparaison avec lui chaque fois que possible.


La tâche consistait à créer les composants suivants:


  • une base de données pour stocker les data marts et les informations de service (utilisateurs, paramètres, etc.);
  • interface Web du système et de son ORM;
  • Fonctionnalité BI - chargement des données, rapports personnalisés, graphiques, tableaux croisés dynamiques.

Comme base, nous avons pris une plate - forme de développement qui comprend un modèle de données, un concepteur de requêtes, un moteur de modèle et prend en charge un modèle de rôle pour le contrôle d'accès des utilisateurs.


L'objet de l'ordonnance est le rapprochement des relevés bancaires et l'analyse de ses données cumulées au cours des 7 dernières années. Les données source sont stockées dans HDFS, les rapports eux-mêmes sont calculés au même endroit, puis leurs résultats tombent dans la fenêtre de la base de données relationnelle. Les vitrines contiennent environ 300 Go de données pour plusieurs dizaines de formulaires de rapport différents et plusieurs centaines de rapports associés. Le système doit servir 20 utilisateurs à la charge de pointe, le temps de réponse de l'interface doit être dans la seconde.


La création d'une structure de données ne nous a pas pris beaucoup de temps: l'éditeur de type de plateforme vous permet de créer et de décrire des objets métier tels quels, sans avoir besoin d'adapter les besoins des utilisateurs aux spécificités de l'environnement (types et dimensions de données, clés, restrictions, noms de champs de données, identifiants, etc.). C'est le premier plus des quintets que nous pouvons corriger.


C'est ainsi que la structure des données est créée - nous définissons les termes nécessaires, et à partir d'eux nous formons les objets de notre domaine:



Après plusieurs minutes d'activité au format copier / coller, nous avons obtenu la structure de données requise pour un formulaire et plusieurs répertoires auxiliaires pour celui-ci. La structure des données du formulaire (après décomposition par l'analyste) ressemble à ceci:




Un analyste familier avec le sens commercial des données présentées ici lit ce qui suit: Chaque instance F110 (formulaire 110 dans la terminologie CB) a une précision donnée (elle peut être «exacte» ou «arrondie») et comprend un ensemble de codes, pour chacun desquels les montants en roubles et en devises sont indiqués.


Dans le navigateur de données, où les données sont présentées selon la structure spécifiée, nous voyons un tel tableau sur les instances de formulaire existant dans le système:




La valeur d'identification du formulaire est sa date de déclaration, les codes de déclaration sont stockés sous la forme d'un tableau subordonné, dont la taille est indiquée entre parenthèses.


Comme mentionné, nous avons un peu décomposé les données, par rapport à la structure d'origine, afin de ne pas stocker de longues lignes de valeurs répétitives dans la base de données, comme ce que voit un programmeur ou un administrateur de base de données:


Tableau avec données dans le système de référence:


Soit dit en passant, c'est pourquoi nous positionnons la plate-forme (ci-après nous l'appellerons Integral) en tant qu'outil de développement pour l'analyste, pas le programmeur.


La structure des données auxiliaires est incommensurablement plus grande, car elle stocke toutes les données initiales, les paramètres du rapport, les règles de vérification de l'intégrité des données à l'intérieur du rapport et dans les rapports associés, l'historique des calculs et des rapprochements, ainsi que certaines règles de transformation des activités de données pendant la construction du rapport:



(la structure n'est pas donnée dans son intégralité)


Lorsque la structure de données est prête, vous pouvez y charger des données. Le moyen le plus simple ici est de télécharger un fichier préparé au format Integral (analogue à .csv, mais avec un balisage de type). Ce format contient une description des données et des données elles-mêmes.


Afficher le fichier

Dans l'exemple ci-dessous, les 3 premières lignes du fichier décrivent la structure du formulaire (s'il n'est pas dans le système, il sera créé), puis les données elles-mêmes - les paramètres du formulaire et les paramètres des objets de rapport qui lui sont subordonnés.


268:110:DATE;277;270; 277::SHORT; 270:  :SHORT;  :SIGNED;   :SIGNED; 268::20121231;281;; 270::A/5.2;1233682389.47;; 277:281:; 270::A/5.3;622836720.22;; 270::A/6.4;19800;; 270::A/9.2;27125165.14;; 270::S16203/1.2;608607846.309999;; 270::S16305/4;2727510994.84;; 270::S16305/4.1;32049069.51;; 270::S16305/14;2737711.65;; 270::S25302/4;2725748122.98999;; 270::S25302/4.1;40952511.36;; 270::IL/2;87429694.5699999;62717458.21; 270::IL/4;33517212.95;; 270::IL/9;1423281.69;8278.24; 270::IL/11;86433534.5699999;519956.63; 270::IA/1;147792224.509999;4517060.94; 270::IA/2;737704.92;; 270::IA/3;27099836.07;2637.79; 270::IA/6;5607868.86;408410.4; 270::IA/8;103837028.49;48841202.69; 270::IA/10;112302573.56;; 268::20121231;280;; 270::A/5.2;1233682;; 277:280:; 270::A/5.3;622837;; 270::A/6.4;20;; 270::A/9.2;27125;; 270::S16203/1.2;608608;; 

Pour ce formulaire, la base de données a 4470 dates de rapport qui, une fois déchargées dans un fichier plat, prennent un peu plus de 1 Mo. Dans la base de données d'origine (Oracle), ils occupent 3,1 Mo (sans index) sous une forme normalisée et 4,2 Mo dans une vitrine dénormalisée, que nous essayons de répéter en tant que quintets. Les quintettes sont indexés et normalisés, et dans leur format, ces données occupent déjà 10 Mo.


Les quantités de données à comparer sont résumées dans le tableau (en mégaoctets):
TexteRDBMSQuintettes
Les données1.13.15.1
Dénormalisé4.2
Indices6.25.1
Données + index9.310.2

À ce stade, il convient de prêter attention à la taille occupée par les bases des approches comparées. En raison de la normalisation supplémentaire des quintets et des dépenses pour les indices composites dans la base de référence, la taille totale occupée par les bases de données est presque la même.


Base de référence:


Quintettes:


De plus, dans Integral, nous avons immédiatement indexé tous les champs de la table, et dans le système de référence - seulement la date, le code et le montant en roubles, ce qui nécessitera des coûts supplémentaires lorsque de nouveaux besoins surgiront.


Pour référence: dans la base de données, la taille totale de ce formulaire, y compris les rapports et les paramètres de prise en charge, est d'environ 400 Mo (il est relativement petit).


Ainsi, les données sont téléchargées et la partie la plus difficile du projet reste à venir - la création d'une interface. L'interface du système de référence vous permet de visualiser les données des formulaires, les rapports annexes, les paramètres et le cycle de vie des formulaires. Pour gérer l'accès et communiquer avec la base de données, nous avons utilisé les capacités de base de notre plateforme - un modèle de rôle et un concepteur de rapports.


La liste des utilisateurs avec leurs rôles ressemble à ceci:




Si vous cliquez sur le nom du rôle (marqué d'un ovale rouge sur la figure ci-dessus), vous pouvez consulter son contenu:




Les objets de rôle peuvent être définis sur 3 niveaux d'accès; le masque peut être utilisé:




L'édition des données se fait également au moyen de l'interface de base. Voici, par exemple, un formulaire d'édition utilisateur:




Nous avons présenté le menu spécifique de notre application et de ses postes de travail dans un fichier compact, car ils sont tous du même type: un formulaire de demande de 2-3 éléments et un tableau avec les résultats des demandes.




L'architecture s'est avérée très simple: nous avons créé de nombreuses demandes de données (vues) et écrit un plug-in qui implémente des échantillons de données arbitraires dans les tables et les champs inclus dans ces vues.


Par exemple, nous avons un soi-disant rapport de décryptage au format 110, il contient des données non agrégées sur lesquelles il est construit. Voici à quoi ressemble ce rapport:




Afin de vérifier l'exactitude du formulaire, l'utilisateur doit pouvoir effectuer toutes les sélections, trier, filtrer, grouper, transposer, ainsi que créer ses propres champs calculés. Notre plugin est appelé par le bouton «Actions» en haut du tableau.




Le plugin répète les fonctionnalités du générateur de requêtes intégré, mais en plus de récupérer des données, il peut dessiner des graphiques et des tableaux croisés dynamiques. Par exemple, nous devons filtrer par section, ajouter une paire de colonnes calculées et faire une sélection des montants regroupés par elles. Nous définissons tout cela:




De nouvelles colonnes sont ajoutées à la liste par le lien du même nom. Par le bouton "Calculs" nous définissons les formules pour eux en utilisant un constructeur simple:




Nous définissons un nouvel ordre de colonne et cliquez sur «Appliquer» et notre rapport est modifié au besoin - au lieu de 7 colonnes de base, nous en voyons trois, dont deux que nous venons de créer:




Comment ça marche sous le capot?

Le plugin communique avec le service web de l'application via api, il a effectué la requête suivante:


 api/neo/report/1392573?FR_date=20181231&FR_%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB=&FR_%D0%9A%D0%BE%D0%B4_%D0%BE%D0%B1%D0%BE%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F=&SELECT=LEFT(\:1392578\:\,5),SUBSTRING(\:1392578\:\,6\,3),1392617:SUM&ORDER=1392617&FR_%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB=1&TOTALS=1392617:SUM&LIMIT=10 

Et j'ai obtenu cette réponse:


 { «columns»: [ «LEFT(:1392578:,5)», «SUBSTRING(:1392578:,6,3)», «1392617» ], «formats»: [ «SHORT», «SHORT», «SIGNED» ], «data»: { «LEFT(:1392578:,5)»: [ «60324», «40817», «47425», «47404», «60302», «47404» ], «SUBSTRING(:1392578:,6,3)»: [ «810», «810», «810», «840», «810», «978» ], «  »: [ «153 825.71», «527 901.11», «2 415 189.23», «3 000 000.02», «5 588 330.88», «58 000 000.00» ] }, «totals»: [ «„, “», «69 685 246.95» ] } 

Si quelqu'un s'intéresse à la requête SQL réelle qui a été exécutée dans la base de données de quintet, alors voici:


Voir SQL

L'analyste ne voit pas ce SQL; il utilise le générateur de requêtes décrit ci-dessous.


 SELECT LEFT(a182088.val, 5) v13, SUBSTRING(a182088.val, 6, 3) v14, SUM(round(a182090.val, 2)) '  ' FROM neo a182081 LEFT JOIN neo a182083 ON a182083.up=a182081.id AND a182083.t=182083 LEFT JOIN neo a182088 ON a182088.up=a182083.id AND a182088.t=182088 LEFT JOIN neo a182090 ON a182090.up=a182083.id AND a182090.t=182090 LEFT JOIN neo a182091 ON a182091.up=a182083.id AND a182091.t=182091 LEFT JOIN neo a182092 ON a182092.up=a182083.id AND a182092.t=182092 LEFT JOIN neo a299 ON a299.t=299 AND a182083.val=a299.val LEFT JOIN neo a328 ON a328.up=a299.id AND a328.t=328 LEFT JOIN neo a303 ON a303.up=a299.id AND a303.t=303 LEFT JOIN neo a304 ON a304.up=a299.id AND a304.t=304 LEFT JOIN neo a182089 ON a182089.up=a182083.id AND a182089.t=182089 WHERE a182081.up!=0 AND length(a182081.val)!=0 AND a182081.t=182081 AND a182081.val='20181231′ AND a328.val ='1AND a303.val>='19000101′ AND a303.val<='20181231AND a304.val>='20181231′ AND a304.val<='20991231GROUP BY v13, v14 ORDER BY CAST(SUM(round(a182090.val, 2)) AS SIGNED) LIMIT 10 

La demande peut sembler un peu plus compliquée que prévu, car elle sélectionne les données actuelles des codes de déclaration en fonction de leur validité. Dans le générateur de rapport intégré, cette requête se compose de colonnes de rapport, de paramètres et de conditions de jointure de table, si nécessaire.




L'intégrale elle-même est capable de générer des conditions de jointure de table, car toutes sont déterminées par des relations de quintet, cependant, dans le cas du versioning, nous avons dû combiner les tables manuellement et spécifier explicitement la condition de JOIN. Les codes de la clause ON sont des identificateurs pour les objets de colonne et de requête.


La requête combine trois tables, en choisissant parmi elles les champs de données suivants ("colonnes de requête" requises):



(image en taille réelle)

Les colonnes de rapport, les champs calculés, les filtres, etc., qui composent la requête SQL, sont répertoriés ici. Le Générateur de rapports vous permet d'implémenter presque n'importe quelle conception du langage SQL, y compris la jonction de requêtes et de requêtes imbriquées.



En plus des sélections avec des regroupements, l'utilisateur peut utiliser le mécanisme des tableaux croisés dynamiques. Nous avons ajouté l'outil de table pivottable.js.org à notre plugin.
Sélectionnez les colonnes qui nous intéressent et passez en mode tableau croisé dynamique:




Ici, en utilisant drag'n'drop, nous pouvons analyser les données reçues par l'échantillon que nous avons configuré, y compris nos champs arbitraires. De plus, ici, vous pouvez également filtrer les données par n'importe quel champ.




Nous avons utilisé le produit gratuit www.amcharts.com pour dessiner des graphiques. Avec lui, comme avec pivotable, tout est assez simple: nous sélectionnons le type de graphique et initialisons le composant avec notre tableau de données obtenu à partir d'Integral:




Avec cela, nous avons, en fait, accompli la tâche dans la mesure qui satisfait les utilisateurs du produit existant. Nous avons maintenant un système qui répond aux exigences de substitution à l'importation: tous les produits sont gratuits et remplaçables. Oui, nous avons réalisé loin de toutes les possibilités qu'offre le système existant, mais seulement celles qui sont nécessaires pour ce client. Mais nous venons de commencer!



Alors volé ou pas?


Nous devrions également discuter des performances du système résultant. Dans ce problème, avec une quantité de données suffisamment importante (des centaines de gigaoctets), les échantillons affectent de petits fragments, dont les quintettes sont toujours collectés à l'aide d'index. Cela conduit au fait que, à n'importe quelle taille de la base de données, les requêtes sont traitées à une vitesse acceptable, sans entraîner une dégradation des performances de type avalanche.


Nous avons enregistré 20 actions utilisateur dans un script de test et l' avons exécuté sur le service loadimpact.com . Il s'est avéré 27 requêtes différentes, car certaines actions sont effectuées en 2 requêtes au serveur (pour construire un affichage de page, par exemple).


Le script de test s'est avéré comme ceci
 import { group, sleep } from 'k6′; import http from 'k6/http'; // Version: 1.3 // Creator: Load Impact URL test analyzer export let options = { stages: [ { «duration»: «3m0s», «target»: 25 } ], maxRedirects: 0, discardResponseBodies: true, }; export default function() { group("page_1 — https://*****.ru/neo/dict«, function() { let req, res; req = [{ «method»: «get», «url»: «https://*****.ru/neo/info», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «upgrade-insecure-requests»: «1», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8» } } }]; res = http.batch(req); sleep(0.62); req = [{ «method»: «get», «url»: «https://*****.ru/neo/info», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «upgrade-insecure-requests»: «1», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392573?FR_date=20181231&LIMIT=10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392573?FR_date=20181130&ORDER=1392617&LIMIT=10&RECORD_COUNT», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392573?FR_date=20181031», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392573?FR_date=20180930&RECORD_COUNT», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «*/*», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1387723?&LIMIT=10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «*/*», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1387723?&LIMIT=10&RECORD_COUNT», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «*/*», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392741?», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392757?&LIMIT=10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392768?&LIMIT=10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/download/neo/img/nav_dropdown_arrow.svg», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/download/neo/img/nav_detailed_report.svg», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/download/neo/img/nav_classifiers.svg», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/download/neo/img/nav_launch_report.svg», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/download/neo/img/nav_manage_form_status.svg», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/download/neo/img/nav_quality_management.svg», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/download/neo/img/nav_download.svg», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «image/webp,image/apng,image/*,*/*;q=0.8», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/download/neo/css/variables.css», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392779?&LIMIT=10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392538?&LIMIT=10&RECORD_COUNT», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/edit_obj/1392129», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/edit_obj/1390552», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/object/18», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1387723?&LIMIT=140,10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1387723?&LIMIT=140,10&RECORD_COUNT», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392573?FR_date=20180731&FR_%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB=&FR_%D0%9A%D0%BE%D0%B4_%D0%BE%D0%B1%D0%BE%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F=&SELECT=1392576,1392617:SUM,1392589&LIMIT=100», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392678?FR_date=20180831&FR_section=1&SELECT=1392698,1392685,1392690&LIMIT=10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392678?FR_date=20180630&FR_section=1&SELECT=1392698,1392685,1392690&LIMIT=10&RECORD_COUNT», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392678?FR_date=20180531&FR_section=1&SELECT=1392698,1392685,1392690&LIMIT=500», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392678?FR_date=20181231&FR_section=2&SELECT=1392698,1392685,1392690&LIMIT=500», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392678?FR_date=20181231&FR_section=4&SELECT=1392698,1392685,1392690,1392694&LIMIT=20», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/report/1392678?FR_date=20181231&FR_section=4&SELECT=1392698,1392685,1392690,1392694&LIMIT=20&RECORD_COUNT», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/neo/info», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/api/neo/report/1392678?FR_date=20190131&FR_section=1&FR_precision=280&SELECT=1392698,1392685,1392715&LIMIT=50», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/api/neo/report/1392678?FR_date=20190131&FR_section=1&FR_precision=280&SELECT=1392698,1392685,1392715&LIMIT=50&RECORD_COUNT», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/api/neo/report/1392573?FR_date=20190131&FR_section=1&FR_precision=280&FR_%D0%9A%D0%BE%D0%B4_%D0%BE%D0%B1%D0%BE%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F=A60302/9&LIMIT=10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://*****.ru/api/neo/report/1392573?FR_date=20190131&FR_section=1&FR_precision=280&ORDER=1392617&FR_%D0%9A%D0%BE%D0%B4_%D0%BE%D0%B1%D0%BE%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F=A60302/9&LIMIT=10», «params»: { «headers»: { «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «text/css,*/*;q=0.1», «referer»: «https://*****.ru/neo/dict» } } },{ «method»: «get», «url»: «https://fonts.gstatic.com/s/roboto/v19/KFOmCnqEu92Fr1Mu4mxP.ttf», «params»: { «headers»: { «origin»: «https://*****.ru», «accept-encoding»: «gzip, deflate», «accept-language»: «en-US», «user-agent»: «Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36», «accept»: «*/*», «referer»: «https://fonts.googleapis.com/css?family=Roboto» } } }]; res = http.batch(req); // Random sleep between 5s and 10s sleep(Math.floor(Math.random()*5+5)); }); } 


Voici ce que nous avons vu lorsque nous avons exécuté le script - le générateur de charge a envoyé à notre application un nombre croissant de demandes par seconde, nous avons obtenu une échelle classique pour trouver les performances maximales / maximales:




Ce générateur envoie des requêtes par lots, et pas de manière uniforme, comme ce serait le cas avec des utilisateurs simultanés. Par conséquent, le graphique montre les pics de charge et les performances globales semblent également pires dans ce mode.


:




, - SQL-. , , 8 33 SQL- : , -, SQL- ( , ) .


, 20-25 ( 20 ), — 1 . , : 1 2.4 1 .


( 10 ) 0.1-0.3 , .


, .

Source: https://habr.com/ru/post/fr451218/


All Articles