Страница 1 из 2

Индексы в Mysql

Добавлено: 2007-06-27 17:00:33
dikens3
Есть запрос:

Код: Выделить всё

explain SELECT count(1) FROM log WHERE (`date-time` BETWEEN '2007-06-27' AND '2007-06-27' + INTERVAL 1 DAY) AND `text-action` = '32' AND ( dst_email LIKE '%user@domain.ru%' );
Есть другой запрос: (Отличие только в знаке отрицания !='32' - не равно 32)

Код: Выделить всё

explain SELECT count(1) FROM log WHERE (`date-time` BETWEEN '2007-06-27' AND '2007-06-27' + INTERVAL 1 DAY) AND `text-action` != '32' AND ( dst_email LIKE '%user@domain.ru%' );
Теперь собственно ломаю голову, почему в 1 случае поле type=range и соответственно поиск идёт только по нужным данным. (это хорошо)
А во втором = ALL (Что не есть гуд и поиск идёт по всем данным)
Подробнее:

Код: Выделить всё

mysql> explain SELECT count(1) FROM log WHERE (`date-time` BETWEEN '2007-06-27' AND '2007-06-27' + INTERVAL 1 DAY) AND `text-action` = '32' AND ( dst_email LIKE '%user@domain.ru%' );
+----+-------------+-------+-------+---------------------------------+-----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys                   | key       | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------------------------+-----------+---------+------+------+-------------+
|  1 | SIMPLE      | log   | range | text-action,date-time,text-date | text-date |       6 | NULL |  149 | Using where |
+----+-------------+-------+-------+---------------------------------+-----------+---------+------+------+-------------+

Код: Выделить всё

mysql> explain SELECT count(1) FROM log WHERE (`date-time` BETWEEN '2007-06-27' AND '2007-06-27' + INTERVAL 1 DAY) AND `text-action` != '32' AND ( dst_email LIKE '%user@domain.ru%' );
+----+-------------+-------+------+---------------------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys                   | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | log   | ALL  | text-action,date-time,text-date | NULL |    NULL | NULL | 2322 | Using where |
+----+-------------+-------+------+---------------------------------+------+---------+------+------+-------------+
Ключи на date-time, text-action (Это то поле, которое 32), и совмещённый text-date(text-action,date-time)

Что делать, как лечить и почему не используется индекс? Никакой. :-(

Описалово:

Код: Выделить всё

ALL
    Для каждой комбинации строк из предыдущих таблиц будет производиться полный просмотр этой таблицы. Это обычно плохо, если таблица - первая из не отмеченных как const, и очень плохо во всех остальных случаях. Как правило, можно избегать типа связывания ALL - путем добавления большего количества индексов таким образом, чтобы строка могла быть найдена при помощи константных значений или значений столбца из предыдущих таблиц.

Добавлено: 2007-06-27 18:53:16
Alex Keda
нифига не понял...

Добавлено: 2007-06-27 20:10:09
dikens3
lissyara писал(а):нифига не понял...
По русски звучит так:

Запрос номер 1 при поиске в базе будет обрабатывать 149 строк (это нормально, индекс работает)

Запрос номер 2 при поиске в базе будет обрабатывать 2322 строк (Это все имеющиеся в базе на данный момент)

За месяц строк будет 100 000, за год 1 000 000 - вот нафиг он их будет все обрабатывать(просматривать), когда можно диапазон уменьшить индексами. (С ростом базы время на запрос будет уходить всё больше и больше про запросу номер 2)

Добавлено: 2007-06-27 22:26:34
Alex Keda
дай

Код: Выделить всё

SHOW CREATE TABLE

Добавлено: 2007-06-27 22:42:13
dikens3
lissyara писал(а):дай

Код: Выделить всё

SHOW CREATE TABLE
Завтра, на работе всё.

Добавлено: 2007-06-28 9:50:01
dikens3

Код: Выделить всё

CREATE TABLE `log` (
  `username` varchar(50) default NULL,
  `text-action` smallint(6) unsigned NOT NULL default '0',
  `src_email` varchar(50) default NULL,
  `dst_email` text,
  `src_ip` varchar(15) default NULL,
  `hostname` varchar(255) default NULL,
  `helo_ip` varchar(255) default NULL,
  `size` int(11) default NULL,
  `date-time` timestamp NOT NULL default CURRENT_TIMESTAMP,
  KEY `text-action` (`text-action`),
  KEY `src_email` (`src_email`),
  KEY `hostname` (`hostname`),
  KEY `helo_ip` (`helo_ip`),
  KEY `date-time` (`date-time`),
  KEY `text-date` (`text-action`,`date-time`)
) ENGINE=InnoDB DEFAULT CHARSET=koi8r COMMENT='Таблица логов'
Ключей уже всяких накидал.

Добавлено: 2007-06-28 10:39:27
Alex Keda
странно... даже не знаю :(
==========
логи лучше хранить в MyISAM - будет быстрей на порядок, когда таблица выростет

Добавлено: 2007-06-28 10:43:46
dikens3
lissyara писал(а):странно... даже не знаю :(
==========
логи лучше хранить в MyISAM - будет быстрей на порядок, когда таблица выростет
Мне внешние ключи нужны, а там их нет.
mysql 4.1.22

Добавлено: 2007-06-29 7:26:05
bakake
Внешние ключи на таблице с логами это извращение :) А все таки, если попробовать все тоже самое в тестовых целях но на таблице myisam (пересоздать и перелить данные на полутора тысячах записей займет минуту в пределе)?

Добавлено: 2007-06-29 9:58:04
dikens3
bakake писал(а):Внешние ключи на таблице с логами это извращение :) А все таки, если попробовать все тоже самое в тестовых целях но на таблице myisam (пересоздать и перелить данные на полутора тысячах записей займет минуту в пределе)?
Смотря чего нужно достичь, если база будет непротиворечивая, то почему нет?

Добавлено: 2007-06-29 13:38:32
bakake
Ну если я правильно понял БД на 500 таблиц у тебя не будет? Таблиц будет скорее всего в пределе 20. А теперь если прикинуть, чем преимущественно занимается эта БД? Я полагаю что в нее постоянно что то пишут и раз в месяц что нибудь оттуда читают. Создавая индекс или констрайнт-форинкей, ты есессно несколько ускоряешь выполнение запросов на выборку и несколько замедляешь процесс вставки/обновления.
А если учесть, что далеко не всегда полный скан таблицы это плохо... :)

