Тест Couchbase показывает очень медленные вставки и получение (с использованием операций с ключевыми значениями); медленнее, чем сохраненные данные MySQL

#mysql #redis #couchbase #benchmarking #aerospike

#mysql #redis #кушетка #сравнительный анализ #aerospike

Вопрос:

Я провел небольшой тестовый тест, чтобы сравнить Couchbase (работающий в Win) с Redis и MySQL (РЕДАКТИРОВАТЬ: добавлен Aerospike для тестирования)

Мы вставляем 100 000 «документов» в формате JSON в три базы данных / хранилища:

  • Redis (просто вставить, больше ничего)
  • Couchbase (эфемерные сегменты в памяти, индекс JSON в JobID)
  • MySQL (простая таблица; Идентификатор (int), данные (MediumText), индекс по идентификатору)
  • Aerospike (хранилище в памяти)

Файл JSON состоит из 67 строк, около 1800 байт.

ВСТАВИТЬ:

  • База данных: 60-100 секунд (РЕДАКТИРОВАТЬ: кажется, немного отличается!)
  • MySQL: 30 секунд
  • Redis: 8 секунд
  • Aerospike: 71 секунда

ЧИТАЙТЕ: мы читаем 1000 раз, и мы делаем это 10 раз и смотрим на средние значения.

  • Couchbase: 600-700 мс для 1000 операций получения (с использованием операций с ключевыми значениями, а не API запросов. При использовании API запросов это занимает около 1500 мс)
  • MySQL: 90-100 мс для 1000 GETs
  • Redis: 50-60 мс для 1000 получений
  • Аэроспайк: 750 мс за 1000 попаданий

Вывод: Couchbase кажется самым медленным (кажется, время ВСТАВКИ сильно меняется), Aerospike также очень медленный. Оба они используют хранилище в памяти (Couchbase => Ephemeral bucket, Aerospike => память хранилища).

Вопрос: Почему запись и чтение в памяти на Couchbase такие медленные, даже медленнее, чем при использовании обычного MySQL (на SSD)?

код

Примечание: использование задачи.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 узел (без кластера), он локальный на моей машине, с 12 ядрами Ryzen 3900x, M.2 SSD, Win10, 32 ГБ ОЗУ.

Если вы зашли так далеко, вот репозиторий GitHub с моим эталонным кодом: https://github.com/tedekeroth/CouchbaseTests

Комментарии:

1. Код Redis и Mysql был бы полезен для обеспечения справедливого сравнения. Например, используются ли массовые API REDIS?

2. Нет, и mysql, и redis являются последовательными вставками, даже без использования Task. Когда все, и все еще очень быстро. Я также обновлю код tht, как и было запрошено.

3. @Ted для другой точки данных для сравнения было бы интересно увидеть результаты выполнения аналогичного теста с помощью инструмента cbc-pillowfight ( docs.couchbase.com/sdk-api/couchbase-c-client-2.4.8 /… ), который будет находиться в каталоге bin вашей установки Couchbase.

4. Извините, я не видел этого комментария до сих пор, странно, здесь не было «ping». Да, это может быть интересно, но с тех пор я, к сожалению, «двигаюсь дальше» и ищу другие решения. Я надеюсь, что это поможет кому-то еще.

5. @Ted достаточно справедливо. Просто хочу, чтобы кто-нибудь наткнулся на это, чтобы знать, что время, которое вы видите, НАМНОГО медленнее, чем мы ожидаем увидеть от Couchbase. Я очень смущен вашими выводами в другом месте о том, что параллельное выполнение вставок было не намного быстрее, чем в последовательном режиме. Если у вас когда-нибудь появится шанс вернуться к этому, было бы интересно посмотреть, поможет ли настройка параметра SDK NumKvConnections. Это определяет количество подключений из SDK к каждому узлу Ключ-значение, и по умолчанию оно равно 1. В подобных сценариях массовой загрузки это может повысить пропускную способность.

Ответ №1:

Я взял ваши тесты CouchbaseTests, прокомментировал биты, отличные от Couchbase. Исправлен запрос на выбор из коллекции ( MyCollection ) вместо jobcache и удален параметр Metrics. И создал индекс на JobID. создайте индекс mybucket_JobId по умолчанию:myBucket.MyScope.MyCollection (JobID) Он вставляет 100 000 документов за 19 секунд, а kv-выборка документов занимает в среднем 146 секунд, а запрос по JobID — в среднем 965 секунд.

 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).
 

Это было в сборке 7.0 3739 на Mac Book Pro с локальным сервером cbserver.

