Проблема:
У нас есть социальный сайт, где участники могут оценивать друг друга для совместимости или соответствия. Эта user_match_ratings
таблица содержит более 220 миллионов строк (данные по 9 ГБ или почти 20 ГБ по индексам). Запросы к этой таблице обычно отображаются в slow.log (порог> 2 секунды) и являются наиболее часто регистрируемым медленным запросом в системе:
Query_time: 3 Lock_time: 0 Rows_sent: 3 Rows_examined: 1051
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 395357 group by rating;"
Query_time: 4 Lock_time: 0 Rows_sent: 3 Rows_examined: 1294
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 4182969 group by rating;"
Query_time: 3 Lock_time: 0 Rows_sent: 3 Rows_examined: 446
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 630148 group by rating;"
Query_time: 5 Lock_time: 0 Rows_sent: 3 Rows_examined: 3788
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1835698 group by rating;"
Query_time: 17 Lock_time: 0 Rows_sent: 3 Rows_examined: 4311
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1269322 group by rating;"
Версия MySQL:
- версия протокола: 10
- версия: 5.0.77-log
- версия bdb: Программное обеспечение Sleepycat: Беркли DB 4.1.24: (29 января 2009 г.)
- машина компиляции версии: x86_64 version_compile_os: redhat-linux-gnu
Информация о таблице:
SHOW COLUMNS FROM user_match_ratings;
дает:
╔═══════════════╦════════════╦════╦═════╦════════╦════════════════╗
║ id ║ int(11) ║ NO ║ PRI ║ NULL ║ auto_increment ║
║ rater_user_id ║ int(11) ║ NO ║ MUL ║ NULL ║ ║
║ rated_user_id ║ int(11) ║ NO ║ MUL ║ NULL ║ ║
║ rating ║ varchar(1) ║ NO ║ ║ NULL ║ ║
║ created_at ║ datetime ║ NO ║ ║ NULL ║ ║
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝
Пример запроса:
select * from mutual_match_ratings where id=221673540;
дает:
╔═══════════╦═══════════════╦═══════════════╦════════╦══════════════════════╗
║ id ║ rater_user_id ║ rated_user_id ║ rating ║ created_at ║
╠═══════════╬═══════════════╬═══════════════╬════════╬══════════════════════╣
║ 221673540 ║ 5699713 ║ 3890950 ║ N ║ 2013-04-09 13:00:38 ║
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝
Индексы
Таблица имеет 3 настроенных индекса:
- единый индекс на
rated_user_id
- составной индекс на
rater_user_id
иcreated_at
- составной индекс на
rated_user_id
иrater_user_id
показать индекс из user_match_ratings;
дает:
╔════════════════════╦════════════╦═══════════════════════════╦══════════════╦═══════════════╦═══════════╦═════════════╦══════════╦════════╦═════════════════════════╦════════════╦══════════════════╗
║ Table ║ Non_unique ║ Key_name ║ Seq_in_index ║ Column_name ║ Collation ║ Cardinality ║ Sub_part ║ Packed ║ Null ║ Index_type ║ Comment ║
╠════════════════════╬════════════╬═══════════════════════════╬══════════════╬═══════════════╬═══════════╬═════════════╬══════════╬════════╬═════════════════════════╬════════════╬══════════════════╣
║ user_match_ratings ║ 0 ║ PRIMARY ║ 1 ║ id ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index1 ║ 1 ║ rater_user_id ║ A ║ 11039059 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index1 ║ 2 ║ created_at ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index2 ║ 1 ║ rated_user_id ║ A ║ 4014203 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index2 ║ 2 ║ rater_user_id ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index3 ║ 1 ║ rated_user_id ║ A ║ 2480687 ║ NULL ║ NULL ║ BTREE ║ ║ ║
╚════════════════════╩════════════╩═══════════════════════════╩══════════════╩═══════════════╩═══════════╩═════════════╩══════════╩════════╩═════════════════════════╩════════════╩══════════════════╝
Даже с индексами эти запросы медленные.
Мой вопрос:
Будет ли разделение этой таблицы / данных в другой базе данных на сервере, на котором достаточно оперативной памяти для хранения этих данных в памяти, ускорит ли это эти запросы? Есть ли что-то в таблицах / индексах, которые мы можем улучшить, чтобы сделать эти запросы быстрее?
В настоящее время у нас есть 16 ГБ памяти; однако мы рассматриваем либо модернизацию существующей машины до 32 ГБ, либо добавление новой машины, по крайней мере, с таким большим, может быть, твердотельным накопителем.
источник
SELECT QUERY
. Не могли бы вы предложить? PS Ваш вопрос заставил меня присоединиться к этому сообществу (у);)Ответы:
Мысли по этому вопросу, выброшенные в случайном порядке:
Очевидный индекс для этого запроса:
(rated_user_id, rating)
. Запрос, который получает данные только для одного из миллиона пользователей и требует 17 секунд, делает что-то не так: читает из(rated_user_id, rater_user_id)
индекса, а затем читает из таблицы (от сотен до тысяч) значений дляrating
столбца, чегоrating
нет в любом индексе. Таким образом, запрос должен прочитать много строк таблицы, которые расположены в разных местах диска.Прежде чем приступить к добавлению многочисленных индексов в таблицы, попробуйте проанализировать производительность всей базы данных, весь набор медленных запросов, еще раз проверить выбор типов данных, используемый вами механизм и параметры конфигурации.
Подумайте о переходе на более новую версию MySQL, 5.1, 5.5 или даже 5.6 (также: версии Percona и MariaDB.) Несколько преимуществ, поскольку исправлены ошибки, улучшен оптимизатор, и вы можете установить низкий порог для медленных запросов менее 1 секунды. (как 10 миллисекунд). Это даст вам гораздо лучшую информацию о медленных запросах.
Выбор типа данных
rating
странный.VARCHAR(1)
? Почему нетCHAR(1)
? Почему нетTINYINT
? Это сэкономит вам немного места, как в таблице, так и в индексах, которые (будут) включать этот столбец. Столбцу varchar (1) требуется еще один байт по сравнению с char (1), и если они имеют значение utf8, столбцам (var) char потребуется 3 (или 4) байта вместо 1 (tinyint).источник
Я занимался таблицами для правительства Германии, иногда с 60 миллионами записей.
У нас было много этих таблиц.
И нам нужно было много раз узнать общее количество строк в таблице.
Поговорив с программистами Oracle и Microsoft, мы не были так счастливы ...
Таким образом, мы, группа программистов баз данных, решили, что в каждой таблице есть запись, всегда одна запись, в которой хранится общее количество записей. Мы обновили это число, в зависимости от строк INSERT или DELETE.
Мы перепробовали все другие способы. Это, безусловно, самый быстрый способ.
Мы используем этот способ с 1998 года и никогда не имели неправильного количества строк во всех наших многомиллионных таблицах записей.
источник
count(*)
есть некоторые улучшения.Я постараюсь разделить на рейтинговые типы, как:
mutat_match_ratings_N, mutat_match_ratings_S и т. д.
Вы должны выполнить запрос для каждого типа, но, возможно, это быстрее, чем другой способ. Попробуйте.
Это предполагает, что у вас есть фиксированное количество типов рейтинга, и вам не нужна эта таблица для других запросов, которые будут хуже с этой новой структурой.
Если это так, вам следует искать другой подход или поддерживать две копии таблицы (исходную таблицу и секционированные), если это доступно с точки зрения пространства и удобства обслуживания (или логики приложения).
источник