这是我的第一个与可扩展性相关的问题。

为了简化问题,我将使用宾果应用程序的想法:

我们有一个宾果游戏应用程序。每个用户都有一张彩票,里面有 90 个随机数中的 15 个。每周都会举行宾果游戏以寻找获胜者。 实时抽取数字 直到出现赢家。前任:

  • 第 15 个号码被抽中 -> 搜索表 -> 没有匹配
  • 第 16 个号码被抽中 -> 搜索表 -> 没有匹配
  • ...
  • 开出第 30 个号码 -> 搜索表 -> 获胜者 -> 停止

  • 问题 1:

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

    想法1:

    table 票
    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>
    

    php
    $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 中完成的某种子字符串函数/正则表达式/LIKE。

    想法2:

    table 票
    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>
    

    php
    $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 周到来时会发生什么?我应该使用同一张 table ,添加一个条件 WHERE week=2 ,还是每张 table 只有 1 周更好?

    更新

    在原游戏中,一张票有 3 行 15 个号码,每行有 5 个号码。在每个号码被现场抽中后,他们还可以计算出在抽中号码中找到一行的彩票(他们也知道有两行的彩票)。在抽奖号码中找到 3 行的彩票将意味着中奖彩票。

    这些信息让我认为表示看起来像:

    想法4:

    table 票
    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.
    

    最佳答案

    我将使用两个 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
    

    现在 or'ing together bits 给你一对数字来代表一张中奖彩票。为此任务使用 | 运算符。

    每个玩家的当前球也将是 or'd - 每次播放后都会打开一个新位。

    然后测试变成这样:
    -- 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 ,卡片上的每一列一个。

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

    重新想法 4

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

    关于php - 用于可扩展性的宾果游戏数据库表示,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49098520/

    10-11 14:56