【问题标题】:indexes don't affect time execution in ms sql 2014 VS mysql (mariaDB 10)索引不影响 ms sql 2014 VS mysql (mariaDB 10) 中的时间执行
【发布时间】:2016-07-01 13:54:09
【问题描述】:

我正在将统计分析器系统从 MySQL (MariaDB 10) 移植到 MS SQL 2014,我发现了一件奇怪的事情。平时我大部分操作都是使用单字段索引和多字段索引:统计数据库在4核pc上保存了大约6000万个事件,分析包括漏斗、事件分割、队列分析、KPI等,所以可能会很慢有时。

但是当我在 MS SQL 上执行了几个查询序列然后删除了所有索引(主分类 id 除外)时,我感到非常惊讶:我看到执行时间甚至减少了!我已经重启了服务器(缓存被清除),但是每次重启后结果都差不多——我的查询在没有索引的情况下工作得更快(实际上速度是一样的,但没有时间花在手动创建索引上)。

我想 MS SQL 为我创建了隐式索引,但在这种情况下,我似乎应该从我的查询中删除所有索引创建?在 MySQL 中,您可以清楚地看到添加索引确实有效。这种 MS SQL 行为是否意味着我不再需要关心索引?我对我的查询进行了几次测试,似乎索引几乎不会影响执行时间。我上次处理 MS SQL 是很久以前的事了,当时是 MS SQL 2000,所以也许 MSFT 在过去 15 年里开发了 f**n' AI? :)


以防万一此测试 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; 

【问题讨论】:

  • 您必须使用查询分析器来解决这个问题,但在某些情况下,进行全表扫描比扫描索引更便宜。您可能已经找到了其中一种情况。
  • 所有生成系统的查询都在 mysql 上进行了测试和分析,创建了索引来加速特定查询,它们确实很有帮助 - 在 mysql 上。这是否意味着不同数据库引擎上复合索引的逻辑不一样?
  • 和第二种情况 - 为 OLAP 生成“星形”方案:由 > 10 个表进行内部连接,主要有 60M 行。在 mysql 中我需要索引才能工作,在 mssql 中再次使用 main 中的一个就足够了。 mssql 分析器请求索引,但创建它并没有加快任何速度。
  • 如果您可以显示查询的执行计划,一个有索引,一个没有,那么更容易看到发生了什么。 SQL Server 不会创建“隐式”索引,最接近的索引是聚簇主键或唯一约束。

标签: mysql sql-server-2014 database-performance database-indexes


【解决方案1】:

evt_db.dbo.events 需要 INDEX(source, event, tmstamp),tmstamp 第三。在 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 过于努力地使用两个索引,当简单地扫描表时工作量会减少。

【讨论】:

  • 此代码以编程方式生成,仅将需要的参数提取到结果表中。因为用户可能想要分析任何事件参数(事件大约有 15 个参数),所以我无法在主要事件表中为它们中的每一个(以及几个复合)创建索引。在这种情况下,“source”和“event_id”将不在结果数据集中。只有时间、uid、id 和 param1。
  • 好的,我不会删除索引创建和使用索引和不使用索引进行测试。顺便说一句,第一个基准测试表明,在类似数据集(涉及数据类型映射)上的 MSSQL 可以在不生成索引的情况下比 MySQL 快 3-4 倍。这就是 MariaDB 10,它的原始引擎速度更快。
  • 在 MySQL 中,速度通常会快 10 倍,具体取决于数据/索引是否被缓存。因此,要进行“公平”比较,请务必适当调整缓存大小,或者故意使用冷缓存,或者运行两次查询以确保缓存已准备好。 (抱歉,如果我告诉你一些你已经知道的事情。)
  • 一切正常,完成了很多次,但我的任务速度提高了 3 到 4 倍。
猜你喜欢
  • 2020-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-24
  • 1970-01-01
  • 1970-01-01
  • 2014-05-03
  • 1970-01-01
相关资源
最近更新 更多