Couchbaseベンチマークは、非常に遅いINSERTとGETを明らかにします(KeyValue操作を使用)。永続化されたMySQLデータよりも遅い
Couchbase(Winで実行)をRedisおよびMySqlと比較するための小さなベンチマークテストを行いました(編集:テストにAerospikeを追加)
100 000JSON「ドキュメント」を3つのdb / storeに挿入しています。
- Redis(挿入するだけで他には何もありません)
- Couchbase(メモリ内のエフェメラルバケット、JobIdのJSONインデックス)
- MySql(単純なテーブル; Id(int)、Data(MediumText)、Idのインデックス)
- Aerospike(インメモリストレージ)
JSONファイルは67行、約1800バイトです。
インサート:
- Couchbase:60-100秒(編集:かなり異なるようです!)
- MySql:30秒
- Redis:8秒
- エアロスパイク:71秒
読む:私たちは1000回読んでおり、これを10回行い、平均を調べます。
- Couchbase:1000 GETで600〜700ミリ秒(クエリAPIではなくKeyValue操作を使用。クエリAPIを使用すると、約1500ミリ秒かかります)
- MySql:1000 GETの場合90〜100ミリ秒
- Redis:1000 GETで50〜60ミリ秒
- Aerospike:1000GETで750ミリ秒
結論: Couchbaseは最も遅いようです(INSERT時間は大きく異なります)、Aerospikeも非常に遅いです。これらは両方ともインメモリストレージを使用しています(Couchbase =>エフェメラルバケット、Aerospike =>ストレージエンジンメモリ)。
質問:Couchbaseでのメモリ内の書き込みと読み取りが非常に遅く、通常のMySQL(SSD上)を使用するよりもさらに遅いのはなぜですか?
コード
注:Task.WhenAllを使用したり、各呼び出しを待機したりしても、違いはありません。
インサート
Couchbase:
IBucket bucket = await cluster.BucketAsync("halo"); // <-- ephemeral
IScope scope = bucket.Scope("myScope");
var collection = scope.Collection("myCollection");
// EDIT: Added this to avoid measuring lazy loading:
JObject t = JObject.FromObject(_baseJsonObject);
t["JobId"] = 0;
t["CustomerName"] = $"{firstnames[rand.Next(0, firstnames.Count - 1)]} {lastnames[rand.Next(0, lastnames.Count - 1)]}"; await collection.InsertAsync("0", t); await collection.RemoveAsync("0"); List<Task> inserTasks = new List<Task>(); sw.Start(); foreach (JObject temp in jsonObjects) // jsonObjects is pre-created so its not a factor in the test { inserTasks.Add(collection.InsertAsync(temp.GetValue("JobId").ToString(), temp)); } await Task.WhenAll(inserTasks); sw.Stop(); Console.WriteLine($"Adding {nbr} to Couchbase took {sw.ElapsedMilliseconds} ms");
Redis(ServiceStackを使用!)
sw.Restart();
using (var client = redisManager.GetClient())
{
foreach (JObject temp in jsonObjects)
{
client.Set($"jobId:{temp.GetValue("JobId")}", temp.ToString()); } } sw.Stop(); Console.WriteLine($"Adding {nbr} to Redis took {sw.ElapsedMilliseconds} ms");
sw.Reset();
Mysql:
MySql.Data.MySqlClient.MySqlConnection mySqlConnection = new MySql.Data.MySqlClient.MySqlConnection("Server=localhost;Database=test;port=3306;User Id=root;password=root;");
mySqlConnection.Open();
sw.Restart();
foreach (JObject temp in jsonObjects)
{
MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand($"INSERT INTO test (id, data) VALUES ('{temp.GetValue("JobId")}', @data)", mySqlConnection); cmd.Parameters.AddWithValue("@data", temp.ToString()); cmd.ExecuteNonQuery(); } sw.Stop(); Console.WriteLine($"Adding {nbr} to MySql took {sw.ElapsedMilliseconds} ms");
sw.Reset();
読んだ
Couchbase:
IBucket bucket = await cluster.BucketAsync("halo");
IScope scope = bucket.Scope("myScope");
var collection = scope.Collection("myCollection");
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++)
{
string key = $"{r.Next(1, 100000)}"; var result = await collection.GetAsync(key); } sw.Stop(); Console.WriteLine($"Couchbase Q: {q}\t{sw.ElapsedMilliseconds}");
Redis:
Stopwatch sw = Stopwatch.StartNew();
using (var client = redisManager.GetClient())
{
for (int i = 0; i < nbr; i++)
{
client.Get<string>($"jobId:{r.Next(1, 100000)}"); } } sw.Stop(); Console.WriteLine($"Redis Q: {q}\t{sw.ElapsedMilliseconds}");
MySQL:
MySqlConnection mySqlConnection = new MySql.Data.MySqlClient.MySqlConnection("Server=localhost;Database=test;port=3306;User Id=root;password=root;");
mySqlConnection.Open();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < nbr; i++)
{
MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand($"SELECT data FROM test WHERE Id='{r.Next(1, 100000)}'", mySqlConnection); using MySqlDataReader rdr = cmd.ExecuteReader(); while (rdr.Read()) { } } sw.Stop(); Console.WriteLine($"MySql Q: {q} \t{sw.ElapsedMilliseconds} ms");
sw.Reset();
Couchbaseのセットアップ:

