MariaDB
 sql >> Baza danych >  >> RDS >> MariaDB

Maksymalizacja wydajności zapytań do bazy danych dla MySQL — część pierwsza

Powolne zapytania, nieefektywne zapytania lub długotrwałe zapytania to problemy, które regularnie nękają administratorów baz danych. Zawsze są wszechobecne, ale są nieuniknioną częścią życia każdego, kto jest odpowiedzialny za zarządzanie bazą danych.

Zły projekt bazy danych może wpłynąć na wydajność zapytania i jego wydajność. Brak wiedzy lub niewłaściwe użycie wywołań funkcji, procedur składowanych lub procedur może również spowodować pogorszenie wydajności bazy danych, a nawet uszkodzić cały klaster bazy danych MySQL.

W przypadku replikacji typu master-slave bardzo częstą przyczyną tych problemów są tabele, które nie mają indeksów podstawowych ani wtórnych. Powoduje to opóźnienie, które może trwać bardzo długo (w gorszym przypadku).

W tym dwuczęściowym blogu przedstawimy kurs przypominający o tym, jak radzić sobie z maksymalizacją zapytań do bazy danych w MySQL, aby zapewnić lepszą wydajność i wydajność.

Zawsze dodawaj unikalny indeks do swojego stołu

Tabele, które nie mają kluczy podstawowych lub unikalnych, zwykle stwarzają ogromne problemy, gdy dane są większe. W takim przypadku prosta modyfikacja danych może spowodować zatrzymanie bazy danych. Brak odpowiednich indeksów i zastosowanie instrukcji UPDATE lub DELETE do danej tabeli, pełny skan tabeli zostanie wybrany jako plan zapytania przez MySQL. Może to powodować wysokie dyskowe operacje we/wy dla odczytów i zapisów oraz obniżyć wydajność bazy danych. Zobacz przykład poniżej:

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)

Podczas gdy tabela z kluczem podstawowym ma bardzo dobry plan zapytań,

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)

Klucze podstawowe lub unikatowe stanowią istotny składnik struktury tabeli, ponieważ jest to bardzo ważne, zwłaszcza podczas konserwacji tabeli. Na przykład korzystanie z narzędzi z pakietu Percona Toolkit (takich jak pt-online-schema-change lub pt-table-sync) zaleca, aby mieć unikalne klucze. Należy pamiętać, że KLUCZ PODSTAWOWY jest już kluczem unikalnym, a klucz podstawowy nie może zawierać wartości NULL, ale klucz unikalny. Przypisanie wartości NULL do klucza podstawowego może spowodować błąd, taki jak:

ERROR 1171 (42000): All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead

W przypadku węzłów podrzędnych często zdarza się, że w niektórych przypadkach klucz podstawowy/unikalny nie jest obecny w tabeli, co oznacza rozbieżność w strukturze tabeli. Możesz użyć mysqldiff, aby to osiągnąć lub możesz mysqldump --no-data … params i uruchomić diff, aby porównać strukturę tabeli i sprawdzić, czy nie ma rozbieżności.

Przeskanuj tabele ze zduplikowanymi indeksami, a następnie je upuść

Zduplikowane indeksy mogą również powodować pogorszenie wydajności, zwłaszcza gdy tabela zawiera ogromną liczbę rekordów. MySQL musi wykonać wiele prób optymalizacji zapytania i wykonuje więcej planów zapytań do sprawdzenia. Obejmuje skanowanie dużej dystrybucji indeksów lub statystyk, co zwiększa wydajność, ponieważ może powodować rywalizację o pamięć lub wysokie wykorzystanie pamięci we/wy.

Degradacja zapytań, gdy obserwowane są zduplikowane indeksy w tabeli, a także atrybuty nasycenia puli buforów. Może to również wpłynąć na wydajność MySQL, gdy punkt kontrolny opróżnia dzienniki transakcji na dysk. Wynika to z przetwarzania i przechowywania niechcianego indeksu (co w rzeczywistości jest marnowaniem miejsca w konkretnej przestrzeni tabel tej tabeli). Zwróć uwagę, że zduplikowane indeksy są również przechowywane w obszarze tabel, który również musi być przechowywany w puli buforów.

