Jak spustit Analytics na MySQL?
MySQL je skvělá databáze pro pracovní zatížení Online Transaction Processing (OLTP). Některým firmám to bývalo na dlouhou dobu více než dostačující. Doba se změnila a obchodní požadavky s ní. Jak podniky aspirují na to, aby byly více řízeny daty, ukládá se stále více dat pro další analýzu; chování zákazníků, vzorce výkonu, síťový provoz, protokoly atd. Bez ohledu na to, v jakém odvětví se pohybujete, je velmi pravděpodobné, že existují data, která chcete uchovávat a analyzovat, abyste lépe porozuměli tomu, co se děje a jak zlepšit své podnikání. Bohužel pro ukládání a dotazování velkého množství dat není MySQL tou nejlepší volbou. Jistě, umí to a má nástroje, které vám pomohou pojmout velké množství dat (např. komprese InnoDB), ale použití specializovaného řešení pro zpracování online analýzy (OLAP) s největší pravděpodobností výrazně zlepší vaši schopnost ukládat a dotazovat velké množství. dat.
Jedním ze způsobů, jak tento problém vyřešit, bude použití specializované databáze pro spouštění analýzy. Obvykle chcete pro takové úlohy použít sloupcové úložiště dat - jsou vhodnější pro zpracování velkého množství dat:data uložená ve sloupcích se obvykle snáze komprimují, je také snazší k nim přistupovat na základě jednotlivých sloupců - obvykle požadujete nějaké data uložená v několika sloupcích – možnost načíst pouze tyto sloupce namísto čtení všech řádků a odfiltrování nepotřebných dat umožňuje rychlejší přístup k datům.
Jak replikovat data z MySQL do ClickHouse?
Příkladem sloupcového úložiště dat, které je vhodné pro analýzu, je ClickHouse, úložiště sloupců s otevřeným zdrojovým kódem. Jedním z problémů je zajistit, aby data v ClickHouse byla synchronizována s daty v MySQL. Jistě, vždy je možné nastavit nějaký datový kanál a provádět automatické dávkové načítání do ClickHouse. Ale pokud můžete žít s určitými omezeními, existuje lepší způsob, jak nastavit replikaci z MySQL do ClickHouse téměř v reálném čase. V tomto příspěvku na blogu se podíváme na to, jak to lze provést.
Instalace ClickHouse
Nejprve musíme nainstalovat ClickHouse. Použijeme rychlý start z webu 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
Jakmile to uděláme, musíme najít způsob, jak přenést data z MySQL do ClickHouse. Jedním z možných řešení je použití Clickhouse-mysql-data-reader od Altinity. Nejprve musíme nainstalovat pip3 (python3-pip v Ubuntu), protože je vyžadován Python ve verzi alespoň 3.4. Potom můžeme použít pip3 k instalaci některých požadovaných modulů Pythonu:
pip3 install mysqlclient
pip3 install mysql-replication
pip3 install clickhouse-driver
Jakmile je toto hotovo, musíme úložiště naklonovat. Pro Centos 7 jsou k dispozici také RPM, je také možné jej nainstalovat pomocí pip3 (balíček clickhouse-mysql), ale zjistili jsme, že verze dostupná přes pip neobsahuje nejnovější aktualizace a chceme použít hlavní větev z úložiště git:
git clone https://github.com/Altinity/clickhouse-mysql-data-reader
Poté jej můžeme nainstalovat pomocí pip:
pip3 install -e /path/to/clickhouse-mysql-data-reader/
Dalším krokem bude vytvoření uživatelů MySQL, které vyžaduje clickhouse-mysql-data-reader pro přístup k datům 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)
Měli byste také zkontrolovat konfiguraci MySQL, abyste se ujistili, že máte povolené binární protokoly, max_binlog_size je nastavena na 768M, binlogy jsou ve formátu „řádek“ a že se nástroj může připojit k MySQL. Níže je výňatek z dokumentace:
[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
Import dat
Když je vše připraveno, můžete importovat data do ClickHouse. V ideálním případě byste import spustili na hostiteli s uzamčenými tabulkami, aby během procesu nedošlo k žádné změně. Jako zdroj dat můžete použít slave. Příkaz ke spuštění bude:
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
Připojí se k MySQL na hostiteli 10.0.0.142 pomocí daných přihlašovacích údajů, zkopíruje tabulku ‚zobrazení stránek‘ ve schématu ‚wiki‘ do ClickHouse spuštěného na místním hostiteli (127.0.0.1). Tabulka bude vytvořena automaticky a data budou migrována.
Pro účely tohoto blogu jsme importovali zhruba 50 milionů řádků z datové sady „zobrazení stránek“, kterou zpřístupnila Wikimedia Foundation. Schéma tabulky v MySQL je:
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)
Nástroj to převedl do následujícího schématu 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.
Jakmile je import hotov, můžeme porovnat obsah MySQL:
mysql> SELECT COUNT(*) FROM wiki.pageviews\G
*************************** 1. row ***************************
COUNT(*): 50986914
1 row in set (24.56 sec)
a v 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.)
I v tak malé tabulce můžete jasně vidět, že MySQL vyžadovalo více času na prohledání než ClickHouse.
Při spouštění procesu sledování událostí v binárním protokolu byste v ideálním případě předali informace o souboru binárního protokolu a pozici, odkud má nástroj začít naslouchat. Po dokončení počátečního importu to můžete snadno zkontrolovat na podřízeném zařízení.
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
Pokud ji neprojdete, začne naslouchat všemu, co přijde:
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
Pojďme načíst další data a uvidíme, jak to pro nás bude fungovat. Můžeme vidět, že vše vypadá v pořádku, když se podíváme na protokoly 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
Co musíme mít na paměti, jsou omezení nástroje. Největší z nich je, že podporuje pouze INSERTy. Neexistuje žádná podpora pro DELETE nebo UPDATE. Neexistuje také žádná podpora pro DDL, takže jakékoli nekompatibilní změny schématu provedené na MySQL naruší replikaci MySQL na ClickHouse.
Za zmínku také stojí skutečnost, že vývojáři skriptu doporučují používat pypy pro zlepšení výkonu nástroje. Pojďme si projít několik kroků potřebných k nastavení.
Nejprve si musíte stáhnout a dekomprimovat 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
Dále musíme nainstalovat pip a všechny požadavky na čtečku clickhouse-mysql-data-reader – přesně to samé, co jsme probrali dříve, když jsme popisovali běžné nastavení:
./bin/pypy -m ensurepip
./bin/pip3 install mysql-replication
./bin/pip3 install clickhouse-driver
./bin/pip3 install mysqlclient
Posledním krokem bude instalace clickhouse-mysql-data-reader z úložiště github (předpokládáme, že již bylo naklonováno):
./bin/pip3 install -e /path/to/clickhouse-mysql-data-reader/
To je vše. Od této chvíle byste měli spouštět všechny příkazy pomocí prostředí vytvořeného pro pypy:
./bin/pypy ./bin/clickhouse-mysql
Testy
Data byla načtena, můžeme ověřit, že vše proběhlo hladce porovnáním velikosti tabulky:
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.)
Všechno vypadá správně. Spusťte několik dotazů, abyste viděli, jak se ClickHouse chová. Mějte prosím na paměti, že toto nastavení zdaleka není produkční. Použili jsme dva malé virtuální počítače, 4 GB paměti, každý jeden vCPU. Proto, i když soubor dat nebyl velký, stačilo vidět rozdíl. Vzhledem k malému vzorku je poměrně těžké provádět „skutečnou“ analýzu, ale přesto můžeme zadat nějaké náhodné dotazy.
Pojďme se podívat, ze kterých dnů v týdnu máme data a kolik stránek bylo za den zobrazeno v našich ukázkových datech:
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.)
V případě MySQL vypadá tento dotaz následovně:
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 můžete vidět, MySQL potřebovalo 3,5 minuty na úplné prohledání tabulky.
Nyní se podívejme, kolik stránek má měsíční hodnotu větší než 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.)
V případě MySQL je to opět 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)
Další dotaz, pouze vyhledávání založené na hodnotách řetězců:
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.)
Další dotaz, provádějící nějaké vyhledávání v řetězci a podmínku založenou na sloupci „měsíční“:
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.)
V případě MySQL to vypadá následovně:
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)
Takže skoro 3 minuty. Druhý dotaz je stejný:
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)
Samozřejmě lze namítnout, že můžete přidat více indexů, abyste zlepšili výkon dotazů, ale faktem je, že přidání indexů bude vyžadovat další data, která budou uložena na disk. Indexy vyžadují místo na disku a také představují provozní problémy – pokud mluvíme o skutečných souborech dat OLAP, mluvíme o terabajtech dat. Zabere to spoustu času a vyžaduje dobře definovaný a otestovaný proces, aby se v takovém prostředí spouštěly změny schématu. To je důvod, proč mohou být vyhrazená sloupcová úložiště dat velmi užitečná a nesmírně pomáhají získat lepší přehled o všech analytických datech, která si každý ukládá.