У меня есть база данных MySQL с довольно простой схемой. Есть parent
, child
и access
таблицы.
parent
хранит 51 поле, все из которых varchar
(длиной от 16 до 512), за исключением 4 longtext
полей и первичного ключа, который является bigint
. Помимо первичного ключа, есть индексы на 3 других поля. Так что таблица child
может ссылаться на нее как на внешний ключ.
child
хранит 23 поля, которые в основном varchar
с некоторыми text
полями. Поле varchar(256)
используется как внешний ключ, чтобы связать его с родителем. Ожидается, что фактическое содержимое поля внешнего ключа будет короче 60 символов.
accesss
имеет поле bigint
и поле varchar
, которые вместе составляют первичный ключ, а поле bigint
является внешним ключом, который связывает его с parent
.
Таблица access
используется для указания того, какие пользователи имеют доступ к каким записям из parent
. Может быть несколько пользователей, которые должны иметь доступ к любой записи.
В parent
имеется около 2e6 строк * (и, следовательно, access
) , а в child
- около 2e7 строк. РЕДАКТИРОВАТЬ: Извините, access
имеет 5329840 строк. то есть в access
есть одна или несколько строк для каждой строки в parent
.
Приведенная выше схема основана на старой базе данных FileMaker, которую мы собираемся перенести в MySQL. Я уверен, что это не идеально, но я не знаю точно, почему.
Запросы выполняются быстро или довольно медленно в зависимости от параметров. Так, например, следующий запрос займет секунду или две, если есть несколько записей, к которым bob
имеет доступ. Однако запрос займет несколько минут (например, 12 минут), если нет записей, к которым у пользователя bob
есть доступ (например, если нет пользователя с именем bob
):
SELECT
p."RecordID", p."SerialNumber", p."Folder", p."NoteType",
p."FirstName", p."LastName", p."DOB", p."Body", p."From",
p."DateTxt", a."UserName" AS Access
FROM parent AS p
INNER JOIN access AS a ON a."RecordID" = p."RecordID"
WHERE p."RecordID" > 123
AND a."UserName" = 'bob'
ORDER BY p."RecordID"
LIMIT 500;
Вот что EXPLAIN
говорит о запросе:
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+
| 1 | SIMPLE | p | range | PRIMARY | PRIMARY | 8 | NULL | 731752 | Using where |
| 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 74 | db.p.RecordID,const | 1 | Using where; Using index |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+
2 rows in set (0.01 sec)
Итак, есть ли способ ускорить несовпадающие запросы? Может ли это быть вызвано использованием полей varchar / text для всего? Может ли использование поля varchar (256) в качестве внешнего ключа вызвать проблемы (хотя оно не используется в приведенном выше запросе)? Или виноват запрос?
РЕДАКТИРОВАТЬ: Я только что понял, что PRIMARY KEY ("RecordID", "UserName")
в таблице access
не используется для SELECT ... FROM access WHERE UserName = 'blah'
. Я создал индекс для столбца UserName, и это, похоже, решило проблему. Буду признателен, если у кого-нибудь есть совет.
Токовый выход EXPLAIN
:
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+
| 1 | SIMPLE | p | range | PRIMARY | PRIMARY | 8 | NULL | 605020 | Using where |
| 1 | SIMPLE | a | eq_ref | PRIMARY,UNidx | PRIMARY | 74 | db.p.RecordID,const | 1 | Using where; Using index |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+
2 rows in set (0.00 sec)
РЕДАКТИРОВАТЬ: предложение @ DRapp действительно имеет огромное значение. Запрос выполняется быстро с индексом на access.UserName или без него. Если я отброшу индекс и попробую запрос DRapp без STRAIGHT_JOIN
, то он снова будет медленным.
Запрос DRapp без индекса при доступе. Имя пользователя:
mysql> explain SELECT STRAIGHT_JOIN p."RecordID", p."SerialNumber", p."Folder", p."NoteType", p."FirstName", p."LastName", p."DOB", p."Body", p."From", p."DateTxt", a."UserName" AS Access FROM access as a, parent AS p where a."UserName" = 'bob' and a."RecordID" > 123 and a."RecordID" = p."RecordID" order by a."RecordID" limit 500;
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+
| 1 | SIMPLE | a | range | PRIMARY | PRIMARY | 8 | NULL | 2382668 | Using where; Using index |
| 1 | SIMPLE | p | eq_ref | PRIMARY | PRIMARY | 8 | bb.a.RecordID | 1 | |
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+
2 rows in set (0.00 sec)
Тот же запрос с индексом на access.UserName
:
mysql> explain SELECT STRAIGHT_JOIN ...;
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+
| 1 | SIMPLE | a | ref | PRIMARY,UNidx | UNidx | 66 | const | 1209780 | Using where; Using index |
| 1 | SIMPLE | p | eq_ref | PRIMARY | PRIMARY | 8 | db.a.RecordID | 1 | |
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+
2 rows in set (0.00 sec)
Тот же запрос без индекса на access.UserName
и без STRAIGHT_JOIN
:
mysql> explain SELECT p."RecordID", p."SerialNumber", p."Folder", p."NoteType", p."FirstName", p."LastName", p."DOB", p."Body", p."From", p."DateTxt", a."UserName" AS Access FROM access as a, parent AS p where a."UserName" = 'zzz' and a."RecordID" > 123 and a."RecordID" = p."RecordID" order by a."RecordID" limit 500;
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+----------------------------------------------+
| 1 | SIMPLE | p | range | PRIMARY | PRIMARY | 8 | NULL | 484016 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 74 | db.p.RecordID,const | 1 | Using where; Using index |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+----------------------------------------------+
2 rows in set (0.00 sec)