AWS NoSQL-Leistungslabor mit Python

In den meisten Finanzunternehmen basiert die Online-Transaktionsverarbeitung (OLTP) oft auf statischen oder selten aktualisierten Daten, auch als Referenzdaten bezeichnet. Quellen für Referenzdaten benötigen nicht immer ACID-Transaktionsfähigkeiten, sondern unterstützen eher schnelle Leseabfragen, die oft auf einfachen Datenzugriffsmustern basieren, und ereignisgesteuerte Architektur, um sicherzustellen, dass die Zielsysteme auf dem neuesten Stand bleiben. NoSQL-Datenbanken stellen sich als ideale Kandidaten zur Erfüllung dieser Anforderungen heraus, und Cloud-Plattformen wie AWS bieten verwaltete und hochresiliente Datenökosysteme.

In diesem Artikel werde ich nicht entscheiden, welche AWS NoSQL-Datenbank besser ist: Das Konzept einer besseren Datenbank existiert nur im Kontext einer bestimmten, zweckdienlichen Situation. Ich werde einen Codierungs-Workshop teilen, um die Leistung von AWS-verwalteten NoSQL-Datenbanken wie DynamoDB, Cassandra, Redis und MongoDB zu messen.

Leistungstest

I will start by defining the performance test case, which will concurrently insert a JSON payload 200 times and then read it 200 times.

JSON-Nutzlast

Die base/parent-Klasse in base_db.py implementiert die Testfalllogik zur Ausführung von 10 gleichzeitigen Threads zum Erstellen und Lesen von 200 Datensätzen.

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

Jeder Thread führt die Schreib-/Leseroutine in den create_records und read_records Funktionen aus, jeweils. Beachten Sie, dass diese Funktionen keine datenbankspezifische Logik enthalten, sondern vielmehr die Leistung jedes Schreib- und Leseausführungszyklus messen.

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

Sobald der Testfall ausgeführt wird, druckt die print_stats Funktion Ausführungsmetriken wie den Lese-/Schreib-Mittelwert und die Standardabweichung (stdev)-Werte, die die Datenbank-Lese-/Schreibleistung und Konsistenz anzeigen (eine kleinere stdev impliziert eine konsistentere Ausführungsleistung).

Python

 

def print_stats(self):

  if len(self.performance_data) > 0:
    # Erstellen eines Pandas DataFrame aus Leistungsdaten
    df = pd.DataFrame.from_dict(self.performance_data, orient='index')

    if not df.empty:
      df.sort_index(inplace=True)
      # Berechnen des Mittelwerts und der Standardabweichung für jede Spalte
      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}")

NoSQL-Code

Im Gegensatz zu relationalen Datenbanken, die standardmäßiges SQL unterstützen, verfügt jede NoSQL-Datenbank über ihre eigene SDK. Die Untertestfallklassen für jede NoSQL-Datenbank müssen nur einen Konstruktor und create_record/read_recod Funktionen implementieren, die die proprietäre Datenbank-SDK enthalten, um eine Datenbankverbindung herzustellen und um in wenigen Codezeilen Datensätze zu erstellen/lesen.

DynamoDB Test Case

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

AWS-Setup

 Um diese Leistungstestfälle in einem AWS-Konto auszuführen, sollten Sie diese Schritte befolgen:

  1. Erstellen Sie eine EC2-IAM-Rolle mit Berechtigungen zum Zugreifen auf die erforderlichen AWS-Datendienste.
  2. Starten Sie eine EC2-Instanz und weisen Sie die neu erstellte IAM-Rolle zu.
  3. Erstellen Sie jede NoSQL-Datenbankinstanz.

 IAM-Rolle

DynamoDB-Tabelle

Cassandra Keyspace/Tabelle

