<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
	<id>http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Chyby_p%C5%99i_n%C3%A1vrhu_rela%C4%8Dn%C3%AD_datab%C3%A1ze</id>
	<title>Chyby při návrhu relační databáze - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Chyby_p%C5%99i_n%C3%A1vrhu_rela%C4%8Dn%C3%AD_datab%C3%A1ze"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Chyby_p%C5%99i_n%C3%A1vrhu_rela%C4%8Dn%C3%AD_datab%C3%A1ze&amp;action=history"/>
	<updated>2026-05-12T22:46:30Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=Chyby_p%C5%99i_n%C3%A1vrhu_rela%C4%8Dn%C3%AD_datab%C3%A1ze&amp;diff=578&amp;oldid=prev</id>
		<title>imported&gt;Pavel: Založena nová stránka s textem „Autor: &#039;&#039;Pavel Stěhule&#039;&#039; Category:Články  Při monitorování systému se snažíme o detekci úzkých hrdel, které mohou být jak v hw, tak v sw.…“</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Chyby_p%C5%99i_n%C3%A1vrhu_rela%C4%8Dn%C3%AD_datab%C3%A1ze&amp;diff=578&amp;oldid=prev"/>
		<updated>2016-11-21T07:40:20Z</updated>

		<summary type="html">&lt;p&gt;Založena nová stránka s textem „Autor: &amp;#039;&amp;#039;Pavel Stěhule&amp;#039;&amp;#039; &lt;a href=&quot;/wiki/Kategorie:%C4%8Cl%C3%A1nky&quot; title=&quot;Kategorie:Články&quot;&gt;Category:Články&lt;/a&gt;  Při monitorování systému se snažíme o detekci úzkých hrdel, které mohou být jak v hw, tak v sw.…“&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nová stránka&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Autor: &amp;#039;&amp;#039;Pavel Stěhule&amp;#039;&amp;#039;&lt;br /&gt;
