Для эффективного проектирования таблиц innodb необходимо понимать, как innodb использует индексы - в частности, что такое кластеризованные индексы и как они работают.
Фоновое чтение
Пожалуйста, уделите некоторое время, чтобы прочитать следующееМои статьи и предыдущие ответы:
Вы также можете найти эту презентацию интересной:
Итак, теперь вы лучше понимаете архитектуру innodb, и мы посмотрим, как оптимизировать вашу модель для механизма innodb.
Поскольку вы предоставили только два примера запросов, мне пришлось сделать определенные предположения, чтобы следующий дизайн был оптимизирован для запросов, которые охватывают site_id и path.Я оставлю вам возможность вносить дальнейшие изменения в дизайн (при необходимости), поскольку вы знаете свои данные лучше, чем я.
Пересмотренная схема (упрощенная версия)
Я изменил ваш дизайн исоздано 3 таблицы: site, site_request и site_request_metric.
Таблица сайта (1024 строки)
drop table if exists site;
create table site
(
site_id smallint unsigned not null auto_increment primary key,
url varchar(255) unique not null,
next_request_id int unsigned not null default 0
)
engine=innodb;
select count(*) from site;
+----------+
| count(*) |
+----------+
| 1024 |
+----------+
Таблица сайта - Пример данных
+---------+------------------+-----------------+
| site_id | url | next_request_id |
+---------+------------------+-----------------+
| 1 | www.site1.com | 167 |
| 2 | www.site2.com | 177 |
| 3 | www.site3.com | 68 |
...
| 1022 | www.site1022.com | 203 |
| 1023 | www.site1023.com | 80 |
| 1024 | www.site1024.com | 239 |
+---------+------------------+-----------------+
Большинство из вышеперечисленных полейСамо собой разумеется, однако next_request_id - это поле счетчика, которое записывает, сколько запросов (путь или q в вашем примере) имеет данный сайт.Например, сайт 1024 имеет 239 отдельных запросов / путей к страницам, по которым мы хотим записать память и показатели выполнения.
Также обратите внимание на числовые типы данных, которые я использовал - большинство ваших плохо определены, как вы, кажется,путать необязательный спецификатор ширины экрана (используется только с нулевым заполнением) с размером целого числа.Важно выбрать наименьший возможный тип данных, чтобы мы могли упаковать больше данных в наш буфер innodb.
http://dev.mysql.com/doc/refman/5.0/en/integer-types.html
Таблица запросов сайта (192 тыс. Строк)
drop table if exists site_request;
create table site_request
(
site_id smallint unsigned not null,
request_id int unsigned not null,
created_date datetime not null,
path varchar(255) not null,
next_metric_id int unsigned not null default 0,
primary key (site_id, request_id)
)
engine=innodb;
select count(*) from site_request;
+----------+
| count(*) |
+----------+
| 192336 |
+----------+
Таблица запросов сайта - Пример данных
+---------+------------+---------------------+----------------------+----------------+
| site_id | request_id | created_date | path | next_metric_id |
+---------+------------+---------------------+----------------------+----------------+
| 1 | 1 | 2011-12-14 17:17:41 | www.site1.com/1 | 250 |
| 1 | 2 | 2011-12-14 17:17:41 | www.site1.com/2 | 132 |
| 1 | 3 | 2011-12-14 17:17:41 | www.site1.com/3 | 345 |
...
| 1 | 166| 2011-12-14 17:17:41 | www.site1.com/166 | 342 |
| 1 | 167| 2011-12-14 17:17:41 | www.site1.com/167 | 231 |
...
| 1024 | 1 | 2011-12-14 17:17:58 | www.site1024.com/1 | 241 |
| 1024 | 2 | 2011-12-14 17:17:58 | www.site1024.com/2 | 266 |
...
| 1024 | 236 | 2011-12-14 17:17:58 | www.site1024.com/236 | 466 |
| 1024 | 237 | 2011-12-14 17:17:58 | www.site1024.com/237 | 459 |
| 1024 | 238 | 2011-12-14 17:17:58 | www.site1024.com/238 | 389 |
| 1024 | 239 | 2011-12-14 17:17:58 | www.site1024.com/239 | 592 |
+---------+------------+---------------------+----------------------+----------------+
Опять же, большинство полей говорят сами за себя.Первичный ключ этой таблицы представляет собой совокупность site_id и request_id, поэтому у сайта 1 есть 167 отдельных запросов / путей, а у сайта 1024 - 239.
Чтобы выбрать отдельный запрос, необходимо указать как site_id, так и request_id:
select * from site_request where site_id = 1 and request_id = 167
+---------+------------+---------------------+-------------------+----------------+
| site_id | request_id | created_date | path | next_metric_id |
+---------+------------+---------------------+-------------------+----------------+
| 1 | 167 | 2011-12-14 17:17:41 | www.site1.com/167 | 231 |
+---------+------------+---------------------+-------------------+----------------+
1 row in set (0.00 sec)
select * from site_request where site_id = 1024 and request_id = 167
+---------+------------+---------------------+----------------------+----------------+
| site_id | request_id | created_date | path | next_metric_id |
+---------+------------+---------------------+----------------------+----------------+
| 1024 | 167 | 2011-12-14 17:17:58 | www.site1024.com/167 | 175 |
+---------+------------+---------------------+----------------------+----------------+
1 row in set (0.00 sec)
Если я хочу добавить новый запрос к сайту, мы используем site.next_request_id + 1, чтобы сгенерировать следующее значение составного первичного ключа для данного site_id.Обычно это делается с помощью триггера следующим образом:
delimiter #
create trigger site_request_before_ins_trig before insert on site_request
for each row
begin
declare v_id int unsigned default 0;
select next_request_id + 1 into v_id from site where site_id = new.site_id;
set new.request_id = v_id, new.created_date = now();
update site set next_request_id = v_id where site_id = new.site_id;
end#
delimiter ;
Почему я просто не создал первичный ключ auto_increment и вторичный индекс для site_id?
create table site_request
(
request_id int unsigned not null auto_increment primary key,
site_id smallint unsigned not null,
...
key (site_id)
)
engine=innodb;
Что ж, я сделалПредположение, что большинство ваших запросов покрывают site_id и path, поэтому кластеризация таблицы запросов на site_id - это полезная оптимизация, даже если накладные расходы будут немного увеличены.Меня больше беспокоит производительность чтения, особенно потому, что эта таблица будет позже объединена с таблицей метрик HUGE.
Таблица метрик запроса сайта (74 миллиона строк)
drop table if exists site_request_metric;
create table site_request_metric
(
site_id smallint unsigned not null,
request_id int unsigned not null,
metric_id int unsigned not null,
created_date datetime not null,
memory_usage int unsigned not null default 0,
execution_time mediumint unsigned not null default 0,
primary key (site_id, request_id, metric_id)
)
engine=innodb;
select count(*) from site_request_metric;
+----------+
| count(*) |
+----------+
| 73858764 |
+----------+
Метрика запроса сайтаТаблица - Пример данных
+---------+------------+-----------+---------------------+--------------+----------------+
| site_id | request_id | metric_id | created_date | memory_usage | execution_time |
+---------+------------+-----------+---------------------+--------------+----------------+
| 1 | 1 | 1 | 2011-12-14 17:17:58 | 18052380 | 7731 |
| 1 | 1 | 2 | 2011-12-14 17:17:58 | 32013204 | 7881 |
| 1 | 1 | 3 | 2011-12-14 17:17:58 | 55779470 | 7274 |
...
| 1 | 1 | 249 | 2011-12-14 17:17:58 | 11527748 | 5126 |
| 1 | 1 | 248 | 2011-12-14 17:17:58 | 19457506 | 4097 |
| 1 | 1 | 247 | 2011-12-14 17:17:58 | 23129432 | 6202 |
...
| 997 | 1 | 1 | 2011-12-14 19:08:48 | 38584043 | 7156 |
| 997 | 1 | 2 | 2011-12-14 19:08:48 | 68884314 | 2185 |
| 997 | 1 | 3 | 2011-12-14 19:08:48 | 31545597 | 207 |
...
| 997 | 1 | 380 | 2011-12-14 19:08:49 | 39123978 | 166 |
| 997 | 1 | 381 | 2011-12-14 19:08:49 | 45114404 | 7310 |
| 997 | 1 | 382 | 2011-12-14 19:08:49 | 55057884 | 506 | +---------+------------+-----------+---------------------+--------------+----------------+
Поле site_request_metric.next_metric_id работает аналогично полю счетчика site.next_request_id и поддерживается с помощью триггера.
delimiter #
create trigger site_request_metric_before_ins_trig before insert on site_request_metric
for each row
begin
declare v_id int unsigned default 0;
select next_metric_id + 1 into v_id from site_request where site_id = new.site_id and request_id = new.request_id;
set new.metric_id = v_id, new.created_date = now();
update site_request set next_metric_id = v_id where site_id = new.site_id and request_id = new.request_id;
end#
delimiter ;
Производительность схемы
Например, сайт 997:
select * from site where site_id = 997;
+---------+-----------------+-----------------+
| site_id | url | next_request_id |
+---------+-----------------+-----------------+
| 997 | www.site997.com | 319 |
+---------+-----------------+-----------------+
1 row in set (0.00 sec)
Сайт 997 имеет 319 запросов / путей к отдельным страницам.
select * from site_request where site_id = 997;
+---------+------------+---------------------+---------------------+----------------+
| site_id | request_id | created_date | path | next_metric_id |
+---------+------------+---------------------+---------------------+----------------+
| 997 | 1 | 2011-12-14 17:17:58 | www.site997.com/1 | 383 |
| 997 | 2 | 2011-12-14 17:17:58 | www.site997.com/2 | 262 |
| 997 | 3 | 2011-12-14 17:17:58 | www.site997.com/3 | 470 |
| 997 | 4 | 2011-12-14 17:17:58 | www.site997.com/4 | 247 |
...
| 997 | 316 | 2011-12-14 17:17:58 | www.site997.com/316 | 176 |
| 997 | 317 | 2011-12-14 17:17:58 | www.site997.com/317 | 441 |
| 997 | 318 | 2011-12-14 17:17:58 | www.site997.com/318 | 419 |
| 997 | 319 | 2011-12-14 17:17:58 | www.site997.com/319 | 601 |
+---------+------------+---------------------+---------------------+----------------+
319 rows in set (0.00 sec)
Сколько метрик у нас для всех сайтов 997запросов?
select sum(next_metric_id) from site_request where site_id = 997;
+---------------------+
| sum(next_metric_id) |
+---------------------+
| 130163 |
+---------------------+
1 row in set (0.00 sec)
Суммирование next_metric_id (как указано выше) для этого сайта выполняется быстрее, чем обычно:
select count(*) from site_request_metric where site_id = 997;
+----------+
| count(*) |
+----------+
| 130163 |
+----------+
1 row in set (0.03 sec)
Таким образом, сайт 997 имеет около 130 КБ памяти и метрики времени выполнения для анализа в пределахстол ок.74 миллиона строк.
Давайте попробуем следующие основные запросы: (все циклы выполнения холодные, т.е. перезапуск mysql, пустые буферы и кеш запросов нет!)
Память
select
hog.*,
sr.path
from
(
select
srm.site_id,
srm.request_id,
count(*) as counter,
avg(srm.memory_usage) as average_memory,
max(srm.memory_usage) as peak_memory,
avg(srm.execution_time) as average_execution_time,
max(srm.execution_time) as peak_execution_time
from
site_request_metric srm
where
srm.site_id = 997
group by
srm.site_id,
srm.request_id
order by
average_memory desc
limit 25
) hog
inner join site_request sr on hog.site_id = sr.site_id and hog.request_id = sr.request_id;
Результаты выглядят следующим образом:
+---------+------------+---------+----------------+-------------+------------------------+---------------------+---------------------+
| site_id | request_id | counter | average_memory | peak_memory | average_execution_time | peak_execution_time | path |
+---------+------------+---------+----------------+-------------+------------------------+---------------------+---------------------+
| 997 | 103 | 184 | 43381803.4293 | 69682361 | 4378.1630 | 8069 | www.site997.com/103 |
| 997 | 151 | 158 | 42594703.1392 | 69329761 | 4422.8481 | 8080 | www.site997.com/151 |
| 997 | 192 | 509 | 42470135.3360 | 69927112 | 4083.1198 | 8098 | www.site997.com/192 |
| 997 | 248 | 161 | 42169276.5590 | 69995565 | 4118.1180 | 7949 | www.site997.com/248 |
| 997 | 221 | 162 | 42156708.4877 | 69233026 | 4151.1667 | 8022 | www.site997.com/221 |
| 997 | 136 | 154 | 42026979.3831 | 69897045 | 4060.5649 | 8098 | www.site997.com/136 |
| 997 | 239 | 424 | 41979697.9788 | 69381215 | 4463.0189 | 8087 | www.site997.com/239 |
| 997 | 77 | 338 | 41864013.0266 | 69991164 | 3942.4142 | 8067 | www.site997.com/77 |
| 997 | 283 | 249 | 41853642.9157 | 69945794 | 3915.7028 | 8034 | www.site997.com/283 |
| 997 | 5 | 228 | 41815274.7851 | 69825743 | 3898.4123 | 8078 | www.site997.com/5 |
| 997 | 216 | 319 | 41766464.5078 | 69777901 | 3899.0752 | 8091 | www.site997.com/216 |
| 997 | 131 | 170 | 41720890.5118 | 69892577 | 4074.2588 | 8097 | www.site997.com/131 |
| 997 | 160 | 385 | 41702556.6545 | 69868379 | 4060.2727 | 8093 | www.site997.com/160 |
| 997 | 245 | 200 | 41683505.3900 | 69668739 | 4052.7950 | 8095 | www.site997.com/245 |
| 997 | 70 | 429 | 41640396.0466 | 69988619 | 3995.3310 | 8099 | www.site997.com/70 |
| 997 | 98 | 485 | 41553544.7649 | 69957698 | 4048.1443 | 8096 | www.site997.com/98 |
| 997 | 153 | 301 | 41542909.4651 | 69754024 | 3884.7409 | 8028 | www.site997.com/153 |
| 997 | 226 | 429 | 41523530.3939 | 69691453 | 4097.7226 | 8096 | www.site997.com/226 |
| 997 | 31 | 478 | 41442100.4435 | 69802248 | 3999.3096 | 8098 | www.site997.com/31 |
| 997 | 171 | 222 | 41405805.8153 | 69433643 | 4364.4414 | 8087 | www.site997.com/171 |
| 997 | 150 | 336 | 41393538.5744 | 69746950 | 4264.5655 | 8077 | www.site997.com/150 |
| 997 | 167 | 526 | 41391595.5741 | 69633242 | 4206.1597 | 8096 | www.site997.com/167 |
| 997 | 182 | 593 | 41288151.5379 | 69992913 | 4351.6476 | 8099 | www.site997.com/182 |
| 997 | 14 | 555 | 41239680.5387 | 69976632 | 4054.6126 | 8084 | www.site997.com/14 |
| 997 | 297 | 410 | 41163572.3805 | 69874576 | 4001.0829 | 8039 | www.site997.com/297 |
+---------+------------+---------+----------------+-------------+------------------------+---------------------+---------------------+
25 rows in set (0.41 sec)
Время выполнения
select
hog.*,
sr.path
from
(
select
srm.site_id,
srm.request_id,
count(*) as counter,
avg(srm.memory_usage) as average_memory,
max(srm.memory_usage) as peak_memory,
avg(srm.execution_time) as average_execution_time,
max(srm.execution_time) as peak_execution_time
from
site_request_metric srm
where
srm.site_id = 997
group by
srm.site_id,
srm.request_id
order by
average_execution_time desc
limit 25
) hog
inner join site_request sr on hog.site_id = sr.site_id and hog.request_id = sr.request_id;
Результаты выглядят следующим образом:
+---------+------------+---------+----------------+-------------+------------------------+---------------------+---------------------+
| site_id | request_id | counter | average_memory | peak_memory | average_execution_time | peak_execution_time | path |
+---------+------------+---------+----------------+-------------+------------------------+---------------------+---------------------+
| 997 | 213 | 159 | 37962517.1321 | 67120491 | 4497.9119 | 8055 | www.site997.com/213 |
| 997 | 239 | 424 | 41979697.9788 | 69381215 | 4463.0189 | 8087 | www.site997.com/239 |
| 997 | 151 | 158 | 42594703.1392 | 69329761 | 4422.8481 | 8080 | www.site997.com/151 |
| 997 | 289 | 382 | 39227749.9869 | 69715783 | 4402.8927 | 8093 | www.site997.com/289 |
| 997 | 69 | 473 | 40099817.4715 | 69798587 | 4380.6850 | 8092 | www.site997.com/69 |
| 997 | 103 | 184 | 43381803.4293 | 69682361 | 4378.1630 | 8069 | www.site997.com/103 |
| 997 | 183 | 236 | 40111564.1356 | 69853507 | 4376.4280 | 8032 | www.site997.com/183 |
| 997 | 171 | 222 | 41405805.8153 | 69433643 | 4364.4414 | 8087 | www.site997.com/171 |
| 997 | 58 | 212 | 39289163.9057 | 69861740 | 4355.8396 | 8087 | www.site997.com/58 |
| 997 | 71 | 388 | 39895200.6108 | 69801188 | 4353.9639 | 8086 | www.site997.com/71 |
| 997 | 182 | 593 | 41288151.5379 | 69992913 | 4351.6476 | 8099 | www.site997.com/182 |
| 997 | 195 | 305 | 39780792.6066 | 69824981 | 4343.0295 | 8081 | www.site997.com/195 |
| 997 | 318 | 419 | 39860696.4415 | 69958266 | 4323.6420 | 8071 | www.site997.com/318 |
| 997 | 303 | 318 | 39357663.3899 | 69850523 | 4322.4686 | 8097 | www.site997.com/303 |
| 997 | 198 | 306 | 38990104.1699 | 69851817 | 4320.0621 | 8088 | www.site997.com/198 |
| 997 | 286 | 227 | 39654671.5859 | 69871305 | 4307.8811 | 8055 | www.site997.com/286 |
| 997 | 105 | 611 | 39055749.5008 | 69813117 | 4296.0802 | 8090 | www.site997.com/105 |
| 997 | 298 | 388 | 40150371.2474 | 69985665 | 4286.9716 | 8095 | www.site997.com/298 |
| 997 | 84 | 517 | 39520438.9497 | 69990404 | 4283.3578 | 8098 | www.site997.com/84 |
| 997 | 106 | 448 | 41099495.4018 | 69902616 | 4282.6094 | 8082 | www.site997.com/106 |
| 997 | 237 | 431 | 39017341.3387 | 69623443 | 4277.4872 | 8071 | www.site997.com/237 |
| 997 | 55 | 381 | 39603109.8294 | 69750984 | 4269.1969 | 8095 | www.site997.com/55 |
| 997 | 34 | 438 | 40697744.4087 | 69843517 | 4266.3288 | 8047 | www.site997.com/34 |
| 997 | 38 | 433 | 40169799.8291 | 69898182 | 4266.1663 | 8088 | www.site997.com/38 |
| 997 | 150 | 336 | 41393538.5744 | 69746950 | 4264.5655 | 8077 | www.site997.com/150 |
+---------+------------+---------+----------------+-------------+------------------------+---------------------+---------------------+
25 rows in set (0.30 sec)
Так что это холодное время выполнения менее 0,5 секунды для обоих запросов к таблице, содержащей ок. 74 миллиона строк (последующее время выполнения - приблизительно 0,06 секунды)
Этот ответ не должен быть окончательным, поскольку есть много других факторов, которые могут повлиять на дизайн таблицы и индекса, которые я не рассматривал. Однако он должен дать вам некоторое представление о том, как простые конструкции таблиц и индексов могут значительно улучшить производительность запросов innodb.
Надеюсь, это поможет:)
Полный сценарий здесь: http://pastie.org/3022142