Подобрете производителността на sql кода за заявка за mariaDB

Разработих приложение, свързано с mariaDB (Версия 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 |                                 |
+------+-------------+----------------+-------+---------------+---------------+---------+----------------+--------+---------------------------------+

my.ini ...

# 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.

ТЕСТ в моето приложение, разработено с 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 и моята конфигурация.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 използва две таблици, едно решение е да направите Materialized изглед за поддържане на таблица, която можете да използвате за сортиране на индекси -› Материализиран изглед в MySQL   -  person Raymond Nijland    schedule 28.07.2019
comment
Колко RAM? Кой двигател?   -  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
Моля, публикувайте текстови резултати от ПОКАЗВАНЕ НА ГЛОБАЛЕН СТАТУС КАТО '%used%'; за анализ. Колко ядра на вашия хост CPU?   -  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 за Kbytes и Megabytes, моля. Моля, публикувайте положителни/отрицателни резултати след цял РАБОТЕН ДЕН на работа.

person Wilson Hauck    schedule 30.07.2019
comment
Направих предложените промени. Изпълненията на първите тестове са същите като предишните. Прилагам настройките, предложени от Wilson Hauck. Благодаря ти - person carmelocony; 31.07.2019
comment
@carmelocony Моля, публикувайте СЛЕД 24 часа работа, текстови резултати от ПОКАЖЕТЕ ГЛОБАЛЕН СТАТУТ КАТО „%used%“; за анализ. Колко ядра на вашия хост CPU? Публикувайте резултати от EXPLAIN SELECT SQL_NO_CACHE (вашето запитване); включително QUERY. - person Wilson Hauck; 31.07.2019
comment
@carmelocony Само нови SESSIONS ще наследят модифицираните настройки на глобалната променлива. Опитайте заявката си с ново влизане в mysql за вашето време и времето за първо изпълнение трябва да се игнорира, моля. - person Wilson Hauck; 31.07.2019
comment
Получих най-добри резултати с тези: key_buffer= 2048M sort_buffer_size = 8M read_buffer_size = 2M read_rnd_buffer_size = 16M innodb_buffer_pool_size = 2048M innodb_log_file_size = 512M - person carmelocony; 01.08.2019
comment
@carmelocony Благодаря, че сподели настройките си за най-добри резултати. Когато имате време, моля, публикувайте СЛЕД 24 часа работа, текстови резултати от A) ПОКАЗВАЙТЕ ГЛОБАЛЕН СТАТУТ КАТО „%used%“; и B) ПОКАЗВАНЕ НА ГЛОБАЛЕН СТАТУТ КАТО '%open%'; за анализ. Колко ядра на вашия хост CPU? Публикувайте текущи резултати от EXPLAIN SELECT SQL_NO_CACHE (вашето запитване); включително QUERY. - person Wilson Hauck; 01.08.2019

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

Промяната на тези стойности в my.ini, в phpmyadmin тук е подобреният резултат.

Времето, необходимо за попълване на мрежата в моето приложение Delphi, сега е 1,9 секунди в сравнение с преди 2,8 секунди.

моят компютър има 8 Gb RAM;

Мога ли да намаля времето за попълване на мрежата в Delphi? Може би трябва да направя нова заявка за това.

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

ПРЕДИ

innodb_log_file_size = 64M

(Общо 83705 del, Заявката отне 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 .. Освен това заявката за начало на теми като цяло е подходът, който е правилен.. - person Raymond Nijland; 28.07.2019
comment
Да, прочетете документа, който сте споделили, ???? - person James; 28.07.2019
comment
Тази формулировка има нещастието да обхване целия Us.*, само за да свие по-голямата част от него по-късно. (избухвам-избухвам). - person Rick James; 28.07.2019