Добавлено: 2007-06-29 13:58:06
dikens3
bakake писал(а):Ну если я правильно понял БД на 500 таблиц у тебя не будет? Таблиц будет скорее всего в пределе 20. А теперь если прикинуть, чем преимущественно занимается эта БД? Я полагаю что в нее постоянно что то пишут и раз в месяц что нибудь оттуда читают. Создавая индекс или констрайнт-форинкей, ты есессно несколько ускоряешь выполнение запросов на выборку и несколько замедляешь процесс вставки/обновления.
А если учесть, что далеко не всегда полный скан таблицы это плохо... :)
Таблиц 12 всего.
Действительно постоянно что-то пишется в таблицу логов.( эдак 20 записей в минуту, что не так много)
Выборка должна происходить максимально быстро. (Использовать индексы)
Вобщем не существенно, т.к. в таблице log у меня не используются внешние ключи.(переделал на MyISAM)

Добавлено: 2007-06-29 14:04:45
bakake
Аааа! Я предложил только попробовать myisam! Кстати, заработало?
PS
Кстати, в myisam можно сжатые таблицы использовать. Для архива логов -- самое оно :)

Добавлено: 2007-06-29 15:17:41
dikens3
bakake писал(а):Аааа! Я предложил только попробовать myisam! Кстати, заработало?
PS
Кстати, в myisam можно сжатые таблицы использовать. Для архива логов -- самое оно :)
А ссылку на сжатые архивы?

Заработало, только прилось мозгами включить и использовать ещё одно поле (size - размер письма). Он существует только в двух случаях:
1. Когда письмо принято (text-action = 33)
2. Когда письмо отправлено (text-action = 32)
Во всех остальных случаях (если вирус, хост заблокирован оно мне не нужно и устанавливается NULL)

В итоге статистика по всем письмам (Принятым, непринятым и отправленным делается запросом с UNION(иначе индексы не хотят работать))
Запрос приобрёл такой вид:

Код: Выделить всё

explain SELECT src_email,dst_email,`text-action`,`date-time` FROM log WHERE ( dst_email LIKE '%user@domain.ru%' ) AND (`date-time` BETWEEN '2007-06-29' AND '2007-06-29' + INTERVAL 1 DAY) AND `text-action` = 33 
UNION SELECT src_email,dst_email,`text-action`,`date-time` FROM log WHERE ( dst_email LIKE '%user@domain.ru%' ) AND (`date-time` BETWEEN '2007-06-29' AND '2007-06-29' + INTERVAL 1 DAY) AND `size` IS NULL 
UNION SELECT src_email,dst_email,`text-action`,`date-time` FROM log WHERE ( src_email LIKE 'user@domain.ru' ) AND (`date-time` BETWEEN '2007-06-29' AND '2007-06-29' + INTERVAL 1 DAY) AND `text-action` = 32 ORDER BY `date-time`

