Laboratoire de performance NoSQL AWS avec Python

Dans la plupart des entreprises financières, le traitement des transactions en ligne (OLTP) repose souvent sur des données statiques ou peu mises à jour, également appelées données de référence. Les sources de données de référence n’ont pas toujours besoin de capacités de transaction ACID, mais ont plutôt besoin de soutien pour les requêtes de lecture rapides souvent basées sur des modèles d’accès aux données simples, et une architecture basée sur des événements pour garantir que les systèmes cibles restent à jour. Les bases de données NoSQL émergent comme des candidats idéaux pour répondre à ces exigences, et les plateformes cloud telles qu’AWS offrent des écosystèmes de données gérés et hautement résilients.

Dans cet article, je ne vais pas déterminer quelle base de données NoSQL AWS est meilleure : le concept d’une base de données meilleure n’existe que dans un contexte spécifiquement ciblé. Je vais partager un laboratoire de codage pour mesurer les performances des bases de données NoSQL gérées par AWS, telles que DynamoDB, Cassandra, Redis, et MongoDB.

Tests de Performance

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

La classe base/parent dans base_db.py met en œuvre la logique du cas de test consistant à exécuter 10 threads simultanés pour créer et lire 200 enregistrements.

Python

 

#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()

Chaque thread exécute la routine de lecture/écriture dans les create_records et read_records, respectivement. Notez que ces fonctions n’incluent aucune logique spécifique à la base de données, mais plutôt, mesurent la performance de chaque exécution de lecture et d’écriture.

Python

 

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

Une fois que le cas de test est exécuté, la fonction print_stats imprime les métriques d’exécution telles que la moyenne de lecture/écriture et la valeur de déviation standard (stdev), qui indiquent la performance et la cohérence de la base de données (une stdev plus petite implique une performance d’exécution plus cohérente).

Python

 

def print_stats(self):

  if len(self.performance_data) > 0:
    # Créer un DataFrame Pandas à partir des données de performance
    df = pd.DataFrame.from_dict(self.performance_data, orient='index')

    if not df.empty:
      df.sort_index(inplace=True)
      # Calculer la moyenne et la déviation standard pour chaque colonne
      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}")

Code NoSQL

Contrairement aux bases de données relationnelles qui prennent en charge SQL standard, chaque base de données NoSQL possède son propre SDK. Les classes de cas de test enfants pour chaque base de données NoSQL doivent simplement implémenter un constructeur et les fonctions create_record/read_recod contenant l’SDK de base de données propriétaire pour instancier une connexion à la base de données et créer/lire des enregistrements en quelques lignes de code.

Cas de Test DynamoDB

Python

 

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()

Configuration AWS

 Pour exécuter ces cas de test de performance dans un compte AWS, vous devriez suivre ces étapes:

  1. Créer un rôle IAM EC2 avec des privilèges pour accéder aux services de données AWS requis.
  2. Lancer une instance EC2 et attribuer le rôle IAM nouvellement créé.
  3. Créer chaque instance de base de données NoSQL.

 Rôle IAM

Table DynamoDB

Espace-clé Cassandra/Table

Veuillez noter que l’hôte et les informations d’identification de la base de données ont été hérités et supprimés dans les modules mongo_db.py et redis_db.py et devront être mis à jour avec les paramètres de connexion à la base de données correspondants pour votre compte AWS. Pour se connecter à DynamoDB et Cassandra, j’ai choisi d’utiliser les informations d’identification de session Boto3 temporairement attribuées au Rôle IAM db_performnace_iam_role. Ce code s’exécutera dans n’importe quel compte AWS de la région Est 1 sans aucune modification.

Python

 

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()

        # Configuration des espaces-clés 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)

Connectez-vous à l’instance EC2 (j’ai utilisé le Gestionnaire de session), et exécutez le script Shell suivant pour effectuer ces tâches:

  1. Installer Git.
  2. Installer Pythion3.
  3. Cloner le dépôt GitHub performance_db.
  4. Installer et activer l’environnement virtuel Python3.
  5. Installer les bibliothèques/dépendances tierces.
  6.  Exécuter chaque cas de test.
Shell

 

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

Vous devriez voir la sortie suivante pour les deux premiers cas de test:

(venv) sh-5.2$ sudo python3 -m dynamo_db

Données de performance:

Création Temps  Lecture Temps

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 lignes x 2 colonnes]

Moyenne du temps de création : 0.0858926808834076, écart-type : 0.07714510154026173

Moyenne du temps de lecture : 0.04880355834960937, écart-type : 0.028805479258627295

Temps d’exécution : 11.499964714050293

(venv) sh-5.2$ sudo python3 -m cassandra_db

Données de performance:

     Temps de création  Temps de lecture

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 lignes x 2 colonnes]

Moyenne du temps de création : 0,009145524501800537, écart-type : 0,005201661271831082

Moyenne du temps de lecture : 0,007248317003250122, écart-type : 0,003557610695674452

Temps d’exécution : 1,6279327869415283

Résultats des tests

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

Mes observations

  • 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 dispose d’un SDK très simple, est amusant à utiliser et offre le meilleur support pour le type de données JSON. Vous pouvez créer des index et exécuter des requêtes complexes sur des attributs JSON imbriqués. Avec l’émergence de nouveaux formats de données binaires, MongoDB pourrait perdre de son attrait.
  • Redis est incroyablement rapide, cependant, à la fin de la journée, c’est une cache clé/valeur même si elle prend en charge des types de données complexes. Redis propose des fonctionnalités puissantes telles que le pipelining et le scripting pour améliorer davantage les performances des requêtes en passant du code à Redis pour l’exécuter côté serveur.

Conclusion

En conclusion, choisir la base de données NoSQL gérée par AWS pour votre plateforme de données de référence d’entreprise dépend de vos priorités spécifiques. Si la performance et la réplication entre régions sont votre principale préoccupation, AWS Cassandra se démarque clairement comme le gagnant. DynamoDB s’intègre bien avec d’autres services AWS tels que Lambda et Kinesis et est donc une excellente option pour une architecture native AWS ou sans serveur. Pour les applications nécessitant un support robuste pour les types de données JSON, MongoDB prend le dessus. Cependant, si votre objectif est d’obtenir des recherches rapides ou de gérer des sessions pour une haute disponibilité, Redis s’avère être une excellente option. En fin de compte, la décision doit correspondre aux besoins uniques de votre organisation.

Comme toujours, vous pouvez trouver le code dans le dépôt GitHub mentionné plus tôt dans cet article (voir la tâche du script Shell #3 ci-dessus). N’hésitez pas à me contacter si vous avez besoin d’aide pour exécuter ce code ou pour la configuration AWS.

Source:
https://dzone.com/articles/aws-nosql-performance-lab-using-python