Улучшить производительность кода sql для запроса mariaDB

Я разработал приложение, связанное с mariaDB (Ver 15.1 Distrib 10.1.31-MariaDB, для Win32) в DelphiXE8. Я хочу улучшить производительность запросов. Опишите упрощенный сценарий:

Таблица de_User (innoDB) (строки 81762)

ID_U   INT PRIMARY KEY
Name   VARCHAR(30)
INDEX ID_U,  Name

Таблица de_doc (innoDB) (строк 260452)

IDD   INT PRIMARY KEY
DataFi  Date
UserID  INT
...
INDEX IDD, UserID, DataFi
----
CONSTRAINT UserID_LK
FOREIGN KEY de_Doc  (UserID)
REFERENCES  de_User (ID_U)
ON DELETE CASCADE
ON UPDATE CASCADE

мой запрос

select User.*, Doc.LastDoc
FROM de_Users AS Us 
LEFT JOIN (
SELECT UserID,MAX(DataFi) AS LastDoc
FROM de_doc 
GROUP BY UserID
) as Doc on Doc.UserID = Us.ID_U

ORDER BY Us.Name ASC, Doc.LastDoc DESC;

-- ОБЪЯСНИТЬ выберите ...

+------+-------------+----------------+-------+---------------+---------------+---------+----------------+--------+---------------------------------+
| id   | select_type | table          | type  | possible_keys | key      | key_len | ref            | rows   | Extra                           |
+------+-------------+----------------+-------+---------------+---------------+---------+----------------+--------+---------------------------------+
|    1 | PRIMARY     | de_User        | ALL   | NULL          | NULL     | NULL    | NULL           |  81762 | Using temporary; Using filesort |
|    1 | PRIMARY     | <derived2>     | ref   | key0          | key0     | 5       | Base.Us.ID_U   |     10 |                                 |
|    2 | DERIVED     | de_Doc         | index | NULL          | UserID_LK| 4       | NULL           | 260452 |                                 |
+------+-------------+----------------+-------+---------------+---------------+---------+----------------+--------+---------------------------------+

мой.ини...

# The MySQL server
[mysqld]
...
key_buffer = 4096M
key_buffer_size=1024M
table_open_cache = 2048
query_cache_size = 128M
max_connections = 100
...
max_allowed_packet = 256M
sort_buffer_size = 4096M
net_buffer_length = 16M
read_buffer_size = 256M
myisam_sort_buffer_size = 256M
log_error = "mysql_error.log"
...
# Comment the following if you are using InnoDB tables
innodb_data_home_dir = "C:/xampp/mysql/data"
innodb_data_file_path = ibdata1:10M:autoextend
innodb_log_group_home_dir = "C:/xampp/mysql/data"
innodb_log_arch_dir = "C:/xampp/mysql/data"
## You can set .._buffer_pool_size up to 50 - 80 %
## of RAM but beware of setting memory usage too high
innodb_buffer_pool_size = 2048M
# DEPRECATED innodb_additional_mem_pool_size = 1024M
## Set .._log_file_size to 25 % of buffer pool size
innodb_log_file_size = 512M
innodb_log_buffer_size = 128M
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50
...
thread_concurrency = 4
...
[isamchk]
key_buffer = 1024M
sort_buffer_size = 256M
read_buffer = 8M
write_buffer = 16M

[myisamchk]
key_buffer = 1024M
sort_buffer_size = 256M
read_buffer = 8M
write_buffer = 8M

ТЕСТ phpmyadmin:

83705 total, the query employed 1,0000 sec.
if I remove "order by Doc.LastDoc DESC" it is very fast
83705 total, the query employed 0,0000 sec.

TEST в моем приложении, разработанном с помощью delphiEX8

view table all rows 2,8 sec.
if I remove "order by Doc.LastDoc DESC" it is very fast
view table all rows 1,8 sec.

Как я могу улучшить производительность?


