【发布时间】: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