Скорость MySQL-запроса к таблицам, содержащим BLOB-объекты, зависит от кеша файловой системы. - PullRequest
24 голосов
/ 01 марта 2012

У меня есть таблица с приблизительно 120 тыс. Строк, которая содержит поле с BLOB-файлом (не более 1 МБ на каждую запись по размеру, как правило, значительно меньше).Моя проблема заключается в том, что всякий раз, когда я запускаю запрос, запрашивающий любые столбцы в этой таблице (, а не , включая BLOB), если кэш файловой системы пуст, для его завершения требуется приблизительно 40 ''.Для всех последующих запросов к одной и той же таблице требуется меньше 1 '' (тестирование из клиента командной строки, на самом сервере).Количество строк, возвращаемых в запросах, варьируется от пустого набора до 60k +

. Я удалил кеш запросов, поэтому он не имеет к этому никакого отношения.Таблица myisam, но я также попытался изменить ее на innodb (и установив ROW_FORMAT = COMPACT), но безуспешно.

Если я удаляю столбец BLOB, запрос всегда выполняется быстро.

Итак, я бы предположил, что сервер читает двоичные объекты с диска (или их части), а файловая система их кэширует.Проблема в том, что на сервере с большим трафиком и ограниченной памятью кэш файловой системы обновляется время от времени, поэтому этот конкретный запрос продолжает вызывать у меня проблемы.

Итак, мой вопрос, есть ли способ значительно ускорить процесс, не удаляя столбец blob из таблицы?

вот 2 примера запросов, выполняемых один за другим, вместе с объяснением, индексами и определением таблиц:

mysql> SELECT ct.score FROM completed_tests ct where ct.status != 'deleted' and ct.status != 'failed' and score < 100;
Empty set (48.21 sec)
mysql> SELECT ct.score FROM completed_tests ct where ct.status != 'deleted' and ct.status != 'failed' and score < 99;
Empty set (1.16 sec)

mysql> explain SELECT ct.score FROM completed_tests ct where ct.status != 'deleted' and ct.status != 'failed' and score < 99;
+----+-------------+-------+-------+---------------+--------+---------+------+-------+-------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows  | Extra       |
+----+-------------+-------+-------+---------------+--------+---------+------+-------+-------------+
|  1 | SIMPLE      | ct    | range | status,score  | status | 768     | NULL | 82096 | Using where |
+----+-------------+-------+-------+---------------+--------+---------+------+-------+-------------+
1 row in set (0.00 sec)


mysql> show indexes from completed_tests;
+-----------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table           | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-----------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| completed_tests |          0 | PRIMARY     |            1 | id          | A         |      583938 |     NULL | NULL   |      | BTREE      |         |
| completed_tests |          1 | users_login |            1 | users_LOGIN | A         |       11449 |     NULL | NULL   | YES  | BTREE      |         |
| completed_tests |          1 | tests_ID    |            1 | tests_ID    | A         |         140 |     NULL | NULL   |      | BTREE      |         |
| completed_tests |          1 | status      |            1 | status      | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |
| completed_tests |          1 | timestamp   |            1 | timestamp   | A         |      291969 |     NULL | NULL   |      | BTREE      |         |
| completed_tests |          1 | archive     |            1 | archive     | A         |           1 |     NULL | NULL   |      | BTREE      |         |
| completed_tests |          1 | score       |            1 | score       | A         |         783 |     NULL | NULL   | YES  | BTREE      |         |
| completed_tests |          1 | pending     |            1 | pending     | A         |           1 |     NULL | NULL   |      | BTREE      |         |
+-----------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

mysql> show create table completed_tests;
+-----------------+--------------------------------------
| Table           | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
+-----------------+--------------------------------------
| completed_tests | CREATE TABLE `completed_tests` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `users_LOGIN` varchar(100) DEFAULT NULL,
  `tests_ID` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `test` longblob,
  `status` varchar(255) DEFAULT NULL,
  `timestamp` int(10) unsigned NOT NULL DEFAULT '0',
  `archive` tinyint(1) NOT NULL DEFAULT '0',
  `time_start` int(10) unsigned DEFAULT NULL,
  `time_end` int(10) unsigned DEFAULT NULL,
  `time_spent` int(10) unsigned DEFAULT NULL,
  `score` float DEFAULT NULL,
  `pending` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `users_login` (`users_LOGIN`),
  KEY `tests_ID` (`tests_ID`),
  KEY `status` (`status`),
  KEY `timestamp` (`timestamp`),
  KEY `archive` (`archive`),
  KEY `score` (`score`),
  KEY `pending` (`pending`)
) ENGINE=InnoDB AUTO_INCREMENT=117996 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED
1 row in set (0.00 sec)

Первоначально я разместил это на mysql-запрос медленно с первого взглядапотом но теперь у меня есть больше информации, поэтому я отвечаю на другой вопрос. Я также разместил его на форуме mysql , но не получил ответа

Заранее спасибо, как всегда

Ответы [ 4 ]

18 голосов
/ 31 мая 2013

Дизайн хранилища BLOB (= TEXT) в MySQL выглядит совершенно некорректным и противоречивым.Я пару раз сталкивался с одной и той же проблемой и не смог найти какого-либо авторитетного объяснения.Наиболее подробный анализ, который я наконец нашел, - это сообщение за 2010 год: http://www.mysqlperformanceblog.com/2010/02/09/blob-storage-in-innodb/

