Portál AbcLinuxu, 7. května 2025 17:25
Dobry den,
uz dlhsie sa snazim efektivne vyriesit nasledujuci problem:
Do 2 rozdielnych adresarov mi pribudaju CSV subory roznej velkosti ale rovnakeho formatu (su to key-value data). V tomto momente sa bavime asi o 30+ suboroch a celkovej velkosti cca 400GB. Je predpoklad ze objem dat bude do 1.5TB.
Potreboval by som efektivne vyriesit "inner join" a vypisat len rovnake riadky zo suborov ulozenych v tychto 2 roznych adresaroch. Nieco ako:
inner_join_csv_files /home/mike/data_A/*.csv /home/mike/data_B/*.csv > rovnake_riadky.csv
Momentalne to riesim importom CSV suborov (obsahuju asi 5% duplicit) do 2 roznych tabuliek databazy Postgresql a naslednym SQL INNER JOIN prikazom (do 30 sekund mam vysledok). Toto uz nie je dostacujuce, pretoze import dalsieho CSV suboru trva aj viac ako 48 hodin koli pravidlam nad oboma tabulkami, ktore ignoruju duplicitne inserty (v databaze su len unikatne zaznamy aby sa pouzil index scan a nie sekvencny scan). Navyse cele to bezi len na obycajnom laptope :(.
Hladam sposob akym by som mohol usetrit tych 48 hodin pri importe, som ochotny vo vysledku pripustit aj duplicity hlavne nech sa relevantne skrati doba. Je mi jedno aky nastroj alebo technologia sa na to pouzije hlavne nech to cele netrva 48 hodin :). Ak Vas napadne nejaky efektivny sposob ako vyriesit "inner join" CSV suborov v 2 adresaroch budem velmi vdacny.
Nad dvema soubory se da pouzit prikaz comm.
Ano, mam dva adresare s CSV subormi. CSV maju rovnaku strukturu (key-value), ale roznu velkost a rozne nazvy. Chcem ziskat iba riadky, ktore sa nachadzaju v oboch adresaroch sucasne. Zhodne data su rozlozene v suboroch nahodne - teda sa musi porovnavat cely balik (oba adresare) sucasne.
Teraz to robim importom do Postgresql a naslednym SQL INNER JOINom
/home/mike/data_A/*.csv ==> import do DB tabulka_A
/home/mike/data_B/*.csv ==> import do DB tabulka_B
Import je strasne pomaly jeden CSV subor importujem aj 2 dni :(. Hladam teda efektivnejsie riesenie
dakujem... toto bude asi presne to co som hladal ale budem sa musiet tomu este povenovat. s pytonom som zatial vobec nerobil, ale podla toho co som v rychlosti nasiel to vyzera celkom jednoducho... spravia sa 2 DataFramy a potom inner join medzi nimy.
http://stackoverflow.com/questions/20906474/import-multiple-csv-files-into-pandas-and-concatenate-into-one-dataframe
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.join.html
http://pandas.pydata.org/pandas-docs/stable/merging.html
Obsah prvního adresáře nasypu do databáze (jde to podstatně rychleji než u SQL databází) a obsah druhého pak řádek po řádku porovnám.
PostgreSQL nemá nic společného s DB4 ani GDBM. Proto jsem navrhl místo PostgreSQL jiné databáze.
Asi som spravne nepochopil. Nieco som som si precital o DB4. Je to cista key-value databaza.
Ak ste to mysleli takto:
/home/mike/data_A/*.csv ==> import do DB4 alebo GDBM
/home/mike/data_B/*.csv ==> sekvencne citanie line-by-line + requesty na DB
Toto riesenie sa ukazalo ako extremne pomale (mal som maly objem dat). pretoze sa musi robit sekvencne precitanie kazdeho riadku v CSV subore, nasledny request do DB a potom porovnanie ci je zhoda (inner join)
Preto som sa rozhodol ze CSV naimportujem do Postgresql, ktory si to sam zaindexuje a urobi jednoduchym SELECTom vsetko za mna - vysledok mam takmer okamzite. No len tento import uz prestava vyhovovat :( pretoze trva aj viac ako 2 dni
Opravdu se podívejte na to předělání importu (jak píšu níže), to je hlavní problém. Změna databáze je krok stranou a původní problém vlastně neřeší.No len tento import uz prestava vyhovovat :( pretoze trva aj viac ako 2 dni
suhlasim.... na tom importe je nieco strasne zle.... uvazoval som ci sa da nejako zaobist bez databazy (aby som nemusel robit tak zdlhavy import) tj. nejako urobit "inner join" CSV suborov primo na filesysteme.
Dokonca som chcel pouzit na tuto ulohu sphinxsearch alebo solr, tie si vedia velmi rychlo naindexovat txt subory (podporuju aj delty) a potom takmer okamzite vyhladavat, ale nedokazu urobit "inner join" 2 indexov :(
import robim cez COPY
copy tabulka_A(key,value) FROM '/home/mike/data_A/fileA.csv' DELIMITER ',' CSV
copy tabulka_B(key,value) FROM '/home/mike/data_B/fileB.csv' DELIMITER ',' CSV
Postgresql ma jednu neprijemnu vlastnost - COPY je jedna trasakcia: ak sa vyskytne duplicita vyhodi hlasku ERROR: duplicate key value violates unique constraint "key", a cely import zlyha. :(
Preto som si napisal pravidlo, ktore ignoruje duplicitny insert do DB. Nieco na tento sposob http://dba.stackexchange.com/questions/30499/optimal-way-to-ignore-duplicate-inserts - a to moze byt kamen urazu.
V podstate sa jedna o exporty z roznych transakcnych systemov, kde sa zaznamenavaju transakcne kroky (data_A) a "podozrive" transakcie (data_B). Tieto vystupy je nutne porovnavat, ked nastane zhoda nasledne manualne vyhodnotit.
/home/mike/data_A/*.csv ==> prirastok je cca 8-10GB za tyzden
/home/mike/data_B/*.csv ==> prirastok je cca 1GB za mesiac
$ wc -l file_A.csv 1000000 file_A.csv $ ls -sh file_A.csv 312M file_A.csv $ head -n3 file_A.csv 2359;c2aa 4ebe 592c 2638 8c8f 5269 2e94 1381 59a9 cadc 2e84 b895 3295 7078 24a6 b512 df94 d803 e5d2 4c15 fc43 c818 49dd 86c5 1c60 90f9 f227 8738 5ef8 95ec e5f4 5288 106a 52b9 dc61 2a34 57a5 bd37 989d fd0e 078f 83b1 a157 b1b0 6399 8b9c a1af 21e3 fa39 7abd 8b9d 0615 bb5f 1afc f302 b0dc de59 18e9 a221 67f4 3a7e 6ea3 891a 619b; 9765;8490 5573 28c2 e229 2519 b612 8745 fee1 e103 2476 3247 ad7d df5e 80ec f5f3 def8 fddc a313 50dd 7b6d 8949 25cd 5fed d7a8 177d b70d d6be 3aa7 8dc0 8df5 de28 34d3 bf03 6413 ca0d 2bb7 f397 a39a 7db0 72aa 7fad de31 4279 9a0b 93f1 4106 74c9 a3fa d1e9 afb7 dadb 0b61 bf0f c674 4c0e 1ecd c0c7 79c3 798d 471c dd5f 8125 dc52 7816; 30671;75c5 f14d f72e 11c0 1833 8248 24b1 a939 38b2 d4d8 3e2e b129 22ad bc0d 2b41 b8da 61ea 18d2 dffb 32ad 83e0 118f 5c81 ae0f 8eb8 a495 a820 d30e 8fe1 a4f6 d234 6e36 a5f8 e17f aa2d 0953 fe34 bced c5bb 63b9 f460 41dc 59ef 8dd4 7043 9a32 54ca d20b 5393 bd72 0fb1 11dc 7980 4ebe eb73 6829 ecf2 b18e 8e52 a3b3 b43a 8ad0 111c b61c;Databáze
CREATE DATABASE import ENCODING = 'UTF8' LC_COLLATE = 'cs_CZ.utf8' LC_CTYPE = 'cs_CZ.utf8'; GRANT ALL ON DATABASE import TO postgres; GRANT ALL ON DATABASE import TO public;Tabulka:
CREATE TABLE t1 ( key integer, value text, extra text );Bez kontrol a bez indexu:
$ psql -U postgres import -c "TRUNCATE t1;" TRUNCATE TABLE $ psql -U postgres import -c "DROP INDEX t1_key_idx;" ERROR: index "t1_key_idx" does not exist $ time psql -U postgres -c "COPY t1(key,value,extra) FROM '/home/ondrej/Test/file_A.csv' DELIMITER ';' CSV;" import COPY 1000000 real 0m19.562s user 0m0.003s sys 0m0.006sBez kontrol s indexem:
$ psql -U postgres import -c "TRUNCATE t1;" TRUNCATE TABLE $ psql -U postgres import -c "CREATE INDEX t1_key_idx ON t1 USING btree (key);" CREATE INDEX $ time psql -U postgres -c "COPY t1(key,value,extra) FROM '/home/ondrej/Test/file_A.csv' DELIMITER ';' CSV;" import COPY 1000000 real 0m29.042s user 0m0.002s sys 0m0.004sPřelití jedinečných záznamů do jiné tabulky:
$ time psql -U postgres import -c "CREATE TABLE t2 AS SELECT DISTINCT ON(key) * FROM t1;" SELECT 32768 real 0m15.888s user 0m0.001s sys 0m0.008sDoplnění jedinečného indexu (t2 neobsahuje duplicity, t1 ano):
$ psql -U postgres import -c "CREATE UNIQUE INDEX t2_key_idx ON t2 USING btree (key);" CREATE INDEX $ psql -U postgres import -c "DROP INDEX t1_key_idx; CREATE UNIQUE INDEX t1_key_idx ON t1 USING btree (key);" ERROR: could not create unique index "t1_key_idx" DETAIL: Key (key)=(12063) is duplicated.Počty řádků v tabulce:
$ psql -U postgres import -c "SELECT count(*) FROM t1;" count --------- 1000000 (1 řádka) $ psql -U postgres import -c "SELECT count(*) FROM t2;" count ------- 32768 (1 řádka)Verze databáze:
$ psql -U postgres import -c "SELECT version();" version ------------------------------------------------------------------------------------------------------------- PostgreSQL 9.4.5 on x86_64-redhat-linux-gnu, compiled by gcc (GCC) 5.1.1 20150618 (Red Hat 5.1.1-4), 64-bit (1 řádka)A s Potgres 9.5.rc1 a UPSERT:
$ psql -U postgres -h localhost -p 5434 import -c "TRUNCATE TABLE t2;" TRUNCATE TABLE $ psql -U postgres -h localhost -p 5434 import -c "DROP INDEX IF EXISTS t2_key_idx;" DROP INDEX $ psql -U postgres -h localhost -p 5434 import -c "CREATE UNIQUE INDEX t2_key_idx ON t2 USING btree (key);" CREATE INDEX $ time psql -U postgres -h localhost -p 5434 import -c "INSERT INTO t2 SELECT * FROM t1 ON CONFLICT (key) DO NOTHING;" INSERT 0 32768 real 0m6.932s user 0m0.005s sys 0m0.006s $ psql -U postgres -h localhost -p 5434 import -c "SELECT version();" version ----------------------------------------------------------------------------------------------------------- PostgreSQL 9.5rc1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-55), 64-bit (1 řádka)
$ time pg_bulkload -h localhost -p 5434 -U postgres -d import file_A.ctl NOTICE: BULK LOAD START NOTICE: BULK LOAD END 0 Rows skipped. 1000000 Rows successfully loaded. 0 Rows not loaded due to parse errors. 0 Rows not loaded due to duplicate errors. 0 Rows replaced with new rows. real 0m10.887s user 0m0.002s sys 0m0.004sA pomocí pgloader:
$ pgloader --type csv --field key --field value --field extra --with "drop indexes" --with "disable triggers" --with "fields terminated by ';'" file_A.csv postgres://postgres:***@localhost:5434/import?tablename=file_A 2015-12-30T22:56:31.075000+01:00 LOG Main logs in '/tmp/pgloader/pgloader.log' 2015-12-30T22:56:31.079000+01:00 LOG Data errors in '/tmp/pgloader/' table name read imported errors total time read write ----------------- --------- --------- --------- -------------- --------- --------- fetch 0 0 0 0.011s ----------------- --------- --------- --------- -------------- --------- --------- file_a 1000000 1000000 0 47.886s 47.148s 18.913s ----------------- --------- --------- --------- -------------- --------- --------- Total import time 1000000 1000000 0 48.109s 47.148s 18.913s
cat /home/mike/data_A/*.csv /home/mike/data_B/*.csv | LANG=C sort | uniq -d
(vezmeš všechno, seřadíš, vypíšeš jen duplicitní)
Pokud by se to mělo dělat opakovaně nad stejnýma souborama, pak se vyplatí je seřadit předem (někam vedle) a potom pomocí
LANG=C sort -m soubor /home/mike/data_A/*.serazeno.csv /home/mike/data_B/*.serazeno.csv | uniq -d
už seřazené soubory zmergoat a zase vypsat duplicity.
Pokud bys nechtěl vyloženě duplicitní řádky, ale duplicitní jen některé klíče, tak bych ty klíče dal jako první sloupec, seřadil, zmergoval a nějakým vlastním skriptem rozhodl, jestli ty řádky teda duplicitní jsou nebo ne a podle toho vypsal takto to funguje teraz. Dostanem novy CSV subor a ten naimportujem do databazy.
CREATE TABLE tabulka_A ( key varchar(16) PRIMARY KEY, value varchar(128) );
vypinanie/zapinanie primarneho kluca je vhodne, kym mam zarucene ze v CSV suboroch nebudu duplicity (teraz je ich cca 5%) - v databaze by som ich nechcel mat. Pri importe chcem mat zapnuty primarny key koli duplicitam.
ak by som pred importom vypol primarny kluc riskoval by som ze v databaze by boli duplicity a nesiel by potom zapnut. Musel by som vsetky duplicity mazat rucne a az tak ho zapnut (pain in ass).
CREATE TABLE t2 ...; INSERT INTO t2 AS SELECT * FROM t1 ON CONFLICT (...) DO NOTHING;Implementace v databázi bude rychlejší než vaše funkce (kdoví jestli je správně implementovaná). Anebo:
CREATE TABLE t2 AS SELECT DISTINCT ON(...) FROM t1;Chce si to s tím trochu pohrát. Kolik řádků má vlastně těch zmiňovaných 400GB?
Chce si to s tím trochu pohrát. Kolik řádků má vlastně těch zmiňovaných 400GB?jj budem sa tomu musiet povenovat a tiez potunit konfigurak - mam teraz defaultny. Momentalne je tam 4184023358 riadkov. Ak sa objem bude blizit ku 1.5TB budem sa musiet zamysliet ako pokracovat dalej. Myslite ze Postgresql je vhodny na spracovanie aj takto velkych (key-value) dat? Na SQL databazach sa mi paci, ze behom kratkej chvile a velmi jednoduchym sposobom dokazem dostat pozadovane data.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.