SQL 字符串字段与多个 int/varchar 字段的性能
SQL performance for string field vs multiple int/varchar fields
我正在努力使数据库设计正确,但我不确定应该选择 2 个选项中的哪一个。将有大约 20 个布尔值用于过滤(为简单起见,样本中有 4 个)。
- prop1
- prop2
- prop3
- prop4
选项 1)
每个道具都有一个 int(1) 或 varchar(1) 字段。查询的过滤器部分可能像
WHERE prop1=1 AND prop3=1 AND prop4=1
选项 2)
使用带有代表道具的字符的单个文本字段
- 一个:prop1
- b: prop2
- c: prop3
- d: prop4
然后类似于选项 1 的查询的过滤器部分将像
WHERE props LIKE '%a%' AND props LIKE '%c%' AND props LIKE '%d%'
或者如果字符是排序的:
WHERE props LIKE '%a%' AND props LIKE '%cd%'
我的想法是选项 2 使添加新道具更容易,所以我喜欢这个选项,但是 LIKE 比较器的性能会比相等比较器差吗?与多个 int(1) 或 varchar(1) 相比,使用单个文本有什么区别吗?我没有想到的任何其他好处或缺点?
从性能的角度来看,这两个选项都有缺点:
几乎不可能对 20 列进行正确的索引,无论它们的数据类型如何,因为查询可能因设置条件 prop1、prop1 + prop2、prop2 + prop3、prop2 + prop4 等而异。所以你需要很多复合索引。此外,由于基数较低(值只能为 0 或 1,请参阅 here for example),布尔值的索引通常表现不佳。
另一方面,带有前导 % 的 LIKE 语句也是一个性能问题。关闭 % 可以,但前导 % 被确定为很慢。
我在这里看到您想为您的记录分配一组属性,每条记录可以分配 0..n propX
,并且您想有效地过滤它。就像一个用户可以分配 0,1,2,...n 个角色。在关系数据库中,它被归类为经典的多对多关系。如果您使用可能的 props
设置 table 并使用联接 table 将它们连接到您的记录,例如 detailed here,您可以仅使用索引数量有限。
主要问题是您是否可以 运行 比扫描整个 table 更快。答案是否定的,除非可以使用 Index(es) 单独处理少量布尔值。
您的 WHERE bools LIKE '%a%c%d%'
是将任意数量的标志组合在一起的巧妙技巧。不过需要逐行查看,LIKE
略重
INT(1)
占用 4 个字节加上开销。 TINYINT
就是你要钓的东西;它需要 1 个字节,加上开销。
具有最多 64 个布尔值的 SET
是另一种技术。编码有点笨拙,但效率很高
INT UNSIGNED
(最多 32 个)或 BIGINT UNSIGNED
(最多 64 个)标志的实现类似于 SET
,并且最多占用 8 个字节。但是编码相当笨拙。让我们在最低有效位中从 0 开始对位进行编号。
WHERE (bools & ( (1 << 0) | (1 << 2) | (1 << 3) ) ) =
( (1 << 0) | (1 << 2) | (1 << 3) )
将检查位 0、2 和 3 是否都已设置。 (这就像您对 a、c、d 的测试。)使用这种方法可以实现多种 AND 和 OR。 (您可以预先计算这些位值——在此示例中为 13。或者使用位文字:0b1101
。)
SET 或 INT 中的位的好处是每行中的 'speed'。不过,必须测试所有行。
因此,我建议对布尔值等进行分类,并决定哪些需要索引以及哪些可以进入此组合列或非布尔型的组合 JSON 列。
我正在努力使数据库设计正确,但我不确定应该选择 2 个选项中的哪一个。将有大约 20 个布尔值用于过滤(为简单起见,样本中有 4 个)。
- prop1
- prop2
- prop3
- prop4
选项 1)
每个道具都有一个 int(1) 或 varchar(1) 字段。查询的过滤器部分可能像
WHERE prop1=1 AND prop3=1 AND prop4=1
选项 2)
使用带有代表道具的字符的单个文本字段
- 一个:prop1
- b: prop2
- c: prop3
- d: prop4
然后类似于选项 1 的查询的过滤器部分将像
WHERE props LIKE '%a%' AND props LIKE '%c%' AND props LIKE '%d%'
或者如果字符是排序的:
WHERE props LIKE '%a%' AND props LIKE '%cd%'
我的想法是选项 2 使添加新道具更容易,所以我喜欢这个选项,但是 LIKE 比较器的性能会比相等比较器差吗?与多个 int(1) 或 varchar(1) 相比,使用单个文本有什么区别吗?我没有想到的任何其他好处或缺点?
从性能的角度来看,这两个选项都有缺点:
几乎不可能对 20 列进行正确的索引,无论它们的数据类型如何,因为查询可能因设置条件 prop1、prop1 + prop2、prop2 + prop3、prop2 + prop4 等而异。所以你需要很多复合索引。此外,由于基数较低(值只能为 0 或 1,请参阅 here for example),布尔值的索引通常表现不佳。
另一方面,带有前导 % 的 LIKE 语句也是一个性能问题。关闭 % 可以,但前导 % 被确定为很慢。
我在这里看到您想为您的记录分配一组属性,每条记录可以分配 0..n propX
,并且您想有效地过滤它。就像一个用户可以分配 0,1,2,...n 个角色。在关系数据库中,它被归类为经典的多对多关系。如果您使用可能的 props
设置 table 并使用联接 table 将它们连接到您的记录,例如 detailed here,您可以仅使用索引数量有限。
主要问题是您是否可以 运行 比扫描整个 table 更快。答案是否定的,除非可以使用 Index(es) 单独处理少量布尔值。
您的 WHERE bools LIKE '%a%c%d%'
是将任意数量的标志组合在一起的巧妙技巧。不过需要逐行查看,LIKE
略重
INT(1)
占用 4 个字节加上开销。 TINYINT
就是你要钓的东西;它需要 1 个字节,加上开销。
具有最多 64 个布尔值的 SET
是另一种技术。编码有点笨拙,但效率很高
INT UNSIGNED
(最多 32 个)或 BIGINT UNSIGNED
(最多 64 个)标志的实现类似于 SET
,并且最多占用 8 个字节。但是编码相当笨拙。让我们在最低有效位中从 0 开始对位进行编号。
WHERE (bools & ( (1 << 0) | (1 << 2) | (1 << 3) ) ) =
( (1 << 0) | (1 << 2) | (1 << 3) )
将检查位 0、2 和 3 是否都已设置。 (这就像您对 a、c、d 的测试。)使用这种方法可以实现多种 AND 和 OR。 (您可以预先计算这些位值——在此示例中为 13。或者使用位文字:0b1101
。)
SET 或 INT 中的位的好处是每行中的 'speed'。不过,必须测试所有行。
因此,我建议对布尔值等进行分类,并决定哪些需要索引以及哪些可以进入此组合列或非布尔型的组合 JSON 列。