+----+--------------+--------------+-------+--------------------------+-----------+---------+-------+------+----------------+
| id | select_type  | table        | type  | possible_keys            | key       | key_len | ref   | rows | Extra          |
+----+--------------+--------------+-------+--------------------------+-----------+---------+-------+------+----------------+
|  1 | PRIMARY      | log          | range | date-time,text-date      | text-date |       6 | NULL  |  284 | Using where    |
|  2 | UNION        | log          | ref   | size,date-time,size-date | size-date |       5 | const |    1 | Using where    |
|  3 | UNION        | log          | range | date-time,text-date      | text-date |       6 | NULL  |  115 | Using where    |
|NULL | UNION RESULT | <union1,2,3> | ALL   | NULL                     | NULL      |    NULL | NULL  | NULL | Using filesort |
+----+--------------+--------------+-------+--------------------------+-----------+---------+-------+------+----------------+
4 rows in set (0.05 sec)
P.S. Как сжатые делать, где почитать?

Добавлено: 2007-06-29 15:22:37
dikens3
сам нашёл, читаю:

Код: Выделить всё

# myisampack -t log.MYI
Compressing log.MYD: (1595 records)
- Calculating statistics

normal:      4  empty-space:       2  empty-zero:         1  empty-fill:   2
pre-space:   0  end-space:         4  intervall-fields:   0  zero:         0
Original trees:  10  After join: 7
- Compressing file
Min record length:     34   Max length:    118   Mean total length:     66
43.23%

User time 0.02, System time 0.00
Maximum resident set size 1452, Integral resident set size 1368
Non-physical pagefaults 183, Physical pagefaults 10, Swaps 0
Blocks in 5 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 16, Involuntary context switches 14

Добавлено: 2007-06-29 15:46:32
bakake
dikens3 писал(а): Заработало, только прилось мозгами включить и использовать ещё одно поле (size - размер письма). Он существует только в двух случаях:
1. Когда письмо принято (text-action = 33)
2. Когда письмо отправлено (text-action = 32)
Во всех остальных случаях (если вирус, хост заблокирован оно мне не нужно и устанавливается NULL)
А все, до меня дошло :) Можно заменить NULL на 0 и, главное, само поле сделать "NOT NULL". Тогда индекс будет работать в случае != :)

Добавлено: 2007-06-29 15:57:14
dikens3
В случае с text-action так и есть (выше пример создания таблицы). NOT NULL default '0'
Индекс не работает. :-(

Добавлено: 2007-06-29 16:06:20
bakake
Опс, действительно. Не посмотрел. Гм.. ну тогда вижу только способ попробовать where text-action in (и тут перечислить все случаи за исключением 32) %)

Добавлено: 2007-06-29 18:02:09
dikens3
Тут тоже есть нюанс, т.к. эти самые случаи постепенно добавляются. Уж очень не хочется править везде. (В php станичках потом лень выискивать, да и забуду однозначно)
Делал через NOT IN ('32','33'), не помогло.

Добавлено: 2007-06-29 22:14:30
bakake
С другой стороны, union работает, в принципе намана :)

