Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Hybrydowe obciążenia baz danych OLTP/Analytics:replikacja danych MySQL do ClickHouse

Jak uruchomić Analytics w MySQL?

MySQL to świetna baza danych dla obciążeń przetwarzania transakcji online (OLTP). Dla niektórych firm przez długi czas było to więcej niż wystarczające. Czasy się zmieniły, a wraz z nimi wymagania biznesowe. Ponieważ firmy aspirują do tego, aby być bardziej opartym na danych, coraz więcej danych jest przechowywanych do dalszej analizy; zachowanie klientów, wzorce wydajności, ruch sieciowy, logi itp. Bez względu na branżę, w której działasz, jest bardzo prawdopodobne, że istnieją dane, które chcesz zachować i przeanalizować, aby lepiej zrozumieć, co się dzieje i jak ulepszyć swój biznes. Niestety, do przechowywania i odpytywania dużej ilości danych MySQL nie jest najlepszą opcją. Jasne, może to zrobić i ma narzędzia, które pomagają pomieścić duże ilości danych (np. kompresja InnoDB), ale użycie dedykowanego rozwiązania do przetwarzania analizy online (OLAP) najprawdopodobniej znacznie poprawi Twoją zdolność do przechowywania i wysyłania zapytań o dużą ilość danych.

Jednym ze sposobów rozwiązania tego problemu będzie wykorzystanie dedykowanej bazy danych do prowadzenia analiz. Zazwyczaj do takich zadań chcesz używać kolumnowego magazynu danych — są one bardziej odpowiednie do obsługi dużych ilości danych:dane przechowywane w kolumnach zwykle są łatwiejsze do skompresowania, a także łatwiejszy dostęp do poszczególnych kolumn — zazwyczaj prosisz o dane przechowywane w kilku kolumnach - możliwość pobrania tylko tych kolumn zamiast odczytywania wszystkich wierszy i odfiltrowywania niepotrzebnych danych przyspiesza dostęp do danych.

Jak zreplikować dane z MySQL do ClickHouse?

Przykładem kolumnowego magazynu danych, który nadaje się do analizy, jest ClickHouse, magazyn kolumnowy typu open source. Jednym z wyzwań jest zapewnienie synchronizacji danych w ClickHouse z danymi w MySQL. Oczywiście, zawsze można skonfigurować pewnego rodzaju potok danych i wykonać automatyczne ładowanie wsadowe do ClickHouse. Ale dopóki możesz żyć z pewnymi ograniczeniami, istnieje lepszy sposób na skonfigurowanie replikacji MySQL w czasie rzeczywistym do ClickHouse. W tym poście na blogu przyjrzymy się, jak można to zrobić.

Instalacja ClickHouse

Przede wszystkim musimy zainstalować ClickHouse. Skorzystamy z szybkiego startu ze strony ClickHouse.

sudo apt-get install dirmngr    # optional
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv E0C56BD4    # optional

echo "deb http://repo.yandex.ru/clickhouse/deb/stable/ main/" | sudo tee /etc/apt/sources.list.d/clickhouse.list
sudo apt-get update
sudo apt-get install -y clickhouse-server clickhouse-client
sudo service clickhouse-server start

Gdy to zrobimy, musimy znaleźć sposób na przeniesienie danych z MySQL do ClickHouse. Jednym z możliwych rozwiązań jest użycie clickhouse-mysql-data-reader firmy Altinity. Przede wszystkim musimy zainstalować pip3 (python3-pip w Ubuntu) ponieważ wymagany jest Python w wersji co najmniej 3.4. Następnie możemy użyć pip3 do zainstalowania niektórych wymaganych modułów Pythona:

pip3 install mysqlclient
pip3 install mysql-replication
pip3 install clickhouse-driver

Gdy to zrobimy, musimy sklonować repozytorium. Dla Centosa 7 dostępne są również RPM-y, można je również zainstalować za pomocą pip3 (pakiet clickhouse-mysql), ale okazało się, że wersja dostępna przez pip nie zawiera najnowszych aktualizacji i chcemy użyć gałęzi master z repozytorium git:

git clone https://github.com/Altinity/clickhouse-mysql-data-reader

