Diskuse o statistikach

Milan Krčmář milan.krcmar na seznam.cz
Sobota Únor 14 22:09:17 CET 2004


Ahoj wifilidi,

jak jsem již psal, vyrobil jsem v týdnu nástroj na sběr statistik. Je to
program, který poběží na každém routeru a bude měřit provoz, jenž přes
něj jde. Naměřené hodnoty se budou posílat na server mrsk, ten je bude
ukládat do lokální SQL databáze.

První verze měřícího prográmku, kterou jsem nechal běžet na hlavním
routeru door asi 12 hodin od čtvrtka cca 2:30 ráno do cca 14:00,
nasbírala velice podrobné údaje. Měřilo se každých 10 sekund. Tato
měření jsem nepsal do databáze, ale do textového souboru, který ve
výsledku za těch 12 hodin měl zhruba 80 MB. Toto první měření mělo jeden
konkrétní účel: odhadnout objem dat, který se bude psát do databáze. O
tomto odhadu a o volbě dat, jež se do databáze budou zapisovat, je
tento příspěvek. Vyzývám proto všechny, kteří tomu alespoň trochu
rozumí, k diskusi...

Tento příspěvek slouží také jako první dokumentace pro ty, kteří budou
chtít nad výsledky uloženými v databázi programovat rozhraní pro
zobrazení výsledků.

---

Měřící program dostane od routeru každý paket. V té nejpodrobnější verzi
z něj přečte tyto údaje:

    src_ip - zdrojová IP adresa paketu
    dst_ip - cílová IP adresa
    in_if  - rozhraní, po kterém paket do routeru vstoupil
    out_if - rozhraní, kterým paket router opouští
    class  - třída (vysvětlím později)
    size   - velikost paketu

Program si pro každou kombinaci (src_ip, dst_ip, in_if, out_if, class)
eviduje dvě čísla: celkový počet paketů a celk
1000
ovou délku paketů.  Každý
paket tedy zaznamená tak, že najde v evidenci záznam pro (src_ip,
dst_ip, ...), k počtu paketů přičte 1, k celkové velikosti přičte
velikost paketu.  Pokud záznam pro (src_ip, dst_ip, ...) neexistuje, tak
jej nejprve vytvoří.  Každých 10 sekund program uloží (nyní na disk,
později do centrální SQL databáze) všechny záznamy a záznamy u sebe v
paměti zároveň vymaže (vynuluje).

Nyní si do jiného okna otevřete adresu

                 http://mrsk.klfree.net/public/traf

Když jsem spustil program tak, jak je popsáno výše, vygeneroval obrovské
množství záznamů. V průměru těch každých 10 sekund zapsal skoro 400
(!!!) záznamů. To je zobrazeno na prvním obrázku: na vodorovné ose je
čas od začátku měření v hodinách (od 0 až skoro do 12), na svislé ose je
vynešen počet záznamů, které program vygeneroval. Není uveden počet
záznamů za těch 10 sekund, ale je to přepočítáno na 60 sekund (na jednu
minutu), tj. vynásobeno číslem 6. V průměru by program do databáze
poslal více než 2000 záznamů za minutu (průměrnou hodnotu můžete
odhadnout z obrázku nebo si ji přesně přečtete v titulku - 2250 záznamů
za minutu).

Další obrázky jsem pouze simuloval. Měl jsem to nejpodrobnější měření, z
něj se dá ovodit, jak by se choval upravený program, který by sbíral
méně podrobné údaje.

Na druhém obrázku (první řada obrázků, druhý obrázek), je vidět, jak by
se program choval, kdyby neměřil každých 10 sekund, ale každou minutu
(každých 60 sekund). Za minutu vznikne jistě více záznamů než za deset
sekund, tj. přes router proběhne více různých kombinací (src_ip, dst_ip,
...). Není to ale 6x více, ale konkrétně asi jen 3x více. V úhrnu se to
projeví tak, že program pošle do databáze průměrně 1227 záznamů za
minutu, což je zhruba polovina oproti původnímu desetisekundovému
internvalu.

Třetí obrázek v první řadě ukazuje chování programu, který by měřil jen
jednou za 5 minut (300 sekund). Celkové vytížení databáze klesne na 707
záznamů za minutu.

Řada obrázků tedy vždy sleduje vliv periody měření na zátěž databáze.
Čím častěji se měří, tím více se databáze zatěžuje, ale na druhou stranu
tím rychleji se údaje obnovují (když na ně koukáte v nějaké aplikaci
zobrazující aktuální data). Zobrazil jsem zde tři rozumné případy:
10 sekund, 1 minutu a 5 minut.

---

První řada obrázků nese název "full", odpovídá plným údajům. Protože
zátěž databáze je příliš veliká, navrhnul jsem různá snížení detailu
ukládané informace, což ukazují další řady obrázků.

