Em muitas empresas financeiras, o processamento de transações online (OLTP) geralmente depende de dados estáticos ou pouco atualizados, também chamados de dados de referência. As fontes de dados de referência nem sempre exigem capacidades de transação ACID, mas precisam de suporte para consultas de leitura rápidas, geralmente baseadas em padrões simples de acesso a dados, e arquitetura orientada a eventos para garantir que os sistemas alvo permaneçam atualizados. Bancos de dados NoSQL surgem como candidatos ideais para atender a essas necessidades, e plataformas em nuvem como o AWS oferecem ecossistemas de dados gerenciados e altamente resilientes.
Neste artigo, não vou determinar qual banco de dados NoSQL do AWS é melhor: o conceito de um banco de dados melhor existe apenas dentro de um contexto específico e intencional. Vou compartilhar um laboratório de codificação para medir o desempenho dos bancos de dados NoSQL gerenciados pelo AWS, como DynamoDB, Cassandra, Redis e MongoDB.
Teste de Desempenho
I will start by defining the performance test case, which will concurrently insert a JSON payload 200 times and then read it 200 times.
Payload JSON
A classe base
/parent
em base_db.py implementa a lógica do caso de teste de executar 10 threads concorrentes para criar e ler 200 registros.
#imports
.....
class BaseDB:
def __init__(self, file_name='instrument.json', threads=10, records=20):
...................................
def execute(self):
create_threads = []
for i in range(self.num_threads):
thread = threading.Thread(
target=self.create_records, args=(i,))
create_threads.append(thread)
thread.start()
for thread in create_threads:
thread.join()
read_threads = []
for i in range(self.num_threads):
thread = threading.Thread(target=self.read_records, args=(i,))
read_threads.append(thread)
thread.start()
for thread in read_threads:
thread.join()
self.print_stats()
Cada thread executa a rotina de escrita/leitura no create_records
e read_records
, respectivamente. Observe que essas funções não incluem nenhuma lógica específica de banco de dados, mas sim, medem o desempenho de cada execução de leitura e escrita.
def create_records(self, thread_id):
for i in range(1, self.num_records + 1):
key = int(thread_id * 100 + i)
start_time = time.time()
self.create_record(key)
end_time = time.time()
execution_time = end_time - start_time
self.performance_data[key] = {'Create Time': execution_time}
def read_records(self, thread_id):
for key in self.performance_data.keys():
start_time = time.time()
self.read_record(key)
end_time = time.time()
execution_time = end_time - start_time
self.performance_data[key]['Read Time'] = execution_time
Uma vez que o caso de teste é executado, a função print_stats
imprime as métricas de execução, como a média de leitura/escrita e os valores de desvio padrão (stdev
), que indicam o desempenho e a consistência do banco de dados (um stdev
menor implica um desempenho de execução mais consistente).
def print_stats(self):
if len(self.performance_data) > 0:
# Criar um DataFrame do Pandas a partir dos dados de desempenho
df = pd.DataFrame.from_dict(self.performance_data, orient='index')
if not df.empty:
df.sort_index(inplace=True)
# Calcular a média e o desvio padrão para cada coluna
create_mean = statistics.mean(df['Create Time'])
read_mean = statistics.mean(df['Read Time'])
create_stdev = statistics.stdev(df['Create Time'])
read_stdev = statistics.stdev(df['Read Time'])
print("Performance Data:")
print(df)
print(f"Create Time mean: {create_mean}, stdev: {create_stdev}")
print(f"Read Time mean: {read_mean}, stdev: {read_stdev}")
Código NoSQL
Ao contrário dos bancos de dados relacionais que suportam SQL padrão, cada banco de dados NoSQL possui seu próprio SDK. As classes de casos de teste filho para cada banco de dados NoSQL precisam apenas implementar um construtor e as funções create_record/read_record
que contêm o SDK de banco de dados proprietário para instanciar uma conexão de banco de dados e para criar/ler registros em algumas linhas de código.
Caso de Teste DynamoDB
import boto3
from base_db import BaseDB
class DynamoDB (BaseDB):
def __init__(self, file_name='instrument.json', threads=10, records=20):
super().__init__(file_name, threads, records)
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table_name = 'Instruments'
self.table = dynamodb.Table(table_name)
def create_record(self, key):
item = {
'key': key,
'data': self.json_data
}
self.table.put_item(Item=item)
def read_record(self, key):
self.table.get_item(Key={'key': key})
if __name__ == "__main__":
DynamoDB().execute()
Configuração AWS
Para executar esses casos de teste de desempenho em uma conta AWS, você deve seguir estes passos:
- Criar um papel IAM de EC2 com privilégios para acessar os serviços de dados AWS necessários.
- Iniciar uma instância EC2 e atribuir o novo papel IAM criado.
- Criar cada instância de banco de dados NoSQL.
Função IAM
Tabela DynamoDB
Espaço/Tabela Cassandra
Por favor, note que o host e as credenciais do banco de dados foram codificados e removidos nos módulos mongo_db.py e redis_db.py e precisarão ser atualizados com as configurações de conexão do banco de dados correspondentes à sua conta AWS. Para conectar-se ao DynamoDB e Cassandra, optei por usar as credenciais da sessão do Boto3 temporariamente atribuídas à db_performnace_iam_role
Função IAM. Este código será executado em qualquer conta AWS na região Leste 1 sem qualquer modificação.
class CassandraDB(BaseDB):
def __init__(self, file_name='instrument.json', threads=10, records=20):
super().__init__(file_name=file_name, threads=threads, records=records)
self.json_data = json.dumps(
self.json_data, cls=DecimalEncoder).encode()
# Configuração de Espaços Cassandra
contact_points = ['cassandra.us-east-1.amazonaws.com']
keyspace_name = 'db_performance'
ssl_context = SSLContext(PROTOCOL_TLSv1_2)
ssl_context.load_verify_locations('sf-class2-root.crt')
ssl_context.verify_mode = CERT_REQUIRED
boto_session = boto3.Session(region_name="us-east-1")
auth_provider = SigV4AuthProvider(session=boto_session)
cluster = Cluster(contact_points, ssl_context=ssl_context, auth_provider=auth_provider,
port=9142)
self.session = cluster.connect(keyspace=keyspace_name)
Conecte-se à instância EC2 (usei o Gerenciador de Sessões), e execute o seguinte script Shell para realizar essas tarefas:
- Instale o Git.
- Instale o Pythion3.
- Clone o repositório GitHub performance_db.
- Instale e ative o ambiente virtual Python3.
- Instale bibliotecas/dependências de terceiros.
- Execute cada caso de teste.
sudo yum install git
sudo yum install python3
git clone https://github.com/dshilman/db_performance.git
sudo git pull
cd db_performance
python3 -m venv venv
source ./venv/bin/activate
sudo python3 -m pip install -r requirements.txt
cd code
sudo python3 -m dynamo_db
sudo python3 -m cassandra_db
sudo python3 -m redis_db
sudo python3 -m mongo_db
Você deve ver a seguinte saída para os dois primeiros casos de teste:
(venv) sh-5.2$ sudo python3 -m dynamo_db Dados de Desempenho: Create Time Read Time 1 0.336909 0.031491 2 0.056884 0.053334 3 0.085881 0.031385 4 0.084940 0.050059 5 0.169012 0.050044 .. … … 916 0.047431 0.041877 917 0.043795 0.024649 918 0.075325 0.035251 919 0.101007 0.068767 920 0.103432 0.037742
[200 linhas x 2 colunas] Média do Create Time: 0.0858926808834076, desvio padrão: 0.07714510154026173 Média do Read Time: 0.04880355834960937, desvio padrão: 0.028805479258627295 Tempo de execução: 11.499964714050293 |
(venv) sh-5.2$ sudo python3 -m cassandra_db Dados de Desempenho: Create Time Read Time 1 0.024815 0.005986 2 0.008256 0.006927 3 0.008996 0.009810 4 0.005362 0.005892 5 0.010117 0.010308 .. … … 916 0.006234 0.008147 917 0.011564 0.004347 918 0.007857 0.008329 919 0.007260 0.007370 920 0.004654 0.006049
[200 linhas x 2 colunas] Média do Tempo de Criação: 0.009145524501800537, desvio padrão: 0.005201661271831082 Média do Tempo de Leitura: 0.007248317003250122, desvio padrão: 0.003557610695674452 Tempo de Execução: 1.6279327869415283 |
Resultados dos Testes
DynamoDB | Cassandra | MongoDB | Redis | |
---|---|---|---|---|
Create | mean: 0.0859 stdev: 0.0771 |
mean: 0.0091 stdev: 0.0052 |
mean: 0.0292 std: 0.0764 |
mean: 0.0028 stdev: 0.0049 |
Read | mean: 0.0488 stdev: 0.0288 |
mean: 0.0072 stdev: 0.0036 |
mean: 0.0509 std: 0.0027 |
mean: 0.0012 stdev: 0.0016 |
Exec Time | 11.45 sec | 1.6279 sec | 10.2608 sec | 0.3465 sec |
Minhas Observações
- I was blown away by Cassandra’s fast performance. Cassandra support for SQL allows rich access pattern queries and AWS Keyspaces offer cross-region replication.
- I find DynamoDB’s performance disappointing despite the AWS hype about it. You should try to avoid the cross-partition table scan and thus must use an index for each data access pattern. DynamoDB global tables enable cross-region data replication.
- MongoDB possui um SDK muito simples, é divertido de usar e tem o melhor suporte para o tipo de dados JSON. Você pode criar índices e executar consultas complexas em atributos JSON aninhados. Com o surgimento de novos formatos de dados binários, o MongoDB pode perder seu apelo.
- Redis tem um desempenho incrivelmente rápido, no entanto, no final do dia, é uma cache de chave/valor, mesmo que suporte tipos de dados complexos. O Redis oferece recursos poderosos, como pipelining e scripting, para melhorar ainda mais o desempenho das consultas, passando código para o Redis executar no lado do servidor.
Conclusão
Em conclusão, escolher o banco de dados NoSQL gerenciado pela AWS para sua plataforma de dados de referência empresarial depende das suas prioridades específicas. Se o desempenho e a replicação entre regiões são sua principal preocupação, a AWS Cassandra se destaca como uma vencedora clara. O DynamoDB se integra bem a outros serviços da AWS, como Lambda e Kinesis, e, portanto, é uma ótima opção para arquitetura nativa da AWS ou sem servidor. Para aplicativos que exigem suporte robusto para tipos de dados JSON, o MongoDB assume a liderança. No entanto, se seu foco é em rastreamento rápido ou gerenciamento de sessão para alta disponibilidade, o Redis prova ser uma excelente opção. Finalmente, a decisão deve estar alinhada com as necessidades únicas de sua organização.
Como sempre, você pode encontrar o código no repositório do GitHub vinculado anteriormente neste artigo (veja a tarefa do script Shell #3 acima). Sinta-se à vontade para me contatar se precisar de ajuda para executar este código ou com a configuração do AWS.
Source:
https://dzone.com/articles/aws-nosql-performance-lab-using-python