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

Automatyzacja sprawdzania obiektów schematu bazy danych

Monitorowanie zmian schematu bazy danych w MySQL/MariaDB zapewnia ogromną pomoc, ponieważ pozwala zaoszczędzić czas na analizowanie wzrostu bazy danych, zmian definicji tabeli, rozmiaru danych, rozmiaru indeksu lub rozmiaru wiersza. W przypadku bazy danych MySQL/MariaDB uruchomienie zapytania odwołującego się do schematu information_schema wraz ze schematem performance_schema daje zbiorcze wyniki do dalszej analizy. Schemat sys zapewnia widoki, które służą jako zbiorcze metryki, które są bardzo przydatne do śledzenia zmian lub aktywności w bazie danych.

Jeśli masz wiele serwerów baz danych, ciągłe uruchamianie zapytań byłoby nużące. Musisz także przetrawić ten wynik na bardziej czytelny i łatwiejszy do zrozumienia.

W tym blogu utworzymy automatyzację, która będzie pomocna jako narzędzie do monitorowania istniejącej bazy danych i zbieranie metryk dotyczących zmian w bazie danych lub operacji zmiany schematu.

 Tworzenie automatyzacji do sprawdzania obiektów schematu bazy danych

W tym ćwiczeniu będziemy monitorować następujące wskaźniki:

  • Brak tabel kluczy podstawowych

  • Powiel indeksy

  • Wygeneruj wykres dla łącznej liczby wierszy w naszych schematach baz danych

  • Wygeneruj wykres przedstawiający całkowity rozmiar schematów naszej bazy danych

To ćwiczenie da ci wskazówki i może być modyfikowane w celu zebrania bardziej zaawansowanych metryk z bazy danych MySQL/MariaDB.

Korzystanie z Puppet dla naszego IaC i automatyzacji

W tym ćwiczeniu wykorzystamy Puppet do zapewnienia automatyzacji i wygenerowania oczekiwanych wyników na podstawie metryk, które chcemy monitorować. Nie będziemy omawiać instalacji i konfiguracji Puppet, w tym serwera i klienta, więc oczekuję, że będziesz wiedział, jak korzystać z Puppet. Możesz odwiedzić nasz stary blog Automated Deployment MySQL Galera Cluster do Amazon AWS with Puppet, który obejmuje konfigurację i instalację Puppet.

W tym ćwiczeniu użyjemy najnowszej wersji Puppet, ale ponieważ nasz kod składa się z podstawowej składni, będzie działał dla starszych wersji Puppet.

Preferowany serwer bazy danych MySQL

W tym ćwiczeniu użyjemy Percona Server 8.0.22-13, ponieważ wolę Percona Server głównie do testowania i niektórych mniejszych wdrożeń, zarówno do użytku biznesowego, jak i osobistego.

Narzędzie do tworzenia wykresów 

Istnieje mnóstwo opcji do wykorzystania, zwłaszcza w środowisku Linux. Na tym blogu użyję najłatwiejszego, jakie znalazłem i narzędzia opensource https://quickchart.io/.

Pobawmy się marionetką

Założenie, które tutaj postawiłem, jest takie, że masz skonfigurowany serwer główny z zarejestrowanym klientem, który jest gotowy do komunikacji z serwerem głównym w celu otrzymywania automatycznych wdrożeń.

Zanim przejdziemy dalej, oto informacje o moim serwerze:

Serwer główny:192.168.40.200

Klient/Serwer agenta:192.168.40.160

W tym blogu nasz serwer klient/agent jest miejscem, w którym działa nasz serwer bazy danych. W rzeczywistym scenariuszu nie musi to być specjalnie do monitorowania. Dopóki jest w stanie bezpiecznie komunikować się z węzłem docelowym, jest to również idealna konfiguracja.

Skonfiguruj moduł i kod

  1. Przejdź do serwera głównego i w ścieżce /etc/puppetlabs/code/environments/production/module, utwórzmy wymagane katalogi do tego ćwiczenia:

mkdir schema_change_mon/{files,manifests}

  1. Utwórz potrzebne nam pliki

touch schema_change_mon/files/graphing_gen.sh
touch schema_change_mon/manifests/init.pp
  1. Wypełnij skrypt init.pp następującą treścią:

