Business Intelligence em russo - Quintetos

Em uma das anotações de nosso blog, abordamos a abordagem do armazenamento e processamento de dados , sobre a qual recebemos várias perguntas bastante esperadas desse plano: “Em geral, estamos aguardando a implementação, agora ele voa ...”. Com base nos resultados de várias implementações, falarei sobre os prós e contras dessa abordagem, usando um de nossos desenvolvimentos como exemplo.


Normalmente, oferecemos aos nossos clientes uma ferramenta de BI bastante poderosa e flexível que pode resolver todos os seus problemas; no entanto, este é um produto comercial estrangeiro, e os clientes estão cada vez mais interessados ​​no tópico de substituição de importações. Como parte de explorar nossas perspectivas a esse respeito, começamos a testar nosso próprio conjunto de ferramentas de BI usando soluções de código aberto e uma plataforma de desenvolvimento baseada em quinteto.




Tomámos como referência o nosso produto comercial existente, pelo que faremos uma comparação sempre que possível.


A tarefa era criar os seguintes componentes:


  • um banco de dados para armazenar data marts e informações de serviço (usuários, configurações etc.);
  • interface web do sistema e seu ORM;
  • Funcionalidade de BI - carregamento de dados, relatórios personalizados, gráficos, tabelas dinâmicas.

Como base, adotamos uma plataforma de desenvolvimento que inclui um modelo de dados, designer de consultas, mecanismo de modelo e suporta um modelo para controle de acesso do usuário.


O assunto do pedido é a reconciliação de extratos bancários e a análise de seus dados acumulados nos últimos 7 anos. Os dados de origem são armazenados no HDFS, os próprios relatórios são calculados no mesmo local e seus resultados caem na janela do banco de dados relacional. As fachadas das lojas contêm cerca de 300 GB de dados para várias dezenas de formulários de relatório diferentes e várias centenas de relatórios relacionados. O sistema deve atender 20 usuários com carga de pico, o tempo de resposta da interface deve estar dentro de 1 segundo.


Criar uma estrutura de dados não demorou muito tempo: o editor de tipo de plataforma permite criar e descrever objetos de negócios como estão, sem a necessidade de adaptar os requisitos do usuário às especificidades do ambiente (tipos e dimensões de dados, chaves, restrições, nomes de campos de dados, identificadores etc.). Esta é a primeira vantagem dos quintetos que podemos consertar.


É assim que a estrutura de dados é criada - definimos os termos necessários e, a partir deles, formamos os objetos da nossa área de assunto:



Após vários minutos de atividade no formato copiar / colar, obtivemos a estrutura de dados necessária para um formulário e vários diretórios auxiliares para ele. A estrutura dos dados do formulário (após decomposição pelo analista) fica assim:




Um analista familiarizado com o senso comercial dos dados apresentados aqui lê o seguinte: Cada instância F110 (Formulário 110 na terminologia CB) possui uma precisão específica (pode ser "exata" ou "arredondada") e inclui um conjunto de códigos, para cada um dos quais valores em rublos e moeda são indicados.


No navegador de dados, onde os dados são apresentados de acordo com a estrutura especificada, vemos uma tabela sobre as instâncias de formulário existentes no sistema:




O valor de identificação do formulário é a data do relatório, os códigos de relatório são armazenados na forma de uma matriz subordinada, cujo tamanho vemos entre parênteses.


Como mencionado, decompusemos um pouco os dados, se comparados com a estrutura original, para não armazenar longas linhas de valores repetidos no banco de dados, como o que um programador ou administrador de banco de dados vê:


Tabela com dados no sistema de referência:


A propósito, é por isso que estamos posicionando a plataforma (a seguir denominada Integral) como uma ferramenta de desenvolvimento para o analista, não para o programador.


A estrutura de dados auxiliar é incomensuravelmente maior, porque armazena todos os dados iniciais, configurações de relatório, regras para verificar a integridade dos dados dentro do relatório e nos relatórios relacionados, o histórico de cálculos e reconciliações, bem como algumas regras para transformação de negócios de dados durante a construção do relatório:



