【发布时间】:2015-06-22 02:13:15
【问题描述】:
我已经阅读了很多类似的帖子,但我不知道该选择什么。 从软件的角度来看,它是游戏排行榜。一张桌子用于所有排行榜或 500 个小桌子,每个游戏级别一张?
我已经测试了这两种变体,并发现:
1 个大表运行速度较慢(创建了所有需要的索引)。
1 个大表应至少分区为 10 个文件以获得足够的速度。
500 个小表没那么方便,但快了一倍(50M 大表 vs 100K 小表)
500 个小表不需要分区(我听说在 mysql 中存在一些问题,可能在 MariaDB 10.0 中我使用的所有内容都已修复,但以防万一)
这里唯一的问题可能是同时打开了许多表。在阅读 phpMyAdmin 中的设置建议之前,我并不认为这是一个问题,所以现在我怀疑我应该使用那么多表吗?
以防万一这里有模式。 “小”表:
CREATE TABLE IF NOT EXISTS `level0` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT '0',
`score` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`),
KEY `score` (`score`),
KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE IF NOT EXISTS `leaderboard` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT '0',
`level_no` int(11) NOT NULL,
`score` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `level_no` (`level_no`),
KEY `score` (`score`),
KEY `timestamp` (`timestamp`),
KEY `lev_sc` (`level_no`,`score`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY HASH (id)
PARTITIONS 10 */
排名查询:
SELECT COUNT(score) FROM level0 WHERE score > $current_score
ORDER BY score desc
SELECT COUNT(score) FROM leaderboard WHERE
level_no = 0 and score > $current_score ORDER BY score desc
更新
我已经了解了索引,并最终得到了大表(20M 行)的以下架构:
CREATE TABLE IF NOT EXISTS `leaderboard` (
`user_id` int(11) NOT NULL DEFAULT '0',
`level_no` smallint(5) unsigned NOT NULL,
`score` int(11) unsigned NOT NULL,
`timestamp` int(11) unsigned NOT NULL,
PRIMARY KEY (`level_no`,`user_id`),
KEY `user_id` (`user_id`),
KEY `score` (`score`),
KEY `level_no_score` (`level_no`,`score`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
对于小型(100K 行,从 level_no=200 的排行榜获得):
CREATE TABLE IF NOT EXISTS `level20` (
`user_id` int(11) NOT NULL DEFAULT '0',
`score` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`user_id`),
KEY `score` (`score`),
KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
具有长文字用户 ID 的共享表:
CREATE TABLE IF NOT EXISTS `player_ids` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`store_user_id` char(64) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `store_user_id` (`store_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
对于测试,我使用了这些查询:
SELECT COUNT(*) AS rank FROM level20 lev WHERE score >
(SELECT score FROM level20 lt INNER JOIN player_ids pids ON
pids.id = lt.user_id WHERE pids.store_user_id='3FGTOHQN6UMwXI47IiRRMf9WI777SSJ6A' );
SELECT COUNT(*) AS rank FROM leaderboard lev WHERE level_no=20 and score >
(SELECT score FROM leaderboard lt INNER JOIN player_ids pids ON
pids.id = lt.user_id WHERE pids.store_user_id='3FGTOHQN6UMwXI47IiRRMf9WI777SSJ6A' and level_no=20 ) ;
我喜欢使用一张大表的想法,但是,虽然我在两个查询中得到了相似的时间(小约 0,050 和大约 0,065),但解释仍然让我有些困惑: 适合小桌子
类型 |关键 | key_len |参考 |行 |额外的
索引;分数; 4; (空值); 50049;使用where,使用索引
对于大桌子:
参考;小学二年级;常量; 164030;在哪里使用
如您所见,小表中扫描的行数减少了 3 倍。所有表中的数据都是相同的,level20被查询填充:
INSERT INTO level20 (user_id, score, timestamp) SELECT user_id, score,
timestamp FROM leaderboard WHERE level_no=20;
另一个更新
今天对表格进行了实验,发现将 int 更改为 medium int 几乎不会改变表格的大小。这是优化后的统计数据(重新创建+分析):
#medium ints
CREATE TABLE IF NOT EXISTS `leaderboard1` (
`user_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
`level_no` smallint(5) unsigned NOT NULL DEFAULT '0',
`score` mediumint(8) unsigned NOT NULL DEFAULT '0',
`timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`level_no`,`user_id`),
KEY `score` (`score`),
KEY `level_no_score` (`level_no`,`score`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Data 628 Mb
Index 521.6 Mb
Total 1.1 Gb
#ints
CREATE TABLE IF NOT EXISTS `leaderboard` (
`user_id` int(11) NOT NULL DEFAULT '0',
`level_no` smallint(5) unsigned NOT NULL,
`score` int(11) unsigned NOT NULL,
`timestamp` int(11) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`level_no`),
KEY `score` (`score`),
KEY `level_no_score` (`level_no`,`score`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Data 670 Mb
Index 597.8Mb
Total 1.2 Gb
我的查询在两个表上的工作方式几乎相同。我觉得中等整数的表更好,我离开了它,但还是有点困惑。
【问题讨论】:
-
现在如果你想搜索你所在的所有记录来寻找一个额外有趣的选择语句......
-
有任何关系吗?如果没有,请考虑使用 MongoDB、其他 NoSQL 或其他形式的 JSON 存储可能会比 MySQL 更快。
-
@DanWhite 它与配置文件表、用户 ID 表(每个 64 个符号)、统计事件表、GCM 表等有关。当然它们可以单独存在,但我更喜欢在一台服务器上。而且我不知道MongoDB。也许 MariaDB/mysql 中没有 nosql 表引擎?还是 nosql 完全意味着另一个软件平台?
-
@JoshuaByer 是的,但是我一次需要所有表的唯一地方是 cron-launched 缓存任务“选择 user_id,从 t0 order by score limit 50 union all .... select user_id ,从 t500 开始按分数限制 50 排序。但它非常快,每小时发生一次。
-
您是如何决定需要对大表进行分区的?基于结构和查询,这样的分区将无济于事,并且可能会减慢查询速度。大表中的
level_no索引是多余的,因为(level_no, score)上也有一个索引。如果行在级别和用户上是唯一的,则最好分别使用(user_id)和(level_no, user_id)作为小型表和大型表的主键。您的时间戳列也应该是无符号的。
标签: mysql performance mariadb