2015-08-24 2 views
4

У меня есть большая таблица в базе данных, называемой предложениями (более 300 000 строк).запрос порядка по rand() слишком медленно

, когда я выполняю нижеследующий запрос, он занимает 3 секунды.

$sql = "SELECT * FROM `offers` WHERE (`start_price`/`price` >= 2) ORDER BY RAND() LIMIT 1"; 

Таблица предлагает

`id` int(11) NOT NULL, 
`title` text NOT NULL, 
`description` text NOT NULL, 
`image` text NOT NULL, 
`price` float NOT NULL, 
`start_price` float NOT NULL, 
`brand` text NOT NULL 

есть ли способ сделать это быстрее? Я хочу выбрать один случайный ряд (start_price/price> = 2)

+0

Не заказывайте запрос, но выбирайте случайную строку из вашего результата с помощью php? – Kevin

+0

генерировать случайный идентификатор, а затем передать запрос. Время занимает меньше времени. –

+1

Обратите внимание, что сохранение цен как поплавок немного странно. И я думаю, что быстрее сказать: цена * 2 Strawberry

ответ

3

Я думаю, ваша проблема в том, что ваш запрос требует полного сканирования таблицы для предложения WHERE. order by делает все хуже - в зависимости от объема, проходящего через фильтр.

Вы могли бы рассмотреть хранения этого числа в таблице и добавление индекса к нему:

alter table offers add column start_to_price float; 

update offers 
    set start_to_price = start_price/price; 

create index idx_offers_s2p on offers(start_to_price); 

Затем ваш запрос может быть быстро:

SELECT o.* 
FROM `offers` o 
WHERE start_to_price >= 2 
ORDER BY RAND() 
LIMIT 1; 

Если производительность все еще остается проблемой, то Я бы, вероятно, использовать where пункт первый:

SELECT o.* 
FROM `offers` o CROSS JOIN 
    (select COUNT(*) as cnt from offers where start_to_price >= 2) oo 
WHERE rand() <= 10/cnt 
ORDER BY RAND() 
LIMIT 1; 

Это вытягивает ABOU t 10 строк в случайном порядке, а затем выбирает один из них.

Если они не работают, тогда есть другие решения, которые становятся все более сложными.

+0

с использованием перекрестного соединения производительности улучшилось, но я хотел бы знать, есть ли еще более сложное решение. Спасибо! – DMande

0

Есть альтернативы. Один я использовал описано здесь: -

http://jan.kneschke.de/projects/mysql/order-by-rand/

По сути вы генерировать случайное число, которое между минимальным и максимальным идентификатором, а затем присоединиться, что против результирующего набора (используя> =), с предел 1. Таким образом, вы получаете набор результатов, начиная со случайной точки в ваших полных результатах, а затем просто захватываете первую запись.

вниз сторона в том, что если вы поля идентификаторов распределяются неравномерно, то это не действительно случайные

Быстрый пример кода, предполагая таблицу оферты имеет уникальный ключ называется идентификатор: -

SELECT offers.* 
FROM offers 
INNER JOIN 
(
    SELECT RAND() * (MAX(Id) - MIN(Id)) + MIN(Id) AS Id 
    FROM offers 
    WHERE (`start_price`/`price` >= 2) 
) AS r2 
ON offers.Id >= r2.Id 
WHERE (`start_price`/`price` >= 2) 
ORDER BY offers.Id LIMIT 1 
+0

Из-за 'INNER JOIN' этот запрос еще хуже уступает оригиналу. Он полностью сканирует таблицу дважды. – axiac

+0

Если вы можете избежать проблемы с вычислением (добавив дополнительный столбец для вычисленного значения, предложенный Гордоном, то это решение должно выполняться гораздо быстрее, чем при использовании ORDER BY RAND(). – Kickstart

+0

Учитывая, что есть индекс в столбце 'id' (ОП не упоминает ничего об индексах, которые они имеют в таблице), действительно, ваш запрос кажется более быстрым. – axiac

1

Один из вариантов, чтобы сделать это быстрее, чтобы убедиться, что вы использовать индексацию:

How does database indexing work?

http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html

В этом случае убедитесь, что у вас есть индекс для start_price вместе с price и в этом точном порядке.

Другим способом является оптимизация коалиции, используемой для базы данных и таблиц, поэтому выберите utf8mb4 над utf8, и если сортировка/локализация не является проблемой для вас, и вы хотите быть полностью анальным, то general_ci над unicode_ci:

What's the difference between utf8_general_ci and utf8_unicode_ci

Несмотря на двигатель хранения MyISAM доставки быстрее скорости чтения (http://www.rackspace.com/knowledge_center/article/mysql-engines-myisam-vs-innodb) Я обнаружил, что существуют различные твики, доступные для механизма хранения InnoDB, который может ускорить вещи больше, чем я смог достичь с помощью MyISAM :

https://dba.stackexchange.com/questions/5666/possible-to-make-mysql-use-more-than-one-core?lq=1

Так что-то вроде следующего будет другой вариант:

[mysqld] // Don't play here unless you have read and understand what is going on 
innodb_read_io_threads=64 
innodb_write_io_threads=64 
innodb_buffer_pool_size=2G 

Еще один вариант взглянуть на альтернативные двигатели хранения: https://www.percona.com/software/mysql-database/percona-server/benchmarks

Вы также можете увидеть другие ответы на рефакторинга вашего запроса :)

+0

Это очень общий комментарий. Согласился с тем, что индексирование таблицы правильно ускорилось - как правило, но в этом случае у нас также есть «ORDER BY RAND()»: как бы индексирование помогло улучшить это, точно? –

+0

@DmitriSologoubenko Я отбросил этот комментарий. После более тщательного изучения вопроса я обнаружил, что запрос медленный из-за предложения WHERE. индекс все еще не помогает, но это еще одна дискуссия. – axiac

+0

@DmitriSologoubenko. Я предложил сначала индексировать, поскольку он цитирует 3 секунды на 300k rec ords. Конечно, я понятия не имею, какое это оборудование, но кажется очень медленным, и похоже, что индексов нет. Как бы то ни было, я считаю, что это достойное решение. –

Смежные вопросы