Пример оптимизации структуры таблицы

15th Ноябрь 2011 | Категории: MySQL | Метки:

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

Итак, начнем.

В 2009 году я начал собирать статистику в одной браузерной игре. Тогда я не особо заботился об оптимизации, поэтому все целочисленные данные были сохранены в полях типа INT. Причем я наивно полагал, что INT(5) — сам ограничит размер поля до 5 символов, и, соответственно, занимаемый размер уменьшится. Сейчас самому очень смешно.

В 2009 году структура таблицы была такой:

CREATE TABLE `s_302clan_old_null` (
  `id` int(7) NOT NULL auto_increment,
  `pl_id` int(11) default NULL,
  `pl_name` varchar(20) default NULL,
  `lastupd` int(11) default NULL,
  `lvl` int(11) default NULL,
  `exp` bigint(20) default NULL,
  `rulbet` int(11) default NULL,
  `rulwin` int(11) default NULL,
  `bat` int(11) default NULL,
  `batw` int(11) default NULL,
  `batl` int(11) default NULL,
  `r1` float default NULL,
  `r2` float default NULL,
  `r3` float default NULL,
  `r4` float default NULL,
  `r5` float default NULL,
  `r6` float default NULL,
  `r7` float default NULL,
  `g1` float default NULL,
  `g2` int(11) default NULL,
  `g3` int(11) default NULL,
  `g4` int(11) default NULL,
  `g5` int(11) default NULL,
  `g6` float default NULL,
  `g7` int(11) default NULL,
  `g8` int(11) default NULL,
  `g9` int(11) default NULL,
  `status` int(11) default NULL,
  `gt` float NOT NULL default '0',
  `r8` float NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `lastupd` (`lastupd`)
) ENGINE=MyISAM AUTO_INCREMENT=136439 DEFAULT CHARSET=utf8

В таблицу раз в сутки сохраняются данные по каждом игроку (pl_id). Заносится информация по всем существующим параметрам персонажа.

Какие здесь есть возможности оптимизации:
Вместо lastupd, в который сохраняется timestamp добавления, можно было бы использовать DATE (экономия с 4 до 3 байт);
Имя персонажа меняется очень редко, поэтому поле pl_name можно вынести в справочную таблицу;
default NULL — стоит заменить на NOT NULL и установить значение по умолчанию. При реализации задачи мы выяснили, что в каждом поле хранится какой-то параметр игрока, который равен определённому значению. Значение NULL просто не может возникнуть при нормальном выполнении программы. Исключительные ситуации должны обрабатываться в коде. Наличие NULL — плохо сказывается на скорости выборки и размере таблицы;
Возможно, стоит удалить поле id и первичный ключ заменить на составной(pl_id, date_add);
Везде, где только возможно, следует заменить тип INT на более экономичные типы: TINYINT, SMALLINT, MEDIUMINT.
Поля с типом FLOAT допустимы, если не важна большая точность после запятой. К тому же, обычно тип FLOAT более экономичен — всего 4 байта. Предположим, нужно сохранить число с 3 знаками после запятой и 5 знаками до запятой: DECIMAL(8,3) — 5 байт;

Проведем исследование.
Размер начальной таблицы: 17405.9 Кб

Этап 1. Избавимся от NULL, зададим значения по умолчанию.

CREATE TABLE `s_302clan_old_not_null` (
  `id` int(7) NOT NULL auto_increment,
  `pl_id` int(11) NOT NULL default '0',
  `pl_name` varchar(20) NOT NULL default '0',
  `lastupd` int(11) NOT NULL default '0',
  `lvl` int(11) NOT NULL default '0',
  `exp` bigint(20) NOT NULL default '0',
  `rulbet` int(11) NOT NULL default '0',
  `rulwin` int(11) NOT NULL default '0',
  `bat` int(11) NOT NULL default '0',
  `batw` int(11) NOT NULL default '0',
  `batl` int(11) NOT NULL default '0',
  `r1` float NOT NULL default '0',
  `r2` float NOT NULL default '0',
  `r3` float NOT NULL default '0',
  `r4` float NOT NULL default '0',
  `r5` float NOT NULL default '0',
  `r6` float NOT NULL default '0',
  `r7` float NOT NULL default '0',
  `g1` float NOT NULL default '0',
  `g2` int(11) NOT NULL default '0',
  `g3` int(11) NOT NULL default '0',
  `g4` int(11) NOT NULL default '0',
  `g5` int(11) NOT NULL default '0',
  `g6` float NOT NULL default '0',
  `g7` int(11) NOT NULL default '0',
  `g8` int(11) NOT NULL default '0',
  `g9` int(11) NOT NULL default '0',
  `status` int(11) NOT NULL default '0',
  `gt` float NOT NULL default '0',
  `r8` float NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `lastupd` (`lastupd`)
) ENGINE=MyISAM AUTO_INCREMENT=136439 DEFAULT CHARSET=utf8

Размер таблицы: 16722.9 Кб

Получили выигрыш в размере на 4%. Эта оптимизация возможна из-за того, что MySQL хранит специальный флаг для каждого поля с разрешенным NULL.

Этап 2. Оптимизируем хранение целочисленных данных.

CREATE TABLE `s_302clan_old_optimize` (
  `id` mediumint(8) unsigned NOT NULL auto_increment,
  `pl_id` mediumint(8) unsigned NOT NULL default '0',
  `pl_name` varchar(20) NOT NULL default '0',
  `lastupd` int(11) unsigned NOT NULL default '0',
  `lvl` tinyint(3) unsigned NOT NULL default '0',
  `exp` bigint(20) unsigned NOT NULL default '0',
  `rulbet` int(11) unsigned NOT NULL default '0',
  `rulwin` int(11) unsigned NOT NULL default '0',
  `bat` mediumint(8) unsigned NOT NULL default '0',
  `batw` mediumint(8) unsigned NOT NULL default '0',
  `batl` mediumint(8) unsigned NOT NULL default '0',
  `r1` float unsigned NOT NULL default '0',
  `r2` float unsigned NOT NULL default '0',
  `r3` float unsigned NOT NULL default '0',
  `r4` float unsigned NOT NULL default '0',
  `r5` float unsigned NOT NULL default '0',
  `r6` float unsigned NOT NULL default '0',
  `r7` float unsigned NOT NULL default '0',
  `g1` float unsigned NOT NULL default '0',
  `g2` mediumint(8) unsigned NOT NULL default '0',
  `g3` mediumint(8) unsigned NOT NULL default '0',
  `g4` mediumint(8) unsigned NOT NULL default '0',
  `g5` mediumint(8) unsigned NOT NULL default '0',
  `g6` float unsigned NOT NULL default '0',
  `g7` mediumint(8) unsigned NOT NULL default '0',
  `g8` mediumint(8) unsigned NOT NULL default '0',
  `g9` mediumint(8) unsigned NOT NULL default '0',
  `status` int(11) unsigned NOT NULL default '0',
  `gt` float unsigned NOT NULL default '0',
  `r8` float unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `lastupd` (`lastupd`)
) ENGINE=MyISAM AUTO_INCREMENT=136439 DEFAULT CHARSET=utf8

Размер таблицы: 14869.7
Оптимизация 17%.

Уменьшение размера каждого ряда и размерности индекса благоприятно сказываются на производительности выборок.

Таких таблиц у меня было около 40 штук. В результате оптимизации каждой общий размер таблиц уменьшился с 1060Мб до 920Мб.

Удачной оптимизации!

Subscribe without commenting


Пока комментариев нет.