środa, 26 lutego 2014

Export danych z PostgreSQL-a

Pisałam już o sposobie export-u danych z MySQL (tego posta możecie znaleźć tutaj), dlatego przyszedł teraz czas na PostgreSQL'a.

Chce zwrócić waszą uwagę na dwa polecenia:
  • psql - program klienta do połączenia z serwerem
  • polecenie SQL: COPY - do kopiowania danych pomiędzy plikiem a tabelą

Program psql

Opcje programu psql przy exporcie danych:
  • -c, --command=ZAPYTANIE - wykonuje zapytanie i kończy połączenie z serwerem
  • -f, --file=ŚCIEŻKA_DO_PLIKU - możecie także wykonać zapytanie, które jest zapisane do pliku
  • -o, --output=ŚCIEŻKA_DO_PLIKU - wynik zapisujemy w podanym pliku
  • -H, --html - wynik zapisany jest w formie html-a
  • -A, --no-align - dane nie są wyrównywane. Dane w kolumnach są oddzielane domyślnie znakiem pipe: "|". 
  • -x, --expanded - dane są wyświetlane wertykalnie (pionowo)
  • -t, --tuples-only - tylko dane są zwracane (bez informacji o nazwie kolumn)
  • -F, --field-separator=ZNAK - dane w kolumnach są oddzielne nowo zdefiniowanym separatorem
  • -R, --record-separator=ZNAK - rekord jest odseparowany przez znak ZNAK, domyślnie jest to znak nowej linii
Przykłady:
∴ /opt/PostgreSQL/9.1/bin/psql -h127.0.0.1 -p7432 -Upostgres --command="select * from test" test
Password for user postgres: 
 c1 |  c2   |             c3             
----+-------+----------------------------
  1 | test  | 2014-01-07 13:30:23.820911
  2 | test2 | 2014-01-07 13:30:29.892732
  3 | test3 | 2014-01-07 13:30:36.644758
  4 | test4 | 2014-01-07 13:30:43.048741
(4 rows)

∴ /opt/PostgreSQL/9.1/bin/psql -h127.0.0.1 -p7432 -Upostgres --command="select * from test" --no-align test
Password for user postgres: 
c1|c2|c3
1|test|2014-01-07 13:30:23.820911
2|test2|2014-01-07 13:30:29.892732
3|test3|2014-01-07 13:30:36.644758
4|test4|2014-01-07 13:30:43.048741
(4 rows)

∴ /opt/PostgreSQL/9.1/bin/psql -h127.0.0.1 -p7432 -Upostgres --command="select * from test" --expanded test
Password for user postgres: 
-[ RECORD 1 ]------------------
c1 | 1
c2 | test
c3 | 2014-01-07 13:30:23.820911
-[ RECORD 2 ]------------------
c1 | 2
c2 | test2
c3 | 2014-01-07 13:30:29.892732
-[ RECORD 3 ]------------------
c1 | 3
c2 | test3
c3 | 2014-01-07 13:30:36.644758
-[ RECORD 4 ]------------------
c1 | 4
c2 | test4
c3 | 2014-01-07 13:30:43.048741

∴ /opt/PostgreSQL/9.1/bin/psql -h127.0.0.1 -p7432 -Upostgres --command="select * from test" --no-align --expanded test
Password for user postgres: 
c1|1
c2|test
c3|2014-01-07 13:30:23.820911

c1|2
c2|test2
c3|2014-01-07 13:30:29.892732

c1|3
c2|test3
c3|2014-01-07 13:30:36.644758

c1|4
c2|test4
c3|2014-01-07 13:30:43.048741

∴ /opt/PostgreSQL/9.1/bin/psql -h127.0.0.1 -p7432 -Upostgres --command="select * from test" --no-align --field-separator=, --tuples-only test
Password for user postgres: 
1,test,2014-01-07 13:30:23.820911
2,test2,2014-01-07 13:30:29.892732
3,test3,2014-01-07 13:30:36.644758
4,test4,2014-01-07 13:30:43.048741


Polecenie COPY

To polecenie ma pewne ograniczenia - tylko superuser kopiować dane do lub z pliku. Jeśli jednak mamy takie prawa, plik który zdefiniujemy dla danych wyjściowych będzie zapisany na maszynie, gdzie działa serwer PostgreSQL.

Jako zwykły użytkownik, możemy tylko kopiować dane do standardowego wyjścia (stdout) lub z standardowego wejścia (stdin).

Opcje, o których warto wspomnieć:
  • FORMAT - TEXT (domyślny), CSV, BINARY
  • DELIMITER - w zależności od formatu, znak oddzielający dane w kolumnach są różne. Dla TEXT jest to znak tabulator-u, CSV - przecinek.
  • NULL - co ma być zwracane gdy pojawi się NULL
  • HEADER - czy zwracać nagłówek
  • ESCAPE - czy i jak escapować dane
O innych szczegółach możecie przeczytać w dokumentacji tutaj.

Przykłady:

postgres@test(localhost) # COPY test (c1,c2,c3) TO '/tmp/test.csv';
COPY 4

14:23:44 ela@localhost:/tmp  ruby-1.9.3-p194 
∴ cat test.csv 
1 test 2014-01-07 13:30:23.820911
2 test2 2014-01-07 13:30:29.892732
3 test3 2014-01-07 13:30:36.644758
4 test4 2014-01-07 13:30:43.048741
Przekierowanie do standardowego wyjścia:
postgres@test(127.0.0.1) # COPY (select * from test) TO stdout WITH (FORMAT CSV, DELIMITER '|', HEADER ON, FORCE_QUOTE (c2));
c1|c2|c3
1|"test"|2014-01-07 13:30:23.820911
2|"test2"|2014-01-07 13:30:29.892732
3|"test3"|2014-01-07 13:30:36.644758
4|"test4"|2014-01-07 13:30:43.048741
postgres@test(127.0.0.1) # COPY (select * from test) TO stdout WITH (FORMAT CSV, DELIMITER '|', HEADER ON, FORCE_QUOTE (c2,c3));
c1|c2|c3
1|"test"|"2014-01-07 13:30:23.820911"
2|"test2"|"2014-01-07 13:30:29.892732"
3|"test3"|"2014-01-07 13:30:36.644758"
4|"test4"|"2014-01-07 13:30:43.048741"
postgres@test(127.0.0.1) # COPY (select * from test) TO stdout WITH (FORMAT CSV, DELIMITER '|', HEADER ON);
c1|c2|c3
1|test|2014-01-07 13:30:23.820911
2|test2|2014-01-07 13:30:29.892732
3|test3|2014-01-07 13:30:36.644758
4|test4|2014-01-07 13:30:43.048741
postgres@test(127.0.0.1) # COPY (select * from test) TO stdout WITH (FORMAT TEXT, DELIMITER '|');
1|test|2014-01-07 13:30:23.820911
2|test2|2014-01-07 13:30:29.892732
3|test3|2014-01-07 13:30:36.644758
4|test4|2014-01-07 13:30:43.048741
postgres@test(127.0.0.1) # COPY (select * from test) TO stdout WITH (FORMAT TEXT);
1 test 2014-01-07 13:30:23.820911
2 test2 2014-01-07 13:30:29.892732
3 test3 2014-01-07 13:30:36.644758
4 test4 2014-01-07 13:30:43.048741

Oczywiście najlepszym rozwiązaniem jest wykonać komendę COPY z polecenia psql i przekierowanie wyniku do pliku:
∴ /opt/PostgreSQL/9.1/bin/psql -h127.0.0.1 -p7432 -Upostgres -c "COPY (select * from test) TO stdout WITH (FORMAT CSV, DELIMITER '|', HEADER ON, FORCE_QUOTE (c2,c3))" test > /tmp/data.csv
Password for user postgres: 

∴ cat /tmp/data.csv
c1|c2|c3
1|"test"|"2014-01-07 13:30:23.820911"
2|"test2"|"2014-01-07 13:30:29.892732"
3|"test3"|"2014-01-07 13:30:36.644758"
4|"test4"|"2014-01-07 13:30:43.048741"


wtorek, 25 lutego 2014

Export danych z MySQL-a


Pytano się mnie niedawno w jaki sposób zapisać wynik zapytania SQL do pliku w bazach danych MySQL. Przedstawię kilka możliwości.


Poniżej wyświetliłam dane z tabeli testowa, która pojawiała się już w moich wcześniejszych postach.
[11:58:33 root@goblin ~] mysql -h 127.0.0.1 -uroot -proot -P3309 -e "select * from test_myisam" ela_db 
+----+--------------+-------------------------------+---------------------+
| c1 | c2           | c3                            | c4                  |
+----+--------------+-------------------------------+---------------------+
|  3 | essss        | 2014-02-17 06:00:46           | 2014-02-17 07:14:29 |
|  1 | essss        | to jest cos innego            | 2014-02-17 06:03:22 |
|  4 | easdfsdfssss | bxcbvx to jest cos innego     | 2014-02-17 06:03:22 |
|  5 | easdfsdfssss | bxcbvx to jest cos innego     | 2014-02-17 07:14:49 |
|  7 | easdfsdfssss | bxcbvx to jest cos innego     | 2014-02-17 06:03:22 |
|  6 | easdfsdfssss | bxcbvx to jest cos innego     | 2014-02-17 07:15:03 |
|  2 | easdfsdfssss | bxcbvx to jest cos innffffego | 2014-02-17 06:03:22 |
|  8 | easdfsdfssss | bxcbvx to jest cos innffffego | 2014-02-17 08:20:08 |
+----+--------------+-------------------------------+---------------------+