class schema_change_mon (
  $db_provider = "mysql",
  $db_user = "root",
  $db_pwd = "[email protected]",
  $db_schema = []
) {

$dbs = ['pauldb', 'sbtest']
service { $db_provider :
ensure       => running,
enable       => true,
hasrestart   => true,
hasstatus    => true
}
exec { "mysql-without-primary-key" :
require     => Service['mysql'],
command => "/usr/bin/sudo MYSQL_PWD=\"${db_pwd}\" /usr/bin/mysql -u${db_user} -Nse \"select concat(tables.table_schema,'.',tables.table_name,', ', tables.engine) from information_schema.tables left join ( select table_schema , table_name from information_schema.statistics group by table_schema , table_name , index_name having  sum( case  when non_unique = 0  and nullable != 'YES' then 1  else 0  end ) = count(*) ) puks on tables.table_schema = puks.table_schema and tables.table_name = puks.table_name where puks.table_name is null and tables.table_type = 'BASE TABLE' and tables.table_schema not in ('performance_schema',  'information_schema', 'mysql');\" >> /opt/schema_change_mon/assets/no-pk.log"
}
$dbs.each |String $db| {
exec { "mysql-duplicate-index-$db" :
require     => Service['mysql'],
command => "/usr/bin/sudo MYSQL_PWD=\"${db_pwd}\" /usr/bin/mysql -u${db_user} -Nse \"SELECT concat(t.table_schema,'.', t.table_name, '.', t.index_name, '(', t.idx_cols,')') FROM ( SELECT table_schema, table_name, index_name, Group_concat(column_name) idx_cols FROM ( SELECT table_schema, table_name, index_name, column_name FROM statistics WHERE table_schema='${db}' ORDER BY index_name, seq_in_index) t GROUP BY table_name, index_name) t JOIN ( SELECT table_schema, table_name, index_name, Group_concat(column_name) idx_cols FROM ( SELECT table_schema, table_name, index_name, column_name FROM statistics WHERE table_schema='pauldb' ORDER BY index_name, seq_in_index) t GROUP BY table_name, index_name) u where t.table_schema = u.table_schema AND t.table_name = u.table_name AND t.index_name<>u.index_name AND locate(t.idx_cols,u.idx_cols);\" information_schema >> /opt/schema_change_mon/assets/dupe-indexes.log"
}
}

$genscript = "/tmp/graphing_gen.sh"
file { "${genscript}" :
ensure => present,
owner  => root,
group  => root,
mode   => '0655',
source => 'puppet:///modules/schema_change_mon/graphing_gen.sh'
}
exec { "generate-graph-total-rows" :
require     => [Service['mysql'],File["${genscript}"]],
path =>  [ '/bin/', '/sbin/' , '/usr/bin/', '/usr/sbin/' ],
provider => "shell",
logoutput => true,
command => "/tmp/graphing_gen.sh total_rows"
}
exec { "generate-graph-total-len" :
require  => [Service['mysql'],File["${genscript}"]],
path =>  [ '/bin/', '/sbin/' , '/usr/bin/', '/usr/sbin/' ],
provider => "shell",
logoutput => true,
command => "/tmp/graphing_gen.sh total_len"
}
}

  1. Wypełnij plik graphing_gen.sh. Ten skrypt uruchomi się na węźle docelowym i wygeneruje wykresy dla całkowitej liczby wierszy w naszej bazie danych, a także całkowitego rozmiaru naszej bazy danych. W przypadku tego skryptu uprośćmy go i zezwólmy tylko na bazy danych typu MyISAM lub InnoDB.

#!/bin/bash
graph_ident="${1:-total_rows}"
unset json myisam innodb nmyisam ninnodb; json='' myisam='' innodb='' nmyisam='' ninnodb='' url=''; json=$(MYSQL_PWD="[email protected]" mysql -uroot -Nse "select json_object('dbschema', concat(table_schema,' - ', engine), 'total_rows', sum(table_rows), 'total_len', sum(data_length+data_length), 'fragment', sum(data_free)) from information_schema.tables where table_schema not in ('performance_schema', 'sys', 'mysql', 'information_schema') and engine in ('myisam','innodb') group by table_schema, engine;" | jq . |  sed ':a;N;$!ba;s/\n//g' | sed 's|}{|},{|g' | sed 's/^/[/g'| sed 's/$/]/g' | jq '.' ); innodb=""; myisam=""; for r in $(echo $json | jq 'keys | .[]'); do if [[ $(echo $json| jq .[$r].'dbschema') == *"MyISAM"* ]]; then nmyisam=$(echo $nmyisam || echo '')$(echo $json| jq .[$r]."${graph_ident}")','; myisam=$(echo $myisam || echo '')$(echo $json| jq .[$r].'dbschema')','; else ninnodb=$(echo $ninnodb || echo '')$(echo $json| jq .[$r]."${graph_ident}")','; innodb=$(echo $innodb || echo '')$(echo $json| jq .[$r].'dbschema')','; fi; done; myisam=$(echo $myisam|sed 's/,$//g'); nmyisam=$(echo $nmyisam|sed 's/,$//g'); innodb=$(echo $innodb|sed 's/,$//g');ninnodb=$(echo $ninnodb|sed 's/,$//g'); echo $myisam "|" $nmyisam; echo $innodb "|" $ninnodb; url=$(echo "{type:'bar',data:{labels:['MyISAM','InnoDB'],datasets:[{label:[$myisam],data:[$nmyisam]},{label:[$innodb],data:[$ninnodb]}]},options:{title:{display:true,text:'Database Schema Total Rows Graph',fontSize:20,}}}"); curl -L -o /vagrant/schema_change_mon/assets/db-${graph_ident}.png -g https://quickchart.io/chart?c=$(python -c "import urllib,os,sys; print urllib.quote(os.environ['url'])")

  1. Na koniec przejdź do katalogu ścieżki modułu lub /etc/puppetlabs/code/environments /produkcja w mojej konfiguracji. Utwórzmy plik manifests/schema_change_mon.pp.