V řadě "integnet" se měří stále všechny údaje (tj. src_ip, dst_ip,
in_if, out_if, class), ale všechny internetové adresy se evidují jako
jedna jediná, nerozlišuje se mezi nimi. Pokud uživatel komunikuje
zároveň s několika počítači na Internetu, kominukace se eviduje jediným
záznamem. To přináší zhruba desetinásobné snížení počtu záznamů. Nikdy
bych si nemyslel, že naši uživatelé Internetu komunikují během deseti
sekund v průměru se čtyřmi počítači najednou (2250:538=4.18)... Protože
sjednocení internetových adres význemně sníží počet záznamů ve
statistikách, vycházel jsem v dalším modelování z tohoto přístupu.

Model "oneip" je dalším zjednodušením. Zde se u každého paketu eviduje
pouze jedna IP adresa. Tím se zjistí KDO komunikuje, ale už se neeviduje
S KÝM. Je tu ale problém. Dejme tomu zřídím nějaký FTP server. Když z
něj bude někdo stahovat a zatěžovat tak síť, tak v těch důležitých
paketech bude jeho IP jako cílová IP. Když tam bude něco nahrávat, tak
jeho IP je naopak zdrojová. Kdo je za zátěž zodpovědný (jinými slovy:
kdo má být ve statistikách)? Oba. Každý paket je potřeba evidovat
dvakrát, jednou pro zdrojovou adresu, jednou pro cílovou. Pokud sečteme
celkový evidovaný traffic, dostaneme přesně dvojnásobek skutečného
trafficu, ale evidence bude korektní... Podle obrázků tento přístup
znamená jisté navýšení počtu záznamů, což jsme původně nechtěli. Na
druhou stranu se ale podstatným způsobem zjednoduší generování
statistik, protože v databázi jsou adresy vedeny jen v jednom sloupečku,
původně byly ve dvou, a dotazy do databáze musí prohledávat více dat
(všimněte si, že s rostoucím ča
1000
sovým intervalem se počty záznamů modelu
"integnet" a "oneip" přibližují a pro třeba hodinovou statistiku budou
skoro stejné, takže "oneip" skutečně přináší dvojnásobný výkon pro
generování statistik).

Dalším modelem je "noif". Vychází z "integnet", ale neeviduje rozhraní,
přes který paket jde, pouze zdrojovou a cílovou adresu. Tímto ochuzením
se počet záznamů téměř nezmění, proto ho zavrhuji.

Poslední model je "users". Ten byl použit v původně připravované verzi
statistik, která z důvodu špatného měřícího programu neuspěla. Místo IP
adresy se eviduje číslo uživatele, kterému adresa patří. Podle obrázků
to ale nepřinese podstatné snížení počtu záznamů. Problém je v tom, že
se nerozlišuje mezi adresami, které ještě nebyly nikomu přiděleny (a
také pro diagnostiku máme méně informací, nemůžeme říci uživateli, která
adresa mu zlobí).

---

Takže teď jste viděli modely zatížení databáze založené na sběru dat za
12 hodin z hlavního routeru. Jak to bude pro celou síť? Toto měření chci
nasadit na VŠECH routerech, to zvýší opět zátěž. Pokud máme N routerů,
nebude navýšení N-násobné. Každý paket nejde přes všechny routery, ale
dejme tomu v průměru přes 3, tj. trojnásobné navýšení. Distribuce měření
na všechny routery nám také ukáže lokální traffic, který přes hlavní
router nejde. Lokální traffic je ale menší než internetový, tedy ne co
do objemu, ale co do počtu uživatelů (a ten má hlavní vliv na počet záznamů
ve statistikách), proto navýšení zatížení nebude ani dvojnásobné (opět
hrubé odhady). Tj. distribuce na všechny routery nebude znamenat více
než šestinásobek (3x2=6) zde uvedených hodnot.

Ještě jsem slíbil vysvětlit význam slova třída (class). Je to další
parametr, který budeme evidovat. Každý paket bude klasifikován do nějaké
třídy. Představme si třeba třídy: p2p, webové stránky, realtime a pod. 
Odhadem zavedení tříd zvedne počet záznamů třeba dvakrát, uživatel
nebude komunikovat najednou v průměru více než dvěma třídami. Možná
přibude ještě jedna zvláštní třída, tou budou pakety zahazované v rámci
řízení provozu.

Celkově při distribuci statistik na všechny routery a při rozumném
zavedení tříd můžeme dle mých odhadů čekat zdesetinásobení hodnot
uvedených na obrázcích. Ano, máte pravdu, je to hodně optimistický
odhad.

---

Posledním faktorem zvyšujícím zatížení databáze, který mě ještě napadá,
je prostě navýšení počtu uživatelů a navýšení kapacity linek. Byl bych
rád, kdyby bylo možné připravit systém, který ustojí desetinásobnou
zátěž oproti zátěži současné. V desetinásobně větší síti si ještě dokážu
představit zachování stávající infrastrukury (jeden nebo dva hlavní
routery do Internetu). Uvidíme, co náš SQL server ustojí.