Spójrz na poniższą tabelę, która zawiera wiele zduplikowanych kluczy:

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)

i ma rozmiar 2,3GiB

root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd

2.3G    /var/lib/mysql/test/sbtest3.ibd

Pozbądźmy się zduplikowanych indeksów i przebudujmy tabelę bez zmiany,

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

Udało się zaoszczędzić do ~59% starego rozmiaru przestrzeni tabel, która jest naprawdę ogromna.

Aby określić zduplikowane indeksy, możesz użyć pt-duplicate-checker do obsługi zadania za Ciebie.

Zwiększ swoją pulę buforów

W tej sekcji mam na myśli tylko silnik przechowywania InnoDB.

Pula buforów jest ważnym komponentem w przestrzeni jądra InnoDB. W tym miejscu InnoDB buforuje dane tabeli i indeksu podczas uzyskiwania dostępu. Przyspiesza przetwarzanie, ponieważ często używane dane są skutecznie przechowywane w pamięci za pomocą BTREE. Na przykład, jeśli masz wiele tabel składających się z>=100GiB i masz do nich duży dostęp, sugerujemy delegowanie szybkiej pamięci ulotnej, zaczynając od rozmiaru 128GiB i rozpoczęcie przypisywania puli buforów z 80% pamięci fizycznej. 80% musi być skutecznie monitorowane. Możesz użyć SHOW ENGINE INNODB STATUS \G lub możesz skorzystać z oprogramowania monitorującego, takiego jak ClusterControl, które oferuje szczegółowe monitorowanie, które obejmuje pulę buforów i odpowiednie metryki kondycji. Ustaw również odpowiednio zmienną innodb_buffer_pool_instances. Możesz ustawić tę wartość większą niż 8 (domyślnie, jeśli innodb_buffer_pool_size>=1GiB), na przykład 16, 24, 32 lub 64 lub więcej, jeśli to konieczne.

Podczas monitorowania puli buforów, należy sprawdzić globalną zmienną stanu Innodb_buffer_pool_pages_free, która dostarcza myśli, czy istnieje potrzeba dostosowania puli buforów, lub może rozważyć, czy istnieją również niechciane lub zduplikowane indeksy, które zużywają bufor. SHOW ENGINE INNODB STATUS \G oferuje również bardziej szczegółowy aspekt informacji o puli buforów, w tym o indywidualnej puli buforów na podstawie ustawionej liczby innodb_buffer_pool_instances.

Użyj indeksów PEŁNOTEKSTOWYCH (ale tylko wtedy, gdy ma to zastosowanie)

Korzystając z zapytań takich jak,

SELECT bookid, page, context FROM books WHERE context like '%for dummies%';

gdzie kontekst jest kolumną typu string (char, varchar, text), jest przykładem bardzo złego zapytania! Wyciąganie dużej zawartości rekordów za pomocą filtra, który musi być zachłanny, kończy się pełnym skanowaniem tabeli, a to jest po prostu szalone. Rozważ użycie indeksu PEŁNOTEKSTOWEGO. Indeksy FULLTEXT mają odwrócony projekt indeksu. Indeksy odwrócone przechowują listę słów, a dla każdego słowa listę dokumentów, w których to słowo występuje. Aby obsługiwać wyszukiwanie zbliżone, informacje o pozycji każdego słowa są również przechowywane jako przesunięcie bajtowe.

Aby użyć FULLTEXT do wyszukiwania lub filtrowania danych, musisz użyć kombinacji składni MATCH() ...AGAINST, a nie jak w powyższym zapytaniu. Oczywiście musisz określić pole, które będzie Twoim indeksem FULLTEXT.

Aby utworzyć indeks FULLTEXT, po prostu określ indeks FULLTEXT jako indeks. Zobacz przykład poniżej:

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)

