почему * выбор * первичного ключа быстрее, чем внешнего ключа? - PullRequest
0 голосов
/ 24 марта 2020

Мне показалось, что у меня почти что работоспособное понимание баз данных, но это предположение только что было выброшено из окна.

У меня довольно большая таблица, и я выполняю простой запрос по ней:

select image_set_id from image where table_id = 554;

image_set_id - это длинный внешний ключ к другой таблице. Для выполнения этого запроса на моем компьютере может потребоваться более 7 секунд (на сервере базы данных, который не имеет такой большой мощности ...)

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

select id from image where table_id = 554;

это почти в сто раз быстрее. Очевидным отличием является то, что id является первичным ключом, а также LONG.

Что удивительно, так это то, что оба запроса сравниваются с внешним ключом в предложении where, поэтому я ожидал, что оба запроса будут занимать примерно одинаковое время. Я имею в виду, что БД должна отфильтровывать все записи, соответствующие внешнему ключу, затем читать LONG из каждой из них, и все. Характер читаемого значения не должен иметь такого же значения, как характер сравниваемого значения. Если я выполню объяснение по двум запросам, результат даже будет выглядеть точно так же:

enter image description here

Ну, очевидно, я совершенно не прав, поэтому у меня есть два вопросы:
а) Почему это так?
б) Можно ли как-нибудь улучшить производительность при выборе внешнего ключа, потому что в конце концов это то, что мне нужно (это подзапрос более крупного запроса, который нуждается в оптимизации, но потребляет более 90% времени выполнения)?

Дополнительные данные:
Структура, используемая здесь, относительно проста. Здесь задействованы три таблицы: одна раздраженно называется image_table, одна называется image_set и одна называется image. Каждое изображение принадлежит определенной image_table. Image_set - это отношение, которое описывает изображения из разных image_tables, которые принадлежат друг другу (это фактически несколько версий одного и того же изображения). Таким образом, изображение имеет внешний ключ как для image_table, так и для image_set, в то время как между image_set и image_table нет никакой связи.

Вот полная структура задействованных таблиц:

explain image;
'id', 'binary(16)', 'NO', 'PRI', NULL, ''
'image_key', 'varchar(190)', 'YES', 'UNI', NULL, ''
'image_preview_key', 'varchar(255)', 'NO', '', NULL, ''
'image_set_id', 'bigint(20)', 'NO', 'MUL', NULL, ''
'table_id', 'bigint(20)', 'YES', 'MUL', NULL, ''
'size', 'int(11)', 'NO', '', '0', ''

SHOW INDEX FROM image;
'image', '0', 'PRIMARY', '1', 'id', 'A', '20201850', NULL, NULL, '', 'BTREE', '', ''
'image', '0', 'UX_image_image_key', '1', 'image_key', 'A', '20201850', NULL, NULL, 'YES', 'BTREE', '', ''
'image', '1', 'FK_image_image_set', '1', 'image_set_id', 'A', '20201850', NULL, NULL, '', 'BTREE', '', ''
'image', '1', 'FK_image_image_table', '1', 'table_id', 'A', '75099', NULL, NULL, 'YES', 'BTREE', '', ''

explain image_set;
'id', 'bigint(20)', 'NO', 'PRI', NULL, 'auto_increment'
'time', 'datetime', 'NO', 'MUL', NULL, ''
'camera_id', 'bigint(20)', 'YES', 'MUL', NULL, ''

SHOW INDEX FROM image_set;
'image_set', '0', 'PRIMARY', '1', 'id', 'A', '8317139', NULL, NULL, '', 'BTREE', '', ''
'image_set', '0', 'UX_image_set_time', '1', 'camera_id', 'A', '29080', NULL, NULL, 'YES', 'BTREE', '', ''
'image_set', '0', 'UX_image_set_time', '2', 'time', 'A', '8317139', NULL, NULL, '', 'BTREE', '', ''
'image_set', '1', 'IX_image_set_time', '1', 'time', 'A', '8317139', NULL, NULL, '', 'BTREE', '', ''

