Gerando Dados Aleatórios com Paralelização no Shell: Guia Prático
Gerar dados aleatórios com paralelização no shell. Falando assim parece até alguma coisa muito importante ou difícil, mas vamos entender sua utilidade na prática. Vez ou outra preciso de uma forma rápida de fazer isso, geralmente quando estou ministrando um curso de MySQL ou MongoDB, ou prestando alguma consultoria.
Durante os cursos, pode haver a necessidade de gerar alguns dados aleatórios no shell, para demonstrar características de índices, compressão, fragmentação etc. Outras vezes, em consultoria, gero dados similares aos que o banco recebe para monitorar seu comportamento. Acontece que gerar os dados não é o problema, mas a velocidade em criá-los ou executar os testes sim, e é neste caso entra a paralelização.
Particularmente, prefiro fazer isso em Perl, mas o shell nos fornece alguns mecanismos interessantes e quero compartilhá-los aqui. Existem ferramentas, como o Sysbench, que permitem simular estes aspectos, mas por vezes queremos algo mais simples, ou não estamos trabalhando diretamente com um banco de dados.
Programar utilizando o shell ainda é, e provavelmente sempre será, um dos grandes diferenciais de um bom administrador de sistemas.
/dev/urandom
Tudo no Linux é um “arquivo”, e o nosso gerador de dados aleatórios não poderia ser diferente. Em /dev/urandom
podemos encontrar esse arquivo, que é uma interface para acessar o gerador de números aleatórios do kernel. Existe também um “irmão mais velho” em /dev/random,
mas é uma interface legada com uma entropia menor.
Sempre que acessamos os dados de /dev/urandom,
ele nos retornará bytes pseudoaleatórios, fornecidos por uma reserva de entropia, em inglês, entropy pool. Apesar de parecer que falamos de um processo aleatório, ele não é. Esse nome aparece porque o nível de “bagunça” (entropia) para gerá-lo é tão complexo e difícil de se reproduzir, que será improvável gerar uma nova sequência com os mesmos valores. A aleatoriedade é uma propriedade extremamente custosa, o que me faz pensar se o universo realmente possui eventos aleatórios…
Mas voltando ao assunto da postagem, o entropy pool é alimentado pelo ruído dos drivers do dispositivo e outros recursos. É daí que vem nossos dados aleatórios, e essa mistura toda garante que novos dados possam ser gerados o tempo todo, já que o entropy pool é reabastecido com certa frequência.
Gerar Dados Aleatórios
Para puxar dados do /dev/urandom
é preciso ter alguns cuidados:
- Utilizar binários que limitem a leitura do início, como por exemplo
head
; - Filtrar ou converter os bytes em caracteres.
O parâmetro -c
de head
controla a quantidade de bytes lida:
head -c10 /dev/urandom | base64 # oQ0rqJqHribc7g== head -c20 /dev/urandom | md5sum # b6964de932cd4efb6f989d44fcf8aa30 head -c40 /dev/urandom | sha256sum # 843cdbc6d898213e5baf2eb38accbc70c63003a989729efcab7109041a1d5da0
Utilizando o -d
no tr
podemos remover os caracteres especificados pelas classes [:alpha:]
ou [:digit:]
e com o parâmetro -c
nós passamos a utilizar o que foi excluído, não o que restou:
tr -dc [:alpha:] < /dev/urandom | head -c10 VRxbBMGCaS tr -dc [:digit:] < /dev/urandom | head -c10 4554571015
As vezes não temos o /dev/urandom
porque estamos em outro sistema utilizando, por exemplo, o Cygwin. Porém, por vezes, temos alguns comandos do Linux disponíveis como o bom e velho openssl. Neste caso o próprio openssl pode ser utilizado diretamente ou mais ou menos da forma como utilizamos o /dev/urandom
:
openssl rand -base64 10 # ktxtdP0kpvm8cA== openssl rand -hex 10 # 3b64934a9716cab18a2c openssl rand 200 | tr -dc [:alpha:] | head -c10 # wgaWFRhNcz
Agora que sabemos como gerar os dados, vamos criar um script para inserir estes dados em um banco, por exemplo, MySQL.
Shell Script Inicial
#!/bin/bash cat <<'EOF' > ~/.my.cnf [client] user = root password = !Abc123 host = 127.0.0.1 EOF mysql -e 'CREATE DATABASE IF NOT EXISTS pseudo'; mysql <<EOF CREATE TABLE IF NOT EXISTS pseudo.aleatorio ( id INT PRIMARY KEY AUTO_INCREMENT, numero INT, texto CHAR(10) ) EOF for X in {1..1000}; do TEXTO=$(tr -dc [:alpha:] < /dev/urandom | head -c 10) NUMERO=$(tr -dc [:digit:] < /dev/urandom | head -c 5) mysql pseudo -e "INSERT INTO aleatorio (numero, texto) VALUES ($NUMERO, '$TEXTO')" done
Este script funciona bem, mas demora bastante, veja:
time bash insert.sh # real 0m11.972s # user 0m6.908s # sys 0m6.435s
Os script levou quase 12 segundos para cadastrar 10 mil registros em um Ryzen 5, e isso é bastante tempo. Mas a culpa não é do script, ele executou o mais rápido que pôde, o problema é o script executar todos estes comandos em sequência, um após o outro.
Shell Script com Paralelização
O próximo script é ligeiramente mais complexo que o anterior. Ele controla a quantidade de processos paralelos pelo módulo da variável de controle $X
do nosso laço for
, além de utilizar um comando built-in wait
do shell. Não adicionamos muita coisa, e já temos um script capaz de gerar dados aleatórios com paralelização no shell:
#!/bin/bash cat <<'EOF' > ~/.my.cnf [client] user = root password = !Abc123 host = 127.0.0.1 EOF mysql -e 'CREATE DATABASE IF NOT EXISTS pseudo'; mysql <<EOF CREATE TABLE IF NOT EXISTS pseudo.aleatorio ( id INT PRIMARY KEY AUTO_INCREMENT, numero INT, texto CHAR(10) ) EOF function _insert { TEXTO=$(tr -dc [:alpha:] < /dev/urandom | head -c 10) NUMERO=$(tr -dc [:digit:] < /dev/urandom | head -c 5) mysql pseudo -e "INSERT INTO aleatorio (numero, texto) VALUES ($NUMERO, '$TEXTO')" } for X in {1..1000}; do _insert & if [ $(($X % 10)) == 0 ]; then wait fi done wait # força espera caso total de iterações não seja múltiplo de 10
O paralelismo foi conseguido através do operador de controle &
mas o truque está na função declarada como _insert
. Declarando uma função no shell, podemos agrupar quantos comandos forem necessários e chamar esta função executando-os todos em segundo plano. Mas para evitar que 10 mil processos sejam gerados, nós fazemos uma pequena verificação.
A verificação feita na estrutura de controle if
retorna 0 (zero) somente quando a variável $X
for um múltiplo de 10 (0, 10, 20, 30, 40…). Como iniciamos a variável de controle como 1, o script só executará wait
quando chegar a 10 processos rodando em segundo plano.
A chamada de wait
, um comando built-in do shell, faz com que o shell espere por processos identificados pelos IDs que fornecermos. No nosso caso não fornecemos ID nenhum, isso faz com que o shell espere por todos os processos filhos, ou seja, todos os nossos processos que estão rodando em segundo plano.
Vamos executá-lo!
Opa! Conseguimos diminuir o tempo pela metade:
time bash insert.sh # real 0m5.456s # user 0m7.631s # sys 0m42.539s
Conclusão
Sem nenhum pacote extra do Linux, apenas com o conhecimento que já temos, conseguimos gerar dados aleatórios com paralelização no shell sem muitas dificuldades.
É possível gerar scripts bastante poderosos, para simular muitas coisas, nas mais diversas aplicações. Ao invés de uma inserção no banco, podemos simular chamadas com curl
em uma api do Kubernetes. Esta forma é bastante rudimentar e exige bem mais da máquina do que uma linguagem de programação graças as constantes chamadas a binários, o que diminui um pouco nossa poder de fogo, mas é bastante útil nos momentos em que não temos essas opções à mão.
Líder em Treinamento e serviços de Consultoria, Suporte e Implantação para o mundo open source. Conheça nossas soluções:
About author
Você pode gostar também
Desbravando o OpenTofu: Parte 01 – Introdução e Fundamentos
Olá pessoal, hoje no blog, vamos falar de uma ferramenta em potencial de Infra as Code chamada OpenTofu. Bora lá Como surgiu o OpenTofu Em 10 de agosto de 2023,
Como versionar sua infraestrutura com Terraform e GitLab
Deixando um pouco o assunto sobre Corona Vírus de lado, chegamos ao penúltimo post da nossa série de postagens sobre Terraform. Aqui iremos falar sobre como versionar sua infraestrutura utilizando
Entenda o Open vSwitch e sua aplicação em nuvens públicas e privadas
Você que tem trabalhado com containers e VMs (máquinas virtuais) em nuvens públicas e privadas já deve ter ouvido falar do Open vSwitch (OVS). Ele é um switch virtual que