Úterý, srpen 12, 2008

Rychlejší je zápis do souboru nebo do databáze?

Rozhoduji se kam ukládat log záznamy z ASP.NET aplikace a jsou dvě varianty: za prvé do databáze a jako druhá varianta do souborového systému, tady jako prostý textový soubor. S daty potom nepotřebuji nic dělat, jenom je archivovat. Sám bych intuitivné zvolil souborový systém, ale vedoucí programátor mě přesvědčuje zvolit databázi. Rozhodl jsem se tedy obě varianty otestovat.

Při testování vkládám 100000 záznamů. Každý záznam obsahuje jedinečné celé číslo, jedinečné datum a čas a jedinečný řetězec. U každé databáze mám dvě varianty. Za prvé bez indexů a za druhé s indexy nad každým sloupcem. Testoval jsem MySql 5.1 MyISAM, MySql 5.1 InnoDB, MS SQL 2005. Testovací program je v C#. Každý test jsem provedl 5x a výsledek je průměr z těchto hodnot. Pokud byl některý test proveden v jiném počtu, je to uvedeno v poznámce. Nastavení MySql je 500 MB Key buffer, 2 MB Sort buffer size , Query cache limit 1 MB a Cache size 15 MB.

Testovací program C#:

public void RychlostInsertxFile() {
 const uint POCET_ZAZNAMU = 100000;
 const string soubor = "c:\\temp\\RychlostInsertxFile.log";
 zaznam z;
 DateTime cas;
 System.TimeSpan sts;
 // ulozeni do souboru
 Stream s = new FileStream(soubor, FileMode.Create);
 TextWriter tw = new StreamWriter(s);
 cas = System.DateTime.Now;
 for (uint k = 0; k < POCET_ZAZNAMU; k++) {
  z = novyZaznam(k);
  tw.WriteLine(z.datum + "\t" + z.cislo + "\t" + z.retezec);
 }
 sts = DateTime.Now - cas;
 Console.WriteLine("Ukladani do souboru (ms): " + sts.TotalMilliseconds);
 tw.Close();
 s.Close();
   
 // ulozeni do databaze MySql
 MySqlConnection mc = new MySqlConnection("Database=testinsert;Data Source=localhost");
 MySqlCommand commInsert = new MySqlCommand();
 commInsert.Connection = mc;
 mc.Open();

 // vymazani obsahu tabulky z predchozich testu
 commInsert.CommandText = "TRUNCATE TABLE tableinsert";
 commInsert.ExecuteNonQuery();
 cas = System.DateTime.Now;

 for (uint k = 0; k < POCET_ZAZNAMU; k++) {
  z = novyZaznam(k);
  commInsert.CommandText = "INSERT INTO tableinsert (cislo,datum, retezec) Values ("
   + z.cislo + ",'"
   + OtherMetod.DateTime2IsoDateTime(z.datum) + "','"
   + z.retezec + "')";

  commInsert.ExecuteNonQuery();
 }
 sts = DateTime.Now - cas;
 Console.WriteLine("Ukladani do databaze MySql (ms): " + sts.TotalMilliseconds);
 mc.Close(); // uzavreni databaze
    
 /* 
 // ulozeni do databaze MS SQL
 SqlConnection conn = new SqlConnection("server=CORE2-DUO-18\\SQLEXPRESS2005;database=testinsert;trusted_connection=yes");
 conn.Open();
 SqlCommand sqlCom = new SqlCommand();
 sqlCom.Connection = conn;
 // vymazani obsahu tabulky z predchozich testu
 sqlCom.CommandText = "DELETE FROM tableinsert";
 sqlCom.ExecuteNonQuery();

 cas = System.DateTime.Now;
 for (uint k = 0; k < POCET_ZAZNAMU; k++) {
  z = novyZaznam(k);
  sqlCom.CommandText = "INSERT INTO tableinsert (cislo,datum, retezec) Values ("
   + z.cislo + ",'"
   + OtherMetod.DateTime2IsoDateTime(z.datum) + "','"
   + z.retezec + "')";
  sqlCom.ExecuteNonQuery();
 }
 sts = DateTime.Now - cas;
 Console.WriteLine("Ukladani do databaze MS SQL (ms): " + sts.TotalMilliseconds);

 conn.Close();
  */
}
struct zaznam {
 public DateTime datum;
 public uint cislo;
 public string retezec;
}
private zaznam novyZaznam(uint cislo) {
 zaznam nzaznam = new zaznam();
 nzaznam.datum = DateTime.Now.AddHours(cislo);
 nzaznam.cislo = cislo;
 nzaznam.retezec = nzaznam.datum.ToString() + "|" + cislo + "|" + nzaznam.datum.ToString();
 return nzaznam;
}