Chociaż używanie indeksów FULLTEXT może przynieść korzyści podczas wyszukiwania słów w bardzo dużym kontekście wewnątrz kolumny, powoduje również problemy, gdy jest używane niepoprawnie.

Podczas wyszukiwania PEŁNEGO TEKSTU dużej tabeli, do której stale uzyskuje się dostęp (gdzie wiele żądań klientów szuka różnych, unikalnych słów kluczowych), może to bardzo obciążać procesor.

W pewnych sytuacjach PEŁNY TEKST nie ma zastosowania. Zobacz ten wpis na zewnętrznym blogu. Chociaż nie próbowałem tego w wersji 8.0, nie widzę żadnych istotnych zmian. Sugerujemy, aby nie używać FULLTEXT do przeszukiwania środowiska danych big data, zwłaszcza w przypadku tabel o dużym natężeniu ruchu. W przeciwnym razie spróbuj wykorzystać inne technologie, takie jak Apache Lucene, Apache Solr, tsearch2 lub Sphinx.

Unikaj używania wartości NULL w kolumnach

Kolumny zawierające wartości null są całkowicie poprawne w MySQL. Ale jeśli używasz w indeksie kolumn z wartościami null, może to wpłynąć na wydajność zapytań, ponieważ optymalizator nie może zapewnić odpowiedniego planu zapytań ze względu na słabą dystrybucję indeksu. Istnieją jednak pewne sposoby optymalizacji zapytań, które zawierają wartości null, ale oczywiście, jeśli spełnia to wymagania. Sprawdź dokumentację MySQL na temat optymalizacji zerowej. Możesz również sprawdzić ten zewnętrzny post, który jest również pomocny.

Zaprojektuj swoją topologię schematu i strukturę tabel wydajnie

Do pewnego stopnia normalizacja tabel bazy danych z 1NF (pierwsza postać normalna) do 3NF (trzecia postać normalna) zapewnia pewną korzyść w zakresie wydajności zapytań, ponieważ znormalizowane tabele mają tendencję do unikania zbędnych rekordów. Właściwe planowanie i projektowanie tabel jest bardzo ważne, ponieważ w ten sposób pobierasz lub pobierasz dane, a każda z tych czynności ma swój koszt. W przypadku tabel znormalizowanych celem bazy danych jest zapewnienie, że każda kolumna niebędąca kluczem w każdej tabeli jest bezpośrednio zależna od klucza; cały klucz i tylko klucz. Jeśli ten cel zostanie osiągnięty, przyniesie korzyści w postaci zmniejszonych zwolnień, mniejszej liczby anomalii i lepszej wydajności.

Chociaż normalizacja stołów ma wiele zalet, nie oznacza to, że musisz normalizować wszystkie stoły w ten sposób. Możesz zaimplementować projekt swojej bazy danych za pomocą Star Schema. Zaletą projektowania tabel przy użyciu schematu gwiezdnego są prostsze zapytania (unikanie złożonych sprzężeń krzyżowych), łatwe pobieranie danych do raportowania, a także zwiększenie wydajności, ponieważ nie ma potrzeby używania związków, złożonych sprzężeń ani szybkich agregacji. Schemat gwiezdny jest prosty do wdrożenia, ale trzeba go dokładnie zaplanować, ponieważ może powodować duże problemy i wady, gdy stół staje się większy i wymaga konserwacji. Schemat Gwiezdny (i powiązane z nim tabele) są podatne na problemy z integralnością danych, więc istnieje duże prawdopodobieństwo, że zbiór danych jest zbędny. Jeśli uważasz, że ta tabela musi być stała (struktura i projekt) i została zaprojektowana tak, aby wykorzystywać wydajność zapytań, to jest to idealny przypadek dla tego podejścia.

Mieszanie projektów baz danych (o ile jesteś w stanie określić i zidentyfikować rodzaj danych, które mają być pobierane z tabel) jest bardzo ważne, ponieważ możesz skorzystać z bardziej wydajnych zapytań, a także pomóc DBA w tworzeniu kopii zapasowych, konserwacji i odzyskiwaniu.

