优化存储空间:许多具有相同值的行和列

时间:2019-07-31 15:25:24

标签: mysql indexing storage innodb

我有多个表,每个表存储1亿多行数据。对于任何给定的列,只有少数可能的唯一值,因此许多列具有重复的值。

最初设计架构时,我决定使用辅助链接表来存储实际值,以优化数据库所需的存储空间。

例如:

代替用于存储用户代理的表:

  • id(int)
  • user_agent(varchar)

我正在使用2张这样的桌子:

表1

  • id(int)
  • user_agent_id(int)

表2

  • id(int)
  • user_agent(varchar)

当有1亿多行时,我发现此模式可节省大量的存储空间,因为只有几百个可能的用户代理,而这些字符串构成了大部分数据。

我遇到的问题是: 使用链接表在许多不同的表中存储如此多的字符串数据,这增加了开发方面的开销,并且由于需要连接,因此查询数据的速度大大降低。

我的问题是: 有没有一种方法可以将所有列放在一个表中,并强制mysql不复制具有重复值的列所需的存储?我开始认为必须有某种内置的方式来处理这种情况,但我的研究没有发现任何东西。

如果我的一列有10个唯一值,并且有1亿多行,为什么MySQL会将每个值(包括重复项)完全保存在存储中,而不仅仅是对唯一值的引用?

谢谢!

2 个答案:

答案 0 :(得分:0)

  

如果我的一列有10个唯一值,并且有1亿多行,为什么MySQL会将每个值(包括重复项)完全保存在存储中,而不仅仅是对唯一值的引用?

MySQL无法预测您将始终只有10个唯一值。您告诉它存储VARCHAR,因此必须假定您要存储 any 字符串。如果要使用数字枚举所有可能的字符串,则该数字实际上需要比字符串本身更长。

要解决您的问题,可以通过使用引用查找表的数字ID来优化存储。由于查找表中不同字符串的数量为数百,因此您至少需要使用SMALLINT(16位整数)。您不需要使用与INT(32位整数)一样大的数字。

在查找表中,将该ID声明为主键。这样应该可以尽快进行连接。

如果您想直接进行反向连接-在100M行表中查询特定的用户代理,然后索引大表中的smallint列。创建索引将占用更多的存储空间,因此在创建索引之前,请确保在每个表中都需要这种类型的查询。

另一个建议:获得更大的存储量。

答案 1 :(得分:0)

经过一些挖掘和测试,我发现了似乎是最好的解决方案:使用varchar列本身而不是使用ID字段来创建索引和外键约束。

INNODB支持带有varchar以及int的外键:https://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html

这里是一个示例:

user_agents 表:

  • user_agent(varchar和唯一索引)

user_requests 表:

  • id
  • user_agent(varchar,外键约束,引用user_agents表的user_agent列)
  • other_columns等...


我发现,使用varchar本身作为外键时,mysql会自行优化存储,并且对于磁盘上的每个唯一user_agent仅会存储1个varchar。添加1000万以上的user_requests行会向磁盘添加很少的信息。

我还注意到它比使用ID链接表的效率更高,就像原始帖子中那样。 MySQL似乎在做一些魔术,可以用很少的磁盘信息链接列。与存储所有字符串本身相比,它的存储效率至少高出100倍,比使用ID进行链接的效率高出数倍。您还可以获得外键和级联的所有好处。不需要连接就可以在任一方向上查询列,因此查询也非常快!

干杯!