MySQL Partitioning показывает низкую производительность

Я пытался проверить, является ли реализация разбиения базы данных MySQL полезной для нашего приложения или нет. Я много слышал о преимуществах использования разбиения для большого количества записей. Но удивительно, что время отклика приложения сократилось в 3 раза при выполнении нагрузочного тестирования после разделения. Может кто-нибудь, пожалуйста, помогите объяснить причину, по которой это может произойти?

Позвольте мне объяснить подробно:

Ниже приведен DDL таблицы, когда разделение не было выполнено.

CREATE TABLE `myTable` ( 
`column1` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
`column2` char(3) NOT NULL, 
`column3` char(3) NOT NULL, 
`column4` char(2) NOT NULL, 
`column5` smallint(4) unsigned NOT NULL, 
`column6` date NOT NULL, 
`column7` varchar(2) NOT NULL, 
`column8` tinyint(3) unsigned NOT NULL COMMENT 'Seat Count Ranges from 0-9.', 
`column9` varchar(2) NOT NULL, 
`column10` varchar(4) NOT NULL, 
`column11` char(2) NOT NULL, 
`column12` datetime NOT NULL, 
`column13` datetime DEFAULT NULL, 
PRIMARY KEY (`column1`), 
KEY `index1` (`column2`,`column3`,`column4`,`column5`,`column7`,`column6`), 
KEY `index2` (`column2`,`column3`,`column6`,`column4`) 
) ENGINE=InnoDB AUTO_INCREMENT=342024674 DEFAULT CHARSET=latin1; 

И ниже - DDL той же таблицы после реализации разбиения "Range" на основе поля даты.

CREATE TABLE `myTable` ( 
`column1` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
`column2` char(3) NOT NULL, 
`column3` char(3) NOT NULL, 
`column4` char(2) NOT NULL, 
`column5` smallint(4) unsigned NOT NULL, 
`column6` date NOT NULL, 
`column7` varchar(2) NOT NULL, 
`column8` tinyint(3) unsigned NOT NULL COMMENT 'Seat Count Ranges from 0-9.', 
`column9` varchar(2) NOT NULL, 
`column10` varchar(4) NOT NULL, 
`column11` char(2) NOT NULL, 
`column12` datetime NOT NULL, 
`column13` datetime DEFAULT NULL, 
PRIMARY KEY (`column1`,`column6`), 
KEY `index1` (`column2`,`column3`,`column4`,`column5`,`column7`,`column6`), 
KEY `index2` (`column2`,`column3`,`column6`,`column4`) 
) ENGINE=InnoDB AUTO_INCREMENT=342024674 DEFAULT CHARSET=latin1 
PARTITION BY RANGE COLUMNS(`column6`) 
(PARTITION date_jul_11 VALUES LESS THAN ('2011-08-01') ENGINE = InnoDB, 
PARTITION date_aug_11 VALUES LESS THAN ('2011-09-01') ENGINE = InnoDB, 
PARTITION date_sep_11 VALUES LESS THAN ('2011-10-01') ENGINE = InnoDB, 
PARTITION date_oct_11 VALUES LESS THAN ('2011-11-01') ENGINE = InnoDB, 
PARTITION date_nov_11 VALUES LESS THAN ('2011-12-01') ENGINE = InnoDB, 
PARTITION date_dec_11 VALUES LESS THAN ('2012-01-01') ENGINE = InnoDB, 
PARTITION date_jan_12 VALUES LESS THAN ('2012-02-01') ENGINE = InnoDB, 
PARTITION date_feb_12 VALUES LESS THAN ('2012-03-01') ENGINE = InnoDB, 
PARTITION date_mar_12 VALUES LESS THAN ('2012-04-01') ENGINE = InnoDB, 
PARTITION date_apr_12 VALUES LESS THAN ('2012-05-01') ENGINE = InnoDB, 
PARTITION date_may_12 VALUES LESS THAN ('2012-06-01') ENGINE = InnoDB, 
PARTITION date_jun_12 VALUES LESS THAN ('2012-07-01') ENGINE = InnoDB, 
PARTITION date_jul_12 VALUES LESS THAN ('2012-08-01') ENGINE = InnoDB, 
PARTITION date_aug_12 VALUES LESS THAN ('2012-09-01') ENGINE = InnoDB, 
PARTITION date_sep_12 VALUES LESS THAN ('2012-10-01') ENGINE = InnoDB, 
PARTITION date_oct_12 VALUES LESS THAN ('2012-11-01') ENGINE = InnoDB, 
PARTITION date_nov_12 VALUES LESS THAN ('2012-12-01') ENGINE = InnoDB, 
PARTITION date_dec_12 VALUES LESS THAN ('2013-01-01') ENGINE = InnoDB, 
PARTITION date_jan_13 VALUES LESS THAN ('2013-02-01') ENGINE = InnoDB, 
PARTITION date_feb_13 VALUES LESS THAN ('2013-03-01') ENGINE = InnoDB, 
PARTITION date_mar_13 VALUES LESS THAN ('2013-04-01') ENGINE = InnoDB, 
PARTITION date_apr_13 VALUES LESS THAN ('2013-05-01') ENGINE = InnoDB, 
PARTITION date_may_13 VALUES LESS THAN ('2013-06-01') ENGINE = InnoDB, 
PARTITION date_jun_13 VALUES LESS THAN ('2013-07-01') ENGINE = InnoDB, 
PARTITION date_oth VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB); 

Ниже приведен пример запроса, который использовался для проведения нагрузочного тестирования для проверки производительности.

SELECT column8, column9
FROM myTable
WHERE column2 = ? AND column3 = ? AND column4 =? AND column5 = ? AND column7 = ? AND column6 = ?
LIMIT 1 

? выше были заменены реальными значениями, присутствующими в базе данных для тестирования.

Обратите внимание, что количество записей в таблице "myTable" составляет около 342 миллионов, а количество тестовых данных, используемых для тестирования производительности, составляет около 2 миллионов.

Однако, как я уже сказал, производительность после реализации разбиения была в три раза ниже. Есть идеи, что могло вызвать это?

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

2 ответа

Помните, что цель разделения заключается в ускорении запросов, когда ваш запрос ограничивает количество разделов, в которых может быть найден результат. Я думаю, что проблема заключается в column6 = ? в вашем тестовом запросе. Я предполагаю, что для столбца 6, требующего точного значения, а не диапазона, ваш набор результатов сокращается до очень небольшого количества значений. Следовательно, в процессе сужения разделов вы уже по существу нашли результат. А так как индексы разделены по нескольким разделам, этот процесс сужается.

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

SELECT column8, column9
FROM myTable
WHERE column6 < ? AND column6 > ? AND column2 = ? AND column3 = ? AND column4 =? AND column5 = ?

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

Это может помочь: http://dev.mysql.com/tech-resources/articles/partitioning.html

Глядя на это, я хотел бы рассмотреть несколько вещей.

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

Затем, ваше разделение жестко закодировано в определенных диапазонах дат - следовательно, вам нужно будет придумать лучший план, когда date_oth начнет заполняться.

И column6 =?

То есть вы тестировали производительность данных только из одного раздела? В лучшем случае это будет не быстрее, чем со всеми данными в одной таблице.

Как указывает Натан, вы разбиваете на столбцы 6 - но у вас нет этого в начале любого из ваших индексов, поэтому СУБД должна искать индекс в каждом разделе, чтобы найти данные - это также причина, почему производительность такая плохая. (Я не согласен с тем, что разбиение помогает только в диапазоне запросов).

Другие вопросы по тегам