sql >> Databáze >  >> RDS >> Sqlserver

Porovnání výkonu SQL Server CE 4.0

Podle mého názoru je nesprávné porovnávat vestavěnou databázi (jako SQL CE) s relační databází na straně serveru (jako všechny ostatní, kromě SQLite a Embedded verze Firebirdu).

Hlavním rozdílem mezi nimi je to, že univerzální relační databáze na straně serveru (jako MS SQL, MySQL, Firebird Classic a SuperServer atd.) se instalují jako nezávislá služba a běží mimo rozsah vaší hlavní aplikace . To je důvod, proč mohou fungovat mnohem lépe, protože mají vlastní podporu pro vícejádrové a víceprocesorové architektury, využívající funkce OS, jako je předběžné ukládání do mezipaměti, VSS atd., ke zvýšení propustnosti v případě intenzivního provozu databáze a mohou vyžadovat tolik paměti jako váš OS může poskytovat jednu službu/aplikaci. Znamená to také, že ukazatele výkonu pro ně jsou víceméně nezávislé na vaší aplikaci, ale do značné míry závisí na vašem hardwaru. V tomto ohledu bych řekl, že serverové verze jakékoli databáze jsou vždy výkonnější než ty vestavěné.

SQL CE (spolu s Firebird Embedded, SQLite, TurboSQL a některými dalšími) jsou embedded DB motory , což znamená, že kompletní databáze je zabalena do jednoho (nebo maximálně 2) DLL souborů, které jsou distribuovány společně s vaší aplikací. Kvůli zjevným omezením velikosti (chtěli byste distribuovat 30 MB DLL společně s vaší 2–3 MB dlouhou aplikací?) také běží přímo v kontextu vaší aplikace a celková paměť a výkon operací přístupu k datům jsou sdíleny s ostatními částmi vaší aplikace -- což se týká jak dostupné paměti, času CPU, propustnosti disku atd. Výpočetně náročná vlákna běžící paralelně s vláknem pro přístup k datům může vést k dramatickému snížení výkonu vaší databáze.

Vzhledem k různým oblastem použití mají tyto databáze různou paletu možností:server-db poskytuje rozsáhlou správu uživatelů a práv, podporu pohledů a uložených procedur, zatímco vestavěné databáze obvykle postrádají jakoukoli podporu pro správu uživatelů a práv a mají omezenou podporu pro pohledy. a uložené procedury (poslední ztrácejí většinu výhod plynoucích ze spuštění na straně serveru). Datová propustnost je obvyklou překážkou RDBMS, serverové verze se obvykle instalují na prokládané svazky RAID, zatímco embedded DB jsou často orientované na paměť (snažte se udržet všechna skutečná data v paměti) a minimalizují operace přístupu k datovým úložištím.

Takže pravděpodobně by dávalo smysl porovnat různé vestavěné RDBMS pro .Net z hlediska jejich výkonu, jako je MS SQL CE 4.0, SQLite, Firebird Embedded, TurboSQL . Neočekával bych drastické rozdíly během běžného nešpičkového provozu, zatímco některé databáze mohou poskytovat lepší podporu pro velké objekty BLOB díky lepší integraci s OS.

-- aktualizovat --

Musím vzít zpět svá poslední slova, protože moje rychlá implementace ukazuje velmi zajímavé výsledky.

Napsal jsem krátkou konzolovou aplikaci pro testování obou poskytovatelů dat, zde je zdrojový kód pro vás, pokud s nimi chcete experimentovat na vlastní pěst.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SQLite;
using System.Data.SqlServerCe;
using System.Data.Common;

namespace TestSQL
{
    class Program
    {
        const int NUMBER_OF_TESTS = 1000;

        private static string create_table;

        private static string create_table_sqlce =  "CREATE TABLE Test ( id integer not null identity primary key, textdata nvarchar(500));";
        private static string create_table_sqlite = "CREATE TABLE Test ( id integer not null primary key, textdata nvarchar(500));";

        private static string drop_table = "DROP TABLE Test";
        private static string insert_data = "INSERT INTO Test (textdata) VALUES ('{0}');";
        private static string read_data = "SELECT textdata FROM Test WHERE id = {0}";
        private static string update_data = "UPDATE Test SET textdata = '{1}' WHERE id = {0}";
        private static string delete_data = "DELETE FROM Test WHERE id = {0}";