Добавлено: 2007-06-29 22:21:56
Alex Keda
а я так и ниасилил JOIN/UNION :(

Re: Индексы в Mysql

Добавлено: 2009-11-29 19:47:12
ProFTP
подскажите есть ли статья или что-то другое, чтобы можно было раздуплиться раз и на всегда с индексами под среднюю или высокую нагрузку или чтобы их правильно использовать?

часто используемые INDEX, KEY,
я например посмотрел разные таблицы форумов в MyISAM и по моему все ставят от фонаря их

есть ли какие-то реальные примеры, статьи...?

Re: Индексы в Mysql

Добавлено: 2009-11-29 20:37:28
zg
ProFTP писал(а):и по моему все ставят от фонаря их
никогда не ставил индексы от фонаря :roll:
ProFTP писал(а):есть ли какие-то реальные примеры, статьи...?
мне мануала хватило :pardon:

Re: Индексы в Mysql

Добавлено: 2009-12-04 1:01:54
ProFTP
ну вот например как понять вот это

Код: Выделить всё

CREATE TABLE `search_main` (
             `word`       varchar(100)  NOT NULL   default '',
             `page`       int(11)       NOT NULL   default '0',
             `relevance`  int(11)       NOT NULL   default '0',
        KEY `word` (`word`,`page`)
) TYPE=MyISAM;
`word`,`page` - это просто что они должны быть одинаковые оба?

Код: Выделить всё

CREATE TABLE {$db_prefix}messages (
  id_msg int(10) unsigned NOT NULL auto_increment,
  id_topic mediumint(8) unsigned NOT NULL default '0',
  id_board smallint(5) unsigned NOT NULL default '0',
  poster_time int(10) unsigned NOT NULL default '0',
  id_member mediumint(8) unsigned NOT NULL default '0',
  id_msg_modified int(10) unsigned NOT NULL default '0',
  subject tinytext NOT NULL,
  poster_name tinytext NOT NULL,
  poster_email tinytext NOT NULL,
  poster_ip tinytext NOT NULL,
  smileys_enabled tinyint(4) NOT NULL default '1',
  modified_time int(10) unsigned NOT NULL default '0',
  modified_name tinytext NOT NULL,
  body text NOT NULL,
  icon varchar(16) NOT NULL default 'xx',
  approved tinyint(3) NOT NULL default '1',
  PRIMARY KEY (id_msg),
  UNIQUE topic (id_topic, id_msg),
  UNIQUE id_board (id_board, id_msg),
  UNIQUE id_member (id_member, id_msg),
  KEY approved (approved),
  KEY ip_index (poster_ip(15), id_topic),
  KEY participation (id_member, id_topic),
  KEY show_posts (id_member, id_board),
  KEY id_topic (id_topic)
) TYPE=MyISAM;
почему UNIQUE стоит именно там?

я вот набрал и не могу найти
Достаточно того, что MySQL сам построит индексы по полю, если при объявлении структуры таблицы мы зададим полю уникальность UNIQUE, говорящую о том, что данное поле не может хранить два одинаковых значения.
# KEY является синонимом для INDEX.
# В MySQL ключ UNIQUE может иметь только различающиеся значения. При попытке добавить новую строку с ключом, совпадающим с существующей строкой, возникает ошибка выполнения команды.
# PRIMARY KEY представляет собой уникальный ключ KEY с дополнительным ограничением, что все столбцы с данным ключом должны быть определены как NOT NULL. В MySQL этот ключ называется PRIMARY (первичный). Таблица может иметь только один первичный ключ PRIMARY KEY. Если PRIMARY KEY отсутствует в таблицах, а некоторое приложение запрашивает его, то MySQL может превратить в PRIMARY KEY первый ключ UNIQUE, не имеющий ни одного столбца NULL.
# PRIMARY KEY может быть многостолбцовым индексом. Однако нельзя создать многостолбцовый индекс, используя в определении столбца атрибут ключа PRIMARY KEY. Именно таким образом только один столбец будет отмечен как первичный. Необходимо использовать синтаксис PRIMARY KEY(index_col_name, ...).
# Если ключ PRIMARY или UNIQUE состоит только из одного столбца и он принадлежит к числовому типу, то на него можно сослаться также как на _rowid (новшество версии 3.23.11).
# Если индексу не назначено имя, то ему будет присвоено первое имя в index_col_name, возможно, с суффиксами (_2, _3, ...), делающими это имя уникальным. Имена индексов для таблицы можно увидеть, используя SHOW INDEX FROM tbl_name. SHOW Syntax.
Достаточно того, что MySQL сам построит индексы по полю, если при объявлении структуры таблицы мы зададим полю уникальность UNIQUE, говорящую о том, что данное поле не может хранить два одинаковых значения.
UNIQUE — опция для INDEX. Используется для указания того, что соотвествующий столбец должен содержать уникальные значения. При указании UNIQUE в MySQL слово INDEX можно опускать (т.к. UNIQUE-столбцы в любом случае будут проиндексированы, но это не даст вставить в столбец неуникальные данные).
как это понимать вообще про UNIQUE? два одинаковых не может, тогда сколько? одно значение должно быть? нафига тогда это поле?
в чем разница между UNIQUE и PRIMARY?

Re: Индексы в Mysql

Добавлено: 2009-12-04 6:32:05
zg
ProFTP писал(а):KEY `word` (`word`,`page`)
мде... смысла в этом мало. Но это просто составной индекс.
ProFTP писал(а):как это понимать вообще про UNIQUE?
если на поле стоит индекс UNIQUE, то в этом поле не может быть двух одинаковых значений на таблицу. Например, его очень часто ставят на поле "логин", чтобы логины не дублировались для разных пользователей.
ProFTP писал(а):в чем разница между UNIQUE и PRIMARY?
если тебе что-то это скажет:
1. UNIQUE может хранить NULL, PRIMARY нет.
2. PRIMARY может быть только один на таблицу, UNIQUE несколько.
Больше различий нет.