这是我的第一个与可扩展性相关的问题。
为了简化问题,我将使用宾果应用程序的想法:
我们有一个宾果游戏应用程序。每个用户都有一张彩票,里面有 90 个随机数中的 15 个。每周都会举行宾果游戏以寻找获胜者。 实时抽取数字 直到出现赢家。前任:
问题 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/