Program klienta do połączenia z serwerem - mysql (w tym przypadku v5.5) może exportować dane:
  • wertykalnie (pionowo)
  • html-u
  • xml-u
  • tabeli (domyślnie)

Chciała bym przekierować wynik tego selektu do pliku tak aby móc później obrabiać te dane przy pomocy innych programów.
Jednym z najprostszych sposobów jest wywołanie komendy mysql z linii poleceń, przekierowanie wyniku do pliku i zakończenie połączenia z serwerem. Wykorzystamy do tego celu opcję: -e, --execute.
Problemem może jednak być to, że w wynik jest w formie tabeli, a dla nas ważne są tylko dane w niej zawarte. Skorzystamy w tym przypadku z opcji --skip-column-names i --silent. Pierwszą chyba nie muszę tłumaczyć ( :-) ). Ta ostatnia powoduje, że dane w kolumnach są oddzielone tabulatorami, a każdy wiersz jest pisany w nowej linii:
[12:11:28 root@goblin ~] /usr/local/mysql5.5/bin/mysql -h 127.0.0.1 -uroot -proot -P3309 --skip-column-names -e "select * from test_myisam" --silent ela_db 
3 essss 2014-02-17 06:00:46 2014-02-17 07:14:29
1 essss to jest cos innego 2014-02-17 06:03:22
4 easdfsdfssss bxcbvx to jest cos innego 2014-02-17 06:03:22
5 easdfsdfssss bxcbvx to jest cos innego 2014-02-17 07:14:49
7 easdfsdfssss bxcbvx to jest cos innego 2014-02-17 06:03:22
6 easdfsdfssss bxcbvx to jest cos innego 2014-02-17 07:15:03
2 easdfsdfssss bxcbvx to jest cos innffffego 2014-02-17 06:03:22
8 easdfsdfssss bxcbvx to jest cos innffffego 2014-02-17 08:20:08


Gdy już jednak jesteśmy zalogowani, także możemy przekierować wynik działania zapytania SELECT do pliku. Może nam pomóc w tym 'SELECT ... INTO OUTFILE'. Poważnym utrudnieniem jest jednak to, że utworzony plik został stworzony na maszynie, gdzie działa serwer MySQL.
mysql (root@ela_db)> select * INTO OUTFILE '/tmp/result.csv' from test_myisam;
Query OK, 8 rows affected (0.07 sec)

[12:18:30 root@goblin ~] cd /tmp/
[12:18:33 root@goblin tmp] ll result.csv
-rw-rw-rw- 1 mysql mysql 469 02-24 12:17 result.csv
[12:18:45 iloop@goblin tmp] cat result.csv 
3 essss 2014-02-17 06:00:46 2014-02-17 07:14:29
1 essss to jest cos innego 2014-02-17 06:03:22
4 easdfsdfssss bxcbvx to jest cos innego 2014-02-17 06:03:22
5 easdfsdfssss bxcbvx to jest cos innego 2014-02-17 07:14:49
7 easdfsdfssss bxcbvx to jest cos innego 2014-02-17 06:03:22
6 easdfsdfssss bxcbvx to jest cos innego 2014-02-17 07:15:03
2 easdfsdfssss bxcbvx to jest cos innffffego 2014-02-17 06:03:22
8 easdfsdfssss bxcbvx to jest cos innffffego 2014-02-17 08:20:08


Jest to o tyle elastyczna opcja, że możemy zdefiniować, że wynik ma być plikiem CSV. To my definiujmy jakim znakiem oddzielać dane w kolumnach i nowie wiersze:
mysql (root@ela_db)> select * FROM test_myisam INTO OUTFILE '/tmp/result.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';
Query OK, 8 rows affected (0.00 sec)

[12:34:07 iloop@goblin tmp] cat result.csv 
"3","essss","2014-02-17 06:00:46","2014-02-17 07:14:29"
"1","essss","to jest cos innego","2014-02-17 06:03:22"
"4","easdfsdfssss","bxcbvx to jest cos innego","2014-02-17 06:03:22"
"5","easdfsdfssss","bxcbvx to jest cos innego","2014-02-17 07:14:49"
"7","easdfsdfssss","bxcbvx to jest cos innego","2014-02-17 06:03:22"
"6","easdfsdfssss","bxcbvx to jest cos innego","2014-02-17 07:15:03"
"2","easdfsdfssss","bxcbvx to jest cos innffffego","2014-02-17 06:03:22"
"8","easdfsdfssss","bxcbvx to jest cos innffffego","2014-02-17 08:20:08"

To somo polecenie możemy wykonać także przy pomocy polecenia mysqldump:
[06:38:49 root@goblin ~] /usr/local/mysql5.5/bin/mysqldump -h 127.0.0.1 -uroot -proot -P3309 \
> --no-create-db --no-create-info --skip-opt --tab=/tmp \
> --fields-terminated-by=,  --fields-enclosed-by='"' ela_db test_myisam
Wynik:
[06:39:13 root@goblin tmp] cat test_myisam.*
"3","essss","2014-02-17 06:00:46","2014-02-17 13:14:29"
"1","essss","to jest cos innego","2014-02-17 12:03:22"
"4","easdfsdfssss","bxcbvx to jest cos innego","2014-02-17 12:03:22"
"5","easdfsdfssss","bxcbvx to jest cos innego","2014-02-17 13:14:49"
"7","easdfsdfssss","bxcbvx to jest cos innego","2014-02-17 12:03:22"
"6","easdfsdfssss","bxcbvx to jest cos innego","2014-02-17 13:15:03"
"2","easdfsdfssss","bxcbvx to jest cos innffffego","2014-02-17 12:03:22"
"8","easdfsdfssss","bxcbvx to jest cos innffffego","2014-02-17 14:20:08"

To mysqldump z tymi opcjami wykonuje SELECT z INTO OUTFILE dlatego plik wynikowy można znaleźć na podanej ścieżce na maszynie działającego serwera MySQL.

piątek, 21 lutego 2014

pt-duplicate-key-checker - Sprawdź czy nie masz zduplikowanych indexów

Jako developerzy lub administratorzy baz danych MySQL, każde z nas spotkało się lub słyszało kiedyś o firmą Percona. Zachęcam do przejrzenia ich strony bo możecie znaleźć ciekawe i darmowe webinaria lub tool-e, które mogą okazać się bardzo przydatne. Jednym z takich narzędzi jest perl-owy skrypt do znajdowania zduplikowanych indexów: pt-duplicate-key-checker, link do strony znajdziecie tutaj.