        static Action<DbConnection> ACreateTable = (a) => CreateTable(a);
        static Action<DbConnection> ATestWrite = (a) => TestWrite(a, NUMBER_OF_TESTS);
        static Action<DbConnection> ATestRead = (a) => TestRead(a, NUMBER_OF_TESTS);
        static Action<DbConnection> ATestUpdate = (a) => TestUpdate(a, NUMBER_OF_TESTS);
        static Action<DbConnection> ATestDelete = (a) => TestDelete(a, NUMBER_OF_TESTS);
        static Action<DbConnection> ADropTable = (a) => DropTable(a);

        static Func<Action<DbConnection>,DbConnection, TimeSpan> MeasureExecTime = (a,b) => { var start = DateTime.Now; a(b); var finish = DateTime.Now; return finish - start; };

        static Action<string, TimeSpan> AMeasureAndOutput = (a, b) => Console.WriteLine(a, b.TotalMilliseconds);

        static void Main(string[] args)
        {
            // opening databases
            SQLiteConnection.CreateFile("sqlite.db");
            SQLiteConnection sqliteconnect = new SQLiteConnection("Data Source=sqlite.db");
            SqlCeConnection sqlceconnect = new SqlCeConnection("Data Source=sqlce.sdf");

            sqlceconnect.Open();
            sqliteconnect.Open();

            Console.WriteLine("=Testing CRUD performance of embedded DBs=");
            Console.WriteLine(" => Samplesize: {0}", NUMBER_OF_TESTS);

            create_table = create_table_sqlite;
            Console.WriteLine("==Testing SQLite==");
            DoMeasures(sqliteconnect);

            create_table = create_table_sqlce;
            Console.WriteLine("==Testing SQL CE 4.0==");
            DoMeasures(sqlceconnect);



            Console.ReadKey();

        }

        static void DoMeasures(DbConnection con)
        {
            AMeasureAndOutput("Creating table: {0} ms", MeasureExecTime(ACreateTable, con));
            AMeasureAndOutput("Writing data: {0} ms", MeasureExecTime(ATestWrite, con));
            AMeasureAndOutput("Updating data: {0} ms", MeasureExecTime(ATestUpdate, con));
            AMeasureAndOutput("Reading data: {0} ms", MeasureExecTime(ATestRead, con));
            AMeasureAndOutput("Deleting data: {0} ms", MeasureExecTime(ATestDelete, con));
            AMeasureAndOutput("Dropping table: {0} ms", MeasureExecTime(ADropTable, con));
        }



        static void CreateTable(DbConnection con)
        {
            var sqlcmd = con.CreateCommand();
            sqlcmd.CommandText = create_table;
            sqlcmd.ExecuteNonQuery();
        }

        static void TestWrite(DbConnection con, int num)
        {
            for (; num-- > 0; )
            {
                var sqlcmd = con.CreateCommand();
                sqlcmd.CommandText = string.Format(insert_data,Guid.NewGuid().ToString());
                sqlcmd.ExecuteNonQuery();
            }

        }

        static void TestRead(DbConnection con, int num)
        {
            Random rnd = new Random(DateTime.Now.Millisecond);
            for (var max = num; max-- > 0; )
            {
                var sqlcmd = con.CreateCommand();
                sqlcmd.CommandText = string.Format(read_data, rnd.Next(1,num-1));
                sqlcmd.ExecuteNonQuery();
            }
        }

        static void TestUpdate(DbConnection con, int num)
        {
            Random rnd = new Random(DateTime.Now.Millisecond);
            for (var max = num; max-- > 0; )
            {
                var sqlcmd = con.CreateCommand();
                sqlcmd.CommandText = string.Format(update_data, rnd.Next(1, num - 1), Guid.NewGuid().ToString());
                sqlcmd.ExecuteNonQuery();
            }
        }

        static void TestDelete(DbConnection con, int num)
        {
            Random rnd = new Random(DateTime.Now.Millisecond);
            var order = Enumerable.Range(1, num).ToArray<int>();
            Action<int[], int, int> swap = (arr, a, b) => { int c = arr[a]; arr[a] = arr[b]; arr[b] = c; };

            // shuffling the array
            for (var max=num; max-- > 0; ) swap(order, rnd.Next(0, num - 1), rnd.Next(0, num - 1));


            foreach(int index in order)
            {
                var sqlcmd = con.CreateCommand();
                sqlcmd.CommandText = string.Format(delete_data, index);
                sqlcmd.ExecuteNonQuery();
            }
        }

