我将统计分析系统从MySQL(MariaDB 10)移植到MS SQL 2014,我发现了一件奇怪的事情。通常我以前使用单场和多场索引进行大多数操作:统计数据库在4核PC上容纳大约6千万个事件,分析包括漏斗,事件分段,队列分析,KPI等等,所以它可能很慢有时。
但是当我从MS SQL执行了几个查询序列然后删除所有索引(主要的clastered id除外)时,我感到非常惊讶:我看到执行时间甚至减少了!我重新启动了服务器(缓存已清除),但每次重启结果都相似之后 - 我的查询在没有索引的情况下工作得更快(实际速度相同,但没有时间花在创建手动索引上)。
我想MS SQL为我创建了隐式索引,但在这种情况下看起来我应该从查询中删除所有索引创建?在MySQL中,您可以清楚地看到添加索引确实有效。这种MS SQL行为是否意味着我不再需要关心索引了?我用我的查询做了几次测试,看起来索引几乎不会影响执行时间。上次我用MS SQL处理了很久以前它是MS SQL 2000,所以也许MSFT开发了f ** n' AI在过去的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;
答案 0 :(得分:0)
evt_db.dbo.events
需要INDEX(source, event, tmstamp)
,tmstamp
需要{3}。对于MySQL,前两个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尝试使用两个索引太难了,当简单地扫描表时工作就会少一些。