Настройка производительности базы данных MySQL, оптимизация схемы - PullRequest
1 голос
/ 24 января 2011

У меня есть база данных 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)

1 Ответ

1 голос
/ 24 января 2011

Мне всегда везло, используя предложение «STRAIGHT_JOIN» в MySQL и ставя основную таблицу на первое место в списке квалификаторов.В этом случае ваша таблица «Доступ» ищет Боба, ТОГДА просматривая записи, которые Боб имеет доступ для просмотра.Если это не удается при запросе ACCESS, не нужно искать глубже.Кроме того, поскольку соединение основано на том же «RecordID», я изменил ссылки на «a».Таблица.Так как этот запрос сначала основан на имени пользователя, у меня тоже будет ключ к нему.Я был бы заинтересован в его объяснении, и производительности тоже.

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
...