【问题标题】:How to store and search efficiently huge MySQL lottery database如何高效地存储和搜索庞大的 MySQL 彩票数据库
【发布时间】:2014-03-12 22:23:26
【问题描述】:

每周超过 100 万张门票。用户选择 1 到 49 之间的 6 个号码。所有号码(门票)将无限期保留。每周都必须选出获胜者。

在一张表中,我有所有具有 ID 号和唯一电子邮件的用户。在门票表中,我有唯一门票 ID、引用用户 ID 的外键、选定的数字字段和时间戳。在某些情况下,一个用户在一周内可以拥有多张票。

  1. 存储此类数据最有效的方法是什么?更具体地说,存储数字的数据类型,请记住会有大量条目,并且还需要不时搜索。

  2. 使用您提出的数据结构,选择本周选择 4 个中奖号码 5.. 和 6 的所有用户的有效方法是什么。

我看到了一个将其存储为二进制的想法,因此 3、6 将是 001001...鉴于我认为自己是一个普通的程序员,这对我来说似乎是天才。易于搜索并且似乎存储的字节数很少(尽管我不知道 MySql 是如何准确存储其数据的)。 有没有更好的办法?我认为该方法的唯一缺点是人类不易阅读。

更新:二进制想法的链接:https://stackoverflow.com/a/1931286/2374034

【问题讨论】:

  • 不是在彩票中买票,希望不是真票
  • 我已经为下周的结果编写了一个算法。有兴趣吗?
  • 您确实明白,如果您想要 49 个不同的选项,您需要一个 64 位数字才能能够存储它,对吗?因为人们可以存储 0 到 2^49(!!) 之间的任何内容,这是一个巨大的数字。
  • 这会花费你的! ;-)
  • @Tularis 需要 49 位...选择的第 N 位表示球的数量。 stackoverflow.com/a/1931286/2374034

标签: php mysql


【解决方案1】:
  1. 整数存储是高效的。一个 TINYINT UNSIGNED 列使用一个字节并且可以存储从 1 到 255 的整数。我假设保持选择数字的正确顺序很重要,并且它们总是有 6 个。因此,我建议您使用 6 TINYINT UNSIGNED 列作为数字。这可能比二进制更好。

  2. 我建议一个星期表和一个带有 week.id、week.name、win.id、win.week_id、win.user_id、win.match_count 的 win 表。

【讨论】:

  • 总是 6 个数字,这是正确的,但顺序并不重要。考虑过维持秩序,但找不到在这种特殊情况下重要的原因。为什么 SET 数据类型不合适?关于问题 #2,我的意思是实际的 SELECT 语句来搜索正确的 4,5 或 6 个数字的用户,抱歉不清楚。
【解决方案2】:

如果要单独存储六个值,则每个数字都需要一个TINYINT,因此总共需要六个 1 字节的列。这是您最好的选择。

您可以通过将列声明为 NOT NULL 来为每列节省一点额外开销。

其他选项没有那么紧凑:

  • 如果数字的范围是 1-49,那么每个数字实际上并不需要 8 位。每个数字只需要 6 位(将 0-48 存储在 6 位中)。

    因此,您可以将 1-49 的六个数字存储在 36 位中。 INT UNSIGNED 是 32 位,所以太小了 但是 BIGINT 可以存储 64 位。 32 到 64 位之间没有 INT 类型。

    要将这六个数字存储在单个 BIGINT 中,请将每个数字移位 6 位,然后将它们按位或运算。

    INT = (A-1) | ((B-1)<<6) | ((C-1)<<12) | ((D-1)<<18) | ((E-1)<<24) | ((F-1)<<30)
    

    结果不会是人类可读的,但至少它是紧凑的。

  • 要使用 SET 存储 1-49 范围内的六个选项的位域,您至少需要 49 位(每个选择的可能数字一位),因此您至少需要 7无论如何字节。 MySQL 的 SET 以 1、2、4 或 8 字节为增量存储,具体取决于不同的 SET 元素的数量。这需要 8 字节大小。

  • MySQL 还有一个BIT 数据类型,你可以声明一个BIT(36) 列。但是这种数据类型以 4 字节为增量使用空间,因此无论如何您最终都会使用 64 位每张票。

最终,您说的是每张售出票的 TicketID + UserID + 6xTINYINT,所以每行可能有 16 个字节。不过,有一些开销。我刚刚测试了将 1048576 行插入到具有此定义的表中。存储大约需要 40MB。

因此,您可以指望每年大约需要 40MB * 52 周 = 2058MB。但是现在,你几乎买不到低于 500GB 的硬盘,所以我想你会没事的。当你装满一个普通的驱动器时,无论如何都是升级到量子计算机的时候了。 :-)


你的评论:

是的,您可以在日期上定义索引并使搜索非常有效。定义正确的索引必须由您需要运行的查询来确定。

或者您可以使用PARTITION BY 定义表并使用日期(或星期)作为分区键。但是,分区时要小心,它并不总是灵丹妙药。你应该仔细阅读它的limitations


你将如何提取所有 6 个数字中的 4 个正确的票?

在 MySQL 中,布尔条件产生 1 或 0,然后您可以在算术中使用它们。

SELECT * FROM tickets
WHERE (A=?) + (B=?) + (C=?) + (D=?) + (E=?) + (F=?) >= 4

这势必会导致表扫描,但无论您使用哪种解决方案来存储数据,您都会遭受这种痛苦。

【讨论】:

  • 我很想使用 SET 数据类型,因为它易于阅读,而且(至少在我的脑海中)用于查找获胜者的 select 语句看起来很简单。关于空间使用,几场演出当然不是问题,但搜索效率呢?由于我只需要在上周插入的条目中进行搜索,如果我有 100 万或 1000 万个条目,在 date1 和 date1+7days 之间进行搜索会很重要吗?我在想(希望)MySQL 可以轻松地在 date1 和 date2 之间“剪切”条目,然后通过它进行搜索。
  • 很抱歉成为一个唠叨者,但似乎我对我的第二个问题不是很清楚。虽然我将使用二进制或 SET 数据类型,但我仍然很好奇使用 6 个 TINYINT 列时选择查询的外观。您将如何提取正确的 6 个数字中的 4 个的所有票?
  • 如果选择获胜者的查询很频繁,您有理由将获胜者存储(缓存)在可以通过索引访问的获胜表中。
  • @TomHaws,是的,我同意非规范化可以是一种合法的优化。
猜你喜欢
  • 2013-10-31
  • 2011-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
相关资源
最近更新 更多