Następnie możemy go zainstalować za pomocą pip:

pip3 install -e /path/to/clickhouse-mysql-data-reader/

Następnym krokiem będzie utworzenie użytkowników MySQL wymaganych przez clickhouse-mysql-data-reader w celu uzyskania dostępu do danych MySQL:

mysql> CREATE USER 'chreader'@'%' IDENTIFIED BY 'pass';
Query OK, 0 rows affected (0.02 sec)
mysql> CREATE USER 'chreader'@'127.0.0.1' IDENTIFIED BY 'pass';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE USER 'chreader'@'localhost' IDENTIFIED BY 'pass';
Query OK, 0 rows affected (0.02 sec)
mysql> GRANT SELECT, REPLICATION CLIENT, REPLICATION SLAVE, SUPER ON *.* TO 'chreader'@'%';
Query OK, 0 rows affected (0.01 sec)
mysql> GRANT SELECT, REPLICATION CLIENT, REPLICATION SLAVE, SUPER ON *.* TO 'chreader'@'127.0.0.1';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT SELECT, REPLICATION CLIENT, REPLICATION SLAVE, SUPER ON *.* TO 'chreader'@'localhost';
Query OK, 0 rows affected, 1 warning (0.01 sec)

Powinieneś również sprawdzić konfigurację MySQL, aby upewnić się, że masz włączone logi binarne, max_binlog_size jest ustawione na 768M, binlogi są w formacie „wierszowym” i że narzędzie może łączyć się z MySQL. Poniżej znajduje się fragment dokumentacji:

[mysqld]
# mandatory
server-id        = 1
log_bin          = /var/lib/mysql/bin.log
binlog-format    = row # very important if you want to receive write, update and delete row events
# optional
expire_logs_days = 30
max_binlog_size  = 768M
# setup listen address
bind-address     = 0.0.0.0

Importowanie danych

Gdy wszystko będzie gotowe, możesz zaimportować dane do ClickHouse. Idealnie byłoby uruchomić import na hoście z zablokowanymi tabelami, aby podczas procesu nie nastąpiły żadne zmiany. Możesz użyć niewolnika jako źródła danych. Polecenie do uruchomienia to:

clickhouse-mysql --src-server-id=1 --src-wait --nice-pause=1 --src-host=10.0.0.142 --src-user=chreader --src-password=pass --src-tables=wiki.pageviews --dst-host=127.0.0.1 --dst-create-table --migrate-table

Połączy się z MySQL na hoście 10.0.0.142 przy użyciu podanych poświadczeń, skopiuje tabelę „odsłony” ze schematu „wiki” do ClickHouse działającego na hoście lokalnym (127.0.0.1). Tabela zostanie utworzona automatycznie, a dane zostaną przeniesione.

Na potrzeby tego bloga zaimportowaliśmy około 50 milionów wierszy ze zbioru danych „odsłony” udostępnionego przez Wikimedia Foundation. Schemat tabeli w MySQL to:

mysql> SHOW CREATE TABLE wiki.pageviews\G
*************************** 1. row ***************************
       Table: pageviews
Create Table: CREATE TABLE `pageviews` (
  `date` date NOT NULL,
  `hour` tinyint(4) NOT NULL,
  `code` varbinary(255) NOT NULL,
  `title` varbinary(1000) NOT NULL,
  `monthly` bigint(20) DEFAULT NULL,
  `hourly` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`date`,`hour`,`code`,`title`)
) ENGINE=InnoDB DEFAULT CHARSET=binary
1 row in set (0.00 sec)

Narzędzie przetłumaczyło to na następujący schemat ClickHouse:

vagrant.vm :) SHOW CREATE TABLE wiki.pageviews\G

SHOW CREATE TABLE wiki.pageviews

Row 1:
──────
statement: CREATE TABLE wiki.pageviews ( date Date,  hour Int8,  code String,  title String,  monthly Nullable(Int64),  hourly Nullable(Int64)) ENGINE = MergeTree(date, (date, hour, code, title), 8192)

1 rows in set. Elapsed: 0.060 sec.

Po zakończeniu importu możemy porównać zawartość MySQL:

