индексы не влияют на время выполнения в ms sql 2014 VS mysql (mariaDB 10)

Переношу систему анализа статистики с MySQL (MariaDB 10) на MS SQL 2014 и обнаружил странную вещь. Обычно я использовал индексы с одним и несколькими полями для большинства операций: база данных статистики содержит около 60 миллионов событий на 4-ядерном компьютере, а анализ включает воронки, сегментацию событий, когортный анализ, ключевые показатели эффективности и многое другое, поэтому это может быть медленным. иногда.

Но я был очень удивлен, когда выполнил несколько последовательностей запросов из MS SQL, а затем удалил все индексы (кроме основного кластеризованного id): я увидел, что время выполнения даже уменьшилось! Я перезапускал сервер (кеш очищался), но после каждого перезапуска результат был одинаков - мои запросы работают быстрее без индексов (фактически скорость та же, но не тратится время на ручное создание индексов).

Я предполагаю, что MS SQL создает для меня неявные индексы, но в этом случае похоже, что я должен удалить все создание индексов из моих запросов? В MySQL вы можете ясно видеть, что добавление индексов действительно работает. Означает ли это поведение MS SQL, что мне больше не нужно заботиться об индексах? Я сделал несколько тестов со своими запросами, и кажется, что индексы почти не влияют на время выполнения. Последний раз, когда я имел дело с MS SQL, это было очень давно, и это был MS SQL 2000, так что, может быть, MSFT разработала чертов ИИ за последние 15 лет? :)


На всякий случай этот тестовый код sql (сгенерированный серверной частью для внешнего интерфейса) приведен ниже. Короче говоря, он создает графические данные для определенного типа событий за последние 3 месяца с течением времени, а затем выполняет сегментацию по одному параметру. Он создает временную таблицу из основной таблицы событий с заданными пользователем ограничениями (период времени, параметры), создает еще несколько временных таблиц и индексов, выполняет несколько объединений и возвращает окончательный результат выбора:

select  min(tmstamp), max(tmstamp)
    from  evt_db.dbo.events
    where  ( ( source = 3 )
              and  ( event_id=24 )
              and  tmstamp > 1451606400
              AND  tmstamp < 1458000000 
           ); 
select  min(param1), max(param1), count(DISTINCT(param1))
    from  evt_db.dbo.events
    WHERE  ( ( source = 3 )
              AND  ( event_id=24 )
              AND  tmstamp > 1451606400
              AND  tmstamp < 1458000000
           ); 

create  table #_tmp_times_calc_analyzer_0_0 (
    tm_start int, 
    tm_end int, 
    tm_origin int, 
    tm_num int
);

insert into  #_tmp_times_calc_analyzer_0_0 values
( 1451606400, 1452211200, 1451606400, 0 ), 
( 1452211200, 1452816000, 1452211200, 1 ), 
( 1452816000, 1453420800, 1452816000, 2 ), 
( 1453420800, 1454025600, 1453420800, 3 ), 
( 1454025600, 1454630400, 1454025600, 4 ), 
( 1454630400, 1455235200, 1454630400, 5 ), 
( 1455235200, 1455840000, 1455235200, 6 ), 
( 1455840000, 1456444800, 1455840000, 7 ), 
( 1456444800, 1457049600, 1456444800, 8 ), 
( 1457049600, 1457654400, 1457049600, 9 ), 
( 1457654400, 1458259200, 1457654400, 10 );

А также...

CREATE INDEX tm_num ON _tmp_times_calc_analyzer_0_0 (tm_num); 

SELECT  id, t1.uid, tmstamp, floor((tmstamp - 1451606400) / 604800) period_num,
        param1 into #_tmp_events_view_analyzer_0_0
    FROM  evt_db.dbo.events t1
    WHERE  ( ( source = 3 )
              AND  ( event_id=24 )
              AND  tmstamp > 1451606400
              AND  tmstamp < 1458000000
           ); 

CREATE INDEX uid ON _tmp_events_view_analyzer_0_0 (uid);

CREATE INDEX period_num ON _tmp_events_view_analyzer_0_0 (period_num);

CREATE INDEX tmstamp ON _tmp_events_view_analyzer_0_0 (tmstamp);

CREATE INDEX _index_param1 ON _tmp_events_view_analyzer_0_0 (param1);

create table #_tmp_median_analyzer_0_0 (ts int );

insert into #_tmp_median_analyzer_0_0
    select  distinct(param1) v
        from  #_tmp_events_view_analyzer_0_0
        where  param1 is not null
        order by  v ; 

select  tm_origin, count(distinct uid), count(distinct id)
    from  #_tmp_times_calc_analyzer_0_0
    left join  #_tmp_events_view_analyzer_0_0 ON period_num = tm_num
    GROUP BY  tm_origin; 