        static void DropTable(DbConnection con)
        {
            var sqlcmd = con.CreateCommand();
            sqlcmd.CommandText = drop_table;
            sqlcmd.ExecuteNonQuery();
        }


    }
}
 

Nezbytné prohlášení:

  1. Na svém počítači jsem získal tyto výsledky:Dell Precision WorkStation T7400 vybavený 2 procesory Intel Xeon E5420 a 8 GB paměti RAM s 64bitovým Win7 Enterprise .
  2. Použil jsem výchozí nastavení pro obě databáze s připojovacím řetězcem "Zdroj dat=název_databázového_souboru".
  3. Použil jsem nejnovější verze SQL CE 4.0 a SQLite/System.Data.SQLite (od dnešního dne, 3. června 2011).

Zde jsou výsledky pro dva různé vzorky:

> =Testování výkonu CRUD embedded DB=> => Velikost vzorku:200> ==Testování SQLite==> Vytváření tabulky:396,0396 ms> Zápis dat:22189,2187 ms> Aktualizace dat:23591,3589 ms> 21 ms čtení dat:0,0,0 ms Mazání dat:20963,0961 ms> Zahození tabulky:85,0085 ms> ==Testování SQL CE 4.0==> Vytvoření tabulky:16,0016 ms> Zápis dat:25,0025 ms> Aktualizace dat:5 ms> 28056 s> Tabulka spouštění:11,0011 ms

... a větší ukázka:

 =testování Crud Performance of Embedded DBS ==> Vzorky:1000 ==Testování SQLite ==Vytváření tabulky:93,0093 MSWriting Data:116632.6621 MSUPDating Data:104967.4957 MSREAding Data:134,0134 MSDeteting Data:83.0000333333333333333333333333333333333333333330. SQL CE 4.0==Vytváření tabulky:16,0016 msZápis dat:128,0128 msAktualizace dat:307,0307 msČtení dat:164,0164 msMazání dat:306,0306 ms
 Jak tedy vidíte, jakékoli operace zápisu (vytvoření, aktualizace, odstranění) vyžadují v SQLite téměř 1000x více času ve srovnání s SQLCE. Nemusí to nutně odrážet obecný špatný výkon této databáze a může to být způsobeno následujícím:

  1. Poskytovatel dat, kterého používám pro SQLite, je System.Data.SQLite , což je smíšené sestavení obsahující spravovaný i nespravovaný kód (SQLite je původně napsán kompletně v C a DLL poskytuje pouze vazby). P/Invoke a zařazování dat pravděpodobně zabere značnou část provozního času.
  2. S největší pravděpodobností SQLCE 4.0 ve výchozím nastavení ukládá všechna data do paměti, zatímco SQLite vyprázdní většinu změn dat přímo do diskového úložiště pokaždé, když ke změně dojde. Prostřednictvím připojovacího řetězce lze dodat stovky parametrů pro obě databáze a vhodně je vyladit.
  3. K testování databáze jsem použil sérii jednotlivých dotazů. SQLCE alespoň podporuje hromadné operace prostřednictvím speciálních tříd .Net, které by se sem hodily lépe. Pokud je podporuje i SQLite (omlouvám se, nejsem zde odborník a moje rychlé hledání nepřineslo nic slibného), bylo by hezké je také porovnat.
  4. Všiml jsem si mnoha problémů s SQLite na počítačích x64 (používajících stejný adaptér .net):od neočekávaného ukončení datového připojení až po poškození souboru databáze. Předpokládám, že existují určité problémy se stabilitou buď s datovým adaptérem, nebo se samotnou knihovnou.


  1. Ruby on Rails MySQL #08S01Špatný handshake – downgrade MySQL?

  2. Jak mohu obdržet e-mail, když je moje tabulka MySQL aktualizována?

  3. MySQL IN dotazy strašně pomalu s poddotazy, ale rychle s explicitními hodnotami

  4. Trvalá databáze využívající svazky dockeru