person carmelocony    schedule 28.07.2019    source источник
comment
У вас есть индекс в таблице de_documents как INDEX UserID, если вы не добавляете и не делитесь выводом explain   -  person James    schedule 28.07.2019
comment
Я не понимаю. мой код намного сложнее, я написал синтезированный. Я описал, что создал индексы для двух таблиц и отношения. Я написал результат EXPLAIN и конфигурацию my.ini. Я написал запрос для оптимизации.   -  person carmelocony    schedule 28.07.2019
comment
Да, я могу прочитать ваш вопрос. Так что ты пытаешься сказать?   -  person James    schedule 28.07.2019
comment
извините, я хочу улучшить производительность запросов. Я считаю, что это может быть быстрее.   -  person carmelocony    schedule 28.07.2019
comment
вот почему я спросил, есть ли индекс для столбца userID?   -  person James    schedule 28.07.2019
comment
извините, я хочу повысить производительность запросов. Думаю, это может быть быстрее. Из вопроса я понимаю, что ORDER BY Us.Name ASC, Doc.LastDoc DESC; является критической точкой? Короткий ответ: вы не можете напрямую индексировать его, так как ORDER BY использует две таблицы, один из обходных путей — создание материализованного view для ведения таблицы, которую можно использовать для сортировки по индексам -› Материализованное представление в MySQL   -  person Raymond Nijland    schedule 28.07.2019
comment
Сколько оперативной памяти? Какой двигатель?   -  person Rick James    schedule 28.07.2019
comment
При выборе времени обязательно избегайте кеша запросов, используя SELECT SQL_NO_CACHE .... Это вероятно причина 0,0000 sec.   -  person Rick James    schedule 28.07.2019
comment
phpmyadmin вероятно добавляет LIMIT, тем самым изменяя действия оптимизатора!   -  person Rick James    schedule 28.07.2019
comment
Пожалуйста, опубликуйте текстовые результаты SHOW GLOBAL STATUS LIKE '%used%'; для анализа. Сколько ядер на вашем хост-процессоре?   -  person Wilson Hauck    schedule 30.07.2019
comment
@carmelocony Отказ от ответственности: я являюсь автором контента веб-сайта, упомянутого в моем профиле, сетевой профиль. После всех расширенных комментариев, вопросов/ответов, пожалуйста, используйте контактную информацию, чтобы связаться со мной.   -  person Wilson Hauck    schedule 01.09.2019


Ответы (5)


Предложения для вашего РАЗДЕЛА my.ini [mysqld]

sort_buffer_size=2M  # from 4096M (4G) of RAM per connection, next 2 are per connect also
read_buffer_size=256K  # from 256M to reduce volume of data retrieved by 99%
read_rnd_buffer_size=256K  # from ? to a reasonable size

Эти три могут быть установлены динамически (как root) с помощью SET GLOBAL variable_name=value, замените K на *1024 и M на *1024*1024 для Кбайт и Мегабайт, пожалуйста. Пожалуйста, опубликуйте положительные/отрицательные результаты после полного РАБОЧЕГО ДНЯ безотказной работы.

person Wilson Hauck    schedule 30.07.2019
comment
Я внес предложенные изменения. Показатели на первых тестах такие же, как и на предыдущих. Я применяю настройки, предложенные Уилсоном Хауком. благодарю вас - person carmelocony; 31.07.2019
comment
@carmelocony Пожалуйста, опубликуйте ПОСЛЕ 24 часов безотказной работы текстовые результаты SHOW GLOBAL STATUS LIKE '%used%'; для анализа. Сколько ядер на вашем хост-процессоре? Опубликовать результаты EXPLAIN SELECT SQL_NO_CACHE (ваш запрос); включая ЗАПРОС. - person Wilson Hauck; 31.07.2019
comment
@carmelocony Только новые СЕССИИ унаследуют измененные настройки глобальной переменной. Попробуйте свой запрос с новым входом в mysql для вашего времени, и 1-е время выполнения следует игнорировать, пожалуйста. - person Wilson Hauck; 31.07.2019
comment
Я получил наилучшие результаты с этими: - person carmelocony; 01.08.2019
comment
@carmelocony Спасибо, что поделились своими настройками для достижения наилучших результатов. Когда у вас будет время, опубликуйте ПОСЛЕ 24 часов безотказной работы текстовые результаты A) ПОКАЗАТЬ ГЛОБАЛЬНЫЙ СТАТУС, КАК '%used%'; и B) ПОКАЗАТЬ ГЛОБАЛЬНЫЙ СТАТУС, КАК '%open%'; для анализа. Сколько ядер на вашем хост-процессоре? Опубликовать текущие результаты EXPLAIN SELECT SQL_NO_CACHE (ваш запрос); включая ЗАПРОС. - person Wilson Hauck; 01.08.2019

  • Это двусмысленно: INDEX IDD, UserID, DataFi
  • Вероятно, User.* должно было быть Us.*? Имейте в виду, что «упрощение» запроса может превратить его в другую проблему.
  • Вероятно, LEFT JOIN не нужен; используйте JOIN.
  • Вам нужен этот составной INDEX(UserID, LastDoc)
  • Вы действительно хотите получить 82 КБ строк на выходе? Что клиент будет делать с таким объемом данных? Спрашиваю, потому что, если клиент будет дальше переваривать результаты, может быть, это лучше сделать на SQL.
  • При выборе времени обязательно избегайте кеша запросов, используя SELECT SQL_NO_CACHE.
  • phpmyadmin, вероятно, устанавливает LIMIT, тем самым изменяя то, что будет делать оптимизатор!
  • ORDER BY t1.a, t2.b (разные таблицы) делает невозможным использование индекса для упорядочения. Это предотвратит любое короткое замыкание запроса.
