Оптимизация случайных запросов mysql - PullRequest
0 голосов
/ 18 марта 2011

У меня есть две таблицы:

Table GAME: id(int), added_at(bigint)
Table META: id_game(int), meta(VARCHAR(64))

Теперь каждая игра может иметь 0 или более метатегов, связанных с ней.Я пытаюсь получить 9 игр:

  • 1 случайная игра с "избранной" META
  • 1 случайной игрой с "премиум" META
  • 1 случайная игра с "doublepoints "META
  • 3 последние игры (ORDER BY добавлен_ 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 запросов, и рандомизация не совсемСлучайные.

Итак, мои три цели и возможные решения:

  1. Как сделать, чтобы 3 случайные игры не повторяли ни одну из вышеупомянутых игр.После выбора первых шести игр я, вероятно, мог бы перечислить их идентификаторы и использовать их в последнем запросе с помощью NOT IN (), но это не было бы слишком оптимизировано.
  2. Как сделать случайный выбор игр случайным образом, не выбираяслучайное смещение и взятие с него n игр?Использование ORDER BY RAND () очевидно, но я слышал очень плохие вещи о том, насколько медленно это происходит, хотя я полагаю, что если в моей таблице нет сотен строк, это не имеет значения?
  3. Как уменьшить количествозапросы?Сгруппируйте первые три запроса в один, у меня осталось 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 (), и некоторые тесты, которые я видел, заставили его выглядеть ужасно медленным.Любые советы, чтобы улучшить его еще больше?

1 Ответ

1 голос
/ 18 марта 2011

Таблица игр:

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.

Повторите приведенное выше утверждение, пока не наберете достаточное количество данных.,Предупреждения, усеченные данными, можно игнорировать, они являются артефактом вставки 0 в столбец enum (), что приводит к игре без флага, чего мы и хотим.

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)

Затем удалите 9 значений из случайного числа, чтобы при последующем использовании таблицы было сгенерировано 9 новых значений (или оставлены 3 последние вещив, если хотите).

...