Avatar billede cbadk Novice
04. november 2005 - 03:21 Der er 18 kommentarer og
2 løsninger

Hastighed i forhold til grafik elementer fra databse

Hejsa

Jeg har tænkt på hvis man gemmer binær data(grafik i mit tilfælde) i en database. Hvordan er hastigheden så i forhold til hvis man henter det direkte fra en mappe. Og hvordan påvirker det serverens performence ?
Avatar billede arne_v Ekspert
04. november 2005 - 08:52 #1
I mange år har det været god latin at performance er meget bedre ved at gemme
filnavne i databasen og selve filerne ved siden af fremfor at gemme indholdet i
databasen.

Men der sker fremskridt hele tiden. Og jeg tror ikke på at rådet er validt længere.

Med moderne software og moderne hardware bør du kunne gemme filerne i databasen.

Et argument for mit synspunkt er at både Oracle og Microsoft enten har eller
er ved at udvikle fil system som bruger databasen til storage.

Og det er jo langt nemmere administrationsmæssigt.
Avatar billede Slettet bruger
05. november 2005 - 05:57 #2
Arne har måske ret hvis og kun hvis performance defineres, som sitet svarer acceptabelt. Hvis det du spørger om er om det er hurtigere at serve billeder direkte fra disk eller serve dem fra en database, så er der vist ingen tvivl om hvad der er hurtigst. Hvis du har billederne i en database så skal du transportere den BLOB , som representere billeder, fra databaseserver til en webserver og du skal med en dynamisk side sende billedet til klienten på et request. Det betyder at du bruger noget båndbrede på dit interne netværk (DB-server til webserver) og lige meget hvordan man vender og drejer det så koster det noget. Hvis du har billederne liggende direkte på disk skal din webserver blot håndterer et statisk request og det vil også være hurtigere end et dynamisk request. Ligeledes hvis du lader web-server håndterer billederne, som statisk request er du fri for selv at skulle holde styr på hvilken type (PNG/GIF/JPEG osv...) dine billeder er, hvor hvis du skal sende dem dynamisk skal du selv sende korrekt header information i forbindelse med request.

Der er som jeg ser det kun et argument for at holde billeder i databaser og det er administration. Specielt hvis du skal sælge et CMS eller ligende til kunder, som ikke har den store ekspertise inhouse så kan du have det hele gemt pænt væk i en database.

Men bottom-line hvis du snakker hvad er hurtigst så er det at serve statiske billeder og hvis du har en meget stort site, som tager meget trafik så lav en server kun med statisk indhold.

just my 2 cents
Avatar billede arne_v Ekspert
05. november 2005 - 12:07 #3
Jeg er lidt uenig

Der er et par faktorer som også skal tages med:
  - det koster meget at åbne filer
  - databaser må formodes at være bedre til at cache da de ved mere om hvordan
    data hænger sammen

Og vi skal også være opmærksom på hvad vi sammenligner - der er faktisk 12 kombinationer:

billede som fil / billede som fil & billedinfo i db / billede & billedinfo i db
lokal db / remote db
lokal fil / remote fil

(ja vel reelt kun 10)

Der er formentlig ikke noget som er hurtigere end den rent statiske løsning:
  billed som fil + lokal fil

Men du sammenligner så:
  billede som fil & billed info i db + lokal fil + remote db
med:
  billede & billedinfo i db + remote db

Mit gæt vil være at det faktisk afhænger af billedets størrelse. Ved tilpas små
billeder kan det sidste faktisk godt være hurtigst. Ved store billeder er det
første nok hurtigst.

Men en mere fair sammenligning ville vel også være:
  billede som fil & billed info i db + remote fil + remote db
med:
  billede & billedinfo i db + remote db
eller:
  billede som fil & billed info i db + lokal fil + lokal db
med:
  billede & billedinfo i db + lokal db

Og der tror jeg altså sagtens at databasen kan være med (ikke mindst
i den første sammenligning !).
Avatar billede Slettet bruger
05. november 2005 - 12:18 #4
Uanset hvordan man ser på det så skal en BLOB fra en DB pakkes i DB's protokol og sendes til noget dynamisk, som sender siden - du koster uanset hvordan det er sat sammen. At sende en statisk fil fra en web-server vil være hurtigere og anvende mindre ressourcer. Ligeledes vil almindelig fil-caching i OS håndterer caching af statiske billeder ligeså godt som en DB-cache.

Vær opmærksom på at jeg ikke taler om, om det performer til praktisk brug, jeg taler alene om hvad der er hurtigst, anvender mindst ressourcer og det er og bliver statiske filer.
Avatar billede arne_v Ekspert
05. november 2005 - 14:34 #5
jeg prøvede lige at lave et lille eksperiment