select  top 600 (param1) seg1, count(distinct uid), count(distinct id)
    from  #_tmp_events_view_analyzer_0_0
    GROUP BY  param1
    order by  1 asc;

А также...

select  seg1, tm_origin, count(distinct uid), count(distinct id)
    from  
      ( SELECT  (param1) seg1, tm_origin, uid, id
            from  #_tmp_times_calc_analyzer_0_0
            left join  #_tmp_events_view_analyzer_0_0 ON period_num = tm_num
            group by  param1, tm_origin, uid, id 
      ) t
    GROUP BY  seg1, tm_origin; 
select  min(param1), max(param1), round(avg(param1),0)
    from  #_tmp_events_view_analyzer_0_0; 

DECLARE @c BIGINT = (SELECT COUNT(*) FROM #_tmp_median_analyzer_0_0);

SELECT  round(AVG(1.0 * ts),0)
    FROM  
      ( SELECT  ts
            FROM  #_tmp_median_analyzer_0_0
            ORDER BY  ts OFFSET (@c - 1) / 2 ROWS
                FETCH NEXT 1 + (1 - @c % 2) ROWS ONLY 
      ) AS median_val; 

person Tertium    schedule 15.03.2016    source источник
comment
вам придется использовать профилировщик запросов, чтобы понять это, но в некоторых случаях дешевле выполнить полное сканирование таблицы, чем сканирование индекса. возможно, вы нашли один из таких случаев.   -  person Marc B    schedule 16.03.2016
comment
все запросы, которые генерирует система, протестированы и профилированы на mysql, индексы были созданы для ускорения конкретных запросов, и они действительно помогли - на mysql. означает ли это, что логика составных индексов на разных механизмах БД не одинакова?   -  person Tertium    schedule 16.03.2016
comment
и второй случай - генерация звездообразной схемы для OLAP: внутреннее соединение по › 10 таблицам, основное имеет 60M строк. в mysql мне нужны индексы, чтобы это работало, в mssql опять же достаточно одного кластера в main. Анализатор mssql запрашивает индекс, но созданный им ничего не ускоряет.   -  person Tertium    schedule 16.03.2016
comment
Если бы вы могли показать планы выполнения запросов, один с индексами и один без них, было бы легче увидеть, что происходит. SQL-сервер не создает неявные индексы, самое близкое к этому — кластеризованный первичный ключ или уникальное ограничение.   -  person steoleary    schedule 16.03.2016


Ответы (1)


evt_db.dbo.events нужен INDEX(source, event, tmstamp), с tmstamp 3-й. В случае MySQL эти первые 2 SELECTs будут полностью выполняться в индексе (поскольку это «покрывающий» индекс). source и event могут быть в любом порядке.

Позже у вас будет похожий SELECT, но с id, t1.uid. Вы можете сделать для него индекс покрытия: INDEX(source, event, tmstamp, uid, id). Опять же, tmstamp должен быть третьим в списке.

select top 600 (param1) seg1, count(distinct uid), count(distinct id) ... может выиграть от INDEX(param1, uid, id), где param1 должно быть первым.

Другие индексы, которые вы перечисляете, возможно, вообще бесполезны. Какие индексы вы пробовали?

Одно различие между MySQL и другими базами данных — MySQL почти никогда не использует более одного индекса в запросе. И, по моему опыту, выбор MySQL «мудр». Возможно, MSSql слишком старается использовать два индекса, тогда как простое сканирование таблицы было бы менее трудоемким.

person Rick James    schedule 16.03.2016
comment
этот код генерируется программно, и в таблицу результатов извлекаются только необходимые параметры. поскольку пользователь может захотеть проанализировать любой параметр события (событие имеет около 15 параметров), я не могу создать индекс для каждого из них (а также нескольких составных) в основной таблице событий. 'source' и 'event_id' в этом случае не будут в результирующем наборе данных. только время, uid, id и param1. - person Tertium; 16.03.2016
comment
ок, не буду убирать создание индексов и тестить с индексами и без. Кстати, первые бенчмарки показывают, что MSSQL на аналогичном наборе данных (включая сопоставление типов данных) работает без генерации индексов в 3-4 раза быстрее, чем MySQL. А это MariaDB 10, который несколько быстрее оригинального движка. - person Tertium; 16.03.2016
comment
В MySQL часто наблюдается 10-кратное ускорение, в зависимости от того, кэшируются данные/индекс или нет. Таким образом, чтобы иметь «справедливое» сравнение, обязательно настройте размеры кеша соответствующим образом, а также либо преднамеренно работайте с холодным кешем, либо дважды запустите запрос, чтобы убедиться, что кеш заполнен. (Извините, если я говорю вам то, что вы уже знаете.) - person Rick James; 16.03.2016
comment
все правильно, делал много раз, а еще в 3-4 раза быстрее на моих задачах. - person Tertium; 17.03.2016