Общее мнение и ожидание состоит в том, что BLOB / TEXT хранятся вне хранилища основной строки (например, см. этот ответ ).Это не правда, хотя.Здесь есть несколько проблем (я опираюсь на статью, приведенную выше):

  1. Если размер элемента BLOB составляет несколько КБ, он включается непосредственно в данные строки.Следовательно, даже если вы ВЫБИРАЕТЕ только столбцы, отличные от BLOB, движок все равно должен загрузить все ваши BLOB-объекты с диска.Скажем, у вас есть 1M строк со 100 байтами данных без BLOB-объектов и 5000 байтов данных BLOB-объектов.Вы выбираете все столбцы, не являющиеся BLOB-объектами, и ожидаете, что MySQL будет читать с диска около 100-120 байт на строку, что составляет 100-120 МБ в целом (+20 для адреса BLOB).Однако реальность такова, что MySQL хранит все BLOB-объекты в одних и тех же дисковых блоках в виде строк, поэтому все они должны читаться вместе , даже если не используется , поэтому размер данных, считываемых с диска, составляет около 5100 МБ = 5 ГБ - это в 50 раз больше, чем вы ожидаете, и означает 50 раз медленнее выполнение запроса.

    Конечно, этот дизайн имеетПреимущество: когда вам нужны все столбцы, включая блоб, запрос SELECT выполняется быстрее, когда BLOB-объекты хранятся со строкой, чем при внешнем: вы избегаете (иногда) 1 дополнительного доступа к странице на строку.Однако это не типичный вариант использования BLOB-объектов, и механизм DB не следует оптимизировать в этом случае.Если ваши данные настолько малы, что они помещаются в ряд, и вы можете загружать их в каждом запросе, независимо от того, нужны они или нет - тогда вы бы использовали тип VARCHAR вместо BLOB / TEXT.

  2. Даже если по какой-то причине (длинный ряд или длинный BLOB-объект) значение BLOB сохраняется внешне, его префикс 768-байтов все еще сохраняется в самой строке.Давайте рассмотрим предыдущий пример: у вас есть 100 байтов данных без BLOB-объектов в каждой строке, но теперь столбец BLOB-объектов содержит элементы размером 1 МБ каждый, поэтому они должны храниться извне.SELECT из столбцов без больших двоичных объектов должен будет считывать примерно 800 байт на строку (без больших двоичных объектов + префикс BLOB-объектов) вместо 100-120 - это снова в 7 раз больше переноса диска, чем вы ожидаете,и 7x медленнее выполнения запроса.

  3. Внешнее хранилище больших двоичных объектов неэффективно при использовании дискового пространства: оно выделяет пространство в блоках по 16 КБ, а один блок не может содержать несколькоэлементов, поэтому, если ваши большие объекты маленькие и занимают, например, 8 КБ каждый, фактическое выделенное пространство будет дважды такого большого размера.

Я надеюсь, что этот дизайн будетОднажды исправим: MySQL будет хранить ВСЕ большие и малые большие двоичные объекты во внешнем хранилище, без префиксов, сохраняемых в БД, при этом выделение внешнего хранилища будет эффективным для элементов любого размера.До того, как это произойдет, выделение BLOB / TEXT столбцов кажется единственным разумным решением - разделение на другую таблицу или файловую систему (каждое значение BLOB хранится в виде файла).

16 голосов
/ 16 ноября 2012

Я какое-то время изучал эту проблему.Многие люди рекомендуют использовать BLOB-объекты только с одним первичным ключом в отдельной таблице и хранить метаданные BLOB-объектов в другой таблице с внешним ключом для таблицы BLOB-объектов.При этом производительность будет значительно выше.

2 голосов
/ 01 марта 2012

Добавление составного индекса в два соответствующих столбца должно позволить выполнять эти запросы без непосредственного доступа к данным таблицы.

CREATE INDEX `IX_score_status` ON `completed_tests` (`score`, `status`);

Если вы можете переключиться на MariaDB, тогда вы сможете максимально использоватьоптимизация таблицы устранения.Это позволит вам разделить поле BLOB на его собственную таблицу и использовать представление для воссоздания существующей структуры таблицы с помощью LEFT JOIN.Таким образом, он будет обращаться к данным BLOB только в том случае, если он явно требуется для выполнения запроса.

0 голосов
/ 27 марта 2018

Просто добавьте индекс или индексы к полям, используемым после запроса WHERE для таблицы с BLOB-объектами.

Например, у вас есть 2 таблицы с этими полями

users : USERID, NAME, ...
userphotos : BLOBID, BLOB, USERNO, ...

select * from userphotos where USERNO=123456; 

Обычно это работает нормально.Если у вас много больших изображений (например, BLOB, MEDIUMBLOB или LONGBLOB более 5 ГБ), это займет много времени (более минут), пока BLOBID является первичным ключом.

Почему-то MySQL выполняет поиск по всем данным, включая изображенияв предложении WHERE отсутствует индекс поля поля BLOB.Когда ваши данные становятся все больше и больше, это занимает много времени.Если вы создадите индекс для поля USERNO, это ускорит вашу базу данных и будет зависеть от размера целых данных.

Решение:

**Add Index to the USERNO at userphotos**

В качестве ответа на ваш вопрос вы должны создать индекс для ct.status

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