######################################################################

У меня есть небольшое приложение LoadDriver для java sdk, которое использует kv api. С 4 потоками он показывает среднее время отклика 54 микросекунды и пропускную способность 73238 запросов в секунду. Он использует корзину с образцами для перемещения на сервере cb на localhost. git@github.com:mikereiche/loaddriver.git

Запуск: секунды: 10, потоки: 4, тайм-аут: 40000us, порог: 8000us запросы в секунду: 0 (максимум), принудительный интервал GC: 0 мс Количество: 729873, запросы в секунду: 72987, макс: 2796us среднее значение: 54us, совокупный rq / s: 73238

Для API запросов я получаю следующее, что в 18 раз медленнее.

Время выполнения: секунды: 10, потоки: 4, тайм-аут: 40000us, порог: 8000us запросы в секунду: 0 (максимум), принудительный интервал GC: 0 мс Количество: 41378, запросы в секунду: 4137, макс: 12032us среднее значение: 965us, совокупный rq / s: 4144

Комментарии:

1. Спасибо за тестирование. Можете ли вы предоставить URL-адрес GitHub или что-то в этом роде? Я хочу протестировать вашу версию этого. Кроме того, насколько я понимаю, операции с значением ключа не должны требовать установки индекса, ключ всегда индексируется, я думал? В любом случае, отказ от добавления этого индекса в любом случае должен ускорить вставку, верно?

2. github.com/mikereiche/CouchbaseTests Ted — я смутно припоминаю попытки без включения индекса, но не припоминаю, чтобы вставки были быстрее. Индексация является асинхронной.

3. @Ted это правильно, использование API-интерфейса ключ-значение не затрагивает индексы. Я думаю, что Майкл упоминает индекс, потому что он также тестировал запросы N1QL.

4. При определенном GSI вставки — с использованием kv-api или иным образом — приведут к индексации новых документов (асинхронно). Как я уже сказал, это, похоже, не привело к увеличению времени вставки — вероятно, потому, что тест выполняется последовательно, а на моей машине 6 процессоров.

Ответ №2:

Мне пришлось бы самому провести такое сравнение, чтобы провести полное расследование, но выделяются две вещи.

  1. Ваше параллельное выполнение не является полностью параллельным. async методы выполняются синхронно вплоть до первого ожидания, поэтому весь код InsertAsync/GetAsync перед первым ожиданием выполняется последовательно по мере добавления ваших задач, а не параллельно.
  2. CouchbaseNetClient выполняет некоторую настройку отложенного подключения в фоновом режиме, и вы оплачиваете эту стоимость в разделе timed . В зависимости от среды, включая согласование SSL и тому подобное, это может быть значительной начальной задержкой.

Вы можете потенциально решить первую проблему, используя Task.Run для запуска операции, но вам может потребоваться предварительно установить размер пула потоков по умолчанию.

Вы можете решить вторую проблему, выполнив по крайней мере одну операцию над корзиной (включая bucket.WaitUntilReadyAsync() ) перед разделом timed.

60 секунд для вставок по-прежнему выглядят ненормально. Сколько узлов и какие настройки долговечности вы используете?

Комментарии:

1. Что касается «параллельного выполнения»: да, задачи создаются последовательно, но это всего лишь создание задач, а не сама работа. Я писал выше, что создание задач, а затем WhenAll не имело никакого значения по сравнению с ожиданием каждого вызова. Я повторно запустил его снова, используя как WhenAll, так и sequantial, ожидая каждого вызова. Это то же самое время, но теперь вместо этого оно занимает 100 секунд…

2. 2: Итак, если я сделаю одну вставку вне временного раздела, а затем перейду? Исправит ли это ленивые вещи?

3. Я обновил приведенный выше вопрос с помощью некоторого средства устранения отложенной загрузки, а также настройки долговечности для корзины, для которой установлено значение «нет».

4. Я повторно запустил его с обновленным кодом (средство отложенной загрузки). Теперь это заняло 85 секунд. Также снова запустил вставки Redis, потребовалось 8,5 секунд.

5. Обновление: если я 1) Удаляю коллекцию, повторно добавляю коллекцию, добавляю ПЕРВИЧНЫЙ ключ; 2) Повторно запускаю код для ВСТАВКИ 100 кб, я снова получаю 100 секунд. Кажется, немного отличается. Компьютер не загружен, на самом деле ничего не работает, кроме Win10, браузера, Visual Studio. Тем не менее, Redis занимает всего 8 секунд… Я добавил репозиторий GitHub, куда я загрузил тестовый код.