1000 billeder af 25 KB
WinXP & MSDE 2000 i default config
lokal db & lokal dir
app i C#
scenarie 1: række i db med sti (VARCHAR) og ekstern fil
scenarie 2: række i db med billede (IMAGE)
hente et random billede af gangen

resultat:

Database and file (1 threads): 85 get per second
Database and file (10 threads): 175 get per second
Only database (1 threads): 250 get per second
Only database (10 threads): 242 get per second

Det er markant hurtigere at gemme billedet i databasen.

Nu skal man være lidt forsigtig med hvad man kan konkludere. Man kan ikke
konkludere at det altid er hurtigst at gemme billeder i databasen. Jeg er
112% sikker på at jeg sagtens kunne konstruere et eksempel som modbeviste det.
Men man må kunne konkludere at det ikke altid er hurtigst at have filer
ved siden af databasen.

Og jeg synes ikke at min konfig er så usædvanelig.

Koden for de nysgerrige:

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Threading;

namespace E
{
    public class TestClass
    {
        private const int NPIC = 1000;
        private const int PICSIZ = 25000;
        private const int NGET = 100000;
        private const string CONNSTR = "Server=ARNEPC3;Integrated Security=SSPI;Database=Test";
        private const string BASEDIR = @"C:\pictest\";
        private static Random rng;
        private static int getprthread;
        public static void SetupDbAndFile()
        {
            SqlConnection con = new SqlConnection(CONNSTR);
            con.Open();
            SqlCommand cre = new SqlCommand("CREATE TABLE daf (id INTEGER PRIMARY KEY, path VARCHAR(255))", con);
            cre.ExecuteNonQuery();
            SqlCommand ins = new SqlCommand("INSERT INTO daf VALUES (@id, @path)", con);
            ins.Parameters.Add("@id", SqlDbType.Int);
            ins.Parameters.Add("@path", SqlDbType.VarChar);
            for(int i = 0; i < NPIC; i++)
            {
                byte[] b = new byte[PICSIZ];
                for(int j = 0; j < b.Length; j++) b[j] = (byte)(i % 256);
                string fnm = BASEDIR + i + ".pic";
                ins.Parameters["@id"].Value = i;
                ins.Parameters["@path"].Value = fnm;
                ins.ExecuteNonQuery();
                Stream stm = new FileStream(fnm, FileMode.CreateNew, FileAccess.Write);
                stm.Write(b, 0, b.Length);
                stm.Close();
            }
            con.Close();
        }
        private static void TestDbAndFileThread()
        {
            for(int i = 0; i < getprthread; i++)
            {
                int id = rng.Next(NPIC);
                SqlConnection con = new SqlConnection(CONNSTR);
                con.Open();
                SqlCommand sel = new SqlCommand("SELECT path FROM daf WHERE id = " + id, con);
                string fnm = (string)sel.ExecuteScalar();
                con.Close();
                Stream stm = new FileStream(fnm, FileMode.Open, FileAccess.Read);
                byte[] b = new byte[stm.Length];
                stm.Read(b, 0, b.Length);
                stm.Close();
                if(b.Length != PICSIZ)
                {
                    Console.WriteLine("Error");
                    Environment.Exit(1);
                }
                for(int j = 0; j < b.Length; j++)
                {
                    if(b[j] != (byte)(id % 256))
                    {
                        Console.WriteLine("Error");
                        Environment.Exit(1);
                    }
                }
            }
        }
        public static void TestDbAndFile(int nthreads)
        {
            rng = new Random(1234567);
            getprthread = NGET / nthreads;
            Thread[] thr = new Thread[nthreads];
            for(int i = 0; i < thr.Length; i++)
            {
                thr[i] = new Thread(new ThreadStart(TestDbAndFileThread));   
            }
            long t1 = DateTime.Now.Ticks;
            for(int i = 0; i < thr.Length; i++)
            {
                thr[i].Start();
            }
            for(int i = 0; i < thr.Length; i++)
            {
                thr[i].Join();
            }
            long t2 = DateTime.Now.Ticks;
            Console.WriteLine("Database and file (" + nthreads + " threads): " + NGET / ((t2 - t1)/1000000) + " get per second");
        }
        public static void DestroyDbAndFile()
        {
            SqlConnection con = new SqlConnection(CONNSTR);
            con.Open();
            SqlCommand drp = new SqlCommand("DROP TABLE daf", con);
            drp.ExecuteNonQuery();
            con.Close();
            string[] allf = Directory.GetFiles(BASEDIR);
            foreach(string f in allf)
            {
                File.Delete(f);
            }
        }
        public static void SetupOnlyDb()
        {
            SqlConnection con = new SqlConnection(CONNSTR);
            con.Open();
            SqlCommand cre = new SqlCommand("CREATE TABLE od (id INTEGER PRIMARY KEY, pic IMAGE)", con);
            cre.ExecuteNonQuery();
            SqlCommand ins = new SqlCommand("INSERT INTO od VALUES (@id, @pic)", con);
            ins.Parameters.Add("@id", SqlDbType.Int);
            ins.Parameters.Add("@pic", SqlDbType.Image);
            for(int i = 0; i < NPIC; i++)
            {
                byte[] b = new byte[PICSIZ];
                for(int j = 0; j < b.Length; j++) b[j] = (byte)(i % 256);
                ins.Parameters["@id"].Value = i;
                ins.Parameters["@pic"].Value = b;
                ins.ExecuteNonQuery();
            }
            con.Close();
        }
        private static void TestOnlyDbThread()
        {
            for(int i = 0; i < getprthread; i++)
            {
                int id = rng.Next(NPIC);
                SqlConnection con = new SqlConnection(CONNSTR);
                con.Open();
                SqlCommand sel = new SqlCommand("SELECT pic FROM od WHERE id = " + id, con);
                byte[] b = (byte[])sel.ExecuteScalar();
                con.Close();
                if(b.Length != PICSIZ)
                {
                    Console.WriteLine("Error");
                    Environment.Exit(1);
                }
                for(int j = 0; j < b.Length; j++)
                {
                    if(b[j] != (byte)(id % 256))
                    {
                        Console.WriteLine("Error");
                        Environment.Exit(1);
                    }
                }
            }
        }
        public static void TestOnlyDb(int nthreads)
        {
            rng = new Random(1234567);
            getprthread = NGET / nthreads;
            Thread[] thr = new Thread[nthreads];
            for(int i = 0; i < thr.Length; i++)
            {
                thr[i] = new Thread(new ThreadStart(TestOnlyDbThread));   
            }
            long t1 = DateTime.Now.Ticks;
            for(int i = 0; i < thr.Length; i++)
            {
                thr[i].Start();
            }
            for(int i = 0; i < thr.Length; i++)
            {
                thr[i].Join();
            }
            long t2 = DateTime.Now.Ticks;
            Console.WriteLine("Only database (" + nthreads + " threads): "  + NGET / ((t2 - t1)/1000000) + " get per second");
        }
        public static void DestroyOnlyDb()
        {
            SqlConnection con = new SqlConnection(CONNSTR);
            con.Open();
            SqlCommand drp = new SqlCommand("DROP TABLE od", con);
            drp.ExecuteNonQuery();
            con.Close();
        }
        public static void Main(string[] args)
        {
            SetupDbAndFile();
            TestDbAndFile(1);
            TestDbAndFile(10);
            DestroyDbAndFile();
            SetupOnlyDb();
            TestOnlyDb(1);
            TestOnlyDb(10);
            DestroyOnlyDb();
        }
    }
}
Avatar billede Slettet bruger
05. november 2005 - 14:52 #6
Du streamer jo billedet fra en fil, du skal lade web server sende billedet. Det er der forskellen vil blive betydende. Pointen er jo netop at en web server væsentligt hurtigere kan sende statisk indhold.
Avatar billede cbadk Novice
05. november 2005 - 20:21 #7
Hvis man nu antager at jeg bruger "database til fil" metoden. Så bruger jeg jo også mere diskplads end jeg vil bruge hvis jeg brugte database med billed metoden. For så vidt jeg ved kan man komprimere binær data i en database også. Derved er der måske sparet endnu mere ? altå både tid og pladsforbrug
Avatar billede arne_v Ekspert
05. november 2005 - 22:15 #8
det er hurtigt at serve filerne direkte af web serveren, men der kan også være
nogle sikkerhedsmæssige problemer ved det