===

Tak do tohoto místa jsem Vám předal nějaké informace a dále čekám
diskusi o tom, co vybrat, co evidovat v databázi. Následuje můj
konkrétní návrh:

Data budeme sbírat v minutových intervalech všude metodou "integnet".
V databázi budou 2 tabulky. První tabulka, pojmenujme ji "stat_detail",
bude obsahovat data v plném detailu za nějakou krátkou dobu. Druhá
tabulka, "stat_sum", bude obsahovat data od spuštění systému statistik
v menším detailu a do historie postupně se prodlužujícími intervaly.

stat_detail
~~~~~~~~~~~
    start - čas začátku měření {8}
    duration - doba měření v sekundách {4}
    router - router, ze kterého pochází statistiky (odkaz do tabulky
        routerů) {4}
    in_if - vstupní rozhraní {4}
    out_if - výstupní rozhraní (obojí může být opět odkaz do tabulky)
             {4}
    src_ip - zdrojová adresa paketů {12}
    dst_ip - cílová adresa paketů {12}
    class - číslo třídy, odkaz do tabulky tříd {4}
    packets - počet paketů {4}
    bytes - součet velikostí paketů {8}

V těch {} závorkách je velikost datového typu v Postgresql, celkem na
záznam zhruba 80 bajtů.

Já bych uložil jednu hodinu po minutách a dalších 48 hodin po
pětiminutách, každou hodinu by se spustila dávka, která by agregovala
minutové záznamy starší než hodinu do pětiminutových, a jednou denně by
se spustila dávka, která by uklízela záznamy starší 48 hodin do tabulky
stat_sum. (Těsně před spuštěním
1000
 hodinové dávky tam bude nejvíce údajů: dvě
hodiny minutovek a až 70 hodin pětiminutovek, tj. 120 minutovek a 840
pětiminutovek, minutovky mají v průměru 164*10 = zhruba 1700 záznamů,
pětiminutovky 50 * 10 = zhruba 500 záznamů, tj. celkem
120*1700+840*500=624000 záznamů, tj. 624000*80 = 47MB) => tabulka by se
vešla krásně celá do operační paměti Mrska.

stat_sum
~~~~~~~~
    start, duration, router, in_if, out_if, class, packets, bytes - jako
        v tabulce stat_detail
    uid - číslo uživatele

Tato tabulka by obsahovala data vymazávaná denně z tabulky stat_detail.
Kromě agregace v čase by se ještě použily techniky modelu "oneip" (v
záznamu by byla jen jedna IP adresa, každý paket dvakrát) a "users"
(neevidujeme IP adresy, ale čísla uživatelů).

Další podrobnosti:
- je otázkou, jestli mít ve stat_detail prvních 48 hodin a zbytek ve
  stat_sum, nebo raději mít ve stat_sum zduplikovaná data ze
  stat_detail, data by se ze stat_detail do stat_sum kopírovala třeba
  každou hodinu, vyhnuli bychom se složitým SQL operacím, které při
  dotazech na stat_sum musejí přidávat i záznamy ze stat_detail, a to
  značně přepočítávané, navrhuji asi druhý přístup, tedy držet ve
  stat_sum duplicitní data, jejichž neakutálnost je max. hodinu
- dále zvažuji, že trojice (router,in_if,out_if) je nevypovídající, více
  nás zajímá zatížení konkrétních linek, v případě peer spojů je
  vytížení spoje v datech dvakrát, bylo by tedy lepší definovat jen
  "zajímavá rozhraní" a k nim přiřadit jméno linky a jen pro tato rozhraní
  by se evidovaly statistiky
- v tabulkách stat_sum i stat_detail jsou u některých polí zmíněny
  odkazy do tabulky uživatelů a pod., pro zachování referenční integrity
  bychom nemohli měnit tabulku uživatelů dokud budou existovat
  související záznamy ve statistikách, proto doporučuji pro statistiky
  vyrobit duplicitní tabulku uživatelů, která bude obsahovat zajímavé
  uživatelské údaje v době pořízení záznamu ve statistikách a pokud se
  údaje změní, tak se tam uživatel vytvoří několikrát; tím také umožním
  oddělení statistik od zbytku informačního systému, můžou být v jiné
  databázi (myslím tím dělení jednoho běžícího databázového stroje na
  několik databází, nikoliv několik různých db strojů)

Takže bychom měli dvě tabulky, stat_detail a stat_sum. K tomu by
existovaly dvě různé aplikace, jedna by uměla zobrazovat detaily, ale
jen za posledních 48 hodin, druhá by zobrazovala dlouhodobý trend a
součty přenesených dat...

Milan


Další informace o konferenci Kladno