El banco de pruebas de Couchbase revela INSERTs y GET muy lentos (usando operaciones KeyValue); más lento que los datos persistentes de MySQL
Hice una pequeña prueba de referencia para comparar Couchbase (ejecutándose en Win) con Redis y MySql (EDITAR: agregado Aerospike para probar)
Estamos insertando 100 000 "documentos" JSON en tres db / stores:
- Redis (solo inserta, no hay nada más)
- Couchbase (depósitos efímeros en memoria, índice JSON en JobId)
- MySql (Tabla simple; Id (int), Datos (MediumText), índice en Id)
- Aerospike (almacenamiento en memoria)
El archivo JSON tiene 67 líneas, aproximadamente 1800 bytes.
INSERTAR:
- Couchbase: 60-100 segundos (EDITAR: ¡parece variar bastante!)
- MySql: 30 segundos
- Redis: 8 segundos
- Aerospike: 71 segundos
LEER: Estamos leyendo 1000 veces, y lo hacemos 10 veces y miramos los promedios.
- Couchbase: 600-700 ms para 1000 GET (con operaciones de KeyValue, no con Query API. Con Query API, esto toma alrededor de 1500 ms)
- MySql: 90-100 ms para 1000 GET
- Redis: 50-60 ms para 1000 GET
- Aerospike: 750 ms para 1000 GET
Conclusión: Couchbase parece el más lento (parece que los tiempos de INSERT varían mucho), Aerospike también es muy lento. Ambos están usando almacenamiento en memoria (Couchbase => depósito efímero, Aerospike => memoria del motor de almacenamiento).
Pregunta: ¿Por qué la escritura y lectura en memoria en Couchbase es tan lenta, incluso más lenta que usando MySQL normal (en un SSD)?
CÓDIGO
Nota: Usar Task.WhenAll, o esperar cada llamada, no hace ninguna diferencia.
INSERTAR
Base de la camilla:
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 (¡usando 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();
LEER
Base de la camilla:
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();
Configuración de la base de la camilla:
y
y durabilidad del cucharón:
Solo tengo 1 nodo (sin clúster), es local en mi máquina, ejecuta Ryzen 3900x 12 núcleos, M.2 SSD, Win10, 32 GB de RAM.
Si llegó hasta aquí, aquí hay un repositorio de GitHub con mi código de referencia: https://github.com/tedekeroth/CouchbaseTests
Respuestas
Tomé sus CouchbaseTests, comenté los bits que no son de Couchbase. Se corrigió la consulta para seleccionar de la colección (myCollection) en lugar de la caché de trabajo y se eliminó la opción Métricas. Y creó un índice en JobId. crear índice mybucket_JobId por defecto: myBucket.myScope.myCollection (JobId) Inserta los 100,000 documentos en 19 segundos y kv-recupera los documentos en promedio 146 usec y consulta por JobId en promedio 965 usec.
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).
Esto fue en 7.0 build 3739 en un Mac Book Pro con cbserver ejecutándose localmente.
################################################ ####################
Tengo una pequeña aplicación LoadDriver para java sdk que usa la api kv. Con 4 subprocesos, muestra un tiempo de respuesta promedio de 54 microsegundos y un rendimiento de 73238 solicitudes / segundo. Utiliza el depósito de muestra de viaje en un servidor cb en localhost. [email protected]: mikereiche / loaddriver.git
Ejecutar: segundos: 10, subprocesos: 4, tiempo de espera: 40000us, umbral: 8000us solicitudes / segundo: 0 (máx.), Intervalo de GC forzado: 0ms recuento: 729873, solicitudes / segundo: 72987, máx .: 2796us prom: 54us, rq agregado / s: 73238
Para la API de consulta, obtengo lo siguiente, que es 18 veces más lento.
Ejecutar: segundos: 10, subprocesos: 4, tiempo de espera: 40000us, umbral: 8000us solicitudes / segundo: 0 (máx.), Intervalo de GC forzado: 0ms recuento: 41378, solicitudes / segundo: 4137, máx: 12032us prom: 965us, rq agregado / s: 4144
Tendría que hacer esa comparación yo mismo para hacer una investigación completa, pero destacan dos cosas.
Su ejecución paralela no es realmente completamente paralela.
asyncLos métodos se ejecutan sincrónicamente hasta la primera espera, por lo que todo el códigoInsertAsync/GetAsyncanterior a la primera espera se ejecuta secuencialmente a medida que agrega sus tareas, no en paralelo.CouchbaseNetClient realiza una configuración de conexión perezosa en segundo plano, y usted paga ese costo en la sección cronometrada. Dependiendo del entorno, incluida la negociación SSL y cosas por el estilo, esta puede ser una latencia inicial significativa.
Potencialmente, puede abordar el primer problema utilizando Task.Runpara iniciar la operación, pero es posible que deba ajustar el tamaño predeterminado del Threadpool.
Puede abordar el segundo problema realizando al menos una operación en el depósito (incluida bucket.WaitUntilReadyAsync()) antes de la sección cronometrada.
60 segundos para las inserciones todavía se ven anormales. ¿Cuántos nodos y qué configuración de durabilidad está utilizando?