Бавните заявки, неефективните заявки или продължителните заявки са проблеми, които редовно тормозят DBA. Те винаги са повсеместни, но са неизбежна част от живота на всеки, който отговаря за управлението на база данни.
Лошият дизайн на базата данни може да повлияе на ефективността на заявката и нейната производителност. Липсата на познания или неправилното използване на извиквания на функции, съхранени процедури или рутинни процедури също може да доведе до влошаване на производителността на базата данни и дори може да навреди на целия клъстер на базата данни MySQL.
За репликация главен-подчинен, много честа причина за тези проблеми са таблици, които нямат първични или вторични индекси. Това причинява забавяне на подчинените, което може да продължи много дълго време (в по-лош случай).
В тази серия от две части блог ще ви дадем опреснителен курс за това как да се справите с максимизирането на вашите заявки към база данни в MySQL, за да стимулирате по-добра ефективност и производителност.
Винаги добавяйте уникален индекс към вашата таблица
Таблици, които нямат първични или уникални ключове, обикновено създават огромни проблеми, когато данните се увеличават. Когато това се случи, проста модификация на данни може да спре базата данни. Липсата на подходящи индекси и изразът UPDATE или DELETE е приложен към конкретната таблица, като план за заявка ще бъде избрано пълно сканиране на таблицата от MySQL. Това може да причини висок I/O диск за четене и запис и да влоши производителността на вашата база данни. Вижте пример по-долу:
root[test]> show create table sbtest2\G
*************************** 1. row ***************************
Table: sbtest2
Create Table: CREATE TABLE `sbtest2` (
`id` int(10) unsigned NOT NULL,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
root[test]> explain extended update sbtest2 set k=52, pad="xx234xh1jdkHdj234" where id=57;
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | UPDATE | sbtest2 | NULL | ALL | NULL | NULL | NULL | NULL | 1923216 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.06 sec)
Като има предвид, че таблица с първичен ключ има много добър план за заявка,
root[test]> show create table sbtest3\G
*************************** 1. row ***************************
Table: sbtest3
Create Table: CREATE TABLE `sbtest3` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2097121 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
root[test]> explain extended update sbtest3 set k=52, pad="xx234xh1jdkHdj234" where id=57;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | UPDATE | sbtest3 | NULL | range | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Първичните или уникалните ключове осигуряват жизненоважен компонент за структурата на таблицата, защото това е много важно, особено при извършване на поддръжка на маса. Например, използването на инструменти от Percona Toolkit (като pt-online-schema-change или pt-table-sync) препоръчва да имате уникални ключове. Имайте предвид, че ПЪРВИЧНИЯ КЛЮЧ вече е уникален ключ и първичният ключ не може да съдържа стойности NULL, а уникален ключ. Присвояването на стойност NULL на първичен ключ може да причини грешка като,
ERROR 1171 (42000): All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead
За подчинените възли също е често срещано, че в определени случаи първичният/уникален ключ не присъства в таблицата, което следователно е несъответствие на структурата на таблицата. Можете да използвате mysqldiff, за да постигнете това или можете да mysqldump --no-data ... params и и да стартирате diff, за да сравните структурата на таблицата и да проверите дали има някакво несъответствие.
Сканирайте таблици с дублиращи се индекси, след което го махнете
Дублиращите се индекси също могат да причинят влошаване на производителността, особено когато таблицата съдържа огромен брой записи. MySQL трябва да извърши множество опити за оптимизиране на заявката и изпълнява повече планове за заявка за проверка. Включва сканиране на голямо разпределение на индекси или статистически данни и това добавя допълнителни разходи за производителност, тъй като може да причини спорове с паметта или високо използване на I/O памет.
Влошаването на заявките, когато се наблюдават дублирани индекси в таблица, също се отразява на насищането на буферния пул. Това също може да повлияе на производителността на MySQL, когато контролната точка изтрива регистрационните файлове на транзакциите на диска. Това се дължи на обработката и съхраняването на нежелан индекс (което всъщност е загуба на място в конкретното пространство за таблици на тази таблица). Обърнете внимание, че дублиращи се индекси също се съхраняват в пространството за таблици, което също трябва да се съхранява в буферния пул.
Разгледайте таблицата по-долу, която съдържа множество дублирани ключове:
root[test]#> show create table sbtest3\G
*************************** 1. row ***************************
Table: sbtest3
Create Table: CREATE TABLE `sbtest3` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k` (`k`,`pad`,`c`),
KEY `kcp2` (`id`,`k`,`c`,`pad`),
KEY `kcp` (`k`,`c`,`pad`),
KEY `pck` (`pad`,`c`,`id`,`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2048561 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
и има размер 2,3GiB
root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd
2.3G /var/lib/mysql/test/sbtest3.ibd
Нека изхвърлим дублиращите се индекси и да изградим отново таблицата с промяна без операция,
root[test]#> drop index kcp2 on sbtest3; drop index kcp on sbtest3 drop index pck on sbtest3;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
root[test]#> alter table sbtest3 engine=innodb;
Query OK, 0 rows affected (28.23 sec)
Records: 0 Duplicates: 0 Warnings: 0
root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd
945M /var/lib/mysql/test/sbtest3.ibd
Успя да спести до ~59% от стария размер на пространството за таблица, което е наистина огромно.
За да определите дублиращи се индекси, можете да използвате pt-duplicate-checker, за да се справите със задачата вместо вас.
Настройте своя пул от буфери
За този раздел имам предвид само механизма за съхранение на InnoDB.
Буферният пул е важен компонент в пространството на ядрото на InnoDB. Това е мястото, където InnoDB кешира данни от таблици и индекси при достъп. Това ускорява обработката, тъй като често използваните данни се съхраняват в паметта ефективно с помощта на BTREE. Например, ако имате множество таблици, състоящи се от>=100GiB и до които се осъществява тежък достъп, тогава ви предлагаме да делегирате бърза летлива памет, започвайки от размер от 128GiB, и да започнете да присвоявате буферния пул с 80% от физическата памет. 80% трябва да се наблюдават ефективно. Можете да използвате SHOW ENGINE INNODB STATUS \G или можете да използвате софтуер за наблюдение като ClusterControl, който предлага фино наблюдение, което включва буферен пул и съответните показатели за здравето. Също така задайте съответно променливата innodb_buffer_pool_instances. Можете да зададете това по-голямо от 8 (по подразбиране, ако innodb_buffer_pool_size>=1GiB), като 16, 24, 32 или 64 или по-високо, ако е необходимо.
Когато наблюдавате буферния пул, трябва да проверите глобалната променлива на състоянието Innodb_buffer_pool_pages_free, която ви дава мисли дали има нужда да коригирате буферния пул или може би да помислите дали има и нежелани или дублиращи се индекси, които консумират буфер. SHOW ENGINE INNODB STATUS \G също предлага по-подробен аспект на информацията за буферния пул, включително неговия индивидуален пул от буфери въз основа на броя на innodb_buffer_pool_instances, който сте задали.
Използвайте FULLTEXT индекси (но само ако е приложимо)
Използване на заявки като,
SELECT bookid, page, context FROM books WHERE context like '%for dummies%';
където контекстът е колона от тип низ (char, varchar, text), е пример за супер лоша заявка! Извличането на голямо съдържание от записи с филтър, който трябва да бъде алчен, завършва с пълно сканиране на таблицата, а това е просто лудост. Помислете за използването на индекс FULLTEXT. Индексите A FULLTEXT имат обърнат дизайн на индекса. Обърнатите индекси съхраняват списък с думи и за всяка дума списък с документи, в които се появява думата. За да се поддържа търсене в близост, информацията за позицията за всяка дума също се съхранява като байтово изместване.
За да използвате FULLTEXT за търсене или филтриране на данни, трябва да използвате комбинацията от MATCH() ... ПРОТИВ синтаксиса, а не като заявката по-горе. Разбира се, трябва да посочите полето да бъде вашето индексно поле FULLTEXT.
За да създадете индекс FULLTEXT, просто посочете FULLTEXT като свой индекс. Вижте примера по-долу:
root[minime]#> CREATE FULLTEXT INDEX aboutme_fts ON users_info(aboutme);
Query OK, 0 rows affected, 1 warning (0.49 sec)
Records: 0 Duplicates: 0 Warnings: 1
root[jbmrcd_date]#> show warnings;
+---------+------+--------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------+
| Warning | 124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+
1 row in set (0.00 sec)
Въпреки че използването на индекси FULLTEXT може да предложи предимства при търсене на думи в много голям контекст в колона, то също създава проблеми, когато се използва неправилно.
Когато правите FULLTEXT търсене на голяма таблица, която е постоянно достъпна (където редица клиентски заявки търсят различни, уникални ключови думи), това може да е много процесорно натоварено.
Има някои случаи, когато ПЪЛЕН ТЕКСТ не е приложим. Вижте тази външна публикация в блога. Въпреки че не съм пробвал това с 8.0, не виждам никакви промени, свързани с това. Предлагаме да не използвате FULLTEXT за търсене в среда с големи данни, особено за таблици с голям трафик. В противен случай опитайте да използвате други технологии, като Apache Lucene, Apache Solr, tsearch2 или Sphinx.
Избягвайте използването на NULL в колони
Колоните, които съдържат нулеви стойности, са напълно добри в MySQL. Но ако използвате колони с нулеви стойности в индекс, това може да повлияе на производителността на заявката, тъй като оптимизаторът не може да осигури правилния план за заявка поради лошо разпределение на индекса. Въпреки това, има определени начини за оптимизиране на заявки, които включват нулеви стойности, но разбира се, ако това отговаря на изискванията. Моля, проверете документацията на MySQL относно нулевата оптимизация. Можете също да проверите тази външна публикация, която също е полезна.
Проектирайте ефективно своята топология на схемата и структура на таблици
До известна степен нормализирането на таблиците на вашата база данни от 1NF (първа нормална форма) до 3NF (трета нормална форма) ви осигурява известна полза за ефективността на заявките, тъй като нормализираните таблици са склонни да избягват излишните записи. Правилното планиране и дизайн за вашите таблици е много важно, защото това е начинът, по който извличате или изтегляте данни и всяко едно от тези действия има цена. При нормализирани таблици целта на базата данни е да гарантира, че всяка неключова колона във всяка таблица е пряко зависима от ключа; целият ключ и нищо освен ключа. Ако тази цел бъде постигната, тя изплаща ползите под формата на намалени съкращения, по-малко аномалии и подобрена ефективност.
Докато нормализирането на вашите таблици има много предимства, това не означава, че трябва да нормализирате всичките си таблици по този начин. Можете да приложите дизайн за вашата база данни с помощта на Star Schema. Проектирането на вашите таблици с помощта на Star Schema има предимството на по-прости заявки (избягвайте сложни кръстосани свързвания), лесни за извличане на данни за отчитане, предлага повишаване на производителността, тъй като няма нужда да се използват обединения или сложни обединения или бързи агрегирания. Star Schema е лесна за изпълнение, но трябва внимателно да планирате, защото може да създаде големи проблеми и недостатъци, когато масата ви стане по-голяма и изисква поддръжка. Star Schema (и основните й таблици) са предразположени към проблеми с целостта на данните, така че може да имате голяма вероятност куп от вашите данни да са излишни. Ако смятате, че тази таблица трябва да бъде постоянна (структура и дизайн) и е проектирана да използва ефективността на заявките, тогава това е идеалният случай за този подход.
Смесването на дизайна на вашите бази данни (стига да можете да определите и идентифицирате какъв вид данни трябва да бъдат изтеглени във вашите таблици) е много важно, тъй като можете да се възползвате с по-ефективни заявки и както помогнете на DBA с архивиране, поддръжка и възстановяване.
Отърви се от постоянни и стари данни
Наскоро написахме някои най-добри практики за архивиране на вашата база данни в облака. Той обхваща как можете да се възползвате от архивирането на данни, преди да отиде в облака. И така, как премахването на стари данни или архивирането на вашите постоянни и стари данни помага на ефективността на заявките? Както беше посочено в предишния ми блог, има предимства за по-големи таблици, които постоянно се модифицират и вмъкват с нови данни, пространството за таблици може да расте бързо. MySQL и InnoDB работят ефективно, когато записи или данни са съседни един на друг и имат значение за следващия ред в таблицата. Това означава, че ако нямате стари записи, които вече не трябва да се използват, тогава оптимизаторът не трябва да включва това в статистиката, предлагайки много по-ефективен резултат. Има смисъл, нали? Освен това, ефективността на заявките не е само от страна на приложението, тя също трябва да вземе предвид ефективността й при извършване на архивиране и при поддръжка или отказ. Например, ако имате лоша и дълга заявка, която може да повлияе на периода на поддръжка или преодоляване на срив, това може да е проблем.
Активирайте регистрирането на заявки, ако е необходимо
Винаги настройвайте бавния регистър на заявките на MySQL в съответствие с вашите персонализирани нужди. Ако използвате Percona Server, можете да се възползвате от тяхното разширено бавно регистриране на заявки. Тя ви позволява обичайно да дефинирате определени променливи. Можете да филтрирате типове заявки в комбинация като full_scan, full_join, tmp_table и др. Можете също да диктувате скоростта на бавно регистриране на заявки чрез променлива log_slow_rate_type и много други.
Важността на разрешаването на регистрирането на заявки в MySQL (като бавна заявка) е от полза за проверка на вашите заявки, така че да можете да оптимизирате или настройвате своя MySQL, като коригирате определени променливи, които отговарят на вашите изисквания. За да активирате бавен регистър на заявките, уверете се, че тези променливи са настроени:
- long_query_time – задайте правилната стойност за това колко време могат да отнемат заявките. Ако заявките отнемат повече от 10 секунди (по подразбиране), те ще паднат до бавния регистрационен файл на заявките, който сте задали.
- slow_query_log - за да го активирате, задайте го на 1.
- slow_query_log_file – това е дестинационният път за вашия регистрационен файл с бавни заявки.
Регистърът на бавните заявки е много полезен за анализ на заявки и диагностициране на лоши заявки, които причиняват спирания, подчинени забавяния, продължителни заявки, интензивно натоварване на паметта или процесора или дори причиняват срив на сървъра. Ако използвате pt-query-digest или pt-index-usage, използвайте бавния регистрационен файл на заявките като изходна цел за отчитане на тези заявки.
Заключение
В този блог обсъдихме някои начини, които можете да използвате, за да увеличите максимално ефективността на заявките за база данни. В тази следваща част ще обсъдим още повече фактори, които могат да ви помогнат да увеличите максимално производителността. Останете на линия!