mysql> SELECT COUNT(*) FROM wiki.pageviews\G
*************************** 1. row ***************************
COUNT(*): 50986914
1 row in set (24.56 sec)

oraz w ClickHouse:

vagrant.vm :) SELECT COUNT(*) FROM wiki.pageviews\G

SELECT COUNT(*)
FROM wiki.pageviews

Row 1:
──────
COUNT(): 50986914

1 rows in set. Elapsed: 0.014 sec. Processed 50.99 million rows, 50.99 MB (3.60 billion rows/s., 3.60 GB/s.)

Nawet w tak małej tabeli wyraźnie widać, że MySQL potrzebował więcej czasu na jego skanowanie niż ClickHouse.

Rozpoczynając proces przeglądania dziennika binarnego pod kątem zdarzeń, najlepiej byłoby przekazać informacje o pliku dziennika binarnego i pozycji, od której narzędzie powinno zacząć nasłuchiwać. Możesz to łatwo sprawdzić na urządzeniu podrzędnym po zakończeniu początkowego importu.

clickhouse-mysql --src-server-id=1 --src-resume --src-binlog-file='binlog.000016' --src-binlog-position=194 --src-wait --nice-pause=1 --src-host=10.0.0.142 --src-user=chreader --src-password=pass --src-tables=wiki.pageviews --dst-host=127.0.0.1 --pump-data --csvpool

Jeśli go nie zdasz, po prostu zacznie nasłuchiwać wszystkiego, co się pojawi:

clickhouse-mysql --src-server-id=1 --src-resume --src-wait --nice-pause=1 --src-host=10.0.0.142 --src-user=chreader --src-password=pass --src-tables=wiki.pageviews --dst-host=127.0.0.1 --pump-data --csvpool

Załadujmy więcej danych i zobaczmy, jak to dla nas wyjdzie. Możemy zobaczyć, że wszystko wydaje się w porządku, patrząc na logi clickhouse-mysql-data-reader:

2019-02-11 15:21:29,705/1549898489.705732:INFO:['wiki.pageviews']
2019-02-11 15:21:29,706/1549898489.706199:DEBUG:class:<class 'clickhouse_mysql.writer.poolwriter.PoolWriter'> insert
2019-02-11 15:21:29,706/1549898489.706682:DEBUG:Next event binlog pos: binlog.000016.42066434
2019-02-11 15:21:29,707/1549898489.707067:DEBUG:WriteRowsEvent #224892 rows: 1
2019-02-11 15:21:29,707/1549898489.707483:INFO:['wiki.pageviews']
2019-02-11 15:21:29,707/1549898489.707899:DEBUG:class:<class 'clickhouse_mysql.writer.poolwriter.PoolWriter'> insert
2019-02-11 15:21:29,708/1549898489.708083:DEBUG:Next event binlog pos: binlog.000016.42066595
2019-02-11 15:21:29,708/1549898489.708659:DEBUG:WriteRowsEvent #224893 rows: 1

Musimy pamiętać o ograniczeniach narzędzia. Największą z nich jest to, że obsługuje tylko WKŁADKI. Nie ma obsługi DELETE ani UPDATE. Nie ma również obsługi DDL, dlatego wszelkie niezgodne zmiany schematu wykonane na MySQL spowodują przerwanie replikacji MySQL do ClickHouse.

Warto również zwrócić uwagę na fakt, że twórcy skryptu zalecają wykorzystanie pypy w celu poprawy wydajności narzędzia. Przejdźmy przez kilka kroków wymaganych do skonfigurowania tego.

Najpierw musisz pobrać i rozpakować pypy:

wget https://bitbucket.org/squeaky/portable-pypy/downloads/pypy3.5-7.0.0-linux_x86_64-portable.tar.bz2
tar jxf pypy3.5-7.0.0-linux_x86_64-portable.tar.bz2
cd pypy3.5-7.0.0-linux_x86_64-portable

Następnie musimy zainstalować pip i wszystkie wymagania dla clickhouse-mysql-data-reader - dokładnie to samo, co omówiliśmy wcześniej, opisując zwykłą konfigurację:

