Blog archive 2009/11

Pomalý DHCP ve Firefoxu a Thunderbirdu v Ubuntu Karmic Koala a problém s IPv6

Nov|28 2009

Již podruhé se mi stalo, že v Ubuntu (momentálně mám verzi Karmic Koala) ve Firefoxu trvalo nesnesitelně dlouho zjištění adresy serveru z doménového jména (tzv. Domain Resolving). Poprvé jsem s tím chvíli žil, nyní jsem se rozhodl učinit tomu přítrž.

DHCP server byl přiřazen správně, jen jeho funkce nebyla optimální, první načtení stránky z jednoho serveru trvalo třeba minutu, někdy se to nepovedlo vůbec. Opakované načtení stránky ze stejného serveru již bylo rychlejší - to mě upozornilo na problém s DHCP.

Po chvíli googlení jsem zjistil, že by mohlo pomoci vypnutí IPv6 ve Firefoxu. Do adresního řádku jsem napsal about:config a hodnotu network.dns.disableIPv6 jsem nastavil na true.

Po restartu Firefoxu se zdá být vše OK.

Podobný problém nastal i v Thunderbirdu, tam lze deaktivovat protokol IPv6 pomocí Config Editoru, ten se nachází zde: Tool -> Options (v linuxu Edit -> Preferences) -> záložka Advanced (Pokročilé) -> Config Editor. Položka network.dns.disableIPv6 nastavena na true by opět měla vyřešit trápení.

Po dalším googlení jsem nejspíše odhalil příčinu. Je jím pravděpodobně jedna ze síťových komponent u mě doma, která nepodporuje protokol IPv6. Konkrétní komponentu jsem nezjišťoval, nicméně řešením je vypnutí podpory IPv6 v celém Ubuntu. Návod je zde.

Tags: Internet | Prohlížeče | Sítě | Počítače



Hledání optimálního dotazu pro filtraci a řazení produktů s atributy

Nov|27 2009

Následující příspěvek se zabývá rychlostí databáze MySQL a hledáním optimálního SQL dotazu pro zobrazení doplňkových atributů u produktů.

Máme dvě tabulky, jednu s produkty, který obsahuje základní atributy a druhou s dodatečnými atributy, jejichž typ je definován sloupcem type. Tabulka atributy obsahuje cizí klíč produkt do tabulky produkty a položky s dvojicí klíčů (produkt, type) nemusí být unikátní. Znamená to, že můžeme mít více atributů stejného typu u jednoho produktu. Tabulky jsou v MySQL definovány následovně:


CREATE TABLE IF NOT EXISTS `produkty` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) COLLATE utf8_czech_ci NOT NULL,
  `price` decimal(8,2) NOT NULL,
  `active` int(11) NOT NULL,
  `text` text COLLATE utf8_czech_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `atributy` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product` int(11) NOT NULL,
  `type` int(11) NOT NULL,
  `value_number` int(11) DEFAULT NULL,
  `value_text` text COLLATE utf8_czech_ci,
  `value_datetime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `product` (`product`)
) ENGINE=InnoDB;

Tabulka pro produkty obsahuje 10 000 záznamů a pro atributy 100 000. Problémem, kterým jsem se zabýval, je vypsání produktů spolu s jejich atributy, přičemž produkty mají být omezeny podle hodnot atributů a seřazeny podle jednoho z atributů.

Konkrétně chceme například vypsat prvních 10 produktů, řazených podle datumu, starších jak červen, aktivní, cena větší než 100 a s textem obsahující řetězec 'sd'.

Dotaz pomocí spojování tabulek - JOIN


SELECT 
	main . * , 
	attr1.value_number AS p_price, 
	attr2.value_text AS p_text, 
	attr3.value_datetime AS p_datetime
FROM `produkty` AS main
LEFT JOIN atributy AS attr1 ON main.id = attr1.produkt
	AND attr1.type =2
LEFT JOIN atributy AS attr2 ON main.id = attr2.produkt
	AND attr2.type =3
LEFT JOIN atributy AS attr3 ON main.id = attr3.produkt
	AND attr3.type =4
WHERE 
	attr3.value_datetime < '2009-06-01 00:00:00'
	AND main.active =1
	AND attr1.value_number >100
	AND attr2.value_text LIKE '%sd%'
GROUP BY main.id
ORDER BY attr3.value_datetime
LIMIT 10