(a estrutura não é fornecida na sua totalidade)


Quando a estrutura de dados estiver pronta, você poderá carregar dados nela. A maneira mais fácil aqui é fazer upload de um arquivo preparado no formato Integral (análogo ao .csv, mas com marcação de tipo). Este formato contém uma descrição dos dados e dos próprios dados.


Ver arquivo

No exemplo abaixo, as 3 primeiras linhas do arquivo descrevem a estrutura do formulário (se não estiver no sistema, ele será criado) e os próprios dados - os parâmetros do formulário e os parâmetros dos objetos de relatório subordinados a ele.


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;; 

Para este formulário, o banco de dados possui 4470 datas de relatório que, quando descarregadas em um arquivo simples, levam um pouco mais de 1 MB. No banco de dados original (Oracle), eles ocupam 3,1 MB (sem índices) em uma forma normalizada e 4,2 MB em uma loja desnormalizada, que estamos tentando repetir como quintetos. Os quintetos são indexados e normalizados e, em seu formato, esses dados já ocupam 10 MB.


As quantidades de dados para comparação estão resumidas na tabela (em megabytes):
TextRDBMSQuintetos
Dados1.13.1.5.1
Desnormalizado4.2
Índices6.25.1
Dados + índices9,310,2

Nesse ponto, deve-se prestar atenção ao tamanho ocupado pelas bases para as abordagens comparadas. Devido à normalização adicional de quintetos e despesas para índices compostos na base de referência, o tamanho total ocupado pelos bancos de dados é quase o mesmo.


Base de referência:


Quintetos:


Além disso, no Integral, indexamos imediatamente todos os campos da tabela e no sistema de referência - apenas a data, o código e a quantidade em rublos, o que exigirá custos adicionais quando surgirem novas necessidades.


Para referência: no banco de dados, o tamanho total deste formulário, incluindo relatórios e configurações de suporte, é de cerca de 400 MB (é relativamente pequeno).


Portanto, os dados são carregados e a parte mais difícil do projeto está à frente - criando uma interface. A interface do sistema de referência permite visualizar os dados dos formulários, relatórios de suporte, configurações e o ciclo de vida dos formulários. Para gerenciar o acesso e a comunicação com o banco de dados, usamos os recursos básicos de nossa plataforma - um modelo e um designer de relatórios.


A lista de usuários com suas funções é semelhante a esta:




Se você clicar no nome da função (marcado com uma figura oval vermelha na figura acima), poderá ver o conteúdo:




Os objetos de função podem ser configurados para 3 níveis de acesso; a máscara pode ser usada:




A edição de dados também é feita por meio da interface básica. Aqui, por exemplo, está um formulário de edição do usuário:




Apresentamos o menu específico de nossa aplicação e seus locais de trabalho em um arquivo compacto, pois são do mesmo tipo: um formulário de solicitação de 2 a 3 elementos e uma tabela com os resultados das solicitações.




A arquitetura acabou sendo muito simples: criamos muitas solicitações de dados (visualizações) e criamos um plug-in que implementa amostras de dados arbitrárias nas tabelas e campos incluídos nessas visualizações.


Por exemplo, temos um chamado relatório de descriptografia no formulário 110, que contém dados não agregados nos quais é construído. É assim que este relatório se parece:




Para verificar a correção do formulário, o usuário deve poder fazer seleções, classificar, filtrar, agrupar, transpor e criar seus próprios campos calculados. Nosso plugin é chamado pelo botão "Ações" na parte superior da tabela.




O plug-in repete a funcionalidade do construtor Integral query, mas além de buscar dados, ele pode desenhar gráficos e tabelas dinâmicas. Por exemplo, precisamos filtrar por Seção, adicionar um par de colunas calculadas e fazer uma seleção dos valores agrupados por elas. Definimos tudo isso:




Novas colunas são adicionadas à lista pelo link com o mesmo nome. Pelo botão "Cálculos", definimos as fórmulas para eles usando um construtor simples:




