Производительность поиска MemSQL: JSON против varchar

Я запускаю некоторые тесты производительности memsql для выборочных данных и очень плохо себя чувствую при запросе данных JSON. У меня есть две таблицы, выглядящие очень похожими и содержащие одинаковую информацию (загруженную из одного и того же CSV-файла). Разница в том, что столбец segments — это JSON, а не varchar(255).

CREATE TABLE `test_events` (
`timestamp` datetime NOT NULL,
`user_id` int(20) NOT NULL,
`segments` JSON COLLATE utf8_bin NOT NULL,
KEY `timestamp` (`timestamp`) /*!90619 USING CLUSTERED COLUMNSTORE */,
/*!90618 SHARD */ KEY `user_id` (`user_id`)


CREATE TABLE `test_events_string` (
`timestamp` datetime NOT NULL,
`user_id` int(20) NOT NULL,
`segments` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
KEY `timestamp` (`timestamp`) /*!90619 USING CLUSTERED COLUMNSTORE */,
/*!90618 SHARD */ KEY `user_id` (`user_id`)

И пример данных (количество элементов в массиве варьируется от 1 до 20):

memsql> select * from test_events limit 1;
+---------------------+---------+------------------------+
| timestamp           | user_id | segments               |
+---------------------+---------+------------------------+
| 2017-01-04 00:00:00 |   26834 | [19,18,9,6,7,22,34,43] |
+---------------------+---------+------------------------+

Ниже приведены 2 запроса, которые извлекают одну и ту же информацию, но отличаются скоростью. Оба запроса были выполнены дважды, и я копирую 2-й запуск:

memsql> select count(*) from test_events where json_array_contains_double(segments, 42);
+----------+
| count(*) |
+----------+
| 79312103 |
+----------+
1 row in set (15.86 sec)

memsql> select count(*) from test_events_string where segments like '%42%';
+----------+
| count(*) |
+----------+
| 79312103 |
+----------+
1 row in set (1.96 sec)

memsql> select count(*) from test_events;
+-----------+
| count(*)  |
+-----------+
| 306939340 |
+-----------+
1 row in set (0.02 sec)

Таким образом, сканирование JSON в 8 раз медленнее, чем %x% LIKE. Есть ли что-то, что может улучшить его?

Может быть, вы можете посоветовать, как решить эту проблему бизнес-логики с другим подходом? По сути, мы регистрируем события для пользователей и к каждому событию мы хотим прикрепить массив идентификаторов некоторых сущностей. Этот массив часто изменяется в течение жизненного цикла пользователя. Мы хотим выполнять фильтрацию запросов по 1 или нескольким идентификаторам, как в примере выше.

На всякий случай немного технических характеристик. 3 одинаковых bare metal сервера. 1 сервер для агрегатора, 2 для данных. Каждая машина имеет NUMA, поэтому всего 4 листовых узла. Быстрые твердотельные накопители, 32 ядра (2 x E5-2650v2 @ 2,60 ГГц), 32 ГБ ОЗУ.


person Alex    schedule 11.01.2017    source источник


Ответы (1)


Я не удивлен, что это медленно. MemSQL использует сжатие на основе паркета для столбцов json, и у нас пока нет такого быстрого поиска (но следите за обновлениями!).

Есть несколько вариантов. Во-первых, если вы всегда будете искать 42, вы можете использовать постоянный столбец (https://docs.memsql.com/docs/persistent-computed-columns). Это вряд ли будет вашим вариантом использования.

Другой вариант: если вы всегда просматриваете один и тот же массив, вы можете создать нормализованную таблицу (https://en.wikipedia.org/wiki/Database_normalization).
Что-то вроде create table test_events_array (timestamp datetime not null, user_id bigint not null, segment bigint, shard(user_id), key(ts) using clustered columnstore), затем выполнение select count(*) from test_events_array where segment=42 будет молниеносным.

Вероятно, с этой схемой он также сожмется почти до нуля.

person Joseph Victor    schedule 11.01.2017