[devel] Mysqld CPU usage at www.sisyphus.ru

Mikhail Yakshin greycat at altlinux.org
Wed Sep 2 00:47:33 UTC 2009


>> > SELECT DISTINCT s.name, s.version, s.rel, m.packager, s.summary, s.repo
>> > FROM srpm as s, maintainers as m, rpm as r WHERE  r.srpm = s.name AND
>> > m.mail=s.packager AND ( s.name RLIKE 'unichrome'  OR  s.summary RLIKE
>> > 'unichrome'  OR  s.description RLIKE 'unichrome'  OR  r.namen RLIKE
>> > 'unichrome' ) AND s.repo='Sisyphus' AND r.repo='Sisyphus' ORDER BY 1
>> > ASC LIMIT 0,20;
>> >
>> > Может быть, заменить RLIKE на MATCH AGAINST ? Будет быстрее?
>>
>> Во-первых, проверить индексы, по которым производится JOIN. Имеет
>> смысл выписать это в явный "INNER JOIN ON что-то":
>>
>> > WHERE  r.srpm = s.name AND m.mail=s.packager
>>
>> Во-вторых, такой RLIKE скорее всего вообще не использует индексы и
>> запрос превращается в FULL SCAN. Проверить индексы, по которым
>> делается RLIKE и заменить его либо на LIKE 'запрос%', либо на
>> равенство.
>>
>> MATCH AGAINST потребует полнотекстовых индексов, да и будет возвращать
>> совсем не то, что хочется, как я понял.
>
> Индексов на summary, description сейчас вообще нет.
>
> RLIKE используется для поиска любого совпадения в name, summary,
> description. 'запрос%' сработает только на совпадение в начале
> строки.

Подавляющее большинство SQL-реализаций (включая MySQL) не умеет
использовать для поисковых запросов типа '%запрос%' (будь они сделаны
через такой LIKE, через RLIKE, через REGEXP или заданы каким-то иным
способом) индексы, т.е. такой запрос будет всегда приводить к FULL
SCAN.

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

По сути, в современном SQL способов готовых не так много, все много -
все они в той или иной степени компромиссны:

1) Искать по полнотекстовым индексам => огребаем необходимость строить
и поддерживать эти индексы и всякие слабо совместимые с практической
задачей поиска пакетов по названием ограничения. Например, "libapr1"
по запросу "apr" в полнотекстовом индексе найти малореально. При
построении индексов "libapr1" будет разбито на 2лексемы "libapr" и
"1". Первая будет заиндексирована, вторая выкинута из-за маленькой
длины и, как следствие, высокой частотности. С другой стороны - для
summary этот способ применить может быть и можно.

2) Остановиться волевым решением на поиске по началу вхождения - LIKE
'запрос%'. Субъективно - мне кажется, для пакетов этого должно быть
вполне достаточно.

3) Жить с FULL SCANом, но перестроить запрос таким образом, чтобы FULL
SCAN производился по небольшому количеству записей - что-то типа
O(table1) + O(table2) + O(table3). Сейчас, возможно, запрос
производится по что-то типа O(table1)*O(table2)*O(table3) записей -
потому, что оптимизатор решает делать сначала FULL OUTER JOIN, а потом
уже отсекать из него нужное путем применения WHERE-фразы. Я уже описал
способы решения - что стоит для начала WHERE (условия join'а)
переписать в явные условия join'а. Если не поможет - тогда разделить
это на 3 отдельных запроса (просто вытаскивающие IDшники) + один
запрос, делающий SELECT * FROM table1 INNER JOIN table2 INNER JOIN
table3 WHERE id IN (вытащенные IDшники).

Если задача - "зафиксить побыстрее" - то вариант #3, как мне кажется,
упрется в минут пять работы и, скорее всего, даст результаты в виде
снижения 5-6 секунд до 200-300-400 мс. С моей точки зрения, впрочем,
200-300-400 мс - это непозволительно много для веб-приложения, но
YMMV.

-- 
WBR, Mikhail Yakshin


More information about the Devel mailing list