SQLinfo.ru - Все о MySQL Webew.ru: теория и практика веб-технологий

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 21.02.2015 18:31:09

mrsol
Участник
Зарегистрирован: 23.03.2010
Сообщений: 12

HDD -> SSD -> MEMORY нет увеличения производительности.

Имеется логи транзакций в текстовиках. Нужно обработать логи, добавляя и изменяя пользовательские данные.
Сервер mariadb-5.5. Тип таблиц myisam. Уникальный ключ varchar(35) - идент пользователя. Кол-во пользователей порядка 20 миллионов. Для оптимизации пользователи разнесены по отдельным таблицам, индекс первый символ идента. То есть
user_a
user_b
user_c
и т.д. Таблиц порядка 120. К какой таблице обращаться для поиска или обновления данных реализовано на уровне скрипта. В таблице данных не много, текущий баланс, на сколько всего было пополнений, дата появления, последняя дата пополнения.  Также дублируются все таблицы (a, b, c ...) для пользователей с отрицательным балансом.
Система linux (kubunta). Файловая ext4.
Скрипт php, коннектор mysql.
Алгоритм скрипта.
1. Получение строки из статистики и разбор. Получение первого символа идента пользователя и определение таблиц, где может быть запись.
2. Выборка из первой таблицы, с поиском по уникальному ключу (таблицы с положительным балансом).
3. Если в первой не найдено, выборка со второй таблицы (таблицы с отрицательным балансом).
4. Если пользователь не найден, то происходит вставка данных в первую таблицу.
5. Если пользователь был найден, смотрится операция и считается текущий баланс на уровне скрипта.
5.1. Если у пользователя положительный баланс и пользователь найден в первой таблице, то просто происходит обновление (update) в первой таблице используя уникальный ключ.
5.2. Если у пользователя отрицательный баланс и пользователь найден в первой таблице, то происходит вставка (insert) во вторую таблицу с обновленными данными, а из первой происходит удаление по уникальному ключу.
5.3. Если у пользователя положительный баланс и пользователь найден во второй таблице, то происходит вставка (insert) в первую таблицу с обновленными данными, а из второй происходит удаление по уникальному ключу.
5.4. Если у пользователя отрицательный баланс и пользователь найден во второй таблице,  то просто происходит обновление (update) во второй таблице используя уникальный ключ.
------ Заморочка с отрицательными балансами была добавлена для оптимизации. Так как правило если пользователь уходит в минус, он уже не возвращается. А основная активность происходит с положительными балансами.

Запуск производился с нулевой базой.
Запуск скрипт показал обработку порядка 800-1000 записей в секунду. Статистика каждые 5 секунд выводится. Когда пользователей стало порядка 5-6 миллионов, то обработка упала до 500-600 транзакций. При этом была только нагрузка на диск, и то не сильно критичная.
Решено было перенести базу на ssd (твердотелый) винчестер, туда же где и установлена система. База переносилась уже с текущими пользователями. Но скорость обработки не увеличилась, то есть поднялась до предыдущих 800-1000. В принципе возврат к этим цифрам достигался и на hdd путем оптимизации таблиц. При этом нагрузки не на винчестер, ни на проце, ни на память не присутствовала.
Изменил класс работы с бд. То есть отключил физически запросы к базе. Всё остальное осталось такое же. То есть происходило по одному сценарию. Пользователь как бы искался в обоих таблицах, потом якобы происходила запись пользователя.
В таком режиме скрипт показал 18-20 к транзакций в минуту.
Тогда было решено перенести таблицы в память, создалась новая база данных с таблицами MEMORY.
Запуск скрипта. Увеличение есть, но мизерное. 900-1200 в секунду. При этом мускул работает в два потока по 50 процентов на каждом ядре, память ещё остается свободная, нагрузки на винтах нету.
Поменял прокладку php с mysql на mysqli. Изменений нет.
Поменял бд с mariadb-5.5 на мускул 5.5. Изменений нет.
Пробовал менять конфиг. И перловский оптимизатор гонял. И как предлагал pgpmyadmin устанавливал данные. И генерировал на каком-то сайте. И использовал практически девственный конфиг. Ноль реакции.