Dotaz trval 4s a mě zajímalo, která část dotazu je nejpomalejší. Zkusil jsem odebraz příkaz ORDER, dotaz trval pouze 2,5s, což je samozřejmě stále špatné. Další změnou bylo úplné vyhození omezujících podmíne, tedy celý příkaz WHERE.

Pokud jsem použil řazení, trval opět 4s, ovšem bez použití podmínek i řazení se rychlost změnila o 2 řády, na použitelných 0,04s. Důvod je zřejmý, při použití řazení musí databáze vypočítat všechny řádky a potom teprve seřadit, zatímco bez použití ORDER příkazu stačí připravit několik řádků, aby byl splněn LIMIT, a tím to hasne.

Bohužel se nemůžeme smířit s tím, že na stránkách budeme zobrazovat produkty náhodně seřazené a nenabídneme uživateli jejich filtrování. Pojďme tedy zkusit jiný typ dotazu, který nám vrátí stejná data.

Dotaz pomocí vloženého dotazu


SELECT 
	main.* 
FROM produkty AS main WHERE
(
	SELECT count(*) 
	FROM atributy AS attr1 
	WHERE attr1.product = main.id 
	AND attr1.type = 2 
	AND attr1.value_number > 700
) AND (
	SELECT count(*) 
	FROM atributy AS attr2 
	WHERE attr2.product = main.id 
	AND attr2.type = 3 
	AND attr2.value_text like '%sd%'
) AND (
	SELECT count(*) 
	FROM atributy AS attr3 
	WHERE attr3.product = main.id 
	AND attr3.type = 4 
	AND attr3.value_datetime > '2009-08-07 23:11:22'
)

Výsledek? Rychlost načítání je srovnatelná s předešlým, ale nelze použít řazení. Atributy se musejí načítat dodatečně. Další možnost, jak načítat podobná data je tu:

Dotaz pomocí obyčejného spojení dvou tabulek


SELECT main . *
FROM produkty AS main, atributy AS a
WHERE main.id = a.product 
AND (
	(a.type =2 AND a.value_number >500)
	OR 
	(a.type =3 AND a.value_text LIKE '%sd%')
	OR 
	(a.type =4 AND a.value_datetime > '2009-06-01 00:00:00')
)
GROUP BY main.id

Rychlost opět srovnatelná, při použití ORDER BY příkazu pomalejší o dva až tři řády (v rozsahu uvedeném na začátku článku řádově minuty).

Závěr:

Ať se snažím, jak se snažím, vymyslet dotaz, který by dokázal řadit i filtrovat produkty s atributy takto rozsáhlých tabulek, se mi nedaří. Na druhou stranu, jak rád říkám, "život je kompromis". Jde tedy o to zvolit ten správný kompromis, na výběr máme následující:

  • využít JOIN tabulek a omezit filtrování produktů na jeden, maximálně dva atributy, s rostoucím počtem filtrovaných atributů je dotaz složitější
  • nepoužívat řazení podle atributů, ale pouze podle sloupců tabulky produkty
  • atributy, podle kterých se nefiltruje, nenačítat pomocí spojování tabulek (nebo obecně při samotném výběru produktů), ale načíst je dodatečně

K poslední možnosti ještě poznámka: Často omezujeme výběr pomocí příkazu LIMIT a tím pádem je daleko výhodnější načíst data u všech takto vybraných produktů najednou (maximálně je jich zpravidla desítky) a přiřadit je k produktům až následně v aplikaci. Načtení atributů samotných je celkem rychlé například takto:


SELECT * FROM atributy WHERE produkt IN (...)

Pokud máte někdo nápady, jak sestavit zmíněné dotazy efektivněji, určitě se o ně podělte v diskuzi.

Tags: Programování | Počítače | Databáze



Testování rychlosti dotazů v MySQL 5.1

Nov|26 2009

V příspěvku jsou popsány základní možnosti, jak zjišťovat rychlost a efektivitu různých SQL dotazů, za účelem výběru nejoptimálnějšího řešení.

Nejdříve něco málo o testování rychlosti dotazů v MySQL. Rychlost dotazů testujeme nejčastěji u sestavování složitějších dotazů, často doprovázené spojováním více tabulek. Pomocí PhpMyAdminu nebo konzole můžeme vytvářet libovolné dotazy a rychlost jejich zpracování je po provedení uvedena.

