Подзапросы и MySQL Cache для 18M + таблицы строк - PullRequest
7 голосов
/ 24 ноября 2010

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

Информация о машине


8 processors
model name      : Intel(R) Xeon(R) CPU           E5440  @ 2.83GHz
cache size      : 6144 KB
cpu cores       : 4 

top - 17:11:48 up 35 days, 22:22, 10 users,  load average: 1.35, 4.89, 7.80
Tasks: 329 total,   1 running, 328 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.0%sy,  0.0%ni, 87.4%id, 12.5%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   8173980k total,  5374348k used,  2799632k free,    30148k buffers
Swap: 16777208k total,  6385312k used, 10391896k free,  2615836k cached

Однако мы собираемся перенести установку mysql на другую машину в кластере с 256 ГБ ОЗУ

Таблица информации


Моя таблица MySQL выглядит как

CREATE TABLE ClusterMatches 
(
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    cluster_index INT, 
    matches LONGTEXT,
    tfidf FLOAT,
    INDEX(cluster_index)   
);

В нем приблизительно 18 миллионов строк, есть 1 миллион уникальных совпадений cluster_index и 6 тысяч уникальных совпадений. SQL-запрос, который я генерирую в PHP выглядит так.

SQL-запрос


$sql_query="SELECT `matches`,sum(`tfidf`) FROM 
(SELECT * FROM Test2_ClusterMatches WHERE `cluster_index` in (".$clusters.")) 
AS result GROUP BY `matches` ORDER BY sum(`tfidf`) DESC LIMIT 0, 10;";

где $ cluster содержит строку из 3000 разделенных запятыми cluster_index's. Этот запрос использует приблизительно 50000 строк и выполняется приблизительно 15 секунд, а при повторном запуске того же запроса требуется приблизительно 1 секунда.

Использование


  1. Содержание таблицы можно считать статическим.
  2. Небольшое количество одновременных пользователей
  3. Вышеприведенный запрос в настоящее время является единственным запросом, который будет выполняться в таблице

подзапросы


На основании этого поста [stackoverflow: Cache / Re-Use a Subquery в MySQL] [1] и улучшения времени запроса, я считаю, что мой подзапрос может быть проиндексирован.

