מעבדת ביצועים של AWS NoSQL באמצעות Python

ברוב החברות הפיננסיות, עיבוד עסקאות מקוון (OLTP) בדרך כלל מסתמך על נתונים סטטיים או שמעולם לא מתעדכנים, המכונים גם נתוני עזר. מקורות הנתונים האלה אינם תמיד דורשים יכולות עסקאות ACID, אלא צריכים תמיכה בשאילתות קריאה מהירות המבוססות לרוב על דפוסי גישה פשוטים לנתונים, וארכיטקטורת מובילה לגירוי כדי להבטיח שהמערכות המטרה יישארו מעודכנות. מסדי נתונים NoSQL מתגלים כמועמדים מושלמים לעמוד בדרישות אלה, ופלטפורמות ענן כמו AWS מציעות סביבות נתונים מנוהלות ובעלות עמידות גבוהה.

במאמר זה, אני לא הולך לקבוע איזה מסד נתונים NoSQL של AWS הוא טוב יותר: המושג של מסד נתונים טוב יותר קיים רק בהקשר מסוים. אני אשתף באופן מעשי קוד למדידת ביצועים של מסדי נתונים NoSQL מנוהלים של AWS כגון DynamoDB, Cassandra, Redis, ו-MongoDB.

בדיקת ביצועים

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

המחלקה base/parent בbase_db.py מיישמת את הלוגיקה של המקרה המבוקש של ביצוע 10 קריאות מקבוצה במקביל ליצירת וקריאת 200 רשומות.

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

כל קו ביצוע מבצע את השגרה לכתיבה/קריאה בcreate_records וread_records בהתאמה. שימו לב שהפונקציות אלה אינן כוללות שום היסודות ספציפיים לבסיס נתונים, אלא מודדות את הביצועים של כל ביצוע קריאה וכתיבה.

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

ברגע שהמקרה המבחן מבוצע, הפונקציה print_stats מדפיסה את המדדים של הביצועים כגון הממוצע של הקריאה/כתיבה ואת ערכי הסטיית תקן (stdev) שמעידים על ביצועים ועקביות של הקריאה/כתיבה בבסיס הנתונים (קטן stdev משמעו ביצועים עקביים יותר).

Python

 

def print_stats(self):

  if len(self.performance_data) > 0:
    # יצירת מסד נתונים פנדאס מנתוני הביצועים
    df = pd.DataFrame.from_dict(self.performance_data, orient='index')

    if not df.empty:
      df.sort_index(inplace=True)
      # חישוב ממוצע וסטיית תקן לכל עמודה
      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

בניגוד למסדי נתונים רפרנסיביים שתומכים ב-SQL הסטנדרטי, לכל מסד נתונים NoSQL יש את ה-SDK שלו. מחלקות המקרים המבחניים הבנויות לכל מסד נתונים NoSQL זקוקות רק ליישום מבנה ולפונקציות create_record/read_recod המכילות את קוד ה-SDK הפרוטטיבי של המסד הנתונים כדי לייצר חיבור למסד הנתונים וליצור/לקרוא רשומות בכמה שורות קוד.

מקרה מבחן 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()

הכנת AWS

 כדי לבצע את מקרי הביצועים אלה בחשבון AWS, עליכם לעקוב אחר השלבים הבאים:

  1. יצירת תפקיד IAM עבור EC2 עם הרשאות לגשת לשירותי נתוני AWS הנדרשים.
  2. פתיחת מופע EC2 והקצאת התפקיד החדש שנוצר.
  3. יצירת מופע מסד נתונים NoSQL בודד.

תפקיד IAM

שולחן DynamoDB

מרחב מפתח Cassandra / שולחן

אנא שימו לב שהתאריך והמredentials של הDB הוקודדו והוסרו במודולים mongo_db.py ו-redis_db.py ויהיה עליכם לעדכן אותם עם ההתקן המתאים לחיבור למסד הנתונים של חשבון AWS שלכם. כדי להתחבר ל-DynamoDB ו-Cassandra, בחרתי להשתמש במredentials של סשן Boto3 המוקצה זמנית לתפקיד IAM db_performnace_iam_role. קוד זה ירוץ בכל חשבון AWS באזור East 1 ללא שום שינוי.

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

        # הגדרת מרחבי מפתח 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)

התחברו לאינסטנס EC2 (השתמשתי במכשיר מנהל הסשן), והפעלו את התוכנית השפריצית הבאה כדי לבצע את המשימות הבאות:

  1. התקן את Git.
  2. התקן את Pythion3.
  3. העתק את מאגר GitHub performance_db.
  4. התקן והפעיל את הסביבה הווירטואלית של Python3.
  5. התקן קבצים שלישיים / תלויים.
  6. ביצוע כל מקרה של בדיקה.
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

אתם צריכים לראות את הפלט הבא עבור שני המקרים הבודקים הראשונים:

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

נתוני ביצוע:

זמן יצירה זמן קריאה

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 שורות x 2 עמודות]

ממוצע זמן יצירה: 0.0858926808834076, סטיית תקן: 0.07714510154026173

ממוצע זמן קריאה: 0.04880355834960937, סטיית תקן: 0.028805479258627295

זמן ביצוע: 11.499964714050293

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

נתוני ביצועים:

     זמן יצירה  זמן קריאה

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 שורות x 2 עמודות]

ממוצע זמן יצירה: 0.009145524501800537, סטיית תקן: 0.005201661271831082

ממוצע זמן קריאה: 0.007248317003250122, סטיית תקן: 0.003557610695674452

זמן ביצוע: 1.6279327869415283

תוצאות המבחן

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

תצפיותי

  • 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 מציע SDK פשוט מאוד, שימושי ובעל תמיכה הטובה ביותר בסוג נתונים JSON. אפשר ליצור אינדקסים ולבצע שאילתות מורכבות על תכונות JSON מקופלות. כשפורחים פורמטי נתונים בינאריים חדשים, יתכן שMongoDB יאבד חופש השימוש.
  • Redis מבצע במהירות מדהימה, אך בסופו של דבר, זה מטמרים מפתח/ערך גם אם זה תומך בסוגי נתונים מורכבים. Redis מציע תכונות חזקות כמו צינורות ותסריטים לשיפור ביצועי השאילתה על ידי שליחת קוד לRedis לביצוע בצד השרת.

מסקנה

לסיכום, בחירת מסד הנתונים הלא-SQL הניהולי על ידי AWS עבור ממשק המידע הארגוני שלך תלויה בעדיפויות הספציפיות שלך. אם ביצועים ושכפול בין אזורים הם הדאגה העיקרית שלך, AWS Cassandra בולט כמנצח ברור. DynamoDB משתלב היטב עם שירותי AWS אחרים כמו Lambda ו-Kinesis ולכן הוא אפשרות מצוינת עבור ארכיטקטורת מקורית של AWS או ללא שרת. עבור יישומים הדרושים תמיכה חזקה בסוגי נתונים JSON, MongoDB מקדים. אם המיקוד שלך הוא על חיפוש מהיר או ניהול של משחקים עבור נכונות גבוהה, Redis מוכיח כאפשרות מצוינת. סופו של דבר, ההחלטה צריכה להתאים לדרישות הייחודיות של הארגון שלך.

כרגיל, תוכלו למצוא את הקוד במאגר GitHub המקושר קודם לכן במאמר זה (ראה משימת תסריט השלד #3 לעיל). אל תהססו ליצור קשר אם אתם זקוקים לעזרה בהפעלת הקוד או בהקמה של AWS.

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