Der Couchbase-Benchmark zeigt sehr langsame INSERTs und GETs (unter Verwendung von KeyValue-Operationen). langsamer als persistierte MySQL-Daten
Ich habe einen kleinen Benchmark-Test durchgeführt, um Couchbase (läuft in Win) mit Redis und MySql zu vergleichen (BEARBEITEN: Aerospike zum Testen hinzugefügt)
Wir fügen 100 000 JSON "Dokumente" in drei DB / Stores ein:
- Redis (einfach einfügen, sonst gibt es nichts)
- Couchbase (Ephemere Buckets im Speicher, JSON-Index auf JobId)
- MySql (einfache Tabelle; ID (int), Daten (MediumText), Index für ID)
- Aerospike (In-Memory-Speicher)
Die JSON-Datei besteht aus 67 Zeilen, ungefähr 1800 Bytes.
EINFÜGEN:
- Couchbase: 60-100 Sekunden (EDIT: scheint ziemlich unterschiedlich zu sein!)
- MySQL: 30 Sekunden
- Redis: 8 Sekunden
- Aerospike: 71 Sekunden
LESEN: Wir lesen 1000 Mal, und wir machen das 10 Mal und schauen uns Durchschnittswerte an.
- Couchbase: 600-700 ms für 1000 GETs (Verwenden von KeyValue-Operationen, nicht der Abfrage-API. Bei Verwendung der Abfrage-API dauert dies etwa 1500 ms.)
- MySql: 90-100 ms für 1000 GETs
- Redis: 50-60 ms für 1000 GETs
- Aerospike: 750 ms für 1000 GETs
Fazit: Couchbase scheint am langsamsten zu sein (die INSERT-Zeiten variieren anscheinend stark), Aerospike ist auch sehr langsam. Beide verwenden In-Memory-Speicher (Couchbase => Ephemeral Bucket, Aerospike => Speicher der Speicher-Engine).
Frage: Warum ist das Schreiben und Lesen im Speicher auf Couchbase so langsam, sogar langsamer als bei Verwendung von normalem MySQL (auf einer SSD)?
CODE
Hinweis: Die Verwendung von Task.WhenAll oder das Warten auf jeden Anruf macht keinen Unterschied.
EINFÜGEN
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 (mit 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();
LESEN
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-Setup:

und

und Eimer Haltbarkeit:

Ich habe nur 1 Knoten (kein Cluster), er ist lokal auf meinem Computer und führt Ryzen 3900x 12 Kerne, M.2 SSD, Win10, 32 GB RAM aus.
Wenn Sie es bis hierher geschafft haben, finden Sie hier ein GitHub-Repo mit meinem Benchmark-Code: https://github.com/tedekeroth/CouchbaseTests
Antworten
Ich habe Ihre CouchbaseTests genommen und die Nicht-Couchbase-Bits auskommentiert. Die Abfrage zur Auswahl aus der Sammlung (myCollection) anstelle des Jobcaches wurde behoben und die Option "Metriken" wurde entfernt. Und einen Index für JobId erstellt. Erstellen Sie standardmäßig den Index mybucket_JobId: myBucket.myScope.myCollection (JobId) Er fügt die 100.000 Dokumente in 19 Sekunden ein und kv-holt die Dokumente im Durchschnitt 146 usec und fragt nach JobId im Durchschnitt 965 usec ab.
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).
Dies war auf 7.0 Build 3739 auf einem Mac Book Pro, wobei der cbserver lokal ausgeführt wurde.
#################################################### ######################
Ich habe eine kleine LoadDriver-Anwendung für das Java SDK, die die KV-API verwendet. Bei 4 Threads wird eine durchschnittliche Antwortzeit von 54 Mikrosekunden und ein Durchsatz von 73238 Anforderungen / Sekunde angezeigt. Es verwendet den Travel-Sample-Bucket auf einem CB-Server auf localhost. [email protected]: mikereiche / loaddriver.git
Ausführen: Sekunden: 10, Threads: 4, Zeitüberschreitung: 40000us, Schwellenwert: 8000us Anforderungen / Sekunde: 0 (max), erzwungenes GC-Intervall: 0 ms Anzahl: 729873, Anforderungen / Sekunde: 72987, max: 2796us Durchschnitt: 54us, aggregierter Bedarf / s: 73238
Für die Abfrage-API erhalte ich Folgendes, was 18-mal langsamer ist.
Ausführen: Sekunden: 10, Threads: 4, Zeitüberschreitung: 40000us, Schwellenwert: 8000us Anforderungen / Sekunde: 0 (max), erzwungenes GC-Intervall: 0 ms Anzahl: 41378, Anforderungen / Sekunde: 4137, max: 12032us Durchschnitt: 965us, Gesamtanforderung / s: 4144
Ich müsste einen solchen Vergleich selbst durchführen, um eine vollständige Untersuchung durchzuführen, aber zwei Dinge fallen auf.
Ihre parallele Ausführung ist nicht wirklich vollständig parallel.
async
Methoden werden bis zum ersten Warten synchron ausgeführt, sodass der gesamte CodeInsertAsync/GetAsync
vor dem ersten Warten nacheinander ausgeführt wird, wenn Sie Ihre Aufgaben hinzufügen, und nicht parallel.CouchbaseNetClient führt im Hintergrund einige verzögerte Verbindungsaufbauten durch, und Sie zahlen diese Kosten im Abschnitt "Zeitgesteuert". Abhängig von der Umgebung, einschließlich SSL-Aushandlung und dergleichen, kann dies eine erhebliche anfängliche Latenz sein.
Sie können möglicherweise das erste Problem Task.Run
beheben, indem Sie den Vorgang starten. Möglicherweise müssen Sie jedoch die Standardgröße des Threadpools vorab anpassen.
Sie können das zweite Problem beheben, indem Sie mindestens eine Operation am Bucket (einschließlich bucket.WaitUntilReadyAsync()
) vor dem zeitgesteuerten Abschnitt ausführen .
60 Sekunden für Einsätze sehen immer noch abnormal aus. Wie viele Knoten und welche Haltbarkeitseinstellung verwenden Sie?