explain image_table;
'id', 'bigint(20)', 'NO', 'PRI', NULL, 'auto_increment'
'name', 'varchar(255)', 'NO', '', NULL, ''
'legacy_name', 'varchar(100)', 'NO', 'UNI', NULL, ''
'camera_id', 'bigint(20)', 'NO', 'MUL', NULL, ''
'instruction_id', 'bigint(20)', 'YES', 'MUL', NULL, ''
'trashed', 'datetime', 'YES', '', NULL, ''

SHOW INDEX FROM image_table;
'image_table', '0', 'PRIMARY', '1', 'id', 'A', '1128', NULL, NULL, '', 'BTREE', '', ''
'image_table', '0', 'legacy_name', '1', 'legacy_name', 'A', '1128', NULL, NULL, '', 'BTREE', '', ''
'image_table', '1', 'FK_image_table_camera', '1', 'camera_id', 'A', '1128', NULL, NULL, '', 'BTREE', '', ''
'image_table', '1', 'FK_image_table_image_table_instruction', '1', 'instruction_id', 'A', '1128', NULL, NULL, 'YES', 'BTREE', '', ''

При описании двух рассматриваемых запросов они выглядят почти одинаково, за исключением того, что один использует индекс, а другой нет ... хотя индекс для него существует:

describe select image_set_id from image where table_id = 554;
{
  "query_block": {
    "select_id": 1,
    "table": {
      "table_name": "image",
      "access_type": "ref",
      "possible_keys": [
        "FK_image_image_table"
      ],
      "key": "FK_image_image_table",
      "used_key_parts": [
        "table_id"
      ],
      "key_length": "9",
      "ref": [
        "const"
      ],
      "rows": 698122,
      "filtered": 100
    }
  }
}

describe select id from image where table_id = 554;
{
  "query_block": {
    "select_id": 1,
    "table": {
      "table_name": "image",
      "access_type": "ref",
      "possible_keys": [
        "FK_image_image_table"
      ],
      "key": "FK_image_image_table",
      "used_key_parts": [
        "table_id"
      ],
      "key_length": "9",
      "ref": [
        "const"
      ],
      "rows": 698122,
      "filtered": 100,
      "using_index": true
    }
  }
}

Ответы [ 2 ]

1 голос
/ 24 марта 2020

Почему это так?

Если у вас есть индекс на table_id, причина, по которой select id ... быстрее, заключается в том, что каждый индекс также несет первичный ключ с ним. Кроме того, индексы намного меньше вашей основной таблицы и часто могут помещаться в рабочую память.

каким-либо образом я могу улучшить производительность?

Возможно, что table_id очень высокий, и в этом случае база данных может решить просто выполнить сканирование таблицы; это, как правило, указывает на дизайн данных fl aws в вашей модели.

Тем не менее, первый шаг - всегда сначала выполнять диагностику:

describe select image_set_id from image where table_id = 554;
describe select id from image where table_id = 554;
0 голосов
/ 26 марта 2020

Руководствуясь предложением Джека для анализа запросов, я заметил, что существенным отличием было то, что при запросе идентификатора в столбце «extra» было указано «USING INDEX», а при запросе image_set_id - нет. Это то, что я добавил в вопросе.

После небольшой дикой погони goose я наконец-то добрался до наиболее очевидного места для поиска ответа - документации для этого конкретного столбца объяснения . вывод , который должен был сказать по этому поводу следующее:

Использование индекса (JSON свойство: using_index)

Информация столбца извлекается из таблицы только с использованием информация в дереве индексов без необходимости дополнительного поиска для чтения фактической строки. Эту стратегию можно использовать, когда в запросе используются только столбцы, которые являются частью одного индекса.

Итак, мне нужно было создать составной индекс, который содержал оба, и только и image_table, и image_set_id, и запрос сработал как ракета с производительностью более 90%. Усиление.

Я также пробовал индекс, включающий идентификатор изображения с двумя, но в этом случае мне пришлось: а) намекнуть на индекс, который будет использоваться, и б) набрать только около 30%, потому что запрос все еще выполнял предложение where (дополнительный столбец объяснения показывал «используя index, используя where»).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...