优化mysql随机查询
Posted
技术标签:
【中文标题】优化mysql随机查询【英文标题】:Optimizing mysql random queries 【发布时间】:2011-03-18 10:53:24 【问题描述】:我有两张桌子:
Table GAME: id(int), added_at(bigint)
Table META: id_game(int), meta(VARCHAR(64))
现在,每个游戏都可以有 0 个或多个与之相关的 Meta 标签。我正在尝试检索 9 个游戏:
1 个带有“特色”META 的随机游戏 1 个带有“高级”META 的随机游戏 1 个带有“双点”META 的随机游戏 3 款最新游戏(ORDER BY added_at DESC) 3 个随机游戏,不属于上述 6 个游戏到目前为止,我有一个非常古怪的系统,它看起来或多或少是这样的:
$feat = getGameMetaRandom(1, 'featured');
$prem = getGameMetaRandom(1, 'premium');
$dubl = getGameMetaRandom(1, 'doublepoints');
$last = getGameLatest(3);
$rand = getGameRandom(3);
目前每个 random 函数需要两个查询(来自getGameMetaRandom($count, $meta);
):
SELECT FLOOR(RAND() * (COUNT(*) - " . ($count - 1) .")) AS `offset`
FROM table_meta WHERE meta = '$meta'
SELECT t1.* FROM table_meta t2
LEFT JOIN table_game t1 ON t1.id = t2.id_game
WHERE t2.meta = '$meta' LIMIT $offset, $count
(gameRandom 非常相似)正如您所见,这忽略了我对 上述 6 种游戏中的任何一种都没有的限制,而且所有这些都需要 9 次查询,并且随机化并不是真正随机的。
所以我的三个目标和可能的解决方案是:
-
如何使 3 个随机游戏不重复任何前面选择的游戏。选择前六场比赛后,我可能会列出他们的 ID,并在最后一个查询中使用 NOT IN (),但这不会过度优化。
如何让随机抽取游戏随机,而不是选择随机偏移并从中抽取 n 场游戏?显然使用 ORDER BY RAND() ,但我听说它有多慢真的很糟糕,尽管我想除非我的表有数百行它没有任何区别?
如何减少查询次数?将前三个查询组合成一个,剩下 5 个查询,或者通过使用 ORDER BY RAND() 我可以忽略第一个“偏移检索”查询并使用类似
SELECT t1.* FROM table_meta t2 LEFT JOIN table_game t1 ON t1.id = t2.id_game WHERE t2.meta = '$meta' ORDER BY RAND() LIMIT $count
但是,这通常需要使用 ORDER BY RAND() 并且我看到的一些测试使它看起来非常慢。有什么提示可以进一步改进吗?
【问题讨论】:
三个问题,没有一个是直接的或琐碎的...... @Richard aka cyberkiwi:我猜。我已经添加了一些可能的解决方案以及我对它们的担忧,如果这使事情更容易考虑的话。我是 mysql 的新手,对它几乎一无所知。 【参考方案1】:游戏桌:
root@localhost [kris]> show create table games\G
*************************** 1. row ***************************
Table: games
Create Table: CREATE TABLE `games` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`flags` enum('features','premium','doublepoints') NOT NULL,
`added_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8184 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
一个示例游戏:
root@localhost [kris]> insert into games values ( NULL, floor(rand() * 4 ), now() - interval 1200 second);
Query OK, 1 row affected, 1 warning (0.00 sec)
Note (Code 1592): Statement may not be safe to log in statement format.
更多示例游戏:
root@localhost [kris]> insert into games select NULL, floor(rand() * 4), now() - interval 1200 second from games;
Query OK, 1 row affected, 1 warning (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
Note (Code 1592): Statement may not be safe to log in statement format.
重复上述语句,直到有足够的样本数据。数据截断警告可以忽略,它们是在 enum() 列中插入 0 的产物,导致游戏无标志,这正是我们想要的。
root@localhost [kris]> select count(*) from games;
+----------+
| count(*) |
+----------+
| 8192 |
+----------+
1 row in set (0.00 sec)
我们创建一个随机的游戏列表:
root@localhost [kris]> create table shuffle like games;
Query OK, 0 rows affected (0.09 sec)
root@localhost [kris]> alter table shuffle modify column id integer not null, drop primary key, add column shuffleid integer not null auto_increment, add primary key (shuffleid), add index(flags), add index(added_at), add index(id);
Query OK, 0 rows affected (0.13 sec)
Records: 0 Duplicates: 0 Warnings: 0
洗牌:
root@localhost [kris]> insert into shuffle select id, flags, added_at, NULL from games order by rand();
Query OK, 8192 rows affected, 1 warning (0.34 sec)
Records: 8192 Duplicates: 0 Warnings: 0
Note (Code 1592): Statement may not be safe to log in statement format.
现在只需获取您需要的内容:
root@localhost [kris]> select min(id) as id from shuffle where flags = 'premium'
union all select min(id) from shuffle where flags = 'features'
union all select min(id) from games where flags = 'doublepoints'
union all ( select id from shuffle order by added_at limit 3 );
+------+
| id |
+------+
| 8216 |
| 8214 |
| 8218 |
| 8213 |
| 8214 |
| 8216 |
+------+
6 rows in set (0.00 sec)
在第二次查询中选择3个不在上述集合中的随机行效率更高:
root@localhost [kris]> select id from shuffle where id not in ( 8216, 8214, 8218, 8213, 8214, 8216) limit 3;
+------+
| id |
+------+
| 8215 |
| 8219 |
| 8220 |
+------+
3 rows in set (0.00 sec)
然后从 shuffle 中删除 9 个值,以便后续使用该表将生成 9 个新值(或者如果您愿意,可以保留 3 个最近的东西)。
【讨论】:
以上是关于优化mysql随机查询的主要内容,如果未能解决你的问题,请参考以下文章