./bin/pypy -m ensurepip
./bin/pip3 install mysql-replication
./bin/pip3 install clickhouse-driver
./bin/pip3 install mysqlclient

Ostatnim krokiem będzie zainstalowanie clickhouse-mysql-data-reader z repozytorium github (zakładamy, że został już sklonowany):

./bin/pip3 install -e /path/to/clickhouse-mysql-data-reader/

To wszystko. Od teraz powinieneś uruchamiać wszystkie polecenia używając środowiska stworzonego dla pypy:

./bin/pypy ./bin/clickhouse-mysql

Testy

Dane zostały załadowane, możemy sprawdzić, czy wszystko poszło gładko, porównując rozmiar tabeli:

MySQL:

mysql> SELECT COUNT(*) FROM wiki.pageviews\G
*************************** 1. row ***************************
COUNT(*): 204899465
1 row in set (1 min 40.12 sec)

ClickHouse:

vagrant.vm :) SELECT COUNT(*) FROM wiki.pageviews\G

SELECT COUNT(*)
FROM wiki.pageviews

Row 1:
──────
COUNT(): 204899465

1 rows in set. Elapsed: 0.100 sec. Processed 204.90 million rows, 204.90 MB (2.04 billion rows/s., 2.04 GB/s.)

Wszystko wygląda poprawnie. Przeprowadźmy kilka zapytań, aby zobaczyć, jak zachowuje się ClickHouse. Pamiętaj, że cała ta konfiguracja jest daleka od jakości produkcyjnej. Użyliśmy dwóch małych maszyn wirtualnych, 4 GB pamięci, po jednym vCPU każdy. Dlatego nawet jeśli zestaw danych nie był duży, wystarczyło, aby zobaczyć różnicę. Ze względu na małą próbkę dość trudno jest przeprowadzić „prawdziwą” analizę, ale nadal możemy rzucać losowe zapytania.

Sprawdźmy, z jakich dni tygodnia mamy dane i ile stron dziennie oglądaliśmy w naszych przykładowych danych:

vagrant.vm :) SELECT count(*), toDayOfWeek(date) AS day FROM wiki.pageviews GROUP BY day ORDER BY day ASC;

SELECT
    count(*),
    toDayOfWeek(date) AS day
FROM wiki.pageviews
GROUP BY day
ORDER BY day ASC

┌───count()─┬─day─┐
│  50986896 │   2 │
│ 153912569 │   3 │
└───────────┴─────┘

2 rows in set. Elapsed: 2.457 sec. Processed 204.90 million rows, 409.80 MB (83.41 million rows/s., 166.82 MB/s.)

W przypadku MySQL to zapytanie wygląda jak poniżej:

mysql> SELECT COUNT(*), DAYOFWEEK(date) AS day FROM wiki.pageviews GROUP BY day ORDER BY day;
+-----------+------+
| COUNT(*)  | day  |
+-----------+------+
|  50986896 |    3 |
| 153912569 |    4 |
+-----------+------+
2 rows in set (3 min 35.88 sec)

Jak widać, MySQL potrzebował 3,5 minuty na wykonanie pełnego skanowania tabeli.

Zobaczmy teraz, ile stron ma miesięczną wartość większą niż 100:

vagrant.vm :) SELECT count(*), toDayOfWeek(date) AS day FROM wiki.pageviews WHERE  monthly > 100 GROUP BY day;

SELECT
    count(*),
    toDayOfWeek(date) AS day
FROM wiki.pageviews
WHERE monthly > 100
GROUP BY day

┌─count()─┬─day─┐
│   83574 │   2 │
│  246237 │   3 │
└─────────┴─────┘

2 rows in set. Elapsed: 1.362 sec. Processed 204.90 million rows, 1.84 GB (150.41 million rows/s., 1.35 GB/s.)

W przypadku MySQL to znowu 3,5 minuty:

mysql> SELECT COUNT(*), DAYOFWEEK(date) AS day FROM wiki.pageviews WHERE YEAR(date) = 2018 AND monthly > 100 GROUP BY day;
^@^@+----------+------+
| COUNT(*) | day  |
+----------+------+
|    83574 |    3 |
|   246237 |    4 |
+----------+------+
2 rows in set (3 min 3.48 sec)