Definimos uma nova ordem das colunas e clicamos em "Aplicar" e nosso relatório é modificado conforme necessário - em vez de sete colunas básicas, vemos três, duas das quais acabamos de criar:




Como funciona sob o capô?

O plug-in se comunica com o serviço web do aplicativo via API, executou a seguinte solicitação:


 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 

E eu recebi esta resposta:


 { «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» ] } 

Se alguém estiver interessado em qual consulta SQL real foi executada no banco de dados do quinteto, então aqui está:


Ver SQL

O analista não vê esse SQL; ele usa o construtor de consultas descrito abaixo.


 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 

A solicitação pode parecer um pouco mais complicada do que o esperado, porque seleciona os dados atuais dos códigos de relatório com base em sua validade. No construtor de relatório Integral, essa consulta consiste em colunas de relatório, parâmetros e condições de junção de tabela, se necessário.




A própria integral é capaz de gerar condições de junção de tabela, uma vez que todas são determinadas por relacionamentos de quinteto, no entanto, no caso de versionamento, tivemos que combinar as tabelas manualmente e especificar explicitamente a condição para JOIN. Os códigos na cláusula ON são identificadores para objetos de coluna e consulta.


A consulta combina três tabelas, escolhendo entre elas os seguintes campos de dados (requisito "Colunas de consulta"):



(imagem em tamanho real)

As colunas do relatório, os campos calculados, os filtros etc. que compõem a consulta SQL estão listados aqui. O Construtor de Relatórios permite implementar praticamente qualquer design da linguagem SQL, incluindo junção de consultas e consultas aninhadas.



Além de seleções com agrupamentos, o usuário pode usar o mecanismo de tabelas dinâmicas. Adicionamos a popular ferramenta de tabela pivottable.js.org ao nosso plugin.
Selecione as colunas de seu interesse e alterne para o modo de tabela dinâmica:




Aqui, usando drag'n'drop, podemos analisar os dados recebidos pela amostra que configuramos, incluindo nossos campos arbitrários. Além disso, aqui você pode filtrar dados adicionalmente por qualquer campo.




Usamos o produto gratuito www.amcharts.com para desenhar gráficos. Com ele, como com o pivottable, tudo é bem simples: selecionamos o tipo de gráfico e inicializamos o componente com nossa matriz de dados obtida do Integral




Com isso, concluímos a tarefa na medida em que satisfaz os usuários do produto existente. Agora, temos um sistema que atende aos requisitos de substituição de importação: todos os produtos são gratuitos e substituíveis. Sim, percebemos longe de todas as possibilidades que o sistema existente oferece, mas apenas as necessárias para esse cliente. Mas nós apenas começamos!



Então voou ou não?


Também devemos discutir o desempenho do sistema resultante. Nesse problema, com uma quantidade de dados suficientemente grande (centenas de gigabytes), as amostras afetam pequenos fragmentos, cujos quintetos são sempre coletados usando índices. Isso leva ao fato de que, em qualquer tamanho do banco de dados, as consultas são processadas a uma velocidade aceitável, sem levar a uma degradação do desempenho semelhante a uma avalanche.


Registramos 20 ações do usuário em um script de teste e o executamos no serviço loadimpact.com . Foram 27 solicitações diferentes, porque algumas ações são executadas em 2 solicitações ao servidor (para criar uma exibição de página, por exemplo).


O script de teste ficou assim
 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)); }); } 


Aqui está o que vimos quando executamos o script - o gerador de carga enviou ao nosso aplicativo um número crescente de solicitações por segundo, obtivemos uma escada clássica para procurar o desempenho máximo / máximo:




Esse gerador envia solicitações em lotes, e não de maneira uniforme, como seria o caso de usuários simultâneos. Portanto, o gráfico mostra picos de carga, e o desempenho geral também parece pior nesse modo.


:




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


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


( 10 ) 0.1-0.3 , .


, .

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


All Articles