У меня есть таблица Mysql со скриптами около 500 тыс. Записей.
CREATE TABLE IF NOT EXISTS 'p_transactions' (
'transaction_id' bigint(10) unsigned NOT NULL,
'amount' decimal(19,2) NOT NULL,
'dt' bigint(1) NOT NULL,
'transaction_status' int(1) NOT NULL,
'transaction_type' varchar(15) NOT NULL,
'payment_method' varchar(25) NOT NULL,
'notes' text NOT NULL,
'member_id' int(10) unsigned NOT NULL,
'new_amount' decimal(19,2) NOT NULL,
'paid_amount' decimal(19,2) NOT NULL,
'secret_code' char(40) NOT NULL,
'internal_status' varchar(40) NOT NULL,
'ip_addr' varchar(15) NOT NULL,
'description' text NOT NULL,
'seller_transaction_id' varchar(50) DEFAULT NULL,
'return_url' varchar(255) DEFAULT NULL,
'fail_url' varchar(255) DEFAULT NULL,
'success_url' varchar(255) DEFAULT NULL,
'result_url' varchar(255) DEFAULT NULL,
'user_fee' decimal(19,3) DEFAULT '0.000',
'currency' char(255) DEFAULT 'USD',
'gateway_transaction_id' char(255) DEFAULT NULL,
'load_amount' decimal(19,2) NOT NULL,
'transaction_mode' varchar(1) NOT NULL DEFAULT '',
'p_fee' decimal(19,2) NOT NULL,
'country' varchar(2) NOT NULL,
'email' varchar(255) NOT NULL,
'vat' decimal(19,2) NOT NULL DEFAULT '0.00',
'name' varchar(255) NOT NULL,
'bdate' varchar(255) NOT NULL,
'child_method' varchar(255) NOT NULL,
'processing_fee' decimal(19,2) NOT NULL DEFAULT '0.00',
'flat_fee' varchar(1) NOT NULL DEFAULT 'n',
'user_fee_sum' decimal(19,2) NOT NULL DEFAULT '0.00',
'p_fee_sum' decimal(19,2) NOT NULL DEFAULT '0.00',
'dt_open' bigint(1) NOT NULL DEFAULT '0',
'user_fee_type' varchar(1) NOT NULL DEFAULT 'r',
'custom_gateway_fee' decimal(19,2) NOT NULL DEFAULT '0.00',
'paid_currency' varchar(3) NOT NULL DEFAULT 'USD',
'paid_microtime' bigint(10) unsigned NOT NULL,
'check_ballance' varchar(1) NOT NULL DEFAULT 'n',
PRIMARY KEY ('transaction_id'),
KEY 'member_id' ('member_id'),
KEY 'payment_method' ('payment_method'),
KEY 'child_method' ('child_method'),
KEY 'check_ballance' ('check_ballance'),
KEY 'dt' ('dt'),
KEY 'transaction_type' ('transaction_type'),
KEY 'paid_microtime' ('paid_microtime')
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Когда я выполняю запрос
SELECT *
FROM 'p_transactions'
WHERE dt >= 1517443200
AND dt <= 1523404799
AND member_id = 2051
ORDER BY 'paid_microtime' DESC
LIMIT 50;
он работает 0,000 сек. (+ 0,016 с)
но если я добавлю к запросу это условие AND transaction_status = 7
SELECT *
FROM 'p_transactions'
WHERE dt >= 1517443200
AND dt <= 1523404799
AND member_id = 2051
AND transaction_status = 7
ORDER BY 'paid_microtime' DESC
LIMIT 50
выполнение запроса 12,938 сек. (+ 0,062 с)
Пожалуйста, помогите мне выяснить причину такого поведения
PS. Был указатель на transaction_status
и он увеличил время выполнения еще больше.
Добавьте подходящий индекс, например:
ON payzoff_transactions (member_id, dt)
или же
ON payzoff_transactions (member_id, dt, transaction_status)
Мы хотим, member_id
столбец member_id
был ведущим столбцом в индексе, из-за сравнения равенства, и мы ожидаем, что результат будет существенно меньшим подмножеством всей таблицы. После этого мы хотим dt
столбца из-за "сканирования диапазона".
Включение дополнительных столбцов в индекс может позволить MySQL проверить это условие, используя значения из индекса, без посещения/поиска строки на основных страницах таблицы.
Любой из этих индексов подходит для обоих запросов, указанных в вопросе.
Используйте EXPLAIN
чтобы увидеть план выполнения... какой индекс используется.
Там действительно не обойти операцию "Использование файловой системы", так как мы тянем небольшое подмножество всей таблицы.
(Если бы мы вытащили всю таблицу (или огромное подмножество), мы могли бы избежать дорогостоящей операции сортировки с планом доступа, который вытягивает строки в обратном порядке индекса, причем у него есть индекс с ведущим столбцом paid_microtime
.)
member_id
избыточным.
Для исходного запроса эти
INDEX(member_id, dt)
INDEX(member_id, paid_microtime)
Для вторичного запроса
INDEX(transaction_status, member_id, dt)
INDEX(transaction_status, member_id, paid_microtime)
Не вдаваясь в детали распределения значений данных, мы не можем объяснить, почему один запрос намного медленнее; однако мои 4 индекса должны заставить оба запроса работать быстрее в большинстве случаев.
Больше обсуждений о том, как я придумал эти индексы (и почему (member_id, dt, transaction_status)
не так хорош): http://mysql.rjweb.org/doc.php/index_cookbook_mysql
SHOW CREATE TABLE
дает точно правильный вывод. При необходимости вы можете отредактировать что-либо показательное и удалить все столбцы, не относящиеся к вашей проблеме. Эта внешняя ссылка может устареть или измениться, поэтому вы должны держать свой вопрос в отдельности.