Kolejne zapytanie, tylko wyszukiwanie oparte na niektórych wartościach ciągu:

vagrant.vm :) select * from wiki.pageviews where title LIKE 'Main_Page' AND code LIKE 'de.m' AND hour=6;

SELECT *
FROM wiki.pageviews
WHERE (title LIKE 'Main_Page') AND (code LIKE 'de.m') AND (hour = 6)

┌───────date─┬─hour─┬─code─┬─title─────┬─monthly─┬─hourly─┐
│ 2018-05-01 │    6 │ de.m │ Main_Page │       8 │      0 │
└────────────┴──────┴──────┴───────────┴─────────┴────────┘
┌───────date─┬─hour─┬─code─┬─title─────┬─monthly─┬─hourly─┐
│ 2018-05-02 │    6 │ de.m │ Main_Page │      17 │      0 │
└────────────┴──────┴──────┴───────────┴─────────┴────────┘

2 rows in set. Elapsed: 0.015 sec. Processed 66.70 thousand rows, 4.20 MB (4.48 million rows/s., 281.53 MB/s.)

Kolejne zapytanie, wykonujące kilka wyszukiwań w ciągu i warunek oparty na kolumnie „miesięcznie”:

vagrant.vm :) select title from wiki.pageviews where title LIKE 'United%Nations%' AND code LIKE 'en.m' AND monthly>100 group by title;

SELECT title
FROM wiki.pageviews
WHERE (title LIKE 'United%Nations%') AND (code LIKE 'en.m') AND (monthly > 100)
GROUP BY title

┌─title───────────────────────────┐
│ United_Nations                  │
│ United_Nations_Security_Council │
└─────────────────────────────────┘

2 rows in set. Elapsed: 0.083 sec. Processed 1.61 million rows, 14.62 MB (19.37 million rows/s., 175.34 MB/s.)

W przypadku MySQL wygląda to jak poniżej:

mysql> SELECT * FROM wiki.pageviews WHERE title LIKE 'Main_Page' AND code LIKE 'de.m' AND hour=6;
+------------+------+------+-----------+---------+--------+
| date       | hour | code | title     | monthly | hourly |
+------------+------+------+-----------+---------+--------+
| 2018-05-01 |    6 | de.m | Main_Page |       8 |      0 |
| 2018-05-02 |    6 | de.m | Main_Page |      17 |      0 |
+------------+------+------+-----------+---------+--------+
2 rows in set (2 min 45.83 sec)

A więc prawie 3 minuty. Drugie zapytanie jest takie samo:

mysql> select title from wiki.pageviews where title LIKE 'United%Nations%' AND code LIKE 'en.m' AND monthly>100 group by title;
+---------------------------------+
| title                           |
+---------------------------------+
| United_Nations                  |
| United_Nations_Security_Council |
+---------------------------------+
2 rows in set (2 min 40.91 sec)

Oczywiście można argumentować, że można dodać więcej indeksów, aby poprawić wydajność zapytań, ale faktem jest, że dodanie indeksów będzie wymagało przechowywania na dysku dodatkowych danych. Indeksy wymagają miejsca na dysku, a także stanowią wyzwanie operacyjne - jeśli mówimy o rzeczywistych zestawach danych OLAP, mówimy o terabajtach danych. Uruchomienie zmian schematu na takim środowisku zajmuje dużo czasu i wymaga dobrze zdefiniowanego i przetestowanego procesu. Dlatego dedykowane kolumnowe magazyny danych mogą być bardzo przydatne i ogromnie pomóc w uzyskaniu lepszego wglądu we wszystkie dane analityczne, które wszyscy przechowują.


  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 monitorować wdrożenia MySQL za pomocą Prometheus i Grafana w ScaleGrid?

  2. MyCLI — klient MySQL/MariaDB z automatycznym uzupełnianiem i podświetlaniem składni

  3. czy istnieje funkcja group_concat w ms-access?

  4. Nieprawidłowa wartość ciągu:'\xF0\x9F\x8E\xB6\xF0\x9F...' MySQL

  5. Jak korzystać z JDBC do łączenia bazy danych MySql