Поиск через пхпмайдами по уникальному ключу
В базе на ssd, в таблице с 500К записей. 0.0041 сек
В базе на ssd, в таблице с 1.5К записей. 0.0048 сек
Аналогичные выборки из базы с таблицами MEMORY 0.0004-0.0009.

Подумал что ключ слишком большой и сделал дополнительный на основе crc32 от идента пользователя. В таблицу загнал как int.
Брал выборку по этому ключу, а потом программно искал нужную запись в ответе от сервера. В апдейтах и делитах использовал сначала крк, а потом идент. То же не дало сильного результата.

Как разогнать этот мускул???? Хочу загрузку проца чтобы аж кипел :-)

Неактивен

 

#2 24.02.2015 13:20:54

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6718

Re: HDD -> SSD -> MEMORY нет увеличения производительности.

А во что упираетесь-то? Сколько потоков, как нагружаете, есть ли синхронизация
внутри приложения? Ну то есть вы вливаете достаточно нагрузки, чтобы система
была нагружена? smile

Если вливаете, и потоков, например, тысяча, и приложение ждет, значит, уперлись
в какой-то mutex, на котором потоки синхронизируются и ждут. Какой — надо
разбираться. В случае с MyISAM и MEMORY это может быть mutex на вход в табличку. Не забудьте
про mutex перед query cache (отключаемый). Но сначала убедитесь таки в том, что
дело не в приложении, как правило, виновато именно оно.

Неактивен

 

#3 24.02.2015 14:07:16

mrsol
Участник
Зарегистрирован: 23.03.2010
Сообщений: 12

Re: HDD -> SSD -> MEMORY нет увеличения производительности.

Изначально лилось в один поток. Поэтому перекрещюющихся или конкурирующих запросов вообще не было.
Потом было обновление, одновременно могло работать до 5-ти потоков, в зависимости от места исполнения текущего алгоритма, и при этом потоки не конкурировали между собой, работали с разными таблицами. Дало прирост но не сильный.
На данный  момент 38 миллионов записей. Пришлось ещё дробить таблицы по ранжированию.  То же есть прирост местами.

Но мне не понятно одно. Что база была на простом винте, потом на твердотелом (sata3), что использовалась только память. Разницы по кол-ву обработанных строк практически не замечалось.
Да и сейчас не замечается. Изменяю алгоритм импорта, тестирую на таблицах в памяти (отдельная бд). А основной импорт происходит на ссд.

