Ter*_*ium 0 mysql performance mariadb
我读了很多类似的帖子,但我不明白该选择什么.从软件角度来看,它是游戏排行榜.所有排行榜或500张小桌子的一张桌子,每个游戏级别一张?
我测试了两种变体,并发现:
1个大表工作得更慢(创建了所有需要的索引).
应将1个大表分区为至少10个文件,以获得足够的速度.
500张小桌子不是那么方便,但速度快了两倍(50M大桌子和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 */
Run Code Online (Sandbox Code Playgroud)
排名查询:
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
Run Code Online (Sandbox Code Playgroud)
更新
我已经了解了索引,最终得到了大表(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;
Run Code Online (Sandbox Code Playgroud)
对于小型(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;
Run Code Online (Sandbox Code Playgroud)
具有长文字用户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;
Run Code Online (Sandbox Code Playgroud)
对于测试,我使用了这些查询:
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 ) ;
Run Code Online (Sandbox Code Playgroud)
我喜欢使用一个大表的想法,但是,虽然我在两个查询中得到了类似的时间(小0~0,050和大0~0,065),解释仍然让我有点困惑:对于小桌子
类型| 关键| key_len | ref | 行| 额外
指数; 得分了; 4; (空值); 50049; 使用where,使用索引
对于大桌子:
REF; 小学2; 常量; 164030; 在哪里使用
如您所见,在小表中扫描的行数减少了3倍.所有表中的数据都相同,level20填充了查询:
INSERT INTO level20 (user_id, score, timestamp) SELECT user_id, score,
timestamp FROM leaderboard WHERE level_no=20;
Run Code Online (Sandbox Code Playgroud)
另一个更新
今天用表进行了实验,发现将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
Run Code Online (Sandbox Code Playgroud)
我的查询在两个表上的工作方式几乎相同.我有一种感觉,中等量的表更好,我离开它,但仍然有点困惑.
你的疑问有点奇怪.试试这个
SELECT COUNT(*)
FROM leaderboard
WHERE level_no = 0 and score > $current_score
Run Code Online (Sandbox Code Playgroud)
这里的ORDER BY是没有意义的,因为这个查询只能返回一行:它是一个没有任何的聚合查询 GROUP BY.
五百张桌子是一个糟糕的主意.您的管理任务将非常不愉快.
此外,对表进行分区很少有助于查询性能.在您提议的情况下,分区hash(id),肯定会破坏您所显示的查询的性能; 每个查询都必须读取每个分区.
把事情简单化.一张桌子.当它变得相当大时,使用EXPLAIN来分析您的查询性能,并考虑添加适当的复合索引.
不要创建不需要的索引.它们减慢了插入速度并浪费了硬盘空间.阅读 http://use-the-index-luke.com/.
编辑 MySQL是为这种具有5亿行的四长字表构建的.如果您有耐心并了解索引,您将获得此工作.不要浪费数百个小桌子或分区的不可替代的时间.但是,更多RAM可能有所帮助.
InnoDB的最佳性能是确保所有常用数据都适合缓冲池.使用您发布的表结构,看起来您需要大约500MB的缓冲池空间来保留缓冲池中的所有数据.
排行榜的更好结构是:
CREATE TABLE IF NOT EXISTS `leaderboard` (
`user_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`level_no` SMALLINT(5) UNSIGNED NOT NULL,
`score` int(10) NOT NULL,
`timestamp` int(10) 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;
Run Code Online (Sandbox Code Playgroud)
变化:
timestamp和user_id列是UNSIGNED:扩展用户ID的范围,我假设你没有使用负时间值,当前的unix时间戳高于有符号的范围.TIMESTAMP类型:TIMESTAMP使用4个字节,INT但显示为日期时间.level_no索引:它与level_no_score索引是冗余的,因为索引的前缀可以用来代替整个索引.(level_no, user_id)如果经常在查询中使用这些列并删除不需要的列(id),则使用主键将有所帮助.InnoDB仅在未明确定义主键时隐式创建主键,因此仅创建用作主键的id列是浪费.
"正确"的主索引还取决于数据和访问模式.表中有什么独特之处?是真的level_no,user_id还是只是用户?如果它只是user_id一个可能是一个更好的主键.
| 归档时间: |
|
| 查看次数: |
575 次 |
| 最近记录: |