mysql> EXPLAIN EXTENDED SELECT `matches`,sum(`tfidf`) FROM 
(SELECT * FROM ClusterMatches WHERE `cluster_index` in (1,2,...,3000) 
AS result GROUP BY `matches` ORDER BY sum(`tfidf`) ASC LIMIT 0, 10;

+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
| id | select_type | table                | type  | possible_keys | key           | key_len | ref  | rows  | Extra                           |
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
|  1 | PRIMARY     |  derived2            | ALL   | NULL          | NULL          | NULL    | NULL | 48528 | Using temporary; Using filesort | 
|  2 | DERIVED     | ClusterMatches       | range | cluster_index | cluster_index | 5       | NULL | 53689 | Using where                     | 
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+

В соответствии с этой более старой статьей [Оптимизация MySQL: запросы и индексы] [2] в разделе Дополнительная информация - плохие, которые можно увидеть здесь, "используют временные" и "используют файловую сортировку"

Информация о конфигурации MySQL


Кэш запросов доступен, но фактически отключен, так как размер в настоящее время установлен на ноль


mysqladmin variables;
+---------------------------------+----------------------+
| Variable_name                   | Value                |
+---------------------------------+----------------------+
| bdb_cache_size                  | 8384512              | 
| binlog_cache_size               | 32768                | 
| expire_logs_days                | 0                    |
| have_query_cache                | YES                  | 
| flush                           | OFF                  |
| flush_time                      | 0                    |
| innodb_additional_mem_pool_size | 1048576              |
| innodb_autoextend_increment     | 8                    |
| innodb_buffer_pool_awe_mem_mb   | 0                    |
| innodb_buffer_pool_size         | 8388608              |
| join_buffer_size                | 131072               |
| key_buffer_size                 | 8384512              |
| key_cache_age_threshold         | 300                  |
| key_cache_block_size            | 1024                 |
| key_cache_division_limit        | 100                  |
| max_binlog_cache_size           | 18446744073709547520 | 
| sort_buffer_size                | 2097144              |
| table_cache                     | 64                   | 
| thread_cache_size               | 0                    | 
| query_cache_limit               | 1048576              |
| query_cache_min_res_unit        | 4096                 |
| query_cache_size                | 0                    |
| query_cache_type                | ON                   |
| query_cache_wlock_invalidate    | OFF                  |
| read_rnd_buffer_size            | 262144               |
+---------------------------------+----------------------+

На основании этой статьи [Поворот производительности базы данных Mysql] [3] я считаю, что значения, которые мне нужно настроить, равны

  1. table_cache
  2. key_buffer
  3. sort_buffer
  4. read_buffer_size
  5. record_rnd_buffer (для условий GROUP BY и ORDER BY)

Области, определенные для улучшения - настройки MySQL Query


  1. Изменение типа данных для совпадений с индексом, который является целым числом, указывающим на другую таблицу [MySQL действительно будет использовать динамический формат строки, если он содержит поля переменной длины, такие как TEXT или BLOB, что в данном случае означает, что сортировка должна выполняться сделано на диске. Решение состоит не в том, чтобы избежать этих типов данных, а в том, чтобы разбить такие поля на связанную таблицу.] [4]
  2. Индексирование нового поля match_index так, чтобы GROUP BY matches происходило быстрее, основываясь на выражении [«Вам, вероятно, следует создать индексы для любого поля, в котором вы выбираете, группируете, упорядочиваете или объединяете».] [5 ]

Инструменты


Для настройки исполнения планирую использовать

  1. [Объяснить] [6] со ссылкой на [выходной формат] [7]
  2. [ab - инструмент тестирования Apache HTTP-сервера] [8]
  3. [Профилирование] [9] с [данными журнала] [10]

Размер базы данных будущего


Цель состоит в том, чтобы создать систему, которая может иметь 1М уникальных значений cluster_index, 1М уникальных значений соответствия, около 3 000 000 000 строк таблицы с временем ответа на запрос около 0,5 с (мы можем добавить больше оперативной памяти при необходимости и распределить базу данных по кластер)

Вопросы


  1. Я думаю, что мы хотим сохранить весь набор записей в оперативной памяти, чтобы запрос не касался диска. Если мы храним всю базу данных в кэше MySQL, устраняет ли это необходимость в memcachedb?
  2. Является ли попытка сохранить всю базу данных в кеше MySQL плохой стратегией, поскольку она не предназначена для постоянной работы? Будет ли лучше использовать что-то вроде memcachedb или redis, если да, то почему?
  3. Является ли временная таблица «результатом», созданная запросом, автоматически уничтожается после его завершения?
  4. Должны ли мы переключиться с Innodb на MyISAM [это хорошо для чтения тяжелых данных, где InnoDB хорош для тяжелой записи] [11]?
  5. мой кэш не отображается как ноль в моем [Конфигурация кеша запросов] [12], почему в данный момент запрос выполняется быстрее при втором запуске?
  6. Могу ли я реструктурировать свой запрос, чтобы исключить возникновение "использования временного" и "использования файловой сортировки", если я буду использоватьобъединение вместо подзапроса?
  7. как вы оцениваете размер MySQL [Data Cache] [13]?
  8. какие размеры для значений table_cache, key_buffer, sort_buffer, read_buffer_size, record_rnd_buffer вы бы предложили в качестве отправной точки?

Ссылки


  • 1: stackoverflow.com/questions/658937/cache-re-use-a-подзапрос в mysql
  • 2: databasejournal.com/features/mysql/article.php/10897_1382791_4/Optimizing-MySQL-Queries-and-Indexes.htm
  • 3: debianhelp.co.uk / mysqlperformance.htm
  • 4: 20bits.com/articles/10-tips-for-optimizing-mysql-query-that-dont-suck /
  • 5: 20bits.com/articles/10-tips-for-optimizing-mysql-queries-that-dont-suck/
  • 6: dev.mysql.com/doc/refman/5.0/en/explain.html
  • 7: dev.mysql.com/doc/refman/5.0/en/explain-output.html
  • 8: httpd.apache.org/docs/2.2/programs/ab.html
  • 9: mtop.sourceforge.net/
  • 10: dev.mysql.com/doc / refman / 5.0 / en / slow-query-log.html
  • 11: 20bits.com/articles/10-tips-for-optimizing-mysql-queries-that-dont-suck/
  • 12: dev.mysql.com/doc/refman/5.0/en/query-cache-configuration.html
  • 13: dev.mysql.com/tech-resources/articles/mysql-query-cache.html

1 Ответ

1 голос
/ 25 ноября 2010

Пеленальный столик


Основываясь на рекомендациях в этом посте о Как выбирать индексы для порядка и группировать по запросам таблица теперь выглядит так

<code>CREATE TABLE ClusterMatches 
(
    cluster_index INT UNSIGNED, 
    match_index INT UNSIGNED,
    id INT NOT NULL AUTO_INCREMENT,
    tfidf FLOAT,
    PRIMARY KEY (match_index,cluster_index,id,tfidf)
);
CREATE TABLE MatchLookup 
(
    match_index INT UNSIGNED NOT NULL PRIMARY KEY,
    image_match TINYTEXT
);

Устранение подзапроса

Запрос без сортировки результатов по сумме (tfidf) выглядит так

<code>SELECT match_index, SUM(tfidf) FROM ClusterMatches 
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index LIMIT 10;

Что исключает использование временных и файловых сортировок

<code>explain extended SELECT match_index, SUM(tfidf) FROM ClusterMatches 
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index LIMIT 10;
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
| id | select_type | table                | type  | possible_keys | key     | key_len | ref  | rows  | Extra                    |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
|  1 | SIMPLE      | ClusterMatches       | range | PRIMARY       | PRIMARY | 4       | NULL | 14938 | Using where; Using index | 
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+

Проблема сортировки

Однако, если я добавлю ORDER BY SUM (tfdif) в <pre>SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index ORDER BY total DESC LIMIT 0,10; +-------------+--------------------+ | match_index | total | +-------------+--------------------+ | 868 | 0.11126546561718 | | 4182 | 0.0238558370620012 | | 2162 | 0.0216601379215717 | | 1406 | 0.0191618576645851 | | 4239 | 0.0168981291353703 | | 1437 | 0.0160425212234259 | | 2599 | 0.0156466849148273 | | 394 | 0.0155945559963584 | | 3116 | 0.0151005545631051 | | 4028 | 0.0149106932803988 | +-------------+--------------------+ 10 rows in set (0.03 sec)

Результат достаточно быстрый в этом масштабе, НО с ORDER BY SUM (tfidf) означает, что он использует временную и файловую сортировку <pre>explain extended SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches WHERE cluster_index IN (1,2,3 ... 3000) GROUP BY match_index ORDER BY total DESC LIMIT 0,10; +----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+ | 1 | SIMPLE | ClusterMatches | range | PRIMARY | PRIMARY | 4 | NULL | 65369 | Using where; Using index; Using temporary; Using filesort | +----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+

Возможные решения?

Я ищу решение, которое не использует временную или файловую сортировку, как <pre>SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches WHERE cluster_index IN (1,2,3 ... 3000) GROUP BY cluster_index, match_index HAVING total>0.01 ORDER BY cluster_index; где мне не нужно жестко задавать пороговое значение для общего количества, какие-либо идеи?

...