我们有一个数据库表,用于存储访问者的浏览器数据,按多种不同的子类型进行细分。为简单起见,我们使用下面的表模式。查询基本上将在任何单个id列,度量列,时间戳列(存储为epoch之后的秒)以及设备,浏览器或os列之一。
我们将对星形与雪花模式进行性能测试(其中所有ID都进入单个列,但随后添加了一个额外的列id_type
以确定该表的标识符类型) ,但只要星型模式(现在是这样)在雪花性能的80%以内,我们就会保留它,因为它会使我们的加载过程更容易。然而,在我这样做之前,我想确保在星型模式上优化索引。
create table browser_data (
id_1 int,
id_2 int,
id_3 int,
id_4 int,
metric varchar(20),
browser varchar(20),
device varchar(20),
os varchar(20),
timestamp bigint
)
仅在id列上创建单个索引会更好,还是在这些索引中也包含metric
和timestamp
列?
答案 0 :(得分:1)
不规范化"连续"值,例如DATETIME
,FLOAT
,INT
。 执行将值保留在主表中。
当您将值移动到其他表格时,特别是"雪花",它会根据稍慢和很多之间的值进行查询。当您需要筛选不在主表中的多个指标时,尤其会发生这种情况。由于"雪花"它们中的任何一个都表现得很差。或者"过度规范化":
WHERE a.x = 123 AND b.y = 345
ORDER BY a.x, b.y
至于要创建的索引 - 这完全取决于您需要执行的查询。因此,我强烈建议您根据您的暂定SELECTs
草拟可能的CREATE TABLEs
。
INT
是4个字节。 TIMESTAMP
是5,FLOAT
是4,等等。也就是说,规范化这些事情在空间上效率也很低。
更多强>
在执行JOINs
时,优化器几乎总是从一个表开始,然后转到另一个表,等等。(参见"嵌套循环连接"。)
例如(基于上面的'代码'),当2列被规范化,并且您正在测试值时,您手边没有两个ids
,您只有这两个值。这使得查询执行效率非常低。对于
SELECT ...
FROM main
JOIN a USING(a_id)
JOIN b USING(b_id)
WHERE a.x = 123 AND b.y = 345
以下很可能是执行计划':
a
找到x = 123的行;获取这些行的id(s)
。这可能包括许多尚未由b.y
过滤的行。 a
需要INDEX(x)
main
表,查找包含这些ID的行。 main
需要INDEX(a_id)
。同样,可以拖运更多不必要的行。b
(使用b_id
)检查y=345
;扔掉你一直拖着的不必要的行。 b
需要INDEX(b_id)
请注意我关于"围绕"的评论。盲目地使用*
(在SELECT *
中)会增加问题 - 所有列都在执行步骤时被拖走。
另一方面......如果x
和y
位于main
表中,那么代码就像:
WHERE main.x = 123
AND main.y = 345
只需要INDEX(x,y)
(按任意顺序)。它可以快速找到所需的行。
对于ORDER BY a.x, b.y
,它不能在任何表上使用任何索引。因此查询必须创建一个tmp表,对其进行排序,然后按所需顺序传递行。
但如果x
和y
位于同一个表格中,那么INDEX(x,y)
(按此顺序)可能对ORDER BY x,y
和避免使用tmp表和排序。
使用单个表格时,优化程序可能会使用WHERE
的索引,或者可能会使用ORDER BY
的索引,具体取决于月亮的相位。在某些情况下,一个索引可以用于两者 - 这是最佳的。
另一个注意事项:如果您还有LIMIT 10
,...如果避免sort
,那么只需要查看10行,而不是WHERE
的整个集合