og gevindsten er ikke så stor som man kunne tro

jeg copy pastede ovenstående kode over i nogle ASP.NET sider og et lille
multi browser simulator program

resultat:

Picture file served by web server (1 threads): 34 get per second
Picture file served by web server (10 threads): 43 get per second
Picture from database via script (1 threads): 30 get per second
Picture from database via script (10 threads): 38 get per second

forskellen er kun knap 10%

og hvis man så lige orker at skrive 5 linier kode så man bruger ASP.NET Cache objekt
så ser resultatet ud som:

Picture file served by web server (1 threads): 34 get per second
Picture file served by web server (10 threads): 43 get per second
Picture from database via script (1 threads): 33 get per second
Picture from database via script (10 threads): 44 get per second

ingen forskel !

Med idags databaser og hardware er der i mange tilfælde ingen performance
mæssige grunde til ikke at smide billeder i databasen. Og gode administrations
mæssige grunde til at gøre det.
Avatar billede arne_v Ekspert
05. november 2005 - 22:16 #9
billeder kan komprimeres både i database og som fil

de fleste billed formater er allerede komprimeeret og kan ikke komprimeres yderligere
Avatar billede cbadk Novice
05. november 2005 - 22:27 #10
har været bange for at min mysql og mssql server vil få min server processor til at "koge" over. Men det lyder ikke til at det forholder sig sådan. Så jeg vil da helt sikkert vælge billede fra database så. Og ja det giver helt sikkert en nemmere mulighed for at styre dem administrativt mæssigt.
Avatar billede arne_v Ekspert
05. november 2005 - 22:50 #11
du kan da ihvertfald prøve og se hvordan det performer hos dig
Avatar billede cbadk Novice
06. november 2005 - 01:23 #12
i php kan man udregne CPU tiden som er brugt på at IIS serveren har brugt. Kender du en måde man kan udregne det i asp ?
Avatar billede Slettet bruger
06. november 2005 - 05:50 #13
Det forekommer mig der er noget galt... Det simple rationale lyder at mængden af kode, som skal eksekveres ved et dynamisk kald er mange, ja rigtigt mange endda, gange større end ved statisk indhold.

