Профилирование Mnesia Queries - PullRequest
13 голосов
/ 05 декабря 2009

Наша база данных Mnesia работает медленно, и мы считаем, что она должна быть несколько быстрее.

Так что нам нужно профилировать это и выяснить, что происходит.

Есть несколько вариантов, которые предлагают себя:

  • запустите fprof и посмотрите, куда идет время
  • запустите cprof и посмотрите, какие функции вызываются много

Однако это довольно стандартные инструменты для мониторинга производительности. Вопрос в том, как мне на самом деле выполнять профилирование запросов - какие запросы занимают больше всего времени. Если бы мы были магазином Oracle или MySQL, мы бы просто запустили профилировщик запросов, который бы возвращал запросы, выполнение которых занимало много времени. Этот инструмент не доступен для Mnesia.

Итак, вопрос:

  • какие существуют методы для профиля Mnesia
  • какие инструменты существуют для профилирования Mnesia - я думаю, ни один из них, но докажите, что я не прав:)
  • как вы профилировали свои запросы и оптимизировали установку базы данных mnesia

Расширено в свете обсуждения

Одна из проблем использования fprof в качестве инструмента профилирования заключается в том, что он сообщает вам только о конкретном запросе, который вы просматриваете. Итак, fprof говорит мне, что X медленный, и я настраиваю его, чтобы ускорить. Тогда, тихо, и вот, операция Y (которая была достаточно быстрой) теперь медленная. Итак, я профилирую Y и понимаю, что способ сделать Y быстрым - это сделать X медленным. Поэтому я заканчиваю серию двусторонних компромиссов ...

Что мне на самом деле нужно - это способ управлять многосторонними компромиссами. Теперь у меня есть 2 метрических сброса реальных пользовательских действий, которые я могу воспроизвести. Эти журналы представляют то, что я хотел бы оптимизировать.

«Надлежащий» анализатор запросов в базе данных SQL сможет профилировать структуру операторов SQL, например, всех операторов в форме:

SELECT [fieldset] FROM [table] WHERE {field = *parameter*}, {field = *parameter*}

и, скажем, 285 запросов этой формы заняли в среднем 0,37 мс для выполнения

Они получают волшебные ответы, когда говорят: 17 запросам этой формы потребовалось 6,34 секунды для выполнения и полной проверки таблицы X, вы должны поместить индекс в поле Y

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

Тестовый шаблон будет выглядеть примерно так:

  • действие X будет выполнять запросы A, C и C быстрее, но запрашивает E и F медленнее
  • проверить и измерить
  • затем одобрить / не одобрить

Я использовал Erlang достаточно долго, чтобы «знать», что такого анализатора запросов нет, и я хотел бы знать, как другие люди (у которых, должно быть, была эта проблема) «рассуждают» об оптимизации мнезии.

Ответы [ 3 ]

3 голосов
/ 08 декабря 2009

Я отстал, потому что я не знаю много ни об Эрланге, ни о Мнесии, но я много знаю о настройке производительности, и из обсуждения пока что это звучит довольно типично.

Эти инструменты fprof и т. Д. Похожи на большинство инструментов, которые получают фундаментальный подход от gprof, а именно: функции инструментов, подсчет вызовов, выборка счетчика программ и т. Д. Мало кто изучил основы этой практики в течение длительного времени. Ваши разочарования звучат типично для пользователей подобных инструментов.

Существует менее известный метод, который вы могли бы рассмотреть, обрисованный в общих чертах здесь . Это основано на взятии небольшого числа (10-20) выборок состояния программы в случайные моменты времени и понимании каждой из них, а не суммировании. Как правило, это означает проверку стека вызовов, но вы можете также изучить другую информацию. Есть разные способы сделать это, но я просто использую кнопку паузы в отладчике. Я не пытаюсь получить точное время или количество вызовов. Это в лучшем случае косвенные ключи. Вместо этого я спрашиваю каждого образца «Что он делает и почему?» Если я обнаружил, что он выполняет какую-то конкретную деятельность, например, выполняет запрос X, где ищет ответ типа y для цели z, и он делает это на более чем одном образце, тогда доля выборок, на которых он это делает, является грубой, но надежной оценкой того, какая часть времени это делает. Скорее всего, это то, с чем я могу что-то сделать, и получить хорошее ускорение.

Вот пример использования метода.

2 голосов
/ 13 декабря 2009

Предложение Майка Данлавей напоминает мне о redbug , который позволяет вам сэмплировать вызовы в производственных системах. Думайте об этом как о простом в использовании erlang:trace, который не дает вам достаточно веревки, чтобы повесить вашу производственную систему.

Использование чего-то вроде этого вызова должно дать вам много трассировок стека, чтобы определить, откуда ваши транзакции mnesia вызываются:

redbug:start(10000,100,{mnesia,transaction,[stack]}).

Невозможно получить длительность звонка для этих трасс.

Если вы организовали все поиски mnesia в модули, которые экспортируют API для их выполнения, вы также можете использовать redbug для получения частоты вызовов только для определенных запросов.

2 голосов
/ 05 декабря 2009

Поскольку запросы Mnesia - это просто функции erlang, я думаю, вы можете профилировать их так же, как и свой собственный код erlang. http://www.erlang.org/doc/efficiency_guide/profiling.html#id2266192 имеет больше информации о доступных инструментах профилирования эрланга.

Обновление В качестве теста я выполнил это дома на тестовом экземпляре mnesia и с помощью fprof отследил возвращенный запрос mnesia qlc, вывел пример, который я включил ниже. Так что он определенно включает в себя больше информации, чем просто запрос запроса.

....
{[{{erl_lint,pack_errors,1},                      2,    0.004,    0.004}],     
 { {lists,map,2},                                 2,    0.004,    0.004},     %
 [ ]}.

{[{{mnesia_tm,arrange,3},                         1,    0.004,    0.004}],     
 { {ets,first,1},                                 1,    0.004,    0.004},     %
 [ ]}.

{[{{erl_lint,check_remote_function,5},            2,    0.004,    0.004}],     
 { {erl_lint,check_qlc_hrl,5},                    2,    0.004,    0.004},     %
 [ ]}.

{[{{mnesia_tm,multi_commit,4},                    1,    0.003,    0.003}],     
 { {mnesia_locker,release_tid,1},                 1,    0.003,    0.003},     %
 [ ]}.

{[{{mnesia,add_written_match,4},                  1,    0.003,    0.003}],     
 { {mnesia,add_match,3},                          1,    0.003,    0.003},     %
 [ ]}.

{[{{mnesia_tm,execute_transaction,5},             1,    0.003,    0.003}],     
 { {erlang,erase,1},                              1,    0.003,    0.003},     %
 [ ]}.

{[{{mnesia_tm,intercept_friends,2},               1,    0.002,    0.002}],     
 { {mnesia_tm,intercept_best_friend,2},           1,    0.002,    0.002},     %
 [ ]}.

{[{{mnesia_tm,execute_transaction,5},             1,    0.002,    0.002}],     
 { {mnesia_tm,flush_downs,0},                     1,    0.002,    0.002},     %
 [ ]}.

{[{{mnesia_locker,rlock_get_reply,4},             1,    0.002,    0.002}],     
 { {mnesia_locker,opt_lookup_in_client,3},        1,    0.002,    0.002},     %
 [ ]}.

{[ ],
 { undefined,                                     0,    0.000,    0.000},     %
 [{{shell,eval_exprs,6},                          0,   18.531,    0.000},      
  {{shell,exprs,6},                               0,    0.102,    0.024},      
  {{fprof,just_call,2},                           0,    0.034,    0.027}]}.
...