[[Category:Články]]&lt;br /&gt;
&lt;br /&gt;
Při monitorování systému se snažíme o detekci úzkých hrdel, které mohou být jak v hw, tak v sw.&lt;br /&gt;
U těch systémů s extrémními problémy bez extrémní zátěže (300-400 připojených uživatelů by na dnešním hw nemělo  pro db&lt;br /&gt;
představovat větší zátěž ), jsou problémy většinou v návrhu databáze a v návrhu dotazů. Je proto dobré začít s popisem nejčastějších a nejhorších problémů.&lt;br /&gt;
&lt;br /&gt;
Pokud je problém v sw, tak primárně musí být v sw také opraven. Krátkodobě si lze pomoci silnějším&lt;br /&gt;
hw (více paměti, SSD disky, apod), tím se ovšem problém nevyřeší, pouze odsouvá.&lt;br /&gt;
&lt;br /&gt;
Aktuálně největší problém vývoje databází je fakt, že minimum vývojářů má realistickou představu&lt;br /&gt;
o tom, jak mohou být databáze rychlé. Někdy se přehlížejí závažné problémy s databází, jindy mají&lt;br /&gt;
vývojáři naprosto nereálná očekávání. &lt;br /&gt;
&lt;br /&gt;
==Jak nenavrhovat schéma relační databáze==&lt;br /&gt;
Při návrhu databáze musíme mít na paměti, pro kolik uživatelů databázi navrhujeme a jak bude databáze&lt;br /&gt;
velká. Pokud máme málo uživatelů, málo dat (do několika málo miliónů řádků), tak nám realita hodně&lt;br /&gt;
chyb promine. Pokud máme mnoho dat, mnoho uživatelů, tak pak chyby v návrhu většinou způsobují&lt;br /&gt;
obtížné provozní problémy a většinou i problémy při údržbě a dalším rozšiřování software.&lt;br /&gt;
&lt;br /&gt;
===Antipattern - pokusy o dědičnost===&lt;br /&gt;
&amp;lt;b&amp;gt;Relační databáze nijak nepodporují dědičnost&amp;lt;/b&amp;gt; (Postgres, je výjimka potvrzující pravidlo). Pokusy&lt;br /&gt;
o implementaci dědičnosti způsobem, kdy každé třídě odpovídá tabulka, vedou k velkému množství tabulek, potažmo  k dotazům s velkým množstvím JOINů (musím je udělat abych získal ucelenou entitu (objekt)).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- špatně!&lt;br /&gt;
CREATE TABLE clovek(&lt;br /&gt;
  id integer PRIMARY KEY,&lt;br /&gt;
  jmeno text,&lt;br /&gt;
  prijmeni text&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE zamestnanec(&lt;br /&gt;
  id integer PRIMARY KEY,&lt;br /&gt;
  zarazeni integer REFERENCES ciselnikX(id),&lt;br /&gt;
  mzda numeric&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE student(&lt;br /&gt;
  id integer PRIMARY KEY,&lt;br /&gt;
  studijni_obor integer REFERENCES ciselnikY(id),&lt;br /&gt;
  rocnik integer&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Co je špatného na velkém množství JOINů (na zbytečném JOINování)? Každý JOIN znamená další chybu&lt;br /&gt;
v odhadu výsledku - u velkého množství JOINů jsou odhady zatížené tak velkou chybou, že se skoro&lt;br /&gt;
nedá pak mluvit o optimálním prováděcím plánu.&lt;br /&gt;
&lt;br /&gt;
Nehezkým fixem je denormalizace - což je vlastně pokus o neúplný model bez dědičnosti - jde se&lt;br /&gt;
několik kroků zpátky. Denormalizace je špatná z několika důvodů: 1) zatemňuje návrh schématu (hůře&lt;br /&gt;
se píší dotazy), 2) aktualizace dat je výrazně náročnější - musí se modifikovat více tabulek, je&lt;br /&gt;
náročnější zajistit konzistenci.&lt;br /&gt;
&lt;br /&gt;
Pokud se používá relační databáze a očekává se větší zátěž - pak je základem zapomenout při&lt;br /&gt;
návrhu schématu na OOP. Mám entity - ty by pak měly odpovídat tabulkám. Zde není plán B, žádná&lt;br /&gt;
možnost, jak efekt chybně navrženého schématu opravit - jedině to utlouct hw, a smířit se s horším&lt;br /&gt;
výkonem (a náročnější údržbou) systému.&lt;br /&gt;
&lt;br /&gt;
Dědičnost v Postgresu nepoužívejte - sice netrpí problémy s JOINy - na rozdíl od výše uvedené&lt;br /&gt;
ukázky dochází k propagaci atributů předka k potomkům, takže tam odpadají JOINy. Závažný problém&lt;br /&gt;
je jinde - pro objektový model není připravena kontrola referenční integrity - tu by si člověk&lt;br /&gt;
musel napsat sám.&lt;br /&gt;
&lt;br /&gt;
Uznávám, že OOP (zde ORM) pomůže při návrhu aplikace. Pro relativně malá data je cena za ORM&lt;br /&gt;
akceptovatelná. U větších databází (desítky, stovky GB) může objektový návrh působit neuvěřitelné&lt;br /&gt;
(a hlavně neopravitelné, neřešitelné) problémy. &lt;br /&gt;
&lt;br /&gt;
===Antipattern - pokus o univerzální model (EAV)===&lt;br /&gt;
[https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model Entity-Attribute-Values] je dalším oblíbeným vzorem vývojářů, který má ale naprosto fatální důsledky&lt;br /&gt;
na výkon databáze. Relační databáze předpokládá, že jsou data uloženy v normalizovaném schématu.&lt;br /&gt;
Každý sloupeček nese jeden atribut, který má jednoznačný význam. EAV tento předpoklad &lt;br /&gt;
staví na hlavu. Jeden sloupeček může obsahovat všechny atributy (v EAV schématu). S EAV, abychom&lt;br /&gt;
získaly entitu, musíme udělat několik self JOINů - tolik kolik má entita atributů. Jakmile se dostanete&lt;br /&gt;
za hranici HashJoinu (vypadnete z RAM), tak rychlost načítání entit z databáze dramaticky poklesne.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--špatně!&lt;br /&gt;
CRATE TABLE data(&lt;br /&gt;
  id_objektu SERIAL PRIMARY KEY,&lt;br /&gt;
  id_vlastnosti integer,&lt;br /&gt;
  hodnota text&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
V ideálním světě mám všechny data uložené v normalizované databázi. V praxi by to ale mohlo vést k&lt;br /&gt;
řídké databázi (tabulkám, kde většina obsahu budou hodnoty NULL). Databázi je to jedno, ale špatně se &lt;br /&gt;
s takovou databází pracuje. Je dobré nalézt kompromis. Atributy, které jsou univerzální např (hmotnost,&lt;br /&gt;
výška, šířka, hloubka, barva) ukládáme normalizovaně. U ostatních atributů si můžeme pomoci strukturovanými hodnotami (XML, JSON, HStore) nebo přinejhorším EAV.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- dobře&lt;br /&gt;
CREATE TABLE produkty(&lt;br /&gt;
  id_objektu SERIAL PRIMARY KEY,&lt;br /&gt;
  nazev text,&lt;br /&gt;
  hmotnost numeric,&lt;br /&gt;
  barva_rgb text,&lt;br /&gt;
  x int,&lt;br /&gt;
  y int,&lt;br /&gt;
  z int,&lt;br /&gt;
  ostatni jsonb&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Nevhodné použití typů pro primární klíče===&lt;br /&gt;
Pokud je primárním klíčem 128 znakový string, tak nemůžete čekat žádnou skvělou rychlost JOINů. Navíc tyto klíče zaberou hodně místa RAM - nevejdete se do &amp;lt;i&amp;gt;work_mem&amp;lt;/i&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Umístění často modifikovaného atributu do tabulky s důležitým primárním klíčem===&lt;br /&gt;
Většinu zámků v relační databázi generuje referenční integrita. Pokud často modifikujete tabulky, které obsahují&lt;br /&gt;
důležité primární klíče (např. id uživatele), pak se množství zámků zvyšuje. S větší zátěží může docházet k deadlockům,&lt;br /&gt;
k dlouhým čekáním na zámky, atd. Je proto praktické často měněné atributy z takových tabulek přesunout do speciálně&lt;br /&gt;
vytvořené tabulky (většinou stejně takový atribut neodpovídá realitě, a je to jen zkratka vývojáře). Např. v &lt;br /&gt;
tabulce uživatelů by neměla být hodnota &amp;quot;čas poslední akce&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Chybějící indexy, nebo příliš mnoho indexů===&lt;br /&gt;
Chybějící indexy - to je taková klasika. Ještě jsem nezažil aplikaci, kde by nějaký index nechyběl. Co mne&lt;br /&gt;
zaráží je neaktivita vývojářů - přidat index je práce na deset minut, a zrychlení aplikace může být neuvěřitelné.&lt;br /&gt;
Díky vyšší rychlosti mohou zmizet provozní problémy (např. vyčerpávání spojení do databáze, atd).&lt;br /&gt;
&lt;br /&gt;
Je důležité aplikaci dostat, co nejrychleji, do produkce ve stavu, kdy nekolabuje a je použitelná. &amp;lt;b&amp;gt;Při startu&lt;br /&gt;
aplikace intenzivně sleduji pomalé dotazy, časté dotazy, zkontroluji je a případné chybějící indexy přidám&amp;lt;/b&amp;gt;. Je&lt;br /&gt;
důležité po pár měsících tuto proceduru zopakovat, aby indexy vždy odpovídaly velikosti databáze, provozu,&lt;br /&gt;
používání. Indexy se často dávají na sloupečky cizích klíčů, a důležitých atributů s dobrou selektivitou&lt;br /&gt;
(název, čas vložení, referenční datum, atd). Na primárních klíčích jsou implicitně. Není ostuda zapomenout index.&lt;br /&gt;
Je ale naprosto ostudné provozovat databázi a chybějící indexy nevytvořit.&lt;br /&gt;
&lt;br /&gt;
Tak jak indexy zrychlují získávání dat, tak zpomalují změny dat - údržba indexu je jedna z nejnáročnějších&lt;br /&gt;
operací v databázi. Příliš mnoho indexů způsobuje pomalé příkazy &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;. Nepoužívané indexy smažte. Statistiku použití indexů najdete v Postgresu v tabulce &amp;lt;code&amp;gt;pg_stat_user_indexes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Abych věděl, kam mám umístit index, potřebuji zjistit pomalé a časté dotazy. Z možností, které mám v Postgresu,&lt;br /&gt;
mi pořád nejvíc vyhovuje [http://pgfouine.projects.pgfoundry.org/ PgFouine] - jednoduchá, jednoúčelová aplikace, která ze zalogovaných dotazů vytvoří&lt;br /&gt;
[http://pgfouine.projects.pgfoundry.org/reports/sample_default.html report] s nejpomalejšími, nejčastějšími, a nejnáročnějšími (čas x četnost) dotazy. Pokud provozujete relační&lt;br /&gt;
kritičtější databázi, tak čtení takového reportu by měl být Váš denní chleba. Samozřejmě, že ze samotného dotazu nic  moc nezjistím - musím umět číst prováděcí plány - viz příkaz [https://www.postgresql.org/docs/9.6/static/using-explain.html&amp;quot; &amp;lt;code&amp;gt;EXPLAIN&amp;lt;/code&amp;gt;] (&amp;lt;code&amp;gt;EXPLAIN ANALYZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Při troše znalostí (těch nutných znalostí opravdu není mnoho) zjistíte, že relační databáze mohou být fantasticky&lt;br /&gt;
rychlé, a že používat relační databáze je hrozně jednoduché.&lt;br /&gt;
&lt;br /&gt;
==Jak nepsat dotazy==&lt;br /&gt;
&lt;br /&gt;
===Nepoužívání čistých predikátů===&lt;br /&gt;
Pro psaní dotazů je dobré znát termín &amp;lt;i&amp;gt;čistý predikát&amp;lt;/i&amp;gt; a používat jej. Čistý predikát je predikát ve formátu&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  atribut OP konstanta nebo výraz (pro výrazy platí určitá omezení)&lt;br /&gt;
&amp;lt;/pre&amp;gt;  &lt;br /&gt;
tj &amp;lt;code&amp;gt;jmeno = &amp;#039;Pavel&amp;#039;&amp;lt;/code&amp;gt; nebo &amp;lt;code&amp;gt;jmeno = upper(&amp;#039;Pavel&amp;#039;)&amp;lt;/code&amp;gt; jsou &amp;lt;i&amp;gt;čisté predikáty&amp;lt;/i&amp;gt;. Naopak &amp;lt;code&amp;gt;upper(jmeno) = &amp;#039;PAVEL&amp;#039;&amp;lt;/code&amp;gt; už &lt;br /&gt;
čistým predikátem není. Pro většinu relačních databází platí, že se indexy použijí jen tehdy, pokud se podmínka&lt;br /&gt;
zapsala jako čistý predikát (nebo kombinace čistých predikátů). Můžeme si pomoci funkcionálními indexy,&lt;br /&gt;
ale to už jsou indexy navíc (víc času budeme pálit na aktualizaci indexů).&lt;br /&gt;
&lt;br /&gt;
Zkušenější programátoři už znají vzory pro různé situace a napíší je efektivně. Např. pro filtrování dat&lt;br /&gt;
z listopadu 2016 mohu napsat několik různých forem predikátů:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;extrémně špatné řešení&amp;lt;/b&amp;gt; citlivé na konfiguraci (pokud se nějak změní datestyle tak přestane fungovat)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
WHERE substring(d::text FROM 1 FOR 7) = &amp;#039;2016-11&amp;#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;spatné řešení (není čistým predikátem, používají se konverze do stringu a stringové operace&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
WHERE to_char(d, &amp;#039;YYYYMM&amp;#039;) = &amp;#039;201611&amp;#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;ne úplně ideální řešení - není čistým predikátem, pracuji s více funkcemi&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
WHERE EXTRACT(month FROM d) = 11 AND EXTRACT(year FROM d) = 2016&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;ideální řešení&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
WHERE d &amp;gt;= &amp;#039;2016-11-01&amp;#039; AND d &amp;lt; &amp;#039;2016-12-01&amp;#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jiný příklad - výběr záznamů vložených předchozích den: &amp;lt;pre&amp;gt;vloženo + 1 = CURRENT_DATE -- špatně!&amp;lt;/pre&amp;gt; není čistým predikátem, a index se nepoužije. Naopak &amp;lt;pre&amp;gt;vloženo = CURRENT_DATE - 1&amp;lt;/pre&amp;gt; je čistým predikátem a index nad sloucem &amp;lt;code&amp;gt;vloženo&amp;lt;/code&amp;gt; se použije.&lt;br /&gt;
&lt;br /&gt;
S málo daty, s málo uživateli mohu relační databázi používat téměř bez jakýchkoliv znalostí. U větších&lt;br /&gt;
dat, u více uživatelů je nezbytné minimum znalostí dost zásadní. Relační databáze fungují spolehlivě,&lt;br /&gt;
jednoduše, když víte co děláte. Vyplatí se do sebe zainvestovat den, dva, aby si člověk přečetl základy,&lt;br /&gt;
a aby pro něj databáze nebyla magická černá skříňka.&lt;/div&gt;</summary>
		<author><name>imported&gt;Pavel</name></author>
	</entry>
</feed>