Musím hned zpočátku upozornit, abyste si předem velmi rozmysleli, jaký dotaz provádíte, pokud to není na vašem stroji, můžete nešikovným dotazem SQL server pěkně potrápit. V případě pochybností nebo i preventivně doporučuji použít na dotaz omezení pomocí LIMIT. To většinou zabrzdí vykonávání dotazu včas.

Při testování rychlosti dotazů často narážíme na první problém - využívání cache. Pokud podobný dotaz provádíme vícekrát nebo jsou data v paměti SQL serveru, výsledky jsou k dispozici rychleji a o složitosti dotazu se mnoho nedozvíme. Zakázat využití cache paměti lze doplněním klíčového slova SQL_NO_CACHE ihned za první SELECT. Tím bychom měli přemluvit server, aby nepoužíval cache.


SELECT SQL_NO_CACHE *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;

Pro pochopení, jak zpracovává složitější dotaz SQL server, lze využívat příkaz EXPLAIN, který se jednoduše přidá před zkoumaný dotaz.


EXPLAIN SELECT *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;

Pokud ale hledáme ideální řešení a vybíráme mezi více dotazy, které se liší strukturou, ale vracejí stejná data, příkaz EXPLAIN nám toho moc neřekne, místo něho je lepší použít proměnou last_query_cost. Optimalizátor tuto hodnotu nastaví vždy při vykonání nějakého dotazu v daném sezení a obsahuje nějaké číslo. Čím větší číslo, tím složitější dotaz.


SELECT *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;
SHOW SESSION STATUS WHERE Variable_name LIKE 'last_query_cost';

Samozřejmě mi nedá abych nepřipomenul, že velkou práci databázovému serveru ušetříte správným používáním indexů. Nakonec přikládám malý prográmek v Pythonu 3, který generuje náhodné hodnoty do tabulek produkty a atributy. Definice tabulek je následující:


CREATE TABLE IF NOT EXISTS `atributy` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`produkt` int(11) NOT NULL,
`type` int(11) NOT NULL,
`value_number` int(11) DEFAULT NULL,
`value_text` text COLLATE utf8_czech_ci,
`value_datetime` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `produkt` (`produkt`,`type`)
) ENGINE=InnoDB;


CREATE TABLE IF NOT EXISTS `produkty` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) COLLATE utf8_czech_ci NOT NULL,
`price` decimal(8,2) NOT NULL,
`active` int(11) NOT NULL,
`text` text COLLATE utf8_czech_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

A slíbený program je zde:


import random

alphabet = 'abcdefghijklmnopqrstuvwxz '

def getString(mn, mx):
"""
Generate random string with the length between mn and mx.
"""
s = ''
for i in range(random.randint(mn, mx)):
s += alphabet[random.randint(0, len(alphabet) -1)]
return s

# generates data for table produkty
for i in range(1000):
print('INSERT INTO `produkty` (`id`, `name`, `price`, `active`, `text`) '
+ 'VALUES (NULL, \'%s\', \'%s\', %s, \'%s\');'
% (getString(3, 20), str(random.randint(10, 1000)),
str(random.randint(0, 1)), getString(50, 500)))

# generates data for table atributy
for i in range(1000):
for j in range(5):
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', %s, \'NULL\', \'NULL\');'
% (str(i), '2', str(random.randint(10, 1000))))
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', \'NULL\', \'%s\', \'NULL\');'
% (str(i), '3', getString(10, 1000)))
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', \'NULL\', \'NULL\', \'%s\');'
% (str(i), '4', ('2009-' + str(random.randint(1, 12)) + '-'
+ str(random.randint(1, 28)) + ' 19:08:23')))
Tags: Internet | Programování | Python | Počítače | Databáze



Wiki2Tex - konverze Wiki syntaxe do LaTeXu psaná v Pythonu

Nov|25 2009

Od letošního roku jsem si zvykl používat DokuWiki pro editaci a správu různých dokumentů v psané podobě - technické zprávy, diplomová práce apod. Nejen že dokumenty jsou kdykoliv kdekoliv přístupné (DokuWiki nepoužívá ani databázi, stačí server s podporou PHP), ale udržují se informace o změnách a v případě chybné editace je možné starší verze dokumentů snadno dohledat. Takový primitivní Subversion, ovšem pro správu textů dostačující.