Bitte beachten Sie, dass die Datenbank-Host und -Zugangsdaten in den Modulen mongo_db.py und redis_db.py hartcodiert und entfernt wurden und mit den entsprechenden Datenbankverbindungseinstellungen für Ihr AWS-Konto aktualisiert werden müssen. Um eine Verbindung zu DynamoDB und Cassandra herzustellen, habe ich mich für die vorübergehend zugewiesenen Boto3-Sitzungscredentials für die db_performnace_iam_role IAM-Rolle entschieden. Dieser Code läuft in jeder AWS-Region Ost 1 ohne Änderungen.

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

        # Konfiguration der Cassandra Keyspaces
        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)

Melden Sie sich beim EC2-Instanz (ich habe den Session Manager verwendet) und führen Sie das folgende Shell-Skript aus, um diese Aufgaben durchzuführen:

  1. Git installieren.
  2. Pythion3 installieren.
  3. Klonen Sie das GitHub performance_db Repository.
  4. Installieren und aktivieren Sie die Python3 virtuelle Umgebung.
  5. Drittanbieterbibliotheken/Abhängigkeiten installieren.
  6.  Führen Sie jede Testfall aus.
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

Sie sollten die folgende Ausgabe für die ersten beiden Testfälle sehen:

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

Leistungsdaten:

Erstellungszeit Lesezeit

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 Zeilen x 2 Spalten]

Durchschnittliche Erstellungszeit: 0.0858926808834076, Standardabweichung: 0.07714510154026173

Durchschnittliche Lesezeit: 0.04880355834960937, Standardabweichung: 0.028805479258627295

Ausführungszeit: 11.499964714050293

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

Leistungsdaten:

     Erstellungszeit Lesezeit

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 Zeilen x 2 Spalten]

Durchschnittliche Erstellungszeit: 0,009145524501800537, Standardabweichung: 0,005201661271831082

Durchschnittliche Lesezeit: 0,007248317003250122, Standardabweichung: 0,003557610695674452

Ausführungszeit: 1,6279327869415283

Testresultate

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

Meine Beobachtungen

  • 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 verfügt über eine sehr einfache SDK, ist eine Freude zu bedienen und bietet die beste Unterstützung für den JSON-Datentyp. Man kann Indizes erstellen und komplexe Abfragen für verschachtelte JSON-Attribute ausführen. Mit dem Aufkommen neuer binärer Datenformate könnte MongoDB jedoch an Reiz verlieren.
  • Redis ist erstaunlich schnell, allerdings handelt es sich trotz Unterstützung komplexer Datentypen letztendlich um einen Schlüssel/Wert-Cache. Redis bietet leistungsstarke Funktionen wie Pipelining und Skripting, um die Abfrageleistung durch Übermittlung von Code zu Redis zur Ausführung auf Serverseite weiter zu verbessern.

Schlussfolgerung

Zusammenfassend hängt die Auswahl der AWS-verwalteten NoSQL-Datenbank für Ihre Unternehmensreferenzdatenplattform von Ihren spezifischen Prioritäten ab. Sollten Leistung und Replikation über Regionen Ihr Hauptanliegen sein, zeichnet sich AWS Cassandra als klares Sieger ab. DynamoDB integriert sich gut mit anderen AWS-Diensten wie Lambda und Kinesis und ist daher eine großartige Option für AWS-native oder serverlose Architekturen. Für Anwendungen, die eine robuste Unterstützung für JSON-Datentypen erfordern, führt MongoDB. Wenn Ihr Fokus jedoch auf schneller Suche oder Sitzungsverwaltung für hohe Verfügbarkeit liegt, erweist sich Redis als ausgezeichnete Option. Letztendlich sollte die Entscheidung sich mit den einzigartigen Anforderungen Ihres Unternehmens vereinbaren.

Wie immer findest du den Code im GitHub-Repository, das weiter oben in diesem Artikel verlinkt wurde (siehe Shell-Skriptaufgabe #3 oben). Zögere nicht, mich zu kontaktieren, wenn du Hilfe bei der Ausführung des Codes oder bei der AWS-Einrichtung benötigst.

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