person Rick James    schedule 28.07.2019
comment
*Возможно, LEFT JOIN не нужен; используйте ПРИСОЕДИНЯЙТЕСЬ. [Важно просмотреть всех пользователей] *Вам нужен этот составной ИНДЕКС(UserID, LastDoc) [Я сделал] *Вы действительно хотите, чтобы на выходе было 82 КБ строк? Что клиент будет делать с таким объемом данных? Спрашиваю, потому что, если клиент будет дальше переваривать результаты, может быть, это лучше сделать на SQL. [Мне нужна сетка в программе, разработанной с помощью Delphi EX8, которая отображает всех пользователей, а затем может применять фильтры к сетке с помощью процедур Delphi. Я всегда так делал, может надо поступить по другому?] - person carmelocony; 28.07.2019
comment
Загрузить 82 КБ строк в программу, затем выполнить фильтрацию? Плохой дизайн. Фильтрация должна выполняться по мере выборки строк. Я не знаю, заставляет ли это Delphi, но вам следует искать лучшую архитектуру. Диски и сетевые подключения работают очень быстро. - person Rick James; 01.08.2019
comment
Спасибо, Рик Джеймс, теперь программное обеспечение находится в стадии бета-тестирования и отлично работает без ошибок, единственная плохая вещь - это производительность в поиске пользователей (81 тыс. пользователей для 260 тыс. документов = 1,9 сек.). Я воспользуюсь вашим советом в следующем программном обеспечении. - person carmelocony; 01.08.2019
comment
@carmelocony Когда у вас есть время, пожалуйста, опубликуйте ПОСЛЕ 24 часов безотказной работы текстовые результаты A) ПОКАЗАТЬ ГЛОБАЛЬНЫЙ СТАТУС, КАК '%used%'; и B) ПОКАЗАТЬ ГЛОБАЛЬНЫЙ СТАТУС, КАК '%open%'; для анализа. Сколько ядер на вашем хост-процессоре? Опубликовать текущие результаты EXPLAIN SELECT SQL_NO_CACHE (ваш запрос); включая ЗАПРОС для предложений по УЛУЧШЕНИЮ времени отклика вашего хост-сервера. - person Wilson Hauck; 18.08.2019

Изменение этих значений в my.ini, в phpmyadmin, вот улучшенный результат.

Время, необходимое для заполнения сетки в моем приложении Delphi, теперь составляет 1,9 секунды по сравнению с 2,8 секунды раньше.

мой компьютер имеет 8 ГБ ОЗУ;

Можно ли сократить время заполнения сетки в Delphi? Возможно, мне придется сделать новый запрос для этого.

innodb_buffer_pool_size = 2048M
# Set .._log_file_size to 25 % of buffer pool size

ДО

innodb_log_file_size = 64M

(всего 83705 дел, запрос занял 10000 секунд)

ПОСЛЕ

innodb_log_file_size = 512M

(всего 83705 дел, запрос занял 0,0000 сек.)

person carmelocony    schedule 29.07.2019

Если ваша цель — «grouwise-max», то вы пропустили пункт:

select  User.*, Doc.LastDoc
    FROM  de_Users AS Us
    LEFT JOIN  
    (
        SELECT  UserID,MAX(DataFi) AS LastDoc
            FROM  de_doc
            GROUP BY  UserID 
    ) as Doc  ON Doc.UserID = Us.ID_U
             AND Doc.LastDoc = Us.DataFi    -- this was missing
    ORDER BY  Us.Name ASC, Doc.LastDoc DESC;

Это также приведет к тому, что будет доставлено намного меньше строк, что решит вопрос производительности.

person Rick James    schedule 16.01.2020

Попробуйте этот запрос и проверьте, совпадает ли результат с вашим запросом.

select Us.*,  max(Doc.DataFi) as LastDoc
FROM de_Users AS Us 
LEFT JOIN de_doc as Doc on Doc.UserID = Us.ID_U
group by Us.ID_U   
ORDER BY Us.Name ASC, LastDoc DESC;
person James    schedule 28.07.2019
comment
Это занимает столько же времени. Без улучшения. - person carmelocony; 28.07.2019
comment
Выход правильный? Если это так, приведенный выше запрос оптимизируется путем удаления нежелательного подзапроса внутри соединения. Что теперь показывает команда explain? - person James; 28.07.2019
comment
Использование select Us.*, .... group by Us.ID_U, как правило, является неправильным SQL для записи в стандарте SQL 1992 года. Но это может быть допустимо в стандарте SQL 1999+, где есть функциональная зависимость, которую поддерживает MySQL 5.7.5+... Но не используйте использование функциональная зависимость в более ранних версиях MySQL, чем 5.7.5 - person Raymond Nijland; 28.07.2019
comment
@RaymondNijland Вы имеете в виду, что вместо * это должны быть имена столбцов????? - person James; 28.07.2019
comment
Вы имеете в виду, что вместо * должны быть имена столбцов? Я не говорю это Обработка MySQL GROUP BY .. Также запрос Topicstarters, как правило, является правильным подходом.. - person Raymond Nijland; 28.07.2019
comment
Да, прочитайте документ, которым вы поделились, ???? - person James; 28.07.2019
comment
Эта формулировка имеет несчастье таскать за собой все Us.* только для того, чтобы потом разрушить большую ее часть. (взорваться-взорваться). - person Rick James; 28.07.2019