Det der kan ske er selvfølgelig at du drukner netværksforbindelsen inden du ser den reele forskel, ved du hvad den maksimale mængde du kan hælde igennem er? Det er velkendt ved mange sammenligninger mellem eksempelvis IIS og Apache at IIS oftest er hurtigst på statisk indhold, men mod argumentet lyder at man når toppen af sin båndbrede inden det på nogen måder bliver betydende. Er det noget tilsvarende vi ser i din test, at du reelt ikke kan hælde mere igennem?

Grunden til at jeg er så insisterende er at min erfaringer, fra det site (top 5 på FDIM så hr du cirka størrelsen) jeg arbejder for tildaglig har nogle ret konkrete eksempler på at vi skal anvende væsentligt færrer front-end ressourcer ved at anvende statisk indhold frem for dynamisk indhold. Det gælder såvel tekst som billed indhold.

Vil lige minde om at jeg på ingen måder syntes løsningen med at anvende billeder i en DB er gal, der kan være rigtigt mange gode grunde og nogle af dem er allerede nævnt, men rå performance så kan jeg ikke se det og min erfaring siger mig noget helt andet.
Avatar billede arne_v Ekspert
06. november 2005 - 11:42 #14
fil åbning er en dyr operation

du mener netværks båndbredden ? det hele er kørt lokalt og jeg kan ikke tro at 1 MB/s
skulle være max der

der er mange faktorer som spiller ind

mit eksempel er med lokal fil og lokal db - hvis det f.eks. er remote db så
vil det gøre db langsommere (medmindre man har en usandsynlig god net forbindelse
til db server)

større billder ville formentlig også favorisere fil fremfor db

og det ville ikke overraske mig hvis ASP fremfor ASP.NET også ville
favorisere fil fremfor db

etc.

jeg tror på at der er mange situationer hvor fil er markant hurtigere end db

det jeg opponerer mod er "alle ved at kun filnavn i db er meget hurtigere
end selve filen i db" (uden nogen forudsætninger tilføjet) som er meget
almindelig

og det er så ikke møntet på dig, da du jo har nogle konkrete erfaringer
Avatar billede arne_v Ekspert
06. november 2005 - 12:36 #15
ligesom normalisering

der er situationer hvor man af performance hensyn kan blive nødt til at have en
denormaliseret tabel struktur

men man starter altid normaliseret og ser om det er OK

I samme stil mener jeg at man idag skal starte med at smide sine billeder
i databasen og se om det er OK

hvis man har performance problemer så undersøger man hvad der kan løse dem
Avatar billede arne_v Ekspert
31. december 2005 - 15:08 #16
cbadk ?
Avatar billede cbadk Novice
06. februar 2006 - 11:43 #17
hmm...

har besluttet mig til og bruge database alligevel...dog hvor den kun henter stien til billedet. Har udvilket et lille billede asp fil...der automatisk henter størrelsen,beskrivelse og alt det andet ind i databasen...så er det lidt nemmere at slette og håndtere filerne...og så selvfølig ved brug af "Filesystem"
Avatar billede cbadk Novice
13. marts 2006 - 09:49 #18
nogle svar :) ?
Avatar billede Slettet bruger
13. marts 2006 - 12:32 #19
why no
Avatar billede arne_v Ekspert
13. marts 2006 - 13:16 #20
gerne
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester