AWS NoSQL Prestatielab met Python

Bij de meeste financiële bedrijven steunt online transactieverwerking (OLTP) zich vaak op statische of zelden bijgewerkte gegevens, ook wel referentiegegevens genoemd. Bronnen van referentiegegevens hebben niet altijd ACID-transactiemogelijkheden nodig, maar hebben eerder ondersteuning voor snelle leesquery’s vaak gebaseerd op eenvoudige gegevenstoegangspatronen, en een gebeurtenisgestuurde architectuur om ervoor te zorgen dat de doelsystemen up-to-date blijven. NoSQL-databases komen als uitstekende kandidaten naar voren om aan deze eisen te voldoen, en cloudplatforms zoals AWS bieden beheerde en zeer robuuste data-ecosystemen.

In dit artikel ga ik niet bepalen welke AWS NoSQL-database beter is: het concept van een betere database bestaat alleen binnen een specifiek doelgericht kader. Ik zal een codelaboraat delen om de prestaties van door AWS beheerde NoSQL-databases zoals DynamoDB, Cassandra, Redis, en MongoDB te meten.

Prestatietesten

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

De base/parent klasse in base_db.py implementeert de testcase-logica van het uitvoeren van 10 gelijktijdige threads om 200 records aan te maken en te lezen.

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

Elk thread voert de write/read routine uit in de create_records en read_records, respectievelijk. Merk op dat deze functies geen database-specifieke logica bevatten, maar eerder, de prestaties van elke read-and-write uitvoering meten.

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

Zodra de testcase wordt uitgevoerd, drukt de print_stats functie de uitvoeringsmetrieken af, zoals de read/write gemiddelde en de standaarddeviatie (stdev) waarden, die de database read/write prestaties en consistentie aangeven (kleinere stdev impliceert meer consistente uitvoeringsprestaties).

Python

 

def print_stats(self):

  if len(self.performance_data) > 0:
    # Maak een Pandas DataFrame van prestatiegegevens
    df = pd.DataFrame.from_dict(self.performance_data, orient='index')

    if not df.empty:
      df.sort_index(inplace=True)
      # Bereken gemiddelde en standaarddeviatie voor elke kolom
      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

In tegenstelling tot relationele databases die standaard SQL ondersteunen, heeft elke NoSQL-database zijn eigen SDK. De kind-testcaseklassen voor elke NoSQL-database hoeven alleen een constructor en create_record/read_recod functies te implementeren die de propriëtaire database SDK bevatten om een databaseverbinding te instantiëren en om records te creëren/lezen in een paar regels code.

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

 Om deze prestatietestcases uit te voeren in een AWS-account, moet u de volgende stappen volgen:

  1. Maak een EC2 IAM-rol met bevoegdheden om de vereiste AWS-gegevensdiensten te benaderen.
  2. Start een EC2-instantie en wijs de zojuist gemaakte IAM-rol toe.
  3. Creëer elke NoSQL-database-instantie.

 IAM Rol

DynamoDB Tabel

Cassandra Keyspace/Tabel

Let op dat de DB host en referenties hardcoded waren en verwijderd zijn in de mongo_db.py en redis_db.py modules en zullen moeten worden bijgewerkt met de corresponderende databaseverbinding instellingen voor uw AWS-account. Om verbinding te maken met DynamoDB en Cassandra, koos ik ervoor om de tijdelijk toegewezen Boto3-sessiereferenties te gebruiken voor de db_performnace_iam_role IAM Rol. Deze code zal in elke AWS-account in de regio East 1 zonder enige aanpassing kunnen worden uitgevoerd.

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

        # Configuratie van 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)

Verbind met de EC2-instantie (ik gebruikte de Session Manager) en voer de volgende Shell-script uit om deze taken uit te voeren:

  1. Installeer Git.
  2. Installeer Pythion3.
  3. Kloon de GitHub performance_db repository.
  4. Installeer en activeer de Python3 virtuele omgeving.
  5. Installeer externe bibliotheken/afhankelijkheden.
  6.  Voer elke testcase uit.
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

U zou het volgende resultaat voor de eerste twee testcases moeten zien:

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

Prestatiegegevens:

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 rijen x 2 kolommen]

Gemiddelde Create Time: 0.0858926808834076, standaarddeviatie: 0.07714510154026173

Gemiddelde Read Time: 0.04880355834960937, standaarddeviatie: 0.028805479258627295

Uitvoeringstijd: 11.499964714050293

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

Prestatiegegevens:

     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 rijen x 2 kolommen]

Gemiddelde Create Time: 0.009145524501800537, standaarddeviatie: 0.005201661271831082

Gemiddelde Read Time: 0.007248317003250122, standaarddeviatie: 0.003557610695674452

Uitvoeringstijd: 1.6279327869415283

Testresultaten

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

Mijn observaties

  • 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 heeft een zeer eenvoudige SDK, is leuk om te gebruiken en heeft het beste ondersteuning voor de JSON-gegevenstype. U kunt indexen maken en complexe query’s uitvoeren op geneste JSON-attributen. Als nieuwe binaire gegevensindelingen opkomen, kan MongoDB zijn aantrekkingskracht verliezen.
  • Redis prestaties zijn verbazingwekkend snel, echter, aan het eind van de dag is het een sleutel/waardecache, zelfs als het complexe gegevenstypen ondersteunt. Redis biedt krachtige functies zoals pipelining en scripting om de queryprestaties verder te verbeteren door code naar Redis te sturen om op de serverzijde uit te voeren.

Conclusie

Concluderend, het kiezen van de AWS-beheerde NoSQL-database voor uw bedrijfsreferentiegegevensplatform hangt af van uw specifieke prioriteiten. Als prestaties en cross-regio replicatie uw belangrijkste zorg zijn, staat AWS Cassandra als duidelijke winnaar uit de bus. DynamoDB integreert goed met andere AWS-services zoals Lambda en Kinesis en is daarom een geweldige optie voor AWS-native of serverloze architectuur. Voor toepassingen die een uitgebreid ondersteuning vereisen voor JSON-gegevenstypen, neemt MongoDB de leiding. Echter, als uw focus ligt op snelle opzoeking of sessiebeheer voor hoge beschikbaarheid, blijkt Redis een uitstekende optie te zijn. Uiteindelijk moet de beslissing aansluiten bij de unieke eisen van uw organisatie.

Zoals altijd, kun je de code vinden in de GitHub-repo die eerder in dit artikel is gelinkt (zie Shell script taak #3 hierboven). Aarzel niet om contact met mij op te nemen als je hulp nodig hebt bij het uitvoeren van deze code of met de AWS-setup.

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