Nyní mě napadlo, že není složité převést syntaxi DokuWiki na syntaxi LaTeXu a tím pádem si ušetřit mnoho práce při odevzdávání. Bohužel jsem podobný program nenašel, tak jsem se pustil do jeho psaní. Vznikla aplikace Wiki2Tex v Pythonu 3 (bohužel není kompatibilní s verzemi 2.x), která pomocí regulárních výrazů převádí text v syntaxi Wiki do syntaxe LaTeXu. Převod je samozřejmě jen orientační a některé závěrečné úpravy musí být provedeny ručně, stejně jako definice dokumentu LaTeXu. Přesto doufám, že se bude hodit alespoň mě, v lepším případě i někomu jinému.

Aplikace Wifi2Tex.zip ke stažení.

Malá ukázka, jakým způsobem je převod implementován:


# images
inputStr = re.sub( r'\\\{\\\{.*:([^:]*)\|(.*)\\\}\\\}',
r'\\begin{figure}\n\centering\n\includegraphics[scale=1]{\1}'
+ r'\n\caption{\2}\n\end{figure}', inputStr)

# chapters and sections
chapters = [ #['chapter', '====='],
['section', '====='],
['subsection', '===='],
['subsubsection', '===']]

for i in range(len(chapters)):
inputStr = re.sub( chapters[i][1] + r'\s*([^=]*[^=\s]+)\s*' + chapters[i][1],
r'\\' + chapters[i][0] + r'{\1}', inputStr)

# bold and italic
inputStr = re.sub( r'\*\*([^\*]+)\*\*',
r'{\bf \1}', inputStr)
inputStr = re.sub( r'//([^/]+)//',
r'{\it \1}', inputStr)

Tags: Programování | Python | Počítače



Vyzkoušel jsem si Chrome OS

Nov|23 2009

Určitě jste zaregistrovali vypuštění první betaverze operačního systému Chrome OS. Netrvalo dlouho a systém je k dispozici nejen ve zdrojových souborech, ale i jako virtuální obraz, takže vyzkoušet si ho může i zdatnější uživatel-neprogramátor. Stačí podle návodu například na Rootu nainstalovat VirtualBox (69MB, v programu je nutné se zdarma registrovat u firmy Sun) a stáhnout připravený obraz (zip 313MB, rozbalený 752MB).

Chrome OSKdo už zkoušel nebo používá Chrome prohlížeč, nebude mu prostředí systému Chrome OS připadat vůbec cizí, skládá se totiž právě z tohoto prohlížeče. Všechny aplikace jsou potom dostupné jako webové aplikace, každá webová aplikace je potom i aplikací systému Chrome OS.

Znamená to sice, že bez připojení k internetu budete mít počítač k ničemu, na druhou stranu začíná být internet čím dál více všudepřítomný a málokdy běžný uživatel používá počítač v prostředí bez dostupnosti k internetu. V budoucnosti tento problém zmizí nadobro.

V souvislosti s Chrome OS se často mluví o netboocích, které se rychle dostávají do povědomí i vlastnictví široké veřejnosti - vždyť surfování je jedna z nejčastějších a v mnohých případech jediná činnost běžného uživatele počítače. Dokáži si tím pádem lehce představit, že předinstalovaný Chrome OS se časem stane nativní součástí všech nových levnějších netbooků. Uvědomíme-li si navíc, že už k samotnému přihlášení k systému je nutné vlastnit účet u Googlu, můžeme očekávat, že svému stvořiteli přinese nemalé zisky.

Pokud i nyní někdo kroutí hlavou, s čím to zas Google přišel, ať počká pár měsíců, borci u Googlu to mají určitě dobře spočítaný.

Tags: Osobní | Internet | Prohlížeče | Počítače



Film Veřejní nepřátelé - komentář

Nov|16 2009

Na první dojem klasická mafiánská gangsterka. V průběhu filmu jsem si ale uvědomil, že je dělaná trochu jinak. Oproti podobným filmům s podobnou tématikou vidím detailnější exteriéry, interiéry i kostýmy. Ty jsou v kontrastu s moderní kamerou a atmosféru výborně doplňují realistické scény přestřelek.

I poslední scéna je trochu netradiční, kdy místo umírajícího hlavního záporáka vidím pro mě daleko dojemnější scénu. Zkrátka i v dnešní době jde film o mafii z 30. let natočit jinak a v tomhle případě se to velmi povedlo.

Verdikt: 90%

Tags: Zábava | Filmy