Przyjrzyjmy się tabeli test:
mysql (root@test)> show create table test \G
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `c1` int(11) NOT NULL,
  `c2` varchar(255) DEFAULT NULL,
  `c3` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`c1`),
  KEY `idx_c2` (`c2`),
  KEY `idx_c2_c3` (`c2`,`c3`),
  KEY `idx_c3_c2` (`c3`,`c2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

Przedstawiona tabela ma 'zduplikowany' index idx_c2, bo działa on tak samo jak index idx_c2_c3.

Oprócz podsumowania, skrypt generuje informacje,
  • które index-y są do siebie podobne
  • o kolumnach na których działają indexy
  • zapytanie do usunięcia indexu

Wynik działania skryptu:
∴ bin/pt-duplicate-key-checker -h127.0.0.1 -uroot -proot -P3307 -d test -t test
# ########################################################################
# test.test                                                               
# ########################################################################

# idx_c2 is a left-prefix of idx_c2_c3
# Key definitions:
#   KEY `idx_c2` (`c2`),
#   KEY `idx_c2_c3` (`c2`,`c3`),
# Column types:
#   `c2` varchar(255) default null
#   `c3` timestamp not null default current_timestamp on update current_timestamp
# To remove this duplicate index, execute:
ALTER TABLE `test`.`test` DROP INDEX `idx_c2`;

# ########################################################################
# Summary of indexes                                                      
# ########################################################################

# Size Duplicate Indexes   3072
# Total Duplicate Indexes  1
# Total Indexes            4


wtorek, 18 lutego 2014

Binarki MySQL-a

Po zainstalowaniu MySQL-a głównie korzystamy z 4 plików binarnych:
  • mysql - program klienta do nawiązywania połączenia z serwerem
  • mysqld/mysqld_safe - główny program do uruchomienia serwera
  • mysqldump - export danych z wybranej bazy danych/tabeli

Ale dostępne (w katalogu bin pod katalogiem głównym) są jeszcze inne programy dla celów administracyjnych i użytkowych.

Aby znaleźć katalog główny, zaloguj się na serwer i wyszukaj zmienną basedir:

mysql (root@127.0.0.1:(none))> show variables like 'basedir';
+---------------+---------------------+
| Variable_name | Value               |
+---------------+---------------------+
| basedir       | /usr/local/mysql5.5 |
+---------------+---------------------+
1 row in set (0.08 sec)

W katalogu głównym znajdziecie dodatkowe programy, które mogą się okazać przydatne i które teraz opiszę. Może się okazać, że pod ręką mamy pomocne narzędzia, o których nic nie wiemy:
  • innochecksum - wyświetla informację o checksumie do plików InnoDB. Program ten może być użyty do kontroli przenoszenia/kopiowania plików.
[05:16:46 root@- bin] sudo ./innochecksum -d /home/mysql/mysql5.5/myisam/ela_db/test.ibd
file /home/mysql/mysql5.5/myisam/ela_db/test.ibd = 98304 bytes (6 pages)...
checking pages in range 0 to 5
page 0: log sequence number: first = 2702026452; second = 2702026452
page 0: old style: calculated = 358136944; recorded = 358136944
page 0: new style: calculated = 610648785; recorded = 610648785
page 1: log sequence number: first = 2702022632; second = 2702022632
page 1: old style: calculated = 3782708855; recorded = 3782708855
page 1: new style: calculated = 472948545; recorded = 472948545
page 2: log sequence number: first = 2702026452; second = 2702026452
page 2: old style: calculated = 3864827530; recorded = 3864827530
page 2: new style: calculated = 1336201095; recorded = 1336201095
page 3: log sequence number: first = 2702026452; second = 2702026452
page 3: old style: calculated = 3221959309; recorded = 3221959309
page 3: new style: calculated = 666523661; recorded = 666523661
page 4: log sequence number: first = 0; second = 0
page 4: old style: calculated = 1371122432; recorded = 0
page 4: new style: calculated = 1575996416; recorded = 0
page 5: log sequence number: first = 0; second = 0
page 5: old style: calculated = 1371122432; recorded = 0
page 5: new style: calculated = 1575996416; recorded = 0

  • myisamchk - program obłóguje pliki tabel MyISAM. Może zwrócić informację o statystykach, sprawdzić czy nie ma błędów, naprawić lub zoptymalizować.
[06:11:36 root@nazgul bin] ./myisamchk --description /home/mysql/mysql5.5/myisam/ela_db/*.MYI

MyISAM file:         /home/mysql/mysql5.5/myisam/ela_db/test_myisam.MYI
Record format:       Packed
Character set:       utf8_general_ci (33)
Data records:                    7  Deleted blocks:                 0
Recordlength:                  788

table description:
Key Start Len Index   Type
[05:59:58 root@nazgul bin] ./myisamchk -i /home/mysql/mysql5.5/myisam/ela_db/*.MYI
Checking MyISAM file: /home/mysql/mysql5.5/myisam/ela_db/test_myisam.MYI
Data records:       7   Deleted blocks:       0
myisamchk: warning: 1 client is using or hasn't closed the table properly
- check file-size
- check record delete-chain
- check key delete-chain
- check index reference
- check record links
          
Records:                 7    M.recordlength:       46   Packed:             0%
Recordspace used:       97%   Empty space:           2%  Blocks/Record:   1.00
Record blocks:           7    Delete blocks:         0
Record data:           327    Deleted data:          0
Lost space:             10    Linkdata:             27
MyISAM-table '/home/mysql/mysql5.5/myisam/ela_db/test_myisam.MYI' is usable but should be fixed

User time 0.00, System time 0.00
Maximum resident set size 1320, Integral resident set size 0
Non-physical pagefaults 414, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 2, Involuntary context switches 2

Stworzyłam tabelę service_myisam z 6997 rekordami.
[06:21:08 root@nazgul bin] ./myisamchk -i /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
Checking MyISAM file: /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
Data records:    6997   Deleted blocks:       0
myisamchk: warning: 1 client is using or hasn't closed the table properly
- check file-size
- check record delete-chain
- check key delete-chain
- check index reference
- check record links
          
Records:              6997    M.recordlength:      101   Packed:             0%
Recordspace used:       99%   Empty space:           0%  Blocks/Record:   1.00
Record blocks:        6997    Delete blocks:         0
Record data:        707608    Deleted data:          0
Lost space:           5459    Linkdata:          26233
MyISAM-table '/home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI' is usable but should be fixed

User time 0.00, System time 0.00
Maximum resident set size 2036, Integral resident set size 0
Non-physical pagefaults 596, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 2, Involuntary context switches 6
Na wszystkich rekordach wykonałam update i usunełam część z wierszy: 2 razy po 1000 rekordów. Po wykonaniu tej operacji:
[06:37:11 root@nazgul bin] ./myisamchk -i /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
Checking MyISAM file: /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
Data records:    4997   Deleted blocks:    2000
- check file-size
- check record delete-chain
- check key delete-chain
- check index reference
- check record links
Records:              4997    M.recordlength:       98   Packed:             0%
Recordspace used:       99%   Empty space:          44%  Blocks/Record:   1.00
Record blocks:        4997    Delete blocks:      2000
Record data:        491470    Deleted data:     225228
Lost space:           3918    Linkdata:          18684

User time 0.00, System time 0.00
Maximum resident set size 2028, Integral resident set size 0
Non-physical pagefaults 594, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 2, Involuntary context switches 8
[06:39:38 root@nazgul bin] ll /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
-rw-rw---- 1 mysql mysql 1024 02-17 06:38 /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
Optymalizacja tabeli:
[06:44:23 root@nazgul bin] ./myisamchk --recover /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
- recovering (with keycache) MyISAM-table '/home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI'
Data records: 4997
Sprawdzenie:
[06:47:38 root@nazgul bin] ./myisamchk -i /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
Checking MyISAM file: /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
Data records:    4997   Deleted blocks:       0
- check file-size
- check record delete-chain
- check key delete-chain
- check index reference
- check record links
Records:              4997    M.recordlength:       98   Packed:             0%
Recordspace used:       99%   Empty space:           0%  Blocks/Record:   1.00
Record blocks:        4997    Delete blocks:         0
Record data:        491470    Deleted data:          0
Lost space:           3918    Linkdata:          18684

User time 0.00, System time 0.00
Maximum resident set size 1812, Integral resident set size 0
Non-physical pagefaults 540, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 2, Involuntary context switches 4
Zachęcam do zaznajomienia się ze wszystkimi opcjami. Pozwalaja one wymusić oprtowanie danych po podanym kluczu, sortowanie i analizę indexów.
Ważne abyśmy nasze operacje wykonywanie gdy inne programy nie mają dostepu do tabel, na których pracujemy (np. przy wyłączonym serwerze). Jeśli jednak jest to dla nas zbyt duzy problem, możemy się polegać na poleceniu CHECK TABLE (Odsyłam do dokumentacji tutaj).
  • myisam_ftdump - Wyświetla informację o index-e FULLTEXT w tabelach MyISAM. O to klika przykładów dla tabeli test_myisam:
CREATE TABLE `test_myisam` (
  `c1` int(11) NOT NULL DEFAULT '0',
  `c2` varchar(255) DEFAULT NULL,
  `c3` text,
  `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`c1`),
  FULLTEXT KEY `idx_ft` (`c3`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
W której mamy klika wierszy:
mysql (root@127.0.0.1:ela_db)> select * from test_myisam order by c1 desc;
+------+--------------+-------------------------------+---------------------+
| c1   | c2           | c3                            | c4                  |
+------+--------------+-------------------------------+---------------------+
|    7 | easdfsdfssss | bxcbvx to jest cos innego     | 2014-02-17 06:03:22 |
|    6 | easdfsdfssss | bxcbvx to jest cos innego     | 2014-02-17 07:15:03 |
|    5 | easdfsdfssss | bxcbvx to jest cos innego     | 2014-02-17 07:14:49 |
|    4 | easdfsdfssss | bxcbvx to jest cos innego     | 2014-02-17 06:03:22 |
|    3 | essss        | 2014-02-17 06:00:46           | 2014-02-17 07:14:29 |
|    2 | easdfsdfssss | bxcbvx to jest cos innffffego | 2014-02-17 06:03:22 |
|    1 | essss        | to jest cos innego            | 2014-02-17 06:03:22 |
+------+--------------+-------------------------------+---------------------+
7 rows in set (0.00 sec)
Informacje o index-e (podaję, jako argument, ścieżkę do tabeli bo mam niestandardową ścieżkę do katalogu z danymi. Ostatni argument opowiada numerze index-u - licząc od 0):
[07:03:52 root@nazgul bin] ./myisam_ftdump /home/mysql/mysql5.5/myisam/ela_db/test_myisam 1
Total rows: 7
Total words: 18
Unique words: 5
Longest word: 10 chars (innffffego)
Median length: 6
Average global weight: -0.008164
Most common word: 6 times, weight: -1.791759 (jest)
[07:17:16 root@nazgul bin] ./myisam_ftdump -c /home/mysql/mysql5.5/myisam/ela_db/test_myisam 1
        1            1.7917595 2014
        5           -0.9162907 bxcbvx
        5           -0.9162907 innego
        1            1.7917595 innffffego
        6           -1.7917595 jest
[07:20:06 root@nazgul bin] ./myisam_ftdump -l /home/mysql/mysql5.5/myisam/ela_db/test_myisam 1
  4:          7 38.89%                    7 38.9%
  6:         10 55.56%                   17 94.4%
 10:          1  5.56%                   18 100.0%
[07:20:26 root@nazgul bin] ./myisam_ftdump /home/mysql/mysql5.5/myisam/ela_db/test_myisam 0
Key 0 in table /home/mysql/mysql5.5/myisam/ela_db/test_myisam is not a FULLTEXT key

  • myisamlog -Podgląd logów tabel MyISAM. Wymaga aby serwer działał w trybie debug i aby logi tabel MyISAM były włączone.
  • myisampack - narzędzie do kompresji tabel MyISAM. Kompresuje każdą kolumnę osobno. Jest to efektywne gdy przy selekcie wyciagamy pojedyncze kolumny. Aby zdekompresować tabelę, używamy programu myisamchk z opcją --unpack.
[07:34:01 root@nazgul bin] ./myisampack /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYI
Compressing /home/mysql/mysql5.5/myisam/ela_db/services_myisam.MYD: (4997 records)
- Calculating statistics
- Compressing file
53.19% 

  • my_print_defaults - wyświetla opcje, które są przypisane do grupy. Domyślnie opcje są czytane z następujących plików konfiguracyjnych w tej kolejności:  /etc/mysql/my.cnf, /etc/my.cnf, ~/.my.cnf. Ponieważ mój serwer działa na innych parametrach i czyta z innego pliku, muszę podać do niego ścieżkę:

[06:52:30 root@- bin] my_print_defaults --defaults-file=/etc/my5.5.cnf client
--port=3309
--socket=/tmp/mysql5.5.sock
--default-character-set=utf8
[06:52:59 root@- bin] my_print_defaults --defaults-file=/etc/my5.5.cnf mysqld
--user=mysql
--skip-name-resolve
--innodb_file_per_table
--skip-external-locking
--report-port=3309
--tmpdir=/home/mysql/mysql5.5/mysqltmp
--event_scheduler=0
--pid-file=/home/mysql/mysql5.5/mysql.pid
--socket=/tmp/mysql5.5.sock
--datadir=/home/mysql/mysql5.5/myisam
--port=3309
--server-id=1
--character-set-server=utf8
--back_log=24
--key_buffer=16M
--max_allowed_packet=16M
--table_cache=4096
--sort_buffer=512k
--read_rnd_buffer_size=512k
--read_buffer_size=512k
--thread_cache=256
--thread_concurrency=10
--myisam_sort_buffer_size=8M
--max_connections=50
--max_tmp_tables=4096
--wait_timeout=3600
--query_cache_type=1
--query_cache_size=512M
--auto_increment_offset=1
--auto_increment_increment=2
--log-output=file
--general_log=1
--general_log_file=/home/mysql/mysql5.5/log/mysql.log
--log_error=/home/mysql/mysql5.5/log/mysql.err
--log-warnings=1
--long_query_time=1
--slow_query_log=1
--slow_query_log_file=/home/mysql/mysql5.5/slow.log
--log_queries_not_using_indexes=1
--log-bin=/home/mysql/mysql5.5/replication/binlog
--binlog-format=ROW
--relay-log=/home/mysql/mysql5.5/replication/relay-bin
--innodb_data_home_dir=/home/mysql/mysql5.5/innodb/
--innodb_data_file_path=ibdata/ibdata1:50M:autoextend
--innodb_log_group_home_dir=/home/mysql/mysql5.5/innodb/iblogs
--innodb_flush_log_at_trx_commit=2
--innodb_fast_shutdown
--innodb_lock_wait_timeout=30
--innodb_log_files_in_group=3
--innodb_thread_concurrency=10
--innodb_max_dirty_pages_pct=30
--innodb_autoextend_increment=32


  • mysqlaccess - Sprawdza prawa dostępu dla kombinacji: host, nazwa użytkownika i nazwa bazy danych. Niestety program będzie działać tylko na standardowym porcie 3306.
[07:50:32 root@nazgul bin] ./mysqlaccess --user=root --password=root --host=127.0.0.1 --db=ela_db --brief
mysqlaccess Version 2.06, 20 Dec 2000
By RUG-AIV, by Yves Carlier (Yves.Carlier@rug.ac.be)
Changes by Steve Harvey (sgh@vex.net)
This software comes with ABSOLUTELY NO WARRANTY.
Password for MySQL superuser root: 

Sele Inse Upda Dele Crea Drop Relo Shut Proc File Gran Refe Inde Alte Show Supe Crea Lock Exec Repl Repl Crea Show Crea Alte Crea Even Trig Ssl_ Ssl_ X509 X509 Max_ Max_ Max_ Max_ | Host,User,DB        
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- + --------------------
 Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    ?    ?    ?    ?    0    0    0    0   | 127.0.0.1,root,ANY_NEW_DB

BUGs can be reported by email to bugs@mysql.com

  • mysqladmin - klient dla operacji administracyjnych. Przykładowe polecenia to: tworzenie (create) i usuwanie (drop) baz danych, zabijać działające wątki (kill), zmieniać hasła, sprawdzać status serwera lub czy w ogóle jest dostępny (ping) i inne.
[08:12:22 root@nazgul bin] ./mysqladmin --host=127.0.0.1 -P3309 -uroot -proot ping status processlist
mysqld is alive
Uptime: 3667  Threads: 3  Questions: 46  Slow queries: 12  Opens: 41  Flush tables: 2  Open tables: 2  Queries per second avg: 0.012
+----+-------------+-----------------------+--------+-------------+------+-----------------------------------------------------------------------------+------------------+
| Id | User        | Host                  | db     | Command     | Time | State                                                                       | Info             |
+----+-------------+-----------------------+--------+-------------+------+-----------------------------------------------------------------------------+------------------+
| 1  | system user |                       |        | Connect     | 3665 | Waiting for master to send event                                            |                  |
| 2  | system user |                       |        | Connect     | 3633 | Slave has read all relay log; waiting for the slave I/O thread to update it |                  |
| 3  | replication | 192.168.100.166:39523 |        | Binlog Dump | 3652 | Master has sent all binlog to slave; waiting for binlog to be updated       |                  |
| 8  | root        | 127.0.0.1:35420       | ela_db | Sleep       | 894  |                                                                             |                  |
| 12 | root        | 127.0.0.1:35424       |        | Query       | 0    |                                                                             | show processlist |
+----+-------------+-----------------------+--------+-------------+------+-----------------------------------------------------------------------------+------------------+

  • mysqlbinlog - Pokazuję zawartość binlog-ów. Dla przykładowego polecenia insert:
mysql (root@127.0.0.1:ela_db)> show master status;
+---------------+----------+--------------+------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+---------------+----------+--------------+------------------+
| binlog.000023 |     1336 |              |                  |
+---------------+----------+--------------+------------------+
1 row in set (0.00 sec)

mysql (root@127.0.0.1:ela_db)> insert into test_myisam (c1,c2,c3,c4) values (8,'easdfsdfssss', 'bxcbvx to jest cos innffffego',now());
Query OK, 1 row affected (0.00 sec)

mysql (root@127.0.0.1:ela_db)> show master status;
+---------------+----------+--------------+------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+---------------+----------+--------------+------------------+
| binlog.000023 |     1644 |              |                  |
+---------------+----------+--------------+------------------+
1 row in set (0.00 sec)
Podgląd w binlog-u:
[08:26:57 root@nazgul bin] ./mysqlbinlog --start-position=1336 --base64-output=DECODE-ROWS --verbose /home/mysql/mysql5.5/replication/binlog.000023
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 1336
#140217  8:20:08 server id 1  end_log_pos 1419  Query thread_id=8 exec_time=0 error_code=0
SET TIMESTAMP=1392646808/*!*/;
SET @@session.pseudo_thread_id=8/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=0/*!*/;
SET @@session.auto_increment_increment=2, @@session.auto_increment_offset=1/*!*/;
/*!\C latin1 *//*!*/;
SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=33/*!*/;
SET @@session.time_zone='SYSTEM'/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at 1419
# at 1477
#140217  8:20:08 server id 1  end_log_pos 1477  Table_map: `ela_db`.`test_myisam` mapped to number 35
#140217  8:20:08 server id 1  end_log_pos 1560  Write_rows: table id 35 flags: STMT_END_F
### INSERT INTO ela_db.test_myisam
### SET
###   @1=8
###   @2='easdfsdfssss'
###   @3='bxcbvx to jest cos innffffego'
###   @4=1392646808
# at 1560
#140217  8:20:08 server id 1  end_log_pos 1644  Query thread_id=8 exec_time=0 error_code=0
SET TIMESTAMP=1392646808/*!*/;
COMMIT
/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
  • mysqlcheck - program do utrzymywania serwera MySQL. Sprawdzanie tabel, analiza kluczy, optymalizacja tabel i naprawianie tabel.
  • mysql_client_test - program do testowania API klienta MySQL-a gdzie testy są napisane w specjalnym języku (zobacz dokumentację tutaj).
  • mysql_client_test_embedded - jest tym samym co mysql_client_test ale używany jest do tego serwer osadzony.
  • mysql_config - wyświetla informację o kompilacji klienta i połączeniu jego do MySQL-a
  • mysql_convert_table_format - perl-owy skrypt do konwersji engin-u tabeli.
  • mysqld_multi - uruchomienie kilku serwerów pod różnymi portami i plikami socket. Aby zobaczyć przykładowy plik konfiguracyjny wykonaj polecenie: ./mysqld_multi --example
  • mysqldumpslow - Pasuje i podsumowywuje zapytania, które zostały zapisane w slow logach. Aby wykonać podsumowanie, program podmienia wszystkie liczby na N, a stringi na S. Udostępnia opcje sortowania po np. czasie trwania.
[10:12:32 root@nazgul bin] ./mysqldumpslow -s 'c' /home/mysql/mysql5.5/slow.log | head -n 6

Reading mysql slow query log from /home/mysql/mysql5.5/slow.log
Count: 4  Time=0.00s (0s)  Lock=0.00s (0s)  Rows=7.0 (28), root[root]@[127.0.0.1]
  select * from test_myisam

Count: 3  Time=0.00s (0s)  Lock=0.00s (0s)  Rows=0.0 (0), root[root]@[127.0.0.1]
  update test_myisam set c1 = N where c1 = N limit N

  • mysqlhotcopy - perl-owy skrypt do kopiowania tabeli/bazy danych MyISAM lub Archive. Program lokuje tabele a następnie kopiuje pliki tabel. Kopiowanie może odbywać się tylko w obrębie tej samej maszyny, na której działa główna baza.
  • mysqlimport - program do importu danych z pliku. W podanym przykładzie importuje dane z pliku CSV, gdzie każdy nowy rekord danych to jedna linia pliku. Dane dla kolumn (zdefiniowanych pod opcją --columns) są oddzielone przecinkiem. Zanim proces importu się zacznie, chce aby tabela była wyczyszczona (opcja --delete). Plik CSV znajduje się na maszynie, na której działa serwer MySQL, dlatego używam opcji --local. Chcę aby pierwsza linia była pominięta (bo jest to nagłówek), dlatego dodałam opcję --ignore-lines. Plik CSV, który importuje musi się nazywać (bez rozszerzenia) jak tabelka, do której chce zaimportować dane.
∴ mysqlimport --fields-optionally-enclosed-by='"' --fields-terminated-by=, --lines-terminated-by="\n" -uroot -proot --delete --local --columns=fake_id,comment,ean_code,price,items --ignore-lines=1 reclame_space restore_test.csv

reclame_space.restore_test: Records: 500000  Deleted: 0  Skipped: 0  Warnings: 499999

  • mysql_plugin - program do obsługi plugin-ów. Lista zainstalowanych plugin-ów jest dostępna przy poleceniu SHOW PLUGINS;
  • mysql_secure_installation - prosty skrypt do zmian bezpieczeństwa: zmiana hasła dla root-a, usunięcie anonimowego konta, wyłączenie możliwości zdalnego logowania się przez użytkownika root, usunięcie testowej bazy danych.
  • mysql_setpermission - perl-owy skrypt do wstawiania praw dostępu.
∴ mysql_setpermission --user=root --password=root --host=127.0.0.1 --port=3307
######################################################################
## Welcome to the permission setter 1.4 for MySQL.
## made by Luuk de Boer
######################################################################
What would you like to do:
  1. Set password for an existing user.
  2. Create a database + user privilege for that database
     and host combination (user can only do SELECT)
  3. Create/append user privilege for an existing database
     and host combination (user can only do SELECT)
  4. Create/append broader user privileges for an existing
     database and host combination
     (user can do SELECT,INSERT,UPDATE,DELETE)
  5. Create/append quite extended user privileges for an
     existing database and host combination (user can do
     SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,
     LOCK TABLES,CREATE TEMPORARY TABLES)
  6. Create/append full privileges for an existing database
     and host combination (user has FULL privilege)
  7. Remove all privileges for for an existing database and
     host combination.
     (user will have all permission fields set to N)
  0. exit this program

Make your choice [1,2,3,4,5,6,7,0]: 0
We hope we can help you next time 

  • mysqlshow - program zwraca dla:
    • bazy danych - listę tabel
    • tabeli - opis tabeli z opisami wszystkich kolumn
    • kolumny - jej opis
    • jeśli nie podamy żadnego z powyższych elementów, zwracana jest lista baz danych
[08:04:13 root@nazgul bin] ./mysqlshow --host=127.0.0.1 --port=3309 --user=root -proot ela_db test
Database: ela_db  Table: test
+-------+--------------+-----------------+------+-----+-------------------+-----------------------------+---------------------------------+---------+
| Field | Type         | Collation       | Null | Key | Default           | Extra                       | Privileges                      | Comment |
+-------+--------------+-----------------+------+-----+-------------------+-----------------------------+---------------------------------+---------+
| c1    | int(11)      |                 | YES  |     |                   |                             | select,insert,update,references |         |
| c2    | varchar(255) | utf8_general_ci | YES  |     |                   |                             | select,insert,update,references |         |
| c3    | text         | utf8_general_ci | YES  |     |                   |                             | select,insert,update,references |         |
| c4    | timestamp    |                 | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | select,insert,update,references |         |
+-------+--------------+-----------------+------+-----+-------------------+-----------------------------+---------------------------------+---------+
  • mysqlslap - program do emulacji obciążenia. Np. chce sprawdzić jakie jest obciążenie przy 20 klientów wykonuje zapytanie (w tym przypadku INSERT) po 50 razy
[08:29:14 root@nazgul bin] ./mysqlslap --host=127.0.0.1 -uroot -proot -P3309 \
> --iterations=20 --query="insert into test (c1, c2,c3,c4) values (rand(), 'test ', 'my test', now());" \
> --concurrency=50 --no-drop
Benchmark
 Average number of seconds to run all queries: 0.435 seconds
 Minimum number of seconds to run all queries: 0.008 seconds
 Maximum number of seconds to run all queries: 1.402 seconds
 Number of clients running queries: 50
 Average number of queries per client: 1


  • mysqltest - Program wykołuje test case-y na serwerze MySQL-a. Zazwyczaj wywołuje się ten program przy pomocy perl-owego skryptu, który możecie znaleźć pod BASEDIR/mysql-test/mysql-test-run.pl
  • mysql_tzinfo_to_sql - ładuje tabele time zone. Pliki mogą się znajdować w katalogu: /usr/share/zoneinfo. Program czyta pliki systemowe o strefach czasowych i generuje zapytania SQL.
[10:51:25 ela@nazgul bin] ./mysql_tzinfo_to_sql
Usage:
 ./mysql_tzinfo_to_sql timezonedir
 ./mysql_tzinfo_to_sql timezonefile timezonename
 ./mysql_tzinfo_to_sql --leap timezonefile
[11:09:13 iloop@nazgul bin] ./mysql_tzinfo_to_sql /usr/share/zoneinfo/Poland CES
INSERT INTO time_zone (Use_leap_seconds) VALUES ('N');
SET @time_zone_id= LAST_INSERT_ID();
INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('CES', @time_zone_id);
INSERT INTO time_zone_transition (Time_zone_id, Transition_time, Transition_type_id) VALUES
 (@time_zone_id, -1717032240, 2)
,(@time_zone_id, -1693706400, 1)
,(@time_zone_id, -1680483600, 2)
...
,(@time_zone_id, 2140045200, 9)
;
INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES
 (@time_zone_id, 0, 5040, 0, 'WMT')
,(@time_zone_id, 1, 7200, 1, 'CEST')
,(@time_zone_id, 2, 3600, 0, 'CET')
,(@time_zone_id, 3, 7200, 1, 'CEST')
,(@time_zone_id, 4, 3600, 0, 'CET')
,(@time_zone_id, 5, 10800, 1, 'EEST')
,(@time_zone_id, 6, 7200, 0, 'EET')
,(@time_zone_id, 7, 7200, 0, 'EET')
,(@time_zone_id, 8, 7200, 1, 'CEST')
,(@time_zone_id, 9, 3600, 0, 'CET')
;

  • mysql_upgrade - sprawdza wszystkie tabelę we wszystkich bazach danych czy są kompatybilne z obecną wersją serwera.
  • perror - wyświetla informację o błędzie, którego kod jest podawany jako argument
[07:03:00 root@nazgul bin] ./perror 13
OS error code  13:  Permission denied
[07:03:03 root@nazgul bin] ./perror 1046
MySQL error code 1046 (ER_NO_DB_ERROR): No database selected


piątek, 7 lutego 2014

Triggers w MySQL-u

Jakiś czas temu postanowiłam dowiedzieć się co nieco o trigger-ach. O to co dowiedziałam się o nich w MySQL-u w wersji 5.5.

Trigger jest nazwanym obiektem bazodanowym, przypisanym do tabeli, który aktywuje się przy pewnych zdarzeniach tej tabeli. Tymi zdarzeniami mogą być:
  • wstawienie nowego wiersza (INSERT)
  • zmiana wartości w wierszu (UPDATE)
  • usunięcie wiersza (DELETE)
Obiekt jest aktywowany przed (BEFORE) lub po (AFTER) zdarzeniu.

Jak stworzyć trigger?

CREATE
    [DEFINER = { user | CURRENT_USER }]
    TRIGGER trigger_name
    { BEFORE | AFTER } { INSERT | UPDATE | DELETE }
    ON tbl_name FOR EACH ROW
    [BEGIN]
    trigger_body
    [END]

Co powinniśmy wiedzieć o trigger-ach?
  • nazwy trigger-ów są unikalne
  • w tym samym momencie może istnieć tylko jeden trigger na tej samej tabeli i akcji
  • do wierszy updatowanych lub usuwanych możemy się odwoływać przez alias OLD.column_name. Dla insertów ten alias nie istnieje
  • do nowego wiersza odwołujemy się przez alias NEW
  • wartość dla kolumny AUTO_INCREMENT w aliasie NEW jest 0. Wywołanie sekwencji następuje gdy wiersz faktycznie zostanie wstawiony do tabeli
  • trigger-y nie są aktywowane przez zmiany w widokach lub przez trigger-y. Nie są także aktywowane przez klucze obce (FOREIGN KEY)
  • w ciele trigger-a możemy deklarować zmienne i error handler-y
  • trigger-y są przechowywane w plikach z rozszerzeniem .TRG
  • trigger-y mogą wywoływać procedury
  • trigger-y nie mogą zwracać wartości. Aby 'wyjść' z trigger-a można użyć polecenia LEAVE
  • w ciele trigger-a jest możliwe odwołanie się do innej tabeli. Nie jest możliwe modyfikowanie tabeli, która jest właśnie używana przez wywołanie funkcji lub trigger-a
  • w zależności od ustawionej replikacji, zmiany spowodowane przez trigger mogą być w różny sposób widziane na slave. Przy replikacji typu:
    • statement - trigger, który jest wywołany na masterze, jest także wykonywany na slave przez wywołanie zapytania
    • row - zmiany spowodowane przez wywołanie trigger-a są replikowane na slave
  • aby zmodyfikować istniejący trigger, trzeba go usunąć i stworzyć na nowo
  • nie jest możliwe tworzenie trigger-ów na bazie 'mysql'
Aby pokazać przykładowe działanie triggerów, przygotowałam małą bazę danych z kilkoma tabelami
Tabele:
  • click_logs - przechowuje wpisy na temat kliknięć jakiś stron, które są identyfikowane przez ID (kolumna site_id). Kolumna timestamp mówi o czasie kliknięcia, a ip_address trzyma informację o adresie IP w formie liczbowej. Kolumna info to dodatkowe informacje np. o przeglądarce klienta
  • click_stats - trzyma statystyki kliknięć per strona (site_id) i dzień (stats_date) w kolumnach click_nr (ilość wszystkich kliknięć) i uniq_click_nr (unikalna ilość kliknięć adresów IP)
  • addresses - przechowuje unikalne adresy ip (w formie liczbowej) dla każdej strony i dnia - dla potrzeb kolumny uniq_click_nr w tabeli opisanej powyżej
Stworzyłam na razie 3 triggery dla tabeli click_logs:

DELIMITER $$

CREATE TRIGGER `click_logs_after_insert` AFTER INSERT ON click_logs FOR EACH ROW
BEGIN 
 DECLARE stats_exist INTEGER DEFAULT 0;
        -- Sprawdzam czy istnieją już jakieś statystyki dla tej strony i tego dnia
 SELECT COUNT(*) INTO stats_exist FROM click_stats WHERE site_id = NEW.site_id AND stats_date = date(NEW.timestamp);

 IF stats_exist > 0 THEN 
                -- statystyki już istnieja, dlatego tylko je updatujemy
  update click_stats set click_nr = click_nr + 1 WHERE site_id = NEW.site_id AND stats_date = date(NEW.timestamp); 
 ELSE 
                -- w tym przypadku statystyki nie istnieją dlatego musi stworzyć pierwszy wiersz
  INSERT INTO click_stats (site_id, stats_date, click_nr, uniq_click_nr) VALUES (NEW.site_id, date(NEW.timestamp), 1, 1); 
 END IF; 
END$$

DELIMITER ;

CREATE TRIGGER `click_logs_after_DEL` AFTER DELETE ON click_logs FOR EACH ROW
-- po usunięciu wiersza, zmieniamy także statystyki aby mieć konsystentne dane
UPDATE click_stats SET click_nr = click_nr - 1 WHERE site_id = OLD.site_id AND stats_date = date(OLD.timestamp);

CREATE TRIGGER `click_logs_before_insert` BEFORE INSERT ON click_logs FOR EACH ROW
SET NEW.info = NEW.ip_address;


Listę trigger-ów naszej bazy danych możecie znaleźć pod poleceniem SHOW TRIGGERS;

Przetestujmy działanie tych trigger-ów:

mysql (root@test_triggers)> select * from click_logs;
Empty set (0.00 sec)

mysql (root@test_triggers)> select * from click_stats;
Empty set (0.00 sec)

mysql (root@test_triggers)> select * from addresses;
Empty set (0.00 sec)

mysql (root@test_triggers)> insert into click_logs (site_id, timestamp, ip_address, info) values (1, now(), INET_ATON('127.0.0.1'), 'test');
Query OK, 1 row affected (0.07 sec)

mysql (root@test_triggers)> select * from click_stats;
+----+---------+------------+----------+---------------+
| id | site_id | stats_date | click_nr | uniq_click_nr |
+----+---------+------------+----------+---------------+
|  1 |       1 | 2014-02-04 |        1 |             1 |
+----+---------+------------+----------+---------------+
1 row in set (0.00 sec)

mysql (root@test_triggers)> select * from click_logs;
+----+---------+---------------------+------------+------------+
| id | site_id | timestamp           | ip_address | info       |
+----+---------+---------------------+------------+------------+
|  1 |       1 | 2014-02-04 18:25:32 | 2130706433 | 2130706433 |
+----+---------+---------------------+------------+------------+
1 row in set (0.00 sec)

mysql (root@test_triggers)> select * from addresses;
Empty set (0.00 sec)

mysql (root@test_triggers)> insert into click_logs (site_id, timestamp, ip_address, info) values (1, now(), INET_ATON('127.20.0.1'), 'test');
Query OK, 1 row affected (0.04 sec)

mysql (root@test_triggers)> select * from click_logs;
+----+---------+---------------------+------------+------------+
| id | site_id | timestamp           | ip_address | info       |
+----+---------+---------------------+------------+------------+
|  1 |       1 | 2014-02-04 18:25:32 | 2130706433 | 2130706433 |
|  2 |       1 | 2014-02-04 18:26:23 | 2132017153 | 2132017153 |
+----+---------+---------------------+------------+------------+
2 rows in set (0.00 sec)

mysql (root@test_triggers)> select * from click_stats;
+----+---------+------------+----------+---------------+
| id | site_id | stats_date | click_nr | uniq_click_nr |
+----+---------+------------+----------+---------------+
|  1 |       1 | 2014-02-04 |        2 |             1 |
+----+---------+------------+----------+---------------+
1 row in set (0.00 sec)

mysql (root@test_triggers)> insert into click_logs (site_id, timestamp, ip_address, info) values (2, now(), INET_ATON('127.20.0.1'), 'test');
Query OK, 1 row affected (0.08 sec)

mysql (root@test_triggers)> select * from click_logs;
+----+---------+---------------------+------------+------------+
| id | site_id | timestamp           | ip_address | info       |
+----+---------+---------------------+------------+------------+
|  1 |       1 | 2014-02-04 18:25:32 | 2130706433 | 2130706433 |
|  2 |       1 | 2014-02-04 18:26:23 | 2132017153 | 2132017153 |
|  3 |       2 | 2014-02-04 18:26:45 | 2132017153 | 2132017153 |
+----+---------+---------------------+------------+------------+
3 rows in set (0.00 sec)

mysql (root@test_triggers)> select * from click_stats;
+----+---------+------------+----------+---------------+
| id | site_id | stats_date | click_nr | uniq_click_nr |
+----+---------+------------+----------+---------------+
|  1 |       1 | 2014-02-04 |        2 |             1 |
|  2 |       2 | 2014-02-04 |        1 |             1 |
+----+---------+------------+----------+---------------+
2 rows in set (0.00 sec)

mysql (root@test_triggers)> -- usuwanie
mysql (root@test_triggers)> delete from click_logs where id = 2;
Query OK, 1 row affected (0.03 sec)

mysql (root@test_triggers)> select * from click_logs;
+----+---------+---------------------+------------+------------+
| id | site_id | timestamp           | ip_address | info       |
+----+---------+---------------------+------------+------------+
|  1 |       1 | 2014-02-04 18:25:32 | 2130706433 | 2130706433 |
|  3 |       2 | 2014-02-04 18:26:45 | 2132017153 | 2132017153 |
+----+---------+---------------------+------------+------------+
2 rows in set (0.00 sec)

mysql (root@test_triggers)> select * from click_stats;
+----+---------+------------+----------+---------------+
| id | site_id | stats_date | click_nr | uniq_click_nr |
+----+---------+------------+----------+---------------+
|  1 |       1 | 2014-02-04 |        1 |             1 |
|  2 |       2 | 2014-02-04 |        1 |             1 |
+----+---------+------------+----------+---------------+
2 rows in set (0.00 sec)

Zmieńmy teraz trigger tak aby uwzględniał także statystykę unikalną. Aby to zrobić, musimy najpierw usunąć stary trigger.

mysql (root@test_triggers)> Drop trigger click_logs_after_insert;
Query OK, 0 rows affected (0.04 sec)

Tworzymy nowy trigger:

CREATE TRIGGER `click_logs_after_insert` AFTER INSERT ON click_logs FOR EACH ROW
-- Edit trigger body code below this line. Do not edit lines above this one
BEGIN 
 DECLARE stats_exist INTEGER DEFAULT 0;
 DECLARE address_exist_error INTEGER DEFAULT 0;
 DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET address_exist_error = 1;
 SELECT COUNT(*) INTO stats_exist FROM click_stats WHERE site_id = NEW.site_id AND stats_date = date(NEW.timestamp);

 INSERT INTO addresses (ip_address, site_id, stats_date) values (NEW.ip_address, NEW.site_id, date(NEW.timestamp));

 IF stats_exist > 0 THEN 
  IF address_exist_error = 1 THEN
   update click_stats set click_nr = click_nr + 1 WHERE site_id = NEW.site_id AND stats_date = date(NEW.timestamp); 
  else
   update click_stats set click_nr = click_nr + 1, uniq_click_nr = uniq_click_nr + 1 WHERE site_id = NEW.site_id AND stats_date = date(NEW.timestamp);
  end if;
 ELSE 
  INSERT INTO click_stats (site_id, stats_date, click_nr, uniq_click_nr) VALUES (NEW.site_id, date(NEW.timestamp), 1, 1); 
 END IF; 
END

Tym razem będziemy mieli wpisy w tabeli addresses:

mysql (root@test_triggers)> truncate table addresses;  truncate table click_logs;  truncate table click_stats;
Query OK, 0 rows affected (0.16 sec)

Query OK, 0 rows affected (0.12 sec)

Query OK, 0 rows affected (0.15 sec)

mysql (root@test_triggers)> insert into click_logs (site_id, timestamp, ip_address, info) values (2, now(), INET_ATON('127.20.0.1'), 'test');
Query OK, 1 row affected (0.04 sec)

mysql (root@test_triggers)> select * from click_logs;
+----+---------+---------------------+------------+------------+
| id | site_id | timestamp           | ip_address | info       |
+----+---------+---------------------+------------+------------+
|  1 |       2 | 2014-02-04 19:00:29 | 2132017153 | 2132017153 |
+----+---------+---------------------+------------+------------+
1 row in set (0.00 sec)

mysql (root@test_triggers)> select * from click_stats;
+----+---------+------------+----------+---------------+
| id | site_id | stats_date | click_nr | uniq_click_nr |
+----+---------+------------+----------+---------------+
|  1 |       2 | 2014-02-04 |        1 |             1 |
+----+---------+------------+----------+---------------+
1 row in set (0.00 sec)

mysql (root@test_triggers)> select * from addresses;
+------------+---------+------------+
| ip_address | site_id | stats_date |
+------------+---------+------------+
| 2132017153 |       2 | 2014-02-04 |
+------------+---------+------------+
1 row in set (0.00 sec)

mysql (root@test_triggers)> insert into click_logs (site_id, timestamp, ip_address, info) values (2, now(), INET_ATON('127.20.0.1'), 'test');
Query OK, 1 row affected (0.04 sec)

mysql (root@test_triggers)> select * from click_logs;
+----+---------+---------------------+------------+------------+
| id | site_id | timestamp           | ip_address | info       |
+----+---------+---------------------+------------+------------+
|  1 |       2 | 2014-02-04 19:00:29 | 2132017153 | 2132017153 |
|  2 |       2 | 2014-02-04 19:03:16 | 2132017153 | 2132017153 |
+----+---------+---------------------+------------+------------+
2 rows in set (0.00 sec)

mysql (root@test_triggers)> select * from click_stats;
+----+---------+------------+----------+---------------+
| id | site_id | stats_date | click_nr | uniq_click_nr |
+----+---------+------------+----------+---------------+
|  1 |       2 | 2014-02-04 |        2 |             1 |
+----+---------+------------+----------+---------------+
1 row in set (0.00 sec)

mysql (root@test_triggers)> select * from addresses;
+------------+---------+------------+
| ip_address | site_id | stats_date |
+------------+---------+------------+
| 2132017153 |       2 | 2014-02-04 |
+------------+---------+------------+
1 row in set (0.00 sec)


Dla statystyk unikalnych możemy mieć problem przy usuwaniu danych tak jak chcieliśmy to zrobić dla zwykłych kliknięć. Nie będę jednak się tym przejmować, bo jest to tylko przykład pokazujący jak tworzyć trigger-y.

Pamiętajmy, że trigger-y mogą spowolnić wykonywanie tylko podstawowych operacji (INSERT, UPDATE, DELETE) albo spowodować bałagan. Dlatego twórzmy je z rozwagą.

sobota, 1 lutego 2014

Status replikacji w MySQL v5.5

Nie wiem czy czytaliście mój niedawny post na temat stawiania replikacji master-master w MySQL-u. Możecie go znaleźć tutaj. Postanowiłam sprawdzić co się stanie, gdy jedna z maszyn, na których stoi serwer padnie, a przy okazji opisać, co możemy się dowiedzieć na temat wyniku zwracanego przez polecenie SHOW SLAVE STATUS lub SHOW MASTER STATUS.

Zaczniemy od momentu gdy serwer ID 1 (192.168.100.166) nie działa, a na serwerze ID 2 (192.168.100.167) zaimportowałam dane z dump-a. Zobaczymy co się stanie gdy serwer ID 1 ponownie zacznie działać.

Przy pomocy polecenia SHOW SLAVE STATUS widzimy, że połączenie do jego mastera nie może zostać nawiązane:

mysql (root@ela_db)> show slave status \G
*************************** 1. row ***************************
               Slave_IO_State: Connecting to master
                  Master_Host: 192.168.100.166
                  Master_User: replication
                  Master_Port: 3309
                Connect_Retry: 60
              Master_Log_File: binlog.000005
          Read_Master_Log_Pos: 1171
               Relay_Log_File: relay-bin.000002
                Relay_Log_Pos: 1314
        Relay_Master_Log_File: binlog.000005
             Slave_IO_Running: Connecting
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 1171
              Relay_Log_Space: 1464
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 2003
                Last_IO_Error: error connecting to master 'replication@192.168.100.166:3309' - retry-time: 60  retries: 86400
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 2
1 row in set (0.02 sec)

Po podniesieniu serwera, replikacja sama zaczęła nawiązywać połączenie:

140124 05:49:02 mysqld_safe Starting mysqld daemon with databases from /home/mysql/mysql5.5/myisam
140124  5:49:02 [Note] Plugin 'FEDERATED' is disabled.
140124  5:49:02 InnoDB: The InnoDB memory heap is disabled
140124  5:49:02 InnoDB: Mutexes and rw_locks use GCC atomic builtins
140124  5:49:02 InnoDB: Compressed tables use zlib 1.2.3
140124  5:49:02 InnoDB: Using Linux native AIO
140124  5:49:02 InnoDB: Initializing buffer pool, size = 128.0M
140124  5:49:02 InnoDB: Completed initialization of buffer pool
140124  5:49:02 InnoDB: highest supported file format is Barracuda.
InnoDB: The log sequence number in ibdata files does not match
InnoDB: the log sequence number in the ib_logfiles!
140124  5:49:02  InnoDB: Database was not shut down normally!
InnoDB: Starting crash recovery.
InnoDB: Reading tablespace information from the .ibd files...
InnoDB: Restoring possible half-written data pages from the doublewrite
InnoDB: buffer...
140124  5:49:02  InnoDB: Waiting for the background threads to start
140124  5:49:03 InnoDB: 1.1.8 started; log sequence number 1601052
140124  5:49:03 [Note] Recovering after a crash using /home/mysql/mysql5.5/replication/binlog
140124  5:49:03 [Note] Starting crash recovery...
140124  5:49:03 [Note] Crash recovery finished.
140124  5:49:04 [Note] Slave SQL thread initialized, starting replication in log 'binlog.000003' at position 502, relay log '/home/mysql/mysql5.5/replication/relay-bin.000002' position: 250
140124  5:49:04 [Note] Event Scheduler: Loaded 0 events
140124  5:49:04 [Note] /usr/local/mysql5.5/bin/mysqld: ready for connections.
Version: '5.5.23-log'  socket: '/tmp/mysql5.5.sock'  port: 3309  MySQL Community Server (GPL)
140124  5:49:04 [Note] Slave I/O thread: connected to master 'replication@192.168.100.167:3309',replication started in log 'binlog.000003' at position 502
140124  5:49:07 [Note] Start binlog_dump to slave_server(1), pos(binlog.000005, 1171)


Na serwerze, który został podniesiony, zaczęły napływać dane:

Gdzie:
  • Master_Log_File - nazwa binlog-u mastera, która jest aktualnie czytana przez slave
  • Read_Master_Log_Pos - pozycja z binlog-u mastera, która jest aktualnie czytana
  • Relay_Log_File - nazwa aktualnego  relay log-u
  • Relay_Log_Pos - aktualna pozycja z relay log-u (wszystkie zamiany sprzed tej pozycji, zostały już wykonane w bazach slave)
  • Relay_Master_Log_File - nazwa binlog-u mastera, gdzie zmiana z relay logu była czytana
  • Exec_Master_Log_Pos - pozycja z binlog-u mastera, która właśnie została wykonana
  • Seconds_Behind_Master - ile sekund slave jest do tyłu w stosunku do mastera


Gdy już wszystkie dane zostały wyrównane, chcieli byśmy usunąć zbędne binlog-i. Może nam w tym pomóc polecenia: PURGE BINARY LOGS, SHOW BINARY LOGS oraz zmienne serwerowe.

Zmienne expire_logs_days, mówi po ilu dniach binary log będzie usuwany. Jeśli wartość zmiennej ustawiona jest na 0, mechanizm usuwania jest wyłączony i wymagane jest usuwanie binary logów ręcznie.

mysql (root@ela_db)> show variables like 'expire_logs_days';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| expire_logs_days | 0     |
+------------------+-------+
1 row in set (0.00 sec)

Polecenie SHOW BINARY LOGS zwraca listę binary logów z ich rozmiarem (maksymalny rozmiar jest ustawiany przez zmienną max_binlog_size), a przy pomocy PURGE BINARY LOGS (dokumentacja: http://dev.mysql.com/doc/refman/5.5/en/purge-binary-logs.html) możemy usunąć stare pliki:

mysql (root@ela_db)> show binary logs;
+---------------+------------+
| Log_name      | File_size  |
+---------------+------------+
| binlog.000001 |      26372 |
| binlog.000002 |     938077 |
| binlog.000003 | 1073879253 |
| binlog.000004 | 1074045928 |
| binlog.000005 | 1073749134 |
| binlog.000006 |  725494868 |
+---------------+------------+
6 rows in set (0.00 sec)

mysql (root@ela_db)> PURGE BINARY LOGS to 'binlog.000006';
Query OK, 0 rows affected (0.44 sec)

mysql (root@ela_db)> show binary logs;
+---------------+-----------+
| Log_name      | File_size |
+---------------+-----------+
| binlog.000006 | 725494868 |
+---------------+-----------+
1 row in set (0.00 sec)

Jeśli chodzi o relay logi w zależności od ustawień serwera, mogą one być np automatycznie usuwane gdy są już nie używane:

mysql (root@(none))> show variables like '%relay%';
+-----------------------+--------------------------------------------+
| Variable_name         | Value                                      |
+-----------------------+--------------------------------------------+
| max_relay_log_size    | 0                                          |
| relay_log             | /home/mysql/mysql5.5/replication/relay-bin |
| relay_log_index       |                                            |
| relay_log_info_file   | relay-log.info                             |
| relay_log_purge       | ON                                         |
| relay_log_recovery    | OFF                                        |
| relay_log_space_limit | 0                                          |
| sync_relay_log        | 0                                          |
| sync_relay_log_info   | 0                                          |
+-----------------------+--------------------------------------------+
9 rows in set (0.82 sec)

  • relay_log_purge - włączone lub wyłączone automatyczne usuwanie relay logu gdy już będzie niepotrzebny
  • relay_log_recovery - gdy włączony, przy starcie serwera relay log jest automatycznie 'pobierany' z serwera mastera dla plików jeszcze niesprocesowanych
  • max_relay_log_size - maksymalny rozmiar relay log plików. Jeśli jest on ustawiony na 0, rozmiar jest definiowany przez zmienną max_binlog_size
  • relay_log - ścieżka pod którą możemy znaleźć pliki
  • relay_log_index - nazwa używana dla pliku indexu relay logów
  • relay_log_info_file - nazwa pliku w którym slave zapisyje informacje o relay logach. 
  • relay_log_space_limit - całkowity limit rozmiaru w bajtach wszystkich relay logów na slave. Jeśli jest ustawione na 0 - oznacza, że nie ma limitu. Dobrze jest ustawić na dostępną powierzchnię dyskową, bo gdy limit zostanie przekroczony, wątek I/O przestanie czytać zdarzenia z binary logi z mastera, dopóki SQL wątek nie dogoni i nie usunie jeszcze niesprocesowanych relay logów. 


I jeszcze kilka poleceń

Aby zobaczyć listę slave-ów możemy użyć polecenia:
mysql (root@ela_db)> show slave hosts;
+-----------+------+------+-----------+
| Server_id | Host | Port | Master_id |
+-----------+------+------+-----------+
|         1 |      | 3309 |         2 |
+-----------+------+------+-----------+
1 row in set (0.00 sec)

Podglądanie zmian w binlogach (polecenie show binlog events):
mysql (root@ela_db)> show binlog events in 'binlog.000008' from 257826769;
+---------------+-----------+-------------+-----------+-------------+--------------------------------+
| Log_name      | Pos       | Event_type  | Server_id | End_log_pos | Info                           |
+---------------+-----------+-------------+-----------+-------------+--------------------------------+
| binlog.000008 | 257826769 | Query       |         2 |   257826852 | BEGIN                          |
| binlog.000008 | 257826852 | Table_map   |         2 |   257826934 | table_id: 52 (ela_db.test)     |
| binlog.000008 | 257826934 | Update_rows |         2 |   257827880 | table_id: 52                   |
| binlog.000008 | 257827880 | Update_rows |         2 |   257828522 | table_id: 52 flags: STMT_END_F |
| binlog.000008 | 257828522 | Xid         |         2 |   257828549 | COMMIT /* xid=5097300 */       |
+---------------+-----------+-------------+-----------+-------------+--------------------------------+
5 rows in set (0.00 sec)

Podgląd relay logów:
mysql (root@ela_db)> SHOW RELAYLOG EVENTS IN 'relay-bin.000034' from 250 limit 10;
+------------------+------+-------------+-----------+-------------+----------------------------+
| Log_name         | Pos  | Event_type  | Server_id | End_log_pos | Info                       |
+------------------+------+-------------+-----------+-------------+----------------------------+
| relay-bin.000034 |  250 | Query       |         1 |   163761726 | BEGIN                      |
| relay-bin.000034 |  333 | Table_map   |         1 |   163761808 | table_id: 93 (ela_db.test) |
| relay-bin.000034 |  415 | Update_rows |         1 |   163762754 | table_id: 93               |
| relay-bin.000034 | 1361 | Update_rows |         1 |   163763700 | table_id: 93               |
| relay-bin.000034 | 2307 | Update_rows |         1 |   163764646 | table_id: 93               |
| relay-bin.000034 | 3253 | Update_rows |         1 |   163765592 | table_id: 93               |
| relay-bin.000034 | 4199 | Update_rows |         1 |   163766538 | table_id: 93               |
| relay-bin.000034 | 5145 | Update_rows |         1 |   163767484 | table_id: 93               |
| relay-bin.000034 | 6091 | Update_rows |         1 |   163768430 | table_id: 93               |
| relay-bin.000034 | 7037 | Update_rows |         1 |   163769376 | table_id: 93               |
+------------------+------+-------------+-----------+-------------+----------------------------+
10 rows in set (0.00 sec)


To tak szybko o replikacji. Jeśli pewne rzeczy były nie jasne, odsyłam do posta wprowadzającego: Instalacja servera i replikacji master-master w MySQL v5.5. Jeśli macie jakieś pytania to komentujcie.