このトピックでは、ApsaraDB for MongoDBインスタンスのパフォーマンスに対するSSL暗号化の影響をテストする方法について説明します。
テスト環境
ECSインスタンスとApsaraDB for MongoDBインスタンスを作成します。 詳細については、「レプリカセットインスタンスの作成」および「コンソールでのECSインスタンスの作成と管理 (エクスプレスバージョン) 」をご参照ください。
次の表に、テストで使用されるECSインスタンスとApsaraDB for MongoDBインスタンスの設定を示します。
ApsaraDB for MongoDB インスタンス
インスタンスアーキテクチャ: レプリカセットインスタンス
プライマリノードとセカンダリノードの数: 3つのノード
読み取り専用ノード数: 1ノード
インスタンスの仕様: 1コアと2 GBのメモリ (汎用)
インスタンスバージョン: MongoDB 4.2
ECS インスタンス
インスタンスタイプ: ecs.e-c1m2.large (2コア、4 GBのメモリ)
テスト方法
テストデータのインポート
テストでは、Yahoo! Cloud Serving Benchmark (YCSB) は、ycsbという名前のデータベース内のusertableという名前のコレクションに1,000,000エントリをインポートするために使用されます。 次のコマンドを実行して、エントリをインポートします。
./bin/ycsb load mongodb -s -p workload=site.ycsb.workloads.CoreWorkload -p recordcount=1000000 -p mongodb.url="mongodb://root:******@dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717,dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717,dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717/admin?readPreference=secondary&replicaSet=mgset-82894753" -threads 8
YCSBによって生成されるコレクションの構造は次のとおりです。
{
"_id": "user1352498093671118016",
"field1": "BinData(0,\"L1s/KS8+MlYhISUuIz8qNVx9PTRuNiooNlolPVMzJCE0ODNuP15zLjB6NSU4LV0pPC98LkovOFttOyQkL1chNV8tOlxrKlstPkp3IzkiLTcuLU93ITRqOzEwPScsIEx5L1o5JQ==\")",
"field0": "BinData(0,\"I0I/NDAmNUkrLFA3PEA5IEI3KC4qKEJ1LCc6JV8rJlo7KUR3MDRqMk95KjhoIlUjLy1qMls9OFU7PTpuKTZsNlUpMVYvNSI4KDlwK1trIUpnMlInKSAmNDg8LSpmOl85PkozMA==\")",
"field7": "BinData(0,\"NzxyPjUkNiYsOEp1MUIpIDA0M1U/JE1nMjMmODhkIko/LEh1NSRqKl1jKDBqJjMqMiw8PSc6KjgsNEwrLFkpKFh9OzQ+MS86LiB4P1oxLVxjIiQgMCdsM0AhO0QzNTFqI1dpMw==\")",
"field6": "BinData(0,\"MVhpIj1uI0RlJ1AtP0t/LkAxMVp9MlU3KFJpK0Z3M0M/OE55IFBhPyA6Njp+L1tnJjh4Iic4K1F/Oi9gKz44PFFpPE5jNydqKUJrPyV4KDY2LDd0Nic6PC9wPDhgNykwOj10Iw==\")",
"field9": "BinData(0,\"OEA/NDRwLilwLzVoMz5oIzVuLStoPlF9NVU5MVd3PDUkPV0nPl07OU05LkYnKDQ8L0VrIkwlJDg+NCRuIitoNjV4PkM9NCt0LyE4PCFkKUI/LVwxKjdsMkgnICsgKFQrJit8Lg==\")",
"field8": "BinData(0,\"P0J/P0ozMCBwIjJwP0IpJUMlPkhhIkI3MU91OjUmMFM9NEBhJD8+OEdpJDh2OyxmMCQkKUgnJjYoKC0kKVMnKTQuODp6JjVwKSRwI1FhJy88PVAhPiE2OCMsKlYvJF1xOEUrJA==\")",
"field3": "BinData(0,\"Mjc6KE4zJTEuJCJ4P18lNCZ4NkAtKTAwLFUvMDEsOzUkIEs3MVEzKT4mL1Q5IVF9N0N3O1snIUZ1L0k9IUY7IV8nNiQ+KTJ0I097Llg1MjE0PCQ4KDV2KzYsIDtwNzhuKkQrMA==\")",
"field2": "BinData(0,\"Jit6L0olLTByJzpuPygyKSE8Li9iK00xIig4PVQhKTBqLkI/O0N9Izs+NVk/ISVmIy14Nko3NyxoI1Z1Pyo4JVt/MylsK1t9PS9yP1kzJFMjPF43P1l5P1xtPDAqLF57IiAsPQ==\")",
"field5": "BinData(0,\"NSd2NiskPzcmITU4Oz58NzliP0ZpOz5wKiI0OVhlNDx0PlV1OllnNzJiOyMmOF45IEg7OD90KUZnP0g5OUJzK0M/OyN4O15lPzA6LTUgNUw9NDVoL0QrOTssISQ2KCw6KUJlKg==\")",
"field4": "BinData(0,\"NDNkKjFuPyxsJzEmNzMqLVJzL0YpMTNwMjs8MTRiNEYtLE05P0I9NEU1LS4yNEFpNjUsOUs9JEs1IU5jOF5vIjpiNTMuKSY6K0s1LDp2JEp9KyFiIS1+J1U/LkoxMEp5OzJiIw==\")"
}
テストスクリプトの実行
YCSBは、SSL暗号化を有効にしたストレステストをサポートしていません。 したがって、このトピックでは、テストスクリプトをストレステストに使用します。 スクリプトには次の内容が含まれています。
import time
import pymongo
import datetime
import logging as log
from functools import wraps
log.basicConfig(
level=log.INFO, format='%(asctime)s - [line %(lineno)d] : %(levelname)s - %(message)s')
console = log.StreamHandler()
console.setLevel(log.INFO)
formatter = log.Formatter(
'%(asctime)s - [line %(lineno)d] : %(levelname)s - %(message)s')
console.setFormatter(formatter)
def time_recorder():
def inner(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
elapsed_time = (end_time - start_time) * 1000
print_latency = kwargs.get("print_latency", False)
return_latency = kwargs.get("return_latency", False)
if print_latency:
log.info(
f"Function {func.__name__} took {elapsed_time:.4f} millseconds to execute.")
if return_latency:
return result, elapsed_time
return result
return wrapper
return inner
@time_recorder()
def build_client_without_ssl(**kwargs):
client = pymongo.MongoClient(
"mongodb://root:******@dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717,dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717,dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717/admin?readPreference=secondary&replicaSet=mgset-82894753")
return client
@time_recorder()
def build_client_with_ssl(**kwargs):
client = pymongo.MongoClient(
"mongodb://root:******@dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717,dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717,dds-bp103175eafac****.mongodb.rds.aliyuncs.com:3717/admin?readPreference=secondary&replicaSet=mgset-82894753&tls=true&tlsAllowInvalidHostnames=true&tlsCAFile=/root/ApsaraDB-CA-Chain.pem")
return client
@time_recorder()
def find_one(client, **kwargs):
client["ycsb"]["usertable"].find_one()
def test_connection():
client = build_client_without_ssl(print_latency=True)
find_one(client)
client = build_client_with_ssl(print_latency=True)
find_one(client)
test_connection()
@time_recorder()
def test(with_ssl=False, repeat=5000, **kwargs):
log.info(f"test, with_ssl: {with_ssl}, repeat: {repeat}")
ssl_suffix = "without-ssl"
if with_ssl:
ssl_suffix = "with-ssl"
suffix = f"{repeat}-{ssl_suffix}.log"
only_connection_file = f"only-connection-{suffix}"
first_query_file = f"first-query-{suffix}"
connection_and_first_query_file = f"connection-and-first-query-{suffix}"
only_connection_f = open(only_connection_file, 'w')
first_query_f = open(first_query_file, 'w')
connection_and_first_query_f = open(connection_and_first_query_file, 'w')
for _ in range(repeat):
client = None
connection_latency = 0
if with_ssl:
client, connection_latency = build_client_with_ssl(
return_latency=True)
else:
client, connection_latency = build_client_without_ssl(
return_latency=True)
_, query_latency = find_one(client, return_latency=True)
only_connection_f.write(f"{connection_latency}\n")
first_query_f.write(f"{query_latency}\n")
connection_and_first_query_f.write(f"{connection_latency + query_latency}\n")
only_connection_f.close()
first_query_f.close()
connection_and_first_query_f.close()
log.info(f"test end, write to {only_connection_file}, {first_query_file} and {connection_and_first_query_file}")
test(False, 5000, print_latency=True)
test(True, 5000, print_latency=True)
@time_recorder()
def run(client, log_file, repeat=5000, **kwargs):
start = time.time()
log.info(f"start to query, repeat: {repeat}")
with open(log_file, 'w') as f:
for _ in range(repeat):
latency = find_one(client, return_latency=True)
f.write(f"{latency}\n")
end = time.time()
total = (end - start) * 1000
avg = total / repeat
log.info(
f"end query, repeat: {repeat}, total: {total} millseconds, avg: {avg} millseconds")
client = build_client_without_ssl()
run(client, "without-ssl.log")
client = build_client_with_ssl()
run(client, "with-ssl.log")
テスト中に、要件に基づいて次のパラメーターを変更できます。
client
:pymongo.MongoClient()
パラメーターの括弧内の接続文字列、ユーザー名、およびパスワードを、ApsaraDB for MongoDBインスタンスの実際の情報に置き換えます。repeat
: テストが実行された回数。 テストは5,000回繰り返し実行される。 要件に基づいてパラメーターを変更できます。
スクリプトは次のパラメータを出力します。
only-connection
: 接続の確立にかかった時間を記録します。first-query
: 接続が確立された後の最初のクエリの期間を記録します。connection-and-first-query
: 接続の確立から最初のクエリの完了までの合計時間を記録します。 パラメーター値は、唯一の接続値と最初のクエリ値の合計です。latency
: pymongo.MongoClient() で指定されたクライアントが再利用されるときの5,000クエリの平均期間を記録します。
テスト結果
以下の表は、5,000の試験の平均値を示す。
項目 | SSL暗号化が無効になっている消費時間 (ms) | SSL暗号化を有効にした消費時間 (ms) |
接続の確立にかかる時間 | 3.7009 | 19.8515 |
接続確立後の最初のクエリの期間 | 20.7456 | 43.5884 |
接続確立から最初のクエリの完了までの合計時間 | 24.4465 | 63.4399 |
指定されたクライアントが再利用される平均クエリ期間 | 0.7506 | 0.7643 |
テスト後、SSL暗号化を有効にした場合と無効にした場合の期間の違いは、主に接続の確立にあります。 既存の接続を再利用する場合、SSL暗号化が有効な場合と無効な場合の平均クエリ期間にわずかな差があります。 小さな期間の違いの理由は、主に次の2つの側面があります。
トランスポート層セキュリティ (TLS) の使用により、クライアントとサーバー間に接続が確立されると、追加のラウンドトリップ時間 (RTT) が存在します。
接続プールを設定すると、接続確立の追加のRTTオーバーヘッドが均等に分散されます。 SSL暗号化機能を有効にした後に指定されたクライアントを再利用すると、各接続確立中にTLS RTTを最小化できます。
接続が確立された後、暗号化および復号化アルゴリズムがメッセージ本体によって使用されるとき、追加のオーバーヘッドが生成される。
CPUは、メッセージ本体を迅速に暗号化および復号化することができ、他のシステム態様は、ディスクI/Oおよびキュー待機などのクエリにおいて長い時間を消費する可能性があり、全体的なパフォーマンスに対する暗号化および復号化の影響は比較的小さい。