touch manifests/schema_change_mon.pp
  1. Następnie wypełnij plik manifests/schema_change_mon.pp następującą zawartością,

node 'pupnode16.puppet.local' { # Applies only to mentioned node. If nothing mentioned, applies to all.
        class { 'schema_change_mon':
        }
}

Jeśli skończyłeś, powinieneś mieć następującą strukturę drzewa, taką jak moja,

[email protected]:/etc/puppetlabs/code/environments/production/modules# tree schema_change_mon
schema_change_mon
├── files
│   └── graphing_gen.sh
└── manifests
    └── init.pp

Co robi nasz moduł?

Nasz moduł o nazwie schema_change_mon gromadzi następujące elementy,

 exec { "mysql-without-primary-key" :

...

Która wykonuje polecenie mysql i uruchamia zapytanie w celu pobrania tabel bez kluczy podstawowych. Następnie

$dbs.each |String $db| {
exec { "mysql-duplicate-index-$db" :

który zbiera zduplikowane indeksy istniejące w tabelach bazy danych.

Następnie linie generują wykresy na podstawie zebranych danych. Oto następujące wiersze,

exec { "generate-graph-total-rows" :
...

exec { "generate-graph-total-len" :
…

Po pomyślnym uruchomieniu zapytania generuje wykres, który zależy od interfejsu API udostępnianego przez https://quickchart.io/.

Oto następujące wyniki wykresu:

Dzienniki plików zawierają po prostu ciągi z nazwami tabel, nazwami indeksów. Zobacz wynik poniżej,

[email protected]:~# tail -n+1 /opt/schema_change_mon/assets/*.log
==> /opt/schema_change_mon/assets/dupe-indexes.log <==
pauldb.c.my_index(n,i)
pauldb.c.my_index2(n,i)
pauldb.d.a_b(a,b)
pauldb.d.a_b2(a,b)
pauldb.d.a_b3(a)
pauldb.d.a_b3(a)
pauldb.t3.b(b)
pauldb.c.my_index(n,i)
pauldb.c.my_index2(n,i)
pauldb.d.a_b(a,b)
pauldb.d.a_b2(a,b)
pauldb.d.a_b3(a)
pauldb.d.a_b3(a)
pauldb.t3.b(b)

==> /opt/schema_change_mon/assets/no-pk.log <==
pauldb.b, MyISAM
pauldb.c, InnoDB
pauldb.t2, InnoDB
pauldb.d, InnoDB
pauldb.b, MyISAM
pauldb.c, InnoDB
pauldb.t2, InnoDB
pauldb.d, InnoDB

Dlaczego nie używać ClusterControl?

Ponieważ nasze ćwiczenie pokazuje automatyzację i pobieranie statystyk schematu bazy danych, takich jak zmiany lub operacje, ClusterControl również to zapewnia. Oprócz tego są inne funkcje i nie musisz wymyślać koła na nowo. ClusterControl może dostarczyć dzienniki transakcji, takie jak zakleszczenia, jak pokazano powyżej, lub długo działające zapytania, jak pokazano poniżej:


 

ClusterControl pokazuje również wzrost bazy danych, jak pokazano poniżej,

ClusterControl podaje również dodatkowe informacje, takie jak liczba wierszy, rozmiar dysku, rozmiar indeksu i całkowity rozmiar.

Analizator schematu w zakładce Wydajność -> Analizator schematu jest bardzo pomocny. Udostępnia tabele bez kluczy podstawowych, tabele MyISAM i zduplikowane indeksy,

Zapewnia również alarmy w przypadku wykrycia zduplikowanych indeksów lub tabel bez podstawowych klawisze takie jak poniżej,

Więcej informacji o ClusterControl i innych funkcjach można znaleźć na naszej stronie Produktu.

Wnioski

Zapewnienie automatyzacji monitorowania zmian w bazie danych lub wszelkich statystyk schematów, takich jak zapisy, duplikaty indeksów, aktualizacje operacji, takie jak zmiany DDL, i wiele działań w bazach danych jest bardzo korzystne dla administratorów baz danych. Pomaga szybko zidentyfikować słabe łącza i problematyczne zapytania, które dają przegląd możliwej przyczyny złych zapytań, które mogą zablokować lub przestarzać bazę danych.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak działa SECOND() w MariaDB

  2. 4 sposoby na wyświetlenie listy wszystkich widoków w bazie danych MariaDB

  3. MariaDB JSON_ARRAYAGG() Objaśnienie

  4. Co nowego w MariaDB 10.6

  5. Przypadki użycia MariaDB i Docker, część 1