Введение
Мне нравится ваш вопрос, поэтому я немного поиграл с MySQL и попытался найти источник проблемы. Для этого я создал несколько тестов.
Данные
Я сгенерировал 100.000 строк образцов данных, используя инструмент под названием Генератор случайных данных (я думаю, документация немного устарела, но она работает). Файл конфигурации, который я передал gendata.pl
, выглядит следующим образом.
$tables = {
rows => [100000],
names => ['ebay_items'],
engines => ['MyISAM'],
pk => ['int auto_increment']
};
$fields = {
types => ['datetime', 'int'],
indexes => [undef]
};
$data = {
numbers => [
'tinyint unsigned',
'smallint unsigned',
'smallint unsigned',
'mediumint unsigned'
],
temporals => ['datetime']
};
Я выполнил два отдельных пакета тестов: один, который использовал таблицу MyISAM, и другой, который использовал InnoDB. (Так что в основном вы заменяете MyISAM на InnoDB в приведенном выше фрагменте.)
Таблица
Инструмент создает таблицу, в которой столбцы называются pk
, col_datetime
и col_int
. Я переименовал их, чтобы они соответствовали столбцам вашей таблицы. Итоговая таблица чуть ниже.
+---------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+----------------+
| endtime | datetime | YES | MUL | NULL | |
| id | int(11) | NO | PRI | NULL | auto_increment |
| price | int(11) | YES | MUL | NULL | |
+---------+----------+------+-----+---------+----------------+
Индексы
Инструмент не создает индексов, потому что мне понравилось создавать их вручную.
CREATE INDEX `endtime` ON `ebay_items` (endtime, price);
CREATE INDEX `price` ON `ebay_items` (price, endtime);
CREATE INDEX `endtime_only` ON `ebay_items` (endtime);
CREATE INDEX `price_only` ON `ebay_items` (price);
Запрос
запрос, который я использовал.
SELECT `ebay_items`.*
FROM `ebay_items`
FORCE INDEX (`endtime|price|endtime_only|price_only`)
WHERE (`endtime` > '2009-01-01' - INTERVAL 1 MONTH)
ORDER BY `price` DESC
(Четыре разных запроса с использованием одного из индексов. Я использовал 2009-01-01
вместо NOW()
, потому что инструмент, похоже, генерирует даты около 2009 года.)
Объясните
Вот вывод EXPLAIN
для вышеприведенного запроса для каждого индекса таблицы MyISAM (вверху) и InnoDB (внизу).
1044 * время окончания *
id: 1
select_type: SIMPLE
table: ebay_items
type: range
possible_keys: endtime
key: endtime
key_len: 9
ref: NULL
rows: 25261
Extra: Using where; Using filesort
id: 1
select_type: SIMPLE
table: ebay_items
type: range
possible_keys: endtime
key: endtime
key_len: 9
ref: NULL
rows: 21026
Extra: Using where; Using index; Using filesort
цена
id: 1
select_type: SIMPLE
table: ebay_items
type: index
possible_keys: NULL
key: price
key_len: 14
ref: NULL
rows: 100000
Extra: Using where
id: 1
select_type: SIMPLE
table: ebay_items
type: index
possible_keys: NULL
key: price
key_len: 14
ref: NULL
rows: 100226
Extra: Using where; Using index
* * Endtime_only тысяча сорок-девять
id: 1
select_type: SIMPLE
table: ebay_items
type: range
possible_keys: endtime_only
key: endtime_only
key_len: 9
ref: NULL
rows: 11666
Extra: Using where; Using filesort
id: 1
select_type: SIMPLE
table: ebay_items
type: range
possible_keys: endtime_only
key: endtime_only
key_len: 9
ref: NULL
rows: 21270
Extra: Using where; Using filesort
price_only
id: 1
select_type: SIMPLE
table: ebay_items
type: index
possible_keys: NULL
key: price_only
key_len: 5
ref: NULL
rows: 100000
Extra: Using where
id: 1
select_type: SIMPLE
table: ebay_items
type: index
possible_keys: NULL
key: price_only
key_len: 5
ref: NULL
rows: 100226
Extra: Using where
Исходя из этого, я решил использовать индекс endtime_only
для своих тестов, потому что мне приходилось также выполнять запросы к MyISAM и к таблице InnoDB. Но, как вы видите, наиболее логичный индекс endtime
кажется лучшим.
Test
Для проверки эффективности запроса (относительно сгенерированной активности ввода-вывода) с таблицами MyISAM и InnoDB я написал следующую простую Java-программу.
static final String J = "jdbc:mysql://127.0.0.1:3306/test?user=root&password=root";
static final String Q = "SELECT * FROM ebay_items FORCE INDEX (endtime_only) WHERE (endtime > '2009-01-01'-INTERVAL 1 MONTH) ORDER BY price desc;";
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++)
try (Connection c = DriverManager.getConnection(J);
Statement s = c.createStatement()) {
TimeUnit.MILLISECONDS.sleep(10L);
s.execute(Q);
} catch (SQLException ex) {
ex.printStackTrace();
}
}
Настройка
Я запускал двоичный файл Windows MySQL 5.5 на ноутбуке Dell Vostro 1015, Intel Core Duo T6670 с частотой 2,20 ГГц, 4 ГБ оперативной памяти. Программа Java взаимодействовала с процессом сервера MySQL через TCP / IP.
Государство
Я зафиксировал состояние процесса mysqld
до и после выполнения моих тестов для таблицы с использованием MyISAM и InnoDB (с использованием Process Explorer ).
До


вечерний MyISAM


вечерний InnoDB


Заключение
По сути, эти два прогона отличаются только количеством отдельных операций чтения ввода-вывода, которое довольно велико, когда в таблице использовался механизм MyISAM. Оба теста длились по 50–60 секунд. Максимальная загрузка процессора в случае с движком MyISAM составляла около 42 процентов, а при использовании InnoDB - около 38.
Я не совсем уверен, каковы последствия большого числа операций чтения ввода-вывода, но в этом случае чем меньше, тем лучше (вероятно). Если у вас есть еще несколько столбцов в вашей таблице (кроме того, который вы указали) и у вас есть конфигурация MySQL не по умолчанию (относительно размеров буфера и тому подобное), возможно, MySQL будет использовать дисковые ресурсы.