Bingo游戏数据库表示可伸缩性

时间:2018-03-04 17:57:35

标签: php mysql sql performance database-design

这是我第一个与可伸缩性相关的问题。

为了简化问题,我将使用宾果游戏的想法:

我们有一个宾果应用程序。每个用户都有一张包含15个随机数的门票。每周都会举行宾果游戏以找到获胜者。在获胜者之前,数字会被直播。例如:

  • 绘制第15个数字 - >搜索表 - >没有比赛
  • 绘制第16个数字 - >搜索表 - >没有比赛
  • ...
  • 绘制第30个数字 - >搜索表 - >赢家 - >停止

问题1:

哪种方式更好/更快地表示表中的数据和搜索该表?表将有超过10万行

想法1:

表门票

id           user_id          week    ticket                                                 created
====================================================================================================
1            100022312        1       1,3,5,7,9,14,15,77,78,79,80,81,82,83,84            <timestamp>
2            102232123        1       2,5,9,22,33,44,55,66,77,,78,79,80,88,89            <timestamp>
3            201141028        1       7,8,9,11,22,33,34,35,37,39,51,55,58,63,66          <timestamp>
...
9.000.000    126387125        1       8,18,28,38,48,58,68,78,79,80,81,82,83,84,85        <timestamp>
10.000.000   126387126        1       1,4,14,24,34,45,56,66,67,68,79,80,81,82,83         <timestamp>

$drawn_numbers = '1,2,3,4,5,6,7,89,10,11,12,13,14,15,16,17,18,19,20,21,22, ...';

$result = query("SELECT * FROM Tickets WHERE sksf('$drawn_numbers')");

其中sksf将是MySql中完成的某种子串函数/ regex / LIKE。

想法2:

表门票

id           user_id          week    n1   n2   n3   ...   n15        created
=============================================================================
1            100022312        1       11   32   52   ...   76     <timestamp>
2            102232123        1       22   52   55         78     <timestamp>
3            201141028        1       77   82   83   ...   89     <timestamp>
...
9.000.000    126387125        1       81   55   32   ...   10     <timestamp>
10.000.000   126387126        1       12   42   13   ...   77     <timestamp>

$drawn_numbers = '1,2,3,4,5,6,7,89,10,11,12,13,14,15,16,17,18,19,20,21,22, ...';

$result = query("SELECT * FROM Tickets WHERE contidion1 AND condition2 AND ...");

不幸的是,在这里我仍然对这些条件一无所知。

想法3:

我选择所有故障单,通过检查所绘制的数字是否包含任何故障单来迭代它们。

$drawn_numbers = '1,2,3,4,5,6,7,89,10,11,12,13,14,15,16,17,18,19,20,21,22, ...';
$all_tickets = query("SELECT * FROM Tickets");

foreach ($all_tickets as $ticket) {
    if $drawn_numbers.contains($ticket['ticket'])
        return $ticket['id'];
}

问题2:

无论如何,排序的数字会有帮助吗? (这15个数字和绘制的数字)

问题3:

第2周到来会发生什么?我应该使用相同的表格,添加条件WHERE week=2,还是每张桌子只有1周更好?

更新

在原始游戏中,一张票有3行15个数字,每行有5个数字。在实时绘制每个数字之后,他们还能够计算在绘制的数字中找到一行的票证(他们也知道具有2行的票证)。在抽取的数字中找到3行的票证将意味着中奖票。

这些信息让我觉得代表看起来像是:

想法4:

表门票

id           user_id          week    row1                row2                 row3                     created
===============================================================================================================
1            100022312         1      1, 3, 5, 7, 9       14,15,77,78,79,      80,81,82,83,84       <timestamp>
2            102232123         1      2, 5, 9,22,33,      44,55,66,77,78,      79,80,87,88,89       <timestamp>
3            201141028         1      7, 8, 9,11,22,      33,34,35,37,39,      51,55,58,63,66       <timestamp>
...
9.000.000    126387125         1      8,18,28,38,48,      58,68,78,79,80,      81,82,83,84,85       <timestamp>
10.000.000   126387126         1      1, 4,14,24,34,      45,56,66,67,68,      79,80,81,82,83       <timestamp>

票证示例:

__  13  __  33  40  __  __  70  __
 2  __  22  37  __  52  62  __  81
__  19  23  __  44  __  63  __  89

绘制数字的示例(不一定按排序顺序)

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 19, 23, 44, 63, 89
-> there is a winner, no more numbers are drawn.
-> Our ticket did not win jackpot, but we won one row [19, 23, 44, 63, 89] (free ticket)
-> One can win also 2 rows.

2 个答案:

答案 0 :(得分:2)

我会使用两个BIGINT列,并且每个位代表一个宾果值(1..90)。构建一组球需要一些操作,但搜索将很容易,存储将 更紧凑等等。

我们有两列,一列是数字1..60,另一列是61..90。 (选择有点随意,但可以很容易地进行可视化。)现在我们可以使用一个BIGINT和一个INT来完成。

IF($value <= 60, 1 << $value, 0) -- the bit for the BIGINT
IF($value >  60, 1 << ($value - 60), 0)  -- the bit for the INT

现在或者在一起比特会给你一对数字来代表中奖彩票。使用|运算符执行此任务。

每个玩家当前的球也将被或 - 每次比赛后都会打开一个新位。

然后测试变成这样:

-- The balls owned by a user:
user_60 BIGINT,
user_30 INT

-- winning balls (12 rows, a total of 5 bits on for each row)
win_60 BIGINT,
win_30 INT

-- Note: FREE SPACE should be pre-populated in both structures

BIT_COUNT(user_60 & win_60) +
BIT_COUNT(user_30 & win_30)  = 5  -- he's a winner!

我遗漏了一个重要的步骤 - 在每个用户的主板上安排数字。这将需要一些前期工作,特别是因为15个数字只能在卡片的第一列。等

你的回答是从而不是其他结构。

另一个想法是有5 SMALLINT UNSIGNED,每张卡上有一个。

(将来,在MySQL 8.0中,BINARY(16)将允许单个列表示一组90个值,从而使代码更清晰。)

重新构思4

构建3位模式 - 每个“行”一个,带有5个数字。将它们与发行的票证进行比较:Foreach票,比较3种模式;算一下多少匹配。

答案 1 :(得分:0)

都不是。

一种方法是use a bitmap票号。您需要2个BIGINT来存储值。如果票号和所选号码的AND返回与票号相同的值,则表示您匹配。但你不能使用索引。

如果仅通过匹配某些数字来赢得胜利,那么您引用的两种方法都会非常低效 - 您需要规范化数据:

 create table ticket (
   id integer auto_increment,
   user_id /* appropriate type for FK to your data about users */,
   week_number integer,
   PRIMARY KEY (id),
 );
 create table numbers (
   number integer not null,
   ticket_id integer not null,
   FOREIGN KEY ticket_id references ticket(id)
 );

这看起来像是一个精心设计的场景来测试可扩展性方法 - 您是否要求我们在这里做作业?