ПыСы. Начал смотреть в сторону нон sql баз. Бенчмарк аироспайка поразил, но при достижении конца память, тупо перешел в режим ридонли :-(

Неактивен

 

#4 24.02.2015 17:50:28

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6718

Re: HDD -> SSD -> MEMORY нет увеличения производительности.

Погодите, вы делаете задачу в один поток — о каком ускорении может идти речь? Однопоточная задача в принципе не может занять больше одного ядра ведь. Ресурс, в который вы упираетесь, — количество потоков. Вы его утилизируете целиком, при этом не утилизируя другие ресурсы, которые вы мониторите.

Неактивен

 

#5 24.02.2015 18:00:11

mrsol
Участник
Зарегистрирован: 23.03.2010
Сообщений: 12

Re: HDD -> SSD -> MEMORY нет увеличения производительности.

Даже при однопоточном режиме задействовались два ядра (из двух) и работали два процесса (из многих) мускул на 50-70 процентов.  Монтирю htop, atop и т.д. При распаралеливании процессов практически ничего не изменилось.

Неактивен

 

#6 24.02.2015 20:25:12

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6718

Re: HDD -> SSD -> MEMORY нет увеличения производительности.

Ооок, на двух ядрах, наверное, можно и однопоточное. Давайте тогда определимся с вопросом.
Мы выяснили, что с вашим профилем нагрузки вы не упираетесь в базу данных. Поэтому смена
движка или хранилища не помогает. Так как это один поток, ни в какие mutex'ы вы тоже не упи-
раетесь. Остается: передача данных (mysql-приложение) и само приложение.

Если вопрос в том, как ускорить, то, видимо, надо профилировать приложение sad

Неактивен

 

#7 24.02.2015 21:02:22

mrsol
Участник
Зарегистрирован: 23.03.2010
Сообщений: 12

Re: HDD -> SSD -> MEMORY нет увеличения производительности.

Ну я в начале уже писал. Попытаюсь расширить.
Работа с мускульными командами вынесена в отдельный класс.
В начале старта скрипта и инициализации класса происходит коннект к базе. В данный момент инициализируется 5 копий класса. То есть 5 параллельных соединений. Но на момент последнего холостого теста, был один класс.

public function init_base($host, $user, $password, $base, $port = null, $socket = null, $charset = 'utf8')
        {
                $this->db = new mysqli($host, $user, $password, $base, $port, $socket);
                $this->db->set_charset($charset);
        }


Ну и собственно сам обработчик запросов в классе.
public function query($query)
        {
                if(!$this->db)
                        return false;
 
                if(is_object($this->result))
                        $this->result->free();
                       
/*выполняем запрос*/
          $this->result = $this->db->query($query);
 
/*если есть ошибки - выводим их*/
 
                if($this->db->errno)
                        die("mysqli error #".$this->db->errno.": ".$this->db->error ." q:".$query);
 
/*если в результате выполнения запроса (например SELECT...) получены данные - возвращаем их.
ВНИМАНИЕ! данные всегда возвращаются в массиве, даже если запрос возвращает одну запись.*/

 
                if(is_object($this->result))
                {
                        $data = array();
                        while($row = $this->result->fetch_assoc())
                                $data[] = $row;
                       
                        return $data;
                }
 
/*если результат отрицательный - возвращаем false*/
 
                else if($this->result == FALSE)
                        return false;
                       
/*если запрос (например UPDATE или INSERT) затронул какие-либо строки - возвращаем их количество*/
 
                else return $this->db->affected_rows;
        }


Класс, не мой. Скачанный с просторов. До этого использовал свой старый проверенный, но он работал на старом расширении mysql, а не mysqli.
Так вот тупо в начале вызова метода query делаем return null. Алгоритм воспринимает это как пустую выборку и действует по одному и тому же сценарию. Проверка на наличие и вставка нового пользователя.
Так вот при таком раскладе идет загрузка скриптом 100 процентов одного ядра и скорость обработки 18-20 тысяч в секунду.
Получается использовал сначала mysql драйвер, потом mysqli, потом вообще поменял библиотеку на mysqlnd которая поддерживает мультизапросность. Изменений нет.

То есть затык получается на уровне базы.
Профилирование, то есть оптимизация?
Ну пытался разпаролелить и сделать упреждающие запросы без ожидания ответа, сильного эффекта не дало.

Неактивен

 

#8 26.02.2015 00:47:14

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6718

Re: HDD -> SSD -> MEMORY нет увеличения производительности.

Профилировать — это найти то место, в которое упирается приложение.

Действия, которые Вы описали, мне ничего не говорят, если честно. Ну то есть аналогия
приблизительно такая: у вас есть магазин, в который вы не пустили покупателей. Вы
наблюдаете за кассиршей, и она всё время занята, совершая 18-20 тысяч операций в секунду
просмотра, нет ли покупателей в очереди в ее кассу. Профилировать нужно с покупателями:
смотреть, на что она тратит много времени, когда делает полезную работу (или обнаружить,
что она делает бесполезную, и это исключить).

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson