2. agenda
● Big Picture
W jakiej kondycji jest moja baza danych?
Gdzie szukać potencjalnych problemów?
● Odpowiednio zaprojektowana struktura
Jak projektować tabele?
Jakich typów danych używać?
● Zmiany struktury bazy
Jak zmienić strukturę danych w dużych tabelach (np 300GB)?
2
4. od czego zacząć?
● MySQL internals
● information_schema
● Percona Toolkit
● pt-duplicate-key-checker
● pt-mysql-summary
4
5. information_schema
● Baza danych
● Zawiera widoki (readonly)
● Zapewnia dostęp do metadanych o:
● tabelach
● kolumnach
● indeksach
● triggerach
● widokach
● itp.
● Można wykonywać na niej standardowe SQLki
5
6. information_schema - overview
SELECT engine,
row_format,
table_name,
concat(round(table_rows/1000000,2),'M') rows,
concat(round(data_length/(1024*1024*1024),2),'G') data,
concat(round(index_length/(1024*1024*1024),2),'G') idx,
concat(round((data_length+index_length)/(1024*1024*1024),2),
'G') total_size,
round(index_length/data_length,2) idxfrac
FROM information_schema.TABLES
WHERE table_schema IN ("your_database_name")
GROUP BY table_name, table_schema, engine
ORDER BY data_length+index_length DESC LIMIT 10;
6
8. engine
Pożądanym enginem jest InnoDB
W bardzo specyficznych warunkach może się nam opłacać utrzymywać inne
engine (np. Archive, RocksDB, TokuDB)
… WHERE
table_schema IN ("your_database_name") AND
Engine != “InnoDB” …
ALTER TABLE table_name ENGINE=InnoDB
8
9. row format
W przypadku InnoDB praktycznie zawsze chcemy mieć skompresowane dane
Kompresja oznacza, że mniej danych jest przesyłanych pomiędzy dyskiem a
pamięcią/procesorem
● Mniej zajętej przestrzeni na dysku
● Mniej zajętej przestrzeni w pamięci RAM (buffer pool)
● Większa utylizacja procesora (narzut na kompresję/dekompresję danych)
… WHERE
table_schema IN ("your_database_name") AND
row_format != “Compressed” …
ALTER TABLE table_name ENGINE=InnoDB ROW_FORMAT=COMPRESSED
9
10. rows, data size, index size, total size
rows - ilość rekordów
data size - wielkość danych w tabeli*
idx size - wielkość indeksów w tabeli
total size - sumaryczna wielkość tabeli (dane + indeksy)
* w przypadku InnoDB Primary Key jest klastrowany, więc jego wielkość liczy się
do wielkości danych, a nie indeksów
10
11. rows, data size, index size, total size
Wielkość (rekordy, bajty) pokazuje na jakich tabelach powinniśmy się skupić.
Warto rozważyć:
● Archiwizację
● Partycjonowanie
● Retencję danych
11
12. index fraction
Stosunek wielkości indeksów do danych
Im mniejszy tym lepiej (oczywiście zakładając istnienie wszystkich potrzebnych
do poprawnego działania indeksów)
Duża wartość może oznaczać niepoprawną strukturę
● Część indeksów może być niepotrzebna/nie używana
● Część indeksów może być zduplikowana
● Primary key mógł zostać wybrany nieoptymalnie (np. VARCHAR) + duża ilość secondary keys
… ORDER BY
idxfrac DESC …
12
14. information_schema - primary key
SELECT
engine,
table_name
FROM
TABLES AS t
WHERE
NOT EXISTS(SELECT null FROM COLUMNS AS c WHERE
c.table_name = t.table_name AND
c.table_schema=t.table_schema AND
c.table_schema=’your_database_name’AND c.COLUMN_KEY='PRI'
LIMIT 1) AND t.table_schema=’your_database_name’
14
16. information_schema - primary key
W InnoDB zawsze powinniśmy posiadać PRIMARY KEY
● Primary Key jest klastrowany
● Jeśli nie zdefiniujemy Primary Key to InnoDB sam doda “implicite” Primary Key - w praktyce nie ma
możliwości zbudowania tabeli InnoDB bez PRIMARY KEY, tylko czasem będzie “ukryty” i nie
będziemy mieli do niego dostępu
● PRIMARY KEY ma wpływ na wielkość/budowę SECONDARY INDEX
Jeśli korzystamy z innego engine’u (np. MyISAM) może się okazać, że PRIMARY KEY faktycznie nie
jest potrzebny, ale jest to rzadkość
16
17. information_schema - auto increment
SELECT
TABLES.table_name,
column_name,
column_type,
auto_increment,
CASE WHEN data_type='int' AND column_type NOT LIKE '%unsigned%' THEN
auto_increment/2147483647 * 100 WHEN data_type='int' AND column_type LIKE '%unsigned%' THEN
auto_increment/4294967295 * 100 END AS capacity
FROM
information_schema.TABLES
JOIN information_schema.COLUMNS ON TABLES.table_name = COLUMNS.table_name AND
TABLES.table_schema = COLUMNS.table_schema
WHERE
TABLES.table_schema like ‘your_database_name’ AND
auto_increment IS NOT NULL AND
COLUMNS.extra='auto_increment'
ORDER BY capacity
17
18. information_schema - auto increment
Tabele w których kończy się zakres integer oznaczone jako auto increment:
+-----------------------------+-----------------------+------------------+----------------+----------+
| table_name | column_name | column_type | auto_increment | capacity |
+-----------------------------+-----------------------+------------------+----------------+----------+
| blocking_information | id | int(10) | 200771026 | 9.3491 |
| link_events | id | int(10) unsigned | 413384049 | 9.6248 |
| user_agent_to_subscribers | id | int(11) | 1908563772 | 88.8744 |
+-----------------------------+-----------------------+------------------+----------------+----------+
18
19. information_schema - auto increment
Jaki typ pola będzie najlepszy dla pola oznaczonego jako auto increment ?
● INT - wystarczy na długi czas (4 bajty - 2^32 wartości)
● BIGINT - gdy nie starcza standardowego INT (8 bajtów - 2^64 wartości)
● UNSIGNED - nie ma żadnych podstaw do tego aby mieć ujemne wartości w polu auto increment
więc zawsze stosujmy pole UNSIGNED
19
21. fragmentacja danych
InnoDB Compression Issue - jak powstają “wolne” przestrzenie?
● Dane trzymane są w jednostkach zwanych Page (8kB - 16kB)
● Jeśli mamy dużo operacji UPDATE to dane wcześniej mieszczące się w jednym Page, mogą się
powiększyć i nie zmieścić się w pierwotne miejsce, wtedy następuje ich przeniesienie do nowego
Page, niestety w starym pozostaje “wolne” miejsce
● Jeśli mamy dużo “losowych” operacji DELETE powstają “wolne” miejsca
● Odzyskanie miejsca następuje dopiero kiedy zajętość danych w Page spadnie poniżej 50%
Jak sobie z tym poradzić?
OPTIMIZE TABLE ‘your_table_name’ / ALTER TABLE ‘your_table_name’,
ENGINE=INNODB
21
22. percona toolkit
Zestaw narzędzi zbudowanych przez Percona
● W sumie ponad 30 narzędzi
● Przydatne dla administratorów/bazodanowców/deweloperów
https://www.percona.com/doc/percona-toolkit/3.0/index.html
22
25. big picture - podsumowanie
Powyższe narzędzia/metody pozwalają zgrubnie określić miejsca w których nasza baza danych ma
problemy ze strukturą
● Pozwala określić największe tabele
● Tabele które mają inny/przestarzały engine
● Tabele bez kompresji danych
● Tabele bez klucza głównego
● Tabele ze zduplikowanymi kluczami
● Tabele w których dane są mocno zfragmentowane
25
27. Używaj najmniejszych możliwych typów danych
● Oczywiste… Ale czy na pewno?
Dlaczego ma to takie znaczenie?
● Wykorzystanie zasobów dyskowych
● Transfer po sieci
● Alokacja pamięci w bufforze
typy danych
27
28. Numeryczne typy danych
● SIGNED / UNSIGNED
● Pole typu status - TINYINT
● Pole typu active / soft delete - TINYINT
● Pole typu gender - ENUM (w rzeczywistości TINYINT) / CHAR(1)
INT vs TINYINT?
● 4 bajty vs 1 bajt
typy danych
28
29. Czasowe typy danych
● Pole typu last_update (bieżące zdarzenia) - TIMESTAMP
● Pole typu birthday (historia) - DATETIME
DATETIME vs TIMESTAMP
● 8 bajtów vs 4 bajty
● TIMESTAMP ma obsługę stref czasowych
typy danych
29
30. Znakowe typy danych
● Pole typu hash / uuid - CHAR
● Pole typu topic / name - VARCHAR
● Nie korzystaj jeśli masz słownik, np x-small, small, medium, large - ENUM
CHAR vs VARCHAR
● CHAR(4) - 4 bajty
● VARCHAR(4) - od 1 do 5 bajtów
● VARCHAR(512) - od 2 do 514 bajtów
typy danych
30
31. Wartość nieznana
● Jeśli pole może mieć nie znaną wartość - NULL
NULL vs NOT NULL
● NULL implementowany jest jako jeden bit
typy danych
31
34. CREATE TABLE `emails` (
`added` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`email` varchar(255) NOT NULL,
`type` tinyint NOT NULL DEFAULT '1',
`source` tinyint NOT NULL DEFAULT '2',
PRIMARY KEY (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=678023341
ROW_FORMAT=COMPRESSED
przykład
34
35. CREATE TABLE `emails` (
`id_emails` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`added` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`email` varchar(255) NOT NULL,
`type` tinyint NOT NULL DEFAULT '1',
`source` tinyint NOT NULL DEFAULT '2',
PRIMARY KEY (`id_emails`)
UNIQUE KEY email(`email`)
) ENGINE=InnoDB AUTO_INCREMENT=678023341
ROW_FORMAT=COMPRESSED
przykład
35
36. Twórz najmniej indexów jak to tylko możliwe
Wybieraj najmniejszy PRIMARY KEY jaki jest możliwy
● PRIMARY KEY jest kopiowany do każdego SECONDARY INDEX
● PRIMARY KEY jest kopiowany do każdej tabeli z którą chcemy się łączyć (FOREIGN KEY)
Unikaj “losowego” PRIMARY KEY (np. email, hash)
● INSERT jest rozrzucony po całej tabeli
Index (innodb)
36
39. ALTER TABLE - stara szkoła
Każdy ALTER blokował tabelę
● Brak możliwości wykonania INSERT / UPDATE / DELETE
● Im większa tabela tym dłuższy czas zablokowania
● Można śledzić postęp tylko w przypadku file_per_table=1(za pomocą ls -la)
39
40. ALTER TABLE - fast index creation
Fast Index Creation
● pojawił się w Mysql 5.1 (InnoDB Plugin) lub Mysql 5.5+
● Można tworzyć Secondary Index bez konieczności kopiowania wszystkich danych w tabeli
● Przyspieszenie dodawania/kasowania indeksów.
40
41. ALTER TABLE - Online DDL change
InnoDB online DDL change
● Pierwsza wersja w MySQL 5.5 (podstawowe możliwości)
● Dużo więcej możliwych operacji od MySQL 5.6
● Umożliwia zmianę schematu bez blokowania tabeli
● Ograniczone do pewnych konkretnych typów operacji (np. nie obsługuje FULL TEXT)
● Dostępne tylko dla InnoDB
● Brak zarządzania “throttlingiem”
41
42. Algorithm
● INPLACE - nie wymaga przebudowania tabeli
● COPY - kopiuje dane i przebudowywuje całą tabelę
Lock mode
● EXCLUSIVE - żadne DML nie są możliwe
● SHARED - można z tabeli czytać, nie ma możliwości zrobić UPDATE/INSERT/DELETE
● NONE - nie ma żadnego lock’a na tabelę, można wykonywać dowolne DML
ALTER TABLE `your_database`.`table_name` ADD COLUMN `example` INT, ALGORITHM=INPLACE
ALTER TABLE `your_database`.`table_name` ENGINE=INNODB, ALGORITHM=COPY, LOCK=SHARED
ALTER TABLE - Online DDL change
42
43. Podczas ALTER’a tabela nie jest modyfikowana, ale za to mamy dodatkowy,
tymczasowy log file gdzie zapisywane są wszystkie zmiany dokonywane w
czasie ALTER’a na tabeli (INSERT/UPDATE/DELETE)
Wielkość log file definiuje innodb_online_alter_log_max_size
● Domyślnie 128MB
● Jeśli zostanie zapełniony, ALTER się nie powiedzie
Długo trwający ALTER może będzie powodował replication lag - najpierw w
całości musi wykonać się na masterze, potem na slave
ALTER TABLE - Online DDL change
43
44. Jak wygląda procedura OSC?
● Tworzymy nową tabelę z identyczną strukturą jak pierwotna
● Wykonujemy polecenia ALTER TABLE na “nowej” / pustej tabeli
● Musimy zadbać o synchronizację DML (triggers / binary log)
● Kopiujemy dane do nowej tabeli
● Zamieniamy tabele poleceniem RENAME TABLE
Online Schema Change Tools
44
45. Stworzone przez Percona
Udostępnione jako część pakietu Percona Toolkit
Open Source
Wykorzystuje TRIGGERY do synchronizacji (synchroniczna)
Powoduje większe obciążenie na bazie (z powodu TRIGGERÓW)
Nie mamy pełnego wpływu na “throttling”
pt-online-schema-change
45
47. Znane problemy / ograniczenia:
● Wymagany PRIMARY KEY / UNIQUE KEY
● Nazwa FOREIGN KEY ulegnie zmianie po alterze
● Na tabeli nie mogą być założone TRIGGERY
● Migracji nie można zapauzować
pt-online-schema-change
47
48. Stworzone przez GitHub
Open Source
Do synchronizacji wykorzystuje binloga (asynchroniczna)
Nie zakłada TRIGGERÓW na tabeli
Mamy pełny wpływ na “throttling”
Dynamicznie możemy zmieniać parametry “throttlingu”
Kontrolowany “cutover” dla nowych, zalterowanych tabel
gh-ost
48
50. Znane problemy / ograniczenia:
⋅ Nie obsługuje aktywnej replikacji MASTER - MASTER
gh-ost
50
51. osc tips
screen - w razie rozłączenia z serwerem
replikacja - patrz i reaguj
DROP INDEX - wbudowany ALTER zrobi to “inplace”, nie rób tego gh-ost’em ani
ptosc
defragmentacja - --alter=”ENGINE=INNODB”
przerwanie migracji - sprawdź czy nie zostały “nowe” tabele lub triggery
51
52. osc - długo jeszcze?
gh-ost i ptosc pokazują progress na stdout (ale nie zawsze dokładnie)
mysql> show table status like ‘%your_table%’;
ls -alh /data/schema/*your_table*.ibd
mysql> select max(id) from your_table;
mysql> show full processlist; - żeby znaleźć aktualne ID PK
52
53. osc - uruchom i zapomnij...
Każda migracja wprowadza obciążenie serwera bazodanowego
Warto mieć zewnętrzne narzędzie które monitoruje średni czas odpowiedzi
aplikacji
Nie uruchamiaj w piątek wieczorem (chyba, że nie masz ochoty wrócić do
domu…)
53