【问题标题】:SmallInt vs Date in MySQL - Performance, Flexibility and SizeMySQL 中的 SmallInt 与 Date - 性能、灵活性和大小
【发布时间】:2012-07-28 07:41:21
【问题描述】:

我正在创建一个表格来存储每周的产品数据,实际上是计数器。

例子:

 id = 1
 productId = 195
 DateTime = 01/07/2012
 Counter = 0

我的问题是关于数据库存储空间、查询灵活性和性能。

我考虑使用 SmallInt 'WeekNumber' 列而不是 DateTime 列。

我将决定星期的开始日期(基准日期)。假设是 2012 年 10 月 10 日。

对于每个产品和每周,都会有一行代表我每天计算的总次数(即特定产品页面的浏览量)。

从我读到的:

日期列是 4 个字节

SmallInt 是 2 个字节

我想节省尽可能多的空间,但我希望能够根据日期范围(2012 年 8 月至 2013 年 9 月)、特定年份的特定星期等查询数据库。

这种架构方法好不好,否则我会发现自己在 SQL 性能、查询灵活性、索引等方面遇到问题。

【问题讨论】:

  • 这听起来真的是毫无意义的微优化。您是否会拥有数百万或数十亿条记录,以便节省的空间实际上很重要?即便如此 - 您是否有真正的存储空间问题需要解决?我会说只是使用适当的日期并完成它
  • 不错,我有 50GB 的限制。每周有 2000 个产品(行)。我需要知道我不会通过它以及性能和查询便利性、索引等方面的其他方面。
  • 但是如果每周有 2000 行,您每周可以节省 4 KB,每年节省 208 KB。节省一兆字节需要十年时间。一万年(大约)可以节省一千兆字节。
  • 嗯,还是这样。我想您可以使用 smallint 并从特定日期开始计算天数,但是您会为此放弃很多便利和速度。如果您每天添加它,也许考虑一些其他方式来防止填满您的空间 - 例如将旧日存档到本地数据库。
  • @IdanShechter 您可以使用 Date/ProductID 作为 PK 并忘记 id 列,没错....请问您的 50GB 限制是如何施加的?

标签: mysql sql


【解决方案1】:

考虑一下为了节省 2 个字节 1 个字节....

为了使用smallint,您将通过一个函数传递对数据的每次调用,以从您自己的任意日期开始获取其“周数”......这既不是更高效也不是更清晰.

同样,查询也不那么灵活,因为每个查询都需要根据您的神奇“开始日期”进行比较,而不仅仅是日期比较/分组。您的查询可能不是 SARGable 并且可能会较慢

编辑:从您的 cmets 中,您有 50GB 的硬限制....对于您正在讨论的聚合数据库来说,这是一个很大的空间。让这件事复杂化是在招致过度的压力和丧失可持续性。

根据 MySQL,DATE 类型只有 3 个字节,而 SMALLINT 的 2 个字节

http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html

因此,您将每行保存 一个字节(您说每周 2000 字节)......所以我们假设每周 2KB,每年 104 KB......

【讨论】:

  • 我可以使用 Date 和 ProductId 作为主键并删除 id 列吗?
  • 好的,非常感谢您的时间和我的新手问题。问题已回答。
【解决方案2】:

如果此表没有子表(没有引用它的外键),为了节省空间,您可以考虑省略代理主键 (id),而使用复合键 (productId,date_) 作为主键。 (根据您的描述,听起来您希望将这些列的组合设为 UNIQUE,并且将这两列都设为 NOT NULL。

如果您要存储的是“周”标识符而不是 DATE,那么在数据库方面没有问题,只要您的查询没有将该列包装在表达式中以获取 DATE 值在谓词中使用。也就是说,为了性能,您的谓词将需要位于裸露的“周标识符”列上,例如

WHERE t.product_id = 195 AND t.week_id >= 27 AND t.week_id < 40

像这样在裸列上的谓词将是 sargable(即,允许使用索引。)您不想将 week_id 列包装在表达式中以返回 DATE,并使用 WHERE 子句在那个表情上。 (在比较的文字方面有表达式不是问题......你只是不希望它们在“表格”方面。

这确实将成为您是否可以使用 week_id 代替 DATE 列的决定因素。

使用“期间 ID”代替 DATE 对于整月的期间实施起来相当简单。 (对于“天”也很简单,但实际上没有什么好处。)在“周”期间实施这种方法更加复杂,因为您需要在两年之间进行一周的处理。

例如,假设今年(2012 年)的最后两天是星期日和星期一,但同一周的星期二到星期六是 2013 年。您需要确定是分开的两个星期,还是是否是同一周。

但是(SMALLINT 与 DATE 相比)节省 1 字节并不是真正的好处。 “week_id”列让你(如我所见)是你有一个标识一周的 id 值。考虑'2012-07-30''2012-07-31''2012-08-01' 的日期值,它们都代表同一周。因此,本周您有多个值,因此 (product_id,date) 上的 UNIQUE 约束并不能真正保证(在数据库方面)您在同一周内没有超过行。 (这当然不是无法解决的问题,你可以指定只存储一个星期日(或星期一)的日期值。)


总之,

为了节省空间,我将首先删除该代理 id 列,并将 product_id 和 DATE 的组合作为主键。

然后我会考虑将 DATE 更改为 SMALLINT,如果我可以保证所有查询都将引用该裸 SMALLINT 列,而不是引用将 SMALLINT 列转换回 DATE 的表达式.

【讨论】:

  • 这是一个非常好的答案,我从中学到了很多。谢谢。
  • @Idan:我一直在您所在的位置,有大量行,并且希望将尽可能多的行打包到尽可能少的块中。我们为月表实现了类似的period_id 列,不是为了节省空间,而是主要是因为应用程序开发人员想要它,因为它更容易让应用程序获取连续的 12 个月。但是对于应用程序来说更容易并不是一个足够的理由,决定因素是一个唯一的整数值来识别一个周期。 (MySQL 没有声明性约束来确保例如 DATE 以“-01”结尾。)
猜你喜欢
  • 2019-05-31
  • 1970-01-01
  • 1970-01-01
  • 2016-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-15
  • 1970-01-01
相关资源
最近更新 更多