そして

およびバケットの耐久性:

ノードは1つだけ(クラスターなし)で、マシン上でローカルであり、Ryzen 3900x 12コア、M.2 SSD、Win10、32 GBRAMを実行しています。
ここまで進んだ場合は、ベンチマークコードを含むGitHubリポジトリを次に示します。 https://github.com/tedekeroth/CouchbaseTests
回答
私はあなたのCouchbaseTestsを取り、Couchbase以外のビットをコメントアウトしました。ジョブキャッシュの代わりにコレクション(myCollection)から選択するようにクエリを修正し、[メトリック]オプションを削除しました。そして、JobIdにインデックスを作成しました。デフォルトでインデックスmybucket_JobIdを作成します:myBucket.myScope.myCollection(JobId)19秒で100,000のドキュメントを挿入し、平均146 usecのドキュメントをkvフェッチし、平均965usecのJobIdでクエリを実行します。
Couchbase Q: 0 187
Couchbase Q: 1 176
Couchbase Q: 2 143
Couchbase Q: 3 147
Couchbase Q: 4 140
Couchbase Q: 5 138
Couchbase Q: 6 136
Couchbase Q: 7 139
Couchbase Q: 8 125
Couchbase Q: 9 129
average et: 146 ms per 1000 -> 146 usec / request
Couchbase Q: 0 1155
Couchbase Q: 1 1086
Couchbase Q: 2 1004
Couchbase Q: 3 901
Couchbase Q: 4 920
Couchbase Q: 5 929
Couchbase Q: 6 912
Couchbase Q: 7 911
Couchbase Q: 8 911
Couchbase Q: 9 927
average et: 965 ms per 1000 -> 965 usec / request. (coincidentally exactly the same as with the java api).
これは、cbserverがローカルで実行されているMac BookProの7.0ビルド3739にありました。
################################################## ####################
kvapiを使用するjavasdk用の小さなLoadDriverアプリケーションがあります。4スレッドの場合、平均応答時間は54マイクロ秒、スループットは73238リクエスト/秒です。ローカルホスト上のcbサーバーでtravel-sampleバケットを使用します。[email protected]:mikereiche / loaddriver.git
実行:秒:10、スレッド:4、タイムアウト:40000us、しきい値:8000usリクエスト/秒:0(最大)、強制GC間隔:0msカウント:729873、リクエスト/秒:72987、最大:2796us平均:54us、集計rq / s:73238
クエリAPIの場合、18倍遅い次のようになります。
実行:秒:10、スレッド:4、タイムアウト:40000us、しきい値:8000usリクエスト/秒:0(最大)、強制GC間隔:0msカウント:41378、リクエスト/秒:4137、最大:12032us平均:965us、集計rq / s:4144
完全な調査を行うには、このような比較を自分で実行する必要がありますが、2つの点が際立っています。
並列実行は完全に並列ではありません。
async
メソッドは最初の待機まで同期して実行されるため、InsertAsync/GetAsync
タスクを追加すると、最初の待機の前にあるすべてのコードが並列ではなく順次実行されます。CouchbaseNetClientはバックグラウンドでいくつかの遅延接続セットアップを実行し、時間指定セクションでそのコストを支払います。SSLネゴシエーションなどを含む環境によっては、これはかなりの初期遅延になる可能性があります。
を使用Task.Run
して操作を開始することで最初の問題に対処できる可能性がありますが、デフォルトのスレッドプールサイズを事前にサイズ設定する必要がある場合があります。
bucket.WaitUntilReadyAsync()
時限セクションの前にバケット(を含む)で少なくとも1つの操作を実行することにより、2番目の問題に対処できます。
インサートの60秒はまだ異常に見えます。使用しているノードの数と耐久性の設定は何ですか?