使用Python进行AWS NoSQL性能实验室

在多数金融机构中,在线事务处理(OLTP)通常依赖于静态或不频繁更新的数据,这类数据也被称为参考数据。参考数据源并不总是需要ACID事务能力,而是需要支持基于简单数据访问模式的快速读取查询,以及事件驱动架构以确保目标系统保持最新状态。NoSQL数据库因其特性成为满足这些需求的理想选择,而AWS等云平台则提供了托管且高度可靠的数据生态系统。

本文中,我并不打算评断哪种AWS的NoSQL数据库更优,因为“更好的数据库”这一概念仅存在于特定目的的上下文中。我将分享一个编码实验室,用于评估AWS托管的NoSQL数据库,如DynamoDBCassandraRedisMongoDB的性能。

性能测试

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_db.py中的base/parent类实现了测试案例逻辑,即执行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_recordsread_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:
    # 从性能数据创建Pandas DataFrame
    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_record函数,这些函数包含专有数据库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. 创建一个具有访问所需AWS数据服务权限的EC2 IAM角色。
  2. 启动一个EC2实例并分配新创建的IAM角色。
  3. 创建每个NoSQL数据库实例。

IAM角色

DynamoDB表

Cassandra键空间/表

请注意,数据库主机和凭证在mongo_db.pyredis_db.py模块中被硬编码并移除,需要更新为您的AWS账户对应的数据库连接设置。为了连接DynamoDB和Cassandra,我选择暂时使用分配给db_performnace_iam_roleIAM角色的Boto3会话凭证。此代码将在任何AWS东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实例(我使用了会话管理器),并运行以下Shell脚本来执行这些任务:

  1. 安装Git。
  2. 安装Python3。
  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在服务器端执行,进一步提高查询性能。

结论

总之,为企业参考数据平台选择AWS管理的NoSQL数据库取决于您的具体优先级。如果性能和跨区域复制是您的首要关注点,AWS Cassandra无疑是明显的赢家。DynamoDB与Lambda和Kinesis等其他AWS服务集成良好,因此是AWS原生或无服务器架构的绝佳选择。对于需要对JSON数据类型提供强大支持的应用程序,MongoDB领先。然而,如果您的重点是快速查找或高可用性的会话管理,Redis证明是一个极好的选项。最终,决策应符合您组织的独特需求。

一如既往,您可以在本文前面提到的GitHub仓库中找到代码(参见上文的Shell脚本任务#3)。如果您在运行此代码或配置AWS方面需要帮助,请随时联系我。

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