Výsledky:

Způsob uložení Doba trvání (s) Koeficient
Soubor 1) 1,016 1,00
MS SQL 2005 bez indexů 47,631 46,88
MS SQL 2005 s indexy 53,088 52,25
MySql 5.1 MyISAM bez indexů 29,684 29,22
MySql 5.1 MyISAM s indexy 34,556 34,01
MySql 5.1 InnoDB bez indexů více jak hodinu 2)
MySql 5.1 InnoDB s indexy více jak hodinu 2)

1) Test byl prováděn s každým testem databáze. Tedy celkově 20x. 2) Test trval moc dlouho a neměl jsem na něj trpělivost.

Pokud mám sám závěr hodnotit, tak v případě, kdy je zapotřebí data pouze ukládat a ne je selektivně číst, je souborový systém stále zdaleka nejrychlejší. Můžeme samozřejmě diskutovat o tom, jak se zachová při havárii, ale pokud například odejde pevný disk, ztratí data i databáze. V obou případech je samozřejmě nejdůležitější mít zajištěný nepřerušitelný zdroj napájení (UPS). Souborový systém je nevhodný ještě v jednom případě a to tehdy, kdy uložení dat neprovádí pouze jeden proces nebo vlákno. Konkurenční přístup je u souborů obtížné zajistit a zde je databáze nenahraditelná. Pokud ovšem potřebujete data pouze ukládat a není problém s více vlákny, jsou soubory stále nepřekonané. Katastrofální výsledek MySql InnoDB je určitě minimálně zčásti zaviněn neoptimálním nastavením InnoDB úložiště. Bohužel to lépe neumím.

Testováno na počítači s CPU Intel Core 2 1.80 GHz, 2 GB RAM, MS Windows XP prof. Vlastní hardware ovšem nepovažuji za důležitý, podstatný je rozdíl mezi způsoby uložení.

Myslím, že jsem tímto testem asi přišel na to, proč Microsoft v MS Windows Vista nepoužil nový souborový systém WinFS, který je založený na MS SQL. Je to prostě moc pomalé.

4 komentářů:

Tomáš Tintěra řekl(a)...

DD, používáte špatný způsob zápisu do databáze. SQL příkaz se pokaždé znovu kompiluje a optimalizuje. Např. pro MS SQL zkuste použít proceduru přesně tak, jak je doporučeno. Výsledky budou zcela jiné.

Tomas J. Kouba řekl(a)...

Nenazval bych to špatným způsobem zápisu do databáze. Domnívám se, že rozdíl ani nebude měřitelný. V každém případě ale děkuji za zajímavý námět. Otestuji a zveřejním v budoucnu.

alvar řekl(a)...

Pokud neco archivujete, tak v tom asi budete chtit nekdy neco najit. Takze byste to mel taky nejak zohlednit.

chudobka řekl(a)...

Zkusil jsem tento test více přiblížit realitě. Myslím, že soubor logu není v normální aplikaci stále otevřený, ale otevírá se pro zapsaání jednoho údaje a pak se hned uzavře.
Pokud jsem otevření a zavření souboru (analogicky otevření a zavření connection do databáze) přesunul do cyklu, pak to vypadá poněkud jinak.
Na mé konfiguraci PC to bylo 57,6s pro soubor a 43,1s pro MySql (indexováno, MyISAM). Takže DB je v tomto případě trošku rychlejší a ještě navíc poskytuje možnost vyhkedávání v záznamech...