Pozbądź się stałych i starych danych

Niedawno napisaliśmy kilka najlepszych praktyk dotyczących archiwizacji bazy danych w chmurze. Zawiera informacje o tym, jak skorzystać z archiwizacji danych, zanim trafią one do chmury. Jak więc pozbycie się starych danych lub zarchiwizowanie stałych i starych danych może zwiększyć wydajność zapytań? Jak wspomniałem na moim poprzednim blogu, większe tabele mają zalety, które są stale modyfikowane i wstawiane z nowymi danymi, przestrzeń tabel może szybko rosnąć. MySQL i InnoDB działają wydajnie, gdy rekordy lub dane przylegają do siebie i mają znaczenie dla następnego wiersza w tabeli. Oznacza to, że jeśli nie masz starych rekordów, które nie są już potrzebne, to optymalizator nie musi uwzględniać ich w statystykach, oferując znacznie bardziej efektywne wyniki. Ma sens, prawda? Ponadto wydajność zapytań jest nie tylko po stronie aplikacji, ale należy również wziąć pod uwagę jej wydajność podczas wykonywania kopii zapasowej oraz podczas konserwacji lub przełączania awaryjnego. Na przykład, jeśli masz złe i długie zapytanie, które może wpłynąć na okres konserwacji lub przełączenie awaryjne, może to stanowić problem.

Włącz rejestrowanie zapytań w razie potrzeby

Zawsze ustaw wolny dziennik zapytań MySQL zgodnie z własnymi potrzebami. Jeśli używasz Percona Server, możesz skorzystać z ich rozszerzonego powolnego rejestrowania zapytań. Pozwala na zwyczajowe definiowanie pewnych zmiennych. Możesz filtrować typy zapytań w kombinacji, takie jak full_scan, full_join, tmp_table itp. Możesz także dyktować szybkość rejestrowania wolnego zapytania za pomocą zmiennej log_slow_rate_type i wielu innych.

Ważność włączenia rejestrowania zapytań w MySQL (takich jak powolne zapytania) jest korzystna przy sprawdzaniu zapytań, dzięki czemu możesz zoptymalizować lub dostroić MySQL poprzez dostosowanie pewnych zmiennych, które odpowiadają Twoim wymaganiom. Aby włączyć powolny dziennik zapytań, upewnij się, że te zmienne są ustawione:

  • long_query_time — przypisz odpowiednią wartość określającą, jak długo mogą trwać zapytania. Jeśli zapytania zajmą więcej niż 10 sekund (domyślnie), spadną one do przypisanego pliku dziennika powolnych zapytań.
  • slow_query_log - aby go włączyć, ustaw go na 1.
  • slow_query_log_file — to jest ścieżka docelowa dla pliku dziennika powolnych zapytań.

Powolny dziennik zapytań jest bardzo pomocny przy analizie zapytań i diagnozowaniu złych zapytań, które powodują przestoje, opóźnienia slave, długotrwałe zapytania, intensywność pamięci lub procesora, a nawet powodują awarię serwera. Jeśli używasz pt-query-digest lub pt-index-usage, użyj pliku dziennika powolnych zapytań jako celu źródłowego do raportowania tych zapytań w podobny sposób.

Wnioski

W tym blogu omówiliśmy kilka sposobów maksymalizacji wydajności zapytań do bazy danych. W następnej części omówimy jeszcze więcej czynników, które mogą pomóc zmaksymalizować wydajność. Bądź na bieżąco!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Replikacja MySQL z ProxySQL na serwerach WHM/cPanel:część druga

  2. Jak odjąć minuty od wartości daty i godziny w MariaDB?

  3. Połączenia HAProxy a połączenia MySQL — co powinieneś wiedzieć

  4. 4 funkcje, które zwracają minuty z wartości czasu w MariaDB

  5. Rozważania dotyczące bezpieczeństwa wdrożeń MariaDB w środowisku chmury hybrydowej