|
| SELECT-kommandoer i tusindvis (PostgreSQL) Fra : Jonas Koch Bentzen |
Dato : 22-06-02 18:41 |
|
Jeg har lavet en søgerobot, der går gennem et eller flere sites og
indekserer siderne. Den smider så dataerne i følgende tabeller:
Tabel "urier":
id | uri | tekst | titel | sidstindekseret
Tabel "ord":
id | ord
Tabel "ordogurier"
ord | uri | antalforekomster
I sidstnævnte tabel kan en række f.eks. være "24 | 13 | 2", hvilket
betyder, at ord nr. 24 forekommer 2 gange på siden med ID'et 13.
Okay, problemet er så, at de to sites, jeg skal indeksere, tilsammen har
over 1.500 sider, og det tager en evighed (og vi taler 7-8 timer her) at
løbe det hele igennem. Jeg prøvede så på et tidspunkt at fjerne
databasekaldene, og pludselig kunne robotten løbe det hele igennem på
ret kort tid (20 minutter, tror jeg, det var). Flaskehalsen er altså
databasen.
Problemet er, at jeg laver i tusindvis af SELECT'er og INSERT'er i løbet
af den tid, robotten kører: For hvert eneste ord på hver eneste side
kører jeg først en "SELECT id FROM ord WHERE ord = 'ordet'" for at se,
om ordet allerede findes i tabellen "ord" (og selvfølgelig for at få
ID'et, som jeg senere skal bruge). Hvis ordet ikke findes i tabellen i
forvejen, så reserverer jeg et nyt ID og indsætter ordet med det ID. Det
betyder som sagt, at jeg skal lave en SELECT for hvert ord på hver side,
og det er det, der tager så helvedes lang tid (ofte 2-5 minutter for
HTML-sider og 20 minutter for rigtig store PDF-filer, som jo indeholder
mange sider og dermed ord). Robotten tager selvfølgelig forbehold for
Last-Modified-headeren, men eftersom langt de fleste sider på de to
sites er dynamisk genererede, bliver Last-Modified-headeren ikke sat, og
så er jeg nødt til at indeksere siden igen.
Hvad kan jeg gøre for at få det til at køre hurtigere? Jeg har skitseret
nogle løsningsmodeller, men vil gerne lige høre jeres kommentar:
- Jeg kan i starten af scriptet lave en midlertidig tabel
("ordmidlertidig"), hvori jeg smider alle ord ned (altså uden at køre en
SELECT for at tjekke, om ordet findes i tabellen i forvejen). I
slutningen af scriptet SELECT'er jeg så DISTINCT fra ordmidlertidig for
at få dannet/ændret "ord"-tabellen.
- Jeg kan holde alle dataerne i PHP indtil jeg har indekseret alle sider
(dvs., at jeg kører alle siderne igennem, gemmer ordene i et array og
først skriver ned i databasen til allersidst i scriptet).
- Jeg kan skifte til MySQL. Jeg må indrømme, jeg ikke tror, det ændrer
så meget, og desuden får jeg muligvis brug for PostgreSQLs avancerede
SELECT-muligheder til forenden, der skal søge i dataerne, så det er nok
ikke den bedste løsning.
| |
Jens Gyldenkærne Cla~ (23-06-2002)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 23-06-02 14:50 |
|
Jonas Koch Bentzen <ingen.email@eksempel.dk> skrev:
> Tabel "urier":
> id | uri | tekst | titel | sidstindekseret
>
> Tabel "ord":
> id | ord
>
> Tabel "ordogurier"
> ord | uri | antalforekomster
>
> I sidstnævnte tabel kan en række f.eks. være "24 | 13 | 2",
> hvilket betyder, at ord nr. 24 forekommer 2 gange på siden med
> ID'et 13.
Hvad har du af index på tabellerne?
Overvej om de to sidstnævnte tabeller ikke med fordel kan lægges
sammen til én (så selve ordet bliver primærnøgle).
Med mange selects på ord bør det under alle omstændigheder være
indekseret - og så vidt jeg kan se kan det så lige så godt også
være primærnøgle i en fælles "forekomster"-tabel.
Du kan måske endda slippe for at lave select på denne tabel når du
vil undersøge om ordet eksisterer. Lav en INSERT - hvis den går
igennem var ordet nyt, og du kan gå videre til næste ord, hvis ikke
kan du fange fejlen og sende en UPDATE af sted.
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Jonas Koch Bentzen (23-06-2002)
| Kommentar Fra : Jonas Koch Bentzen |
Dato : 23-06-02 15:52 |
|
Jens Gyldenkærne Clausen wrote:
>
> Hvad har du af index på tabellerne?
urier og ord har begge indekser på id. ordogurier har indeks på ord og uri.
> Overvej om de to sidstnævnte tabeller ikke med fordel kan lægges
> sammen til én (så selve ordet bliver primærnøgle).
Det betyder gentagelse af ordene, og det er jeg ikke glad for. Det
strider lidt imod god normalisering/strukturering af data.
> Med mange selects på ord bør det under alle omstændigheder være
> indekseret
Ah, det havde jeg slet ikke tænkt på. id-kolonnen i ord-tabellen er der
indeks på, men ikke selve ord-kolonnen. Måske det hjælper at lave et
indeks på ord-kolonnen.
> Du kan måske endda slippe for at lave select på denne tabel når du
> vil undersøge om ordet eksisterer. Lav en INSERT - hvis den går
> igennem var ordet nyt, og du kan gå videre til næste ord, hvis ikke
> kan du fange fejlen og sende en UPDATE af sted.
Men i så fald skal jeg jo stadig lave en SELECT for at finde ID'et, hvis
INSERT'en mislykkes. Og så er det jo i øvrigt lidt hack-agtigt at kode
på den måde : )
Tak for hjælpen.
| |
Jens Gyldenkærne Cla~ (23-06-2002)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 23-06-02 23:34 |
|
Jonas Koch Bentzen <ingen.email@eksempel.dk> skrev:
> urier og ord har begge indekser på id. ordogurier har indeks
> på ord og uri.
Du laver mange opslag på ord i ord-tabellen - der bør derfor også
være indeks her (som du vist også selv nåede frem til).
>> Overvej om de to sidstnævnte tabeller ikke med fordel kan
>> lægges sammen til én (så selve ordet bliver primærnøgle).
>
> Det betyder gentagelse af ordene, og det er jeg ikke glad for.
> Det strider lidt imod god normalisering/strukturering af data.
Der er ikke noget problem i forhold til normaliseringen. En tabel
med felterne ord, uriID og antal med de to førstnævnte som
sammensat primærnøgle overholder alle de seks normalformer jeg
kender.
Jeg ved godt at der er nogle der mener at man aldrig bør have
betydningsbærende data i en primærnøgle - det princip mener jeg er
o.k. som rettesnor, men bestemt heller ikke mere.
Når du ikke gemmer andre oplysninger om et ord end selve ordet
mener jeg at en selvstændig ordtabel er overflødig og
performancemæssig uheldig. Der vil altid være et indeks på
primærnøglen, men det kan ikke bruges til noget fornuftigt, fordi
det konstant vil være ordet, og ikke et "anonymt" ordID man vil slå
op på.
> Ah, det havde jeg slet ikke tænkt på. id-kolonnen i
> ord-tabellen er der indeks på, men ikke selve ord-kolonnen.
> Måske det hjælper at lave et indeks på ord-kolonnen.
Det skulle meget gerne hjælpe på dine SELECTS. Til gengæld bliver
dine INSERTS i samme tabel langsommere. Men jeg vil tro at det vil
give en temmelig mærkbar performanceforbedring at lægge indeks ind
her.
>> Du kan måske endda slippe for at lave select på denne tabel
>> når du vil undersøge om ordet eksisterer.
[klip]
> Men i så fald skal jeg jo stadig lave en SELECT for at finde
> ID'et, hvis INSERT'en mislykkes.
Det er netop fordelen ved ikke at benytte en ordtabel. Så er dit ID
det samme som selve ordet.
Eksempel (uriID er fundet på forhånd - det skal kun findes én gang
pr. side):
INSERT INTO forekomster (ord, uriID, antal)
VALUES ('rødgrød', 23, 1)
Går den igennem kan man fortsætte til næste ord. Ellers sendes
næste forespørgsel af sted:
UPDATE forekomster
SET antal = antal + 1
WHERE ord = 'rødgrød' AND uriID = 23
Og så er det ord behandlet. Man kan evt. tage sætningerne i omvendt
rækkefølge - så skal man ikke teste for fejl, men for antal berørte
poster (jeg ved ikke om man kan nogen af delene i postgreSQL).
> Og så er det jo i øvrigt lidt hack-agtigt at kode på den måde : )
Måske - men jeg kan ikke se noget galt i at benytte fejlhåndtering
hvis det kan spare et SQL-kald.
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Barnabas (23-06-2002)
| Kommentar Fra : Barnabas |
Dato : 23-06-02 18:01 |
|
Du kan også kaste dig ud i at lave noget caching af sql enten i db eller i
din kode (såfremt du har unik adgang til dataene mens dit job kører).
| |
Jakob Møbjerg Nielse~ (23-06-2002)
| Kommentar Fra : Jakob Møbjerg Nielse~ |
Dato : 23-06-02 19:23 |
|
Jonas Koch Bentzen wrote:
> - Jeg kan holde alle dataerne i PHP indtil jeg har indekseret alle
> sider (dvs., at jeg kører alle siderne igennem, gemmer ordene i et
> array og først skriver ned i databasen til allersidst i scriptet).
Bruger du en stopords-liste? Det kan måske hjælpe lidt.
--
Jakob Møbjerg Nielsen | "Five exclamation marks, the
jakob@dataloger.dk | sure sign of an insane mind."
| -- Terry Pratchett, Reaper Man
| |
Jonas Koch Bentzen (23-06-2002)
| Kommentar Fra : Jonas Koch Bentzen |
Dato : 23-06-02 19:37 |
|
Jakob Møbjerg Nielsen skrev:
>
> Bruger du en stopords-liste?
Ja, det gør jeg.
> Det kan måske hjælpe lidt.
Ikke nok, åbenbart : )
| |
Nis Jorgensen (24-06-2002)
| Kommentar Fra : Nis Jorgensen |
Dato : 24-06-02 09:57 |
|
On Sat, 22 Jun 2002 19:41:29 +0200, Jonas Koch Bentzen
<ingen.email@eksempel.dk> wrote:
>Jeg har lavet en søgerobot, der går gennem et eller flere sites og
>indekserer siderne. Den smider så dataerne i følgende tabeller:
>
>Tabel "urier":
>id | uri | tekst | titel | sidstindekseret
>
>Tabel "ord":
>id | ord
>
>Tabel "ordogurier"
>ord | uri | antalforekomster
>
>I sidstnævnte tabel kan en række f.eks. være "24 | 13 | 2", hvilket
>betyder, at ord nr. 24 forekommer 2 gange på siden med ID'et 13.
>
>Okay, problemet er så, at de to sites, jeg skal indeksere, tilsammen har
>over 1.500 sider, og det tager en evighed (og vi taler 7-8 timer her) at
>løbe det hele igennem. Jeg prøvede så på et tidspunkt at fjerne
>databasekaldene, og pludselig kunne robotten løbe det hele igennem på
>ret kort tid (20 minutter, tror jeg, det var). Flaskehalsen er altså
>databasen.
Mit bud ville være at køre hver URI igennem separat, og derefter
skrive den til databasen. Noget i retning af (pseudoperl - det er lang
tid siden):
for each $uri in @urier
{
var %ordliste;
for each $ord in $uri
{
$ordliste[$ord]++;
}
db.kør "DELETE FROM ordogurier WHERE uri = '$uri'";
for each $ord in %ordliste
{
db.kør "INSERT INTO ordogurier (uri,ord) VALUES ('$uri', '$ord',
$ordliste[$ord]"
}
Hvis det er for hårdt ved databasen, kan du evt sammensætte en længere
SQL-streng i koden, ala
INSERT INTO ordogurier (uri,ord)
VALUES
('$uri','$ord1', '$count1'),
('$uri','$ord2', '$count2'),
....
--
Nis Jorgensen
Amsterdam
Please include only relevant quotes, and reply below the quoted text. Thanks
| |
Jonas Koch Bentzen (24-06-2002)
| Kommentar Fra : Jonas Koch Bentzen |
Dato : 24-06-02 10:55 |
|
Nis Jorgensen skrev:
>
> for each $uri in @urier
> {
> var %ordliste;
> for each $ord in $uri
> {
> $ordliste[$ord]++;
> }
> db.kør "DELETE FROM ordogurier WHERE uri = '$uri'";
> for each $ord in %ordliste
> {
> db.kør "INSERT INTO ordogurier (uri,ord) VALUES ('$uri', '$ord',
> $ordliste[$ord]"
Men så er vi igen ovre i noget, hvor ordet bliver gentaget flere gange i
tabellen.
> Hvis det er for hårdt ved databasen, kan du evt sammensætte en længere
> SQL-streng i koden, ala
>
> INSERT INTO ordogurier (uri,ord)
> VALUES
> ('$uri','$ord1', '$count1'),
> ('$uri','$ord2', '$count2'),
Det virker vist kun i MySQL, ikke i PostgreSQL.
| |
Jens Gyldenkærne Cla~ (24-06-2002)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 24-06-02 11:03 |
|
Jonas Koch Bentzen skrev:
>> db.kør "INSERT INTO ordogurier (uri,ord) VALUES ('$uri',
>> '$ord',
>> $ordliste[$ord]"
>
> Men så er vi igen ovre i noget, hvor ordet bliver gentaget
> flere gange i tabellen.
Og det vil stadig spare dig for en masse opslag i forhold til din
udgave med ordID. Prøv at kigge på hvor mange forespørgsler der
skal køres for hver af de to datamodeller. Jeg vil tro at fjernelse
af ordtabellen alene vil give en drastisk performanceforbedring.
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Nis Jorgensen (24-06-2002)
| Kommentar Fra : Nis Jorgensen |
Dato : 24-06-02 11:56 |
|
On Mon, 24 Jun 2002 11:54:51 +0200, Jonas Koch Bentzen
<ingen.email@eksempel.dk> wrote:
>Nis Jorgensen skrev:
> >
>> for each $uri in @urier
>> {
>> var %ordliste;
>> for each $ord in $uri
>> {
>> $ordliste[$ord]++;
>> }
>> db.kør "DELETE FROM ordogurier WHERE uri = '$uri'";
>> for each $ord in %ordliste
>> {
>> db.kør "INSERT INTO ordogurier (uri,ord) VALUES ('$uri', '$ord',
>> $ordliste[$ord]"
>
>Men så er vi igen ovre i noget, hvor ordet bliver gentaget flere gange i
>tabellen.
Ikke for den samme URI? Ideen er netop at $ordliste[$ord]++ lægger een
til for hver gang ordet findes i siden.
(hvis det drejer sig om ord contra ordID er jeg enig med Jens - det
kan lige så godt gemmes i een tabel.
>> INSERT INTO ordogurier (uri,ord)
>> VALUES
>> ('$uri','$ord1', '$count1'),
>> ('$uri','$ord2', '$count2'),
>
>Det virker vist kun i MySQL, ikke i PostgreSQL.
Jeg glemte et feltnavn (antalforekomster), men derudover skulle det
virke i postgres også.
--
Nis Jorgensen
Amsterdam
Please include only relevant quotes, and reply below the quoted text. Thanks
| |
Jonas Koch Bentzen (24-06-2002)
| Kommentar Fra : Jonas Koch Bentzen |
Dato : 24-06-02 12:03 |
|
Nis Jorgensen skrev:
> On Mon, 24 Jun 2002 11:54:51 +0200, Jonas Koch Bentzen
> <ingen.email@eksempel.dk> wrote:
>
>
>>Nis Jorgensen skrev:
>>
>>>for each $uri in @urier
>>>{
>>> var %ordliste;
>>> for each $ord in $uri
>>> {
>>> $ordliste[$ord]++;
>>> }
>>> db.kør "DELETE FROM ordogurier WHERE uri = '$uri'";
>>> for each $ord in %ordliste
>>> {
>>> db.kør "INSERT INTO ordogurier (uri,ord) VALUES ('$uri', '$ord',
>>>$ordliste[$ord]"
>>
>>Men så er vi igen ovre i noget, hvor ordet bliver gentaget flere gange i
>>tabellen.
>
>
> Ikke for den samme URI? Ideen er netop at $ordliste[$ord]++ lægger een
> til for hver gang ordet findes i siden.
Ja, men så vidt jeg kan se, vil ordet stadig forekomme flere gange, hvis
det forekommer på flere forskellige sider (men der vil kun være én række
pr. ord pr. side).
> (hvis det drejer sig om ord contra ordID er jeg enig med Jens - det
> kan lige så godt gemmes i een tabel.
Okay, men for 1.500 sider giver det altså en enorm stor tabel. Er der
nogen, der ved, hvor godt PostgreSQL håndterer tabeller på flere
millioner rækker?
>>>INSERT INTO ordogurier (uri,ord)
>>>VALUES
>>>('$uri','$ord1', '$count1'),
>>>('$uri','$ord2', '$count2'),
>>
>>Det virker vist kun i MySQL, ikke i PostgreSQL.
>
> Jeg glemte et feltnavn (antalforekomster), men derudover skulle det
> virke i postgres også.
PostgreSQL 7.2.1:
test=> CREATE TABLE testtabel ( id INTEGER, navn CHARACTER VARYING(50) );
test=> INSERT INTO testtabel ( id, navn ) VALUES ( 1, 'Jonas' );
INSERT 397680 1
test=> INSERT INTO testtabel ( id, navn ) VALUES ( 1, 'Jonas' ), ( 2,
'Nis' );
ERROR: parser: parse error at or near ","
| |
Nis Jorgensen (24-06-2002)
| Kommentar Fra : Nis Jorgensen |
Dato : 24-06-02 12:23 |
|
On Mon, 24 Jun 2002 13:03:05 +0200, Jonas Koch Bentzen
<ingen.email@eksempel.dk> wrote:
>PostgreSQL 7.2.1:
>
>test=> CREATE TABLE testtabel ( id INTEGER, navn CHARACTER VARYING(50) );
>test=> INSERT INTO testtabel ( id, navn ) VALUES ( 1, 'Jonas' );
>INSERT 397680 1
>test=> INSERT INTO testtabel ( id, navn ) VALUES ( 1, 'Jonas' ), ( 2,
>'Nis' );
>ERROR: parser: parse error at or near ","
OK, det ser ud til at det ikke er standard. Underligt - det virker som
en logisk udvidelse (som både mySQL og Access da også har
implementeret). Undskyld besværet.
--
Nis Jorgensen
Amsterdam
Please include only relevant quotes, and reply below the quoted text. Thanks
| |
Kristian Damm Jensen (24-06-2002)
| Kommentar Fra : Kristian Damm Jensen |
Dato : 24-06-02 20:46 |
|
Nis Jorgensen wrote:
>
> On Mon, 24 Jun 2002 13:03:05 +0200, Jonas Koch Bentzen
> <ingen.email@eksempel.dk> wrote:
>
> >PostgreSQL 7.2.1:
> >
> >test=> CREATE TABLE testtabel ( id INTEGER, navn CHARACTER VARYING(50) );
> >test=> INSERT INTO testtabel ( id, navn ) VALUES ( 1, 'Jonas' );
> >INSERT 397680 1
> >test=> INSERT INTO testtabel ( id, navn ) VALUES ( 1, 'Jonas' ), ( 2,
> >'Nis' );
> >ERROR: parser: parse error at or near ","
>
> OK, det ser ud til at det ikke er standard. Underligt - det virker som
> en logisk udvidelse (som både mySQL og Access da også har
> implementeret). Undskyld besværet.
Så vidt jeg husker, er det en del af standarden... Men en af de nyere,
så ikke alle systemer har implementeret den.
--
Kristian Damm Jensen | Feed the hungry at www.thehungersite.com
kristian-damm.jensen@cgey.com | Two wrongs doesn't make a right,
ICQ# 146728724 | but three lefts do.
| |
Nis Jorgensen (24-06-2002)
| Kommentar Fra : Nis Jorgensen |
Dato : 24-06-02 12:26 |
|
On Mon, 24 Jun 2002 13:03:05 +0200, Jonas Koch Bentzen
<ingen.email@eksempel.dk> wrote:
>
>> Ikke for den samme URI? Ideen er netop at $ordliste[$ord]++ lægger een
>> til for hver gang ordet findes i siden.
>
>Ja, men så vidt jeg kan se, vil ordet stadig forekomme flere gange, hvis
>det forekommer på flere forskellige sider (men der vil kun være én række
>pr. ord pr. side).
Ja. Altså som nu, bare med ord i stedet for ordID
>> (hvis det drejer sig om ord contra ordID er jeg enig med Jens - det
>> kan lige så godt gemmes i een tabel.
>
>Okay, men for 1.500 sider giver det altså en enorm stor tabel. Er der
>nogen, der ved, hvor godt PostgreSQL håndterer tabeller på flere
>millioner rækker?
Du skal under alle omstændigheder have en tabel med millioner af
rækker - spørgsmålet er udelukkende om du skal bruge en surrogatnøgle
for ordene.
Så vidt jeg ved vil Postgresql fint kunne håndtere en sådan tabel -
selvfølgelig afhængigt af hardware og belastning.
--
Nis Jorgensen
Amsterdam
Please include only relevant quotes, and reply below the quoted text. Thanks
| |
Jonas Koch Bentzen (24-06-2002)
| Kommentar Fra : Jonas Koch Bentzen |
Dato : 24-06-02 13:06 |
|
Nis Jorgensen skrev:
> On Mon, 24 Jun 2002 13:03:05 +0200, Jonas Koch Bentzen
> <ingen.email@eksempel.dk> wrote:
>
>
>>>Ikke for den samme URI? Ideen er netop at $ordliste[$ord]++ lægger een
>>>til for hver gang ordet findes i siden.
>>
>>Ja, men så vidt jeg kan se, vil ordet stadig forekomme flere gange, hvis
>>det forekommer på flere forskellige sider (men der vil kun være én række
>>pr. ord pr. side).
>
>
> Ja. Altså som nu, bare med ord i stedet for ordID
Nej - lige nu er alle ordene gemt i tabellen "ord", og hvert ord findes
kun én i tabellen. Relationen mellem ord og sider bliver så lavet i
"ordogurier". Med dit forslag (som jeg da seriøst vil overveje) kan et
ord forekomme mange i tabellen, hvis det forekommer på mange sider, men
det kan kun forekomme én gang per side.
| |
Jens Gyldenkærne Cla~ (24-06-2002)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 24-06-02 13:16 |
|
Jonas Koch Bentzen skrev:
>> Ja. Altså som nu, bare med ord i stedet for ordID
>
> Nej - lige nu er alle ordene gemt i tabellen "ord", og hvert
> ord findes kun én i tabellen. Relationen mellem ord og sider
> bliver så lavet i "ordogurier". Med dit forslag (som jeg da
> seriøst vil overveje) kan et ord forekomme mange i tabellen,
> hvis det forekommer på mange sider, men det kan kun forekomme
> én gang per side.
Men pointen er at antallet er poster i ordogurier er det samme
uanset om du benytter ord eller ordID her. Fysisk vil tabellen
(formentlig) optage mere plads, da et int-felt fylder noget mindre
end en varchar/nvarchar (eller hvad tekstfelter nu kaldes i
postgreSQL). Men selvom opslag på et int-felt skulle være hurtigere
end opslag på et tekstfelt kan du ikke bruge det til noget i dette
tilfælde. For hver gang du skal finde int-værdien til et ord skal
du lave et opslag på en tekst-værdi - oven i købet en tekst-værdi
der fornuftigt anvendt kan føre dig direkte til den rigtige post.
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Nis Jorgensen (24-06-2002)
| Kommentar Fra : Nis Jorgensen |
Dato : 24-06-02 14:17 |
|
On Mon, 24 Jun 2002 14:06:05 +0200, Jonas Koch Bentzen
<ingen.email@eksempel.dk> wrote:
>
>> Ja. Altså som nu, bare med ord i stedet for ordID
>
>Nej - lige nu er alle ordene gemt i tabellen "ord", og hvert ord findes
>kun én i tabellen. Relationen mellem ord og sider bliver så lavet i
>"ordogurier". Med dit forslag (som jeg da seriøst vil overveje) kan et
>ord forekomme mange i tabellen, hvis det forekommer på mange sider, men
>det kan kun forekomme én gang per side.
Ja. Hvert ord-id findes altså flere gange i tabellen. Det er det jeg
mener med ovenstående sætning.
Fordelen ved mit forslag er at det aldrig er nødvendigt at benytte
returværdier/fejlbehandling ved SQL-kaldene. Dit script behøver ikke
bekymre sig om hvorvidt alle ord fra een side er fuldt registreret i
databasen, før den næste side behandles.
--
Nis Jorgensen
Amsterdam
Please include only relevant quotes, and reply below the quoted text. Thanks
| |
Jørgen Østergaard (24-06-2002)
| Kommentar Fra : Jørgen Østergaard |
Dato : 24-06-02 22:58 |
|
Hej Jonas,
ved du hvor meget data du kommer ud for at skulle behandle? -hvis du kan
holde en del af dem i memory, så gør det.
Typisk koster SQL kald meget mere end håndtering i memory, så mit råd til
dig ville være at behandle så meget som muligt i memory, og bruge databasen
hvor det er nødvendigt.
En hybrid af dine løsningsskitser nedenfor vil jeg tro kunne være en mulig
udvej; Hvorfor ikke lave et PHP array, som du tæller op i, og ved en given
arraystørrelse insert/updater du ned i basen?
På den måde vil du sandsynligvis spare en del omkostningsfyldte SQL-kald
(afhængigt af dine data's fordeling).
Alternativt: Har du undersøgt om du har mulighed for at lave bulk inserts?
vh. Jørgen
"Jonas Koch Bentzen" <ingen.email@eksempel.dk> wrote in message
news:3D14B6C9.2060004@eksempel.dk...
> Jeg har lavet en søgerobot, der går gennem et eller flere sites og
> indekserer siderne. Den smider så dataerne i følgende tabeller:
>
....
> Hvad kan jeg gøre for at få det til at køre hurtigere? Jeg har skitseret
> nogle løsningsmodeller, men vil gerne lige høre jeres kommentar:
>
> - Jeg kan i starten af scriptet lave en midlertidig tabel
> ("ordmidlertidig"), hvori jeg smider alle ord ned (altså uden at køre en
> SELECT for at tjekke, om ordet findes i tabellen i forvejen). I
> slutningen af scriptet SELECT'er jeg så DISTINCT fra ordmidlertidig for
> at få dannet/ændret "ord"-tabellen.
>
> - Jeg kan holde alle dataerne i PHP indtil jeg har indekseret alle sider
> (dvs., at jeg kører alle siderne igennem, gemmer ordene i et array og
> først skriver ned i databasen til allersidst i scriptet).
>
> - Jeg kan skifte til MySQL. Jeg må indrømme, jeg ikke tror, det ændrer
> så meget, og desuden får jeg muligvis brug for PostgreSQLs avancerede
> SELECT-muligheder til forenden, der skal søge i dataerne, så det er nok
> ikke den bedste løsning.
>
| |
Snedker (25-06-2002)
| Kommentar Fra : Snedker |
Dato : 25-06-02 10:02 |
|
On Mon, 24 Jun 2002 23:58:28 +0200, "Jørgen Østergaard"
<joesterg@hotmail.com> wrote:
>ved du hvor meget data du kommer ud for at skulle behandle? -hvis du kan
>holde en del af dem i memory, så gør det.
Hvad vil det sige at behandle noget i memory...så'n i praksis?
mvh
Morten Snedker
| |
Jørgen Østergaard (25-06-2002)
| Kommentar Fra : Jørgen Østergaard |
Dato : 25-06-02 19:27 |
|
Hej Morten,
blot at lave temporære tabeller i memory -dvs. noget lignende et array, du
bare tæller op i, og tømmer, når det bliver for stort.
F.eks. istedet for at lave en update for hvert ord, Jonas støder på, så
kunne han blot tælle op i et/flere arrays, og når disse blev "for store", så
gemme ved at lave inserts. -Afhængigt af om hans ordene følger en eller
anden statistisk fordeling vil han enten kunne gøre dette meget mere
effektivt, eller også vil det skalere proportionalt med løsningen hvor han
laver en insert pr. ord.
Grundtanken er, at det er dyrt at lave databasekald, men billigt at bruge
programmering og memory, så jo færre databasekald vi behøver, jo mere
performance får vi ud af systemet.
Jeg har ikke real-world tal på dette for MySQL og PostgreSQL, men hvis de
bare nogenlunde ligner deres større fætre/kusiner, så skulle det undre mig
om ikke det også holder her...
Well, my 2ct's worth... ;)
-Jørgen
"Snedker" <morten@nospam_dbconsult.dk> wrote in message
news:jacghuccdvv0vc8snal4cck8pbuv56nggm@4ax.com...
> On Mon, 24 Jun 2002 23:58:28 +0200, "Jørgen Østergaard"
> <joesterg@hotmail.com> wrote:
>
> >ved du hvor meget data du kommer ud for at skulle behandle? -hvis du kan
> >holde en del af dem i memory, så gør det.
>
> Hvad vil det sige at behandle noget i memory...så'n i praksis?
>
> mvh
> Morten Snedker
| |
Kristian Damm Jensen (24-06-2002)
| Kommentar Fra : Kristian Damm Jensen |
Dato : 24-06-02 21:03 |
|
Jonas Koch Bentzen wrote:
>
> Jeg har lavet en søgerobot, der går gennem et eller flere sites og
> indekserer siderne. Den smider så dataerne i følgende tabeller:
>
> Tabel "urier":
> id | uri | tekst | titel | sidstindekseret
>
> Tabel "ord":
> id | ord
>
> Tabel "ordogurier"
> ord | uri | antalforekomster
>
> I sidstnævnte tabel kan en række f.eks. være "24 | 13 | 2", hvilket
> betyder, at ord nr. 24 forekommer 2 gange på siden med ID'et 13.
>
> Okay, problemet er så, at de to sites, jeg skal indeksere, tilsammen har
> over 1.500 sider, og det tager en evighed (og vi taler 7-8 timer her) at
> løbe det hele igennem. Jeg prøvede så på et tidspunkt at fjerne
> databasekaldene, og pludselig kunne robotten løbe det hele igennem på
> ret kort tid (20 minutter, tror jeg, det var). Flaskehalsen er altså
> databasen.
>
> Problemet er, at jeg laver i tusindvis af SELECT'er og INSERT'er i løbet
> af den tid, robotten kører: For hvert eneste ord på hver eneste side
> kører jeg først en "SELECT id FROM ord WHERE ord = 'ordet'" for at se,
> om ordet allerede findes i tabellen "ord" (og selvfølgelig for at få
> ID'et, som jeg senere skal bruge). Hvis ordet ikke findes i tabellen i
> forvejen, så reserverer jeg et nyt ID og indsætter ordet med det ID. Det
> betyder som sagt, at jeg skal lave en SELECT for hvert ord på hver side,
> og det er det, der tager så helvedes lang tid (ofte 2-5 minutter for
> HTML-sider og 20 minutter for rigtig store PDF-filer, som jo indeholder
> mange sider og dermed ord). Robotten tager selvfølgelig forbehold for
> Last-Modified-headeren, men eftersom langt de fleste sider på de to
> sites er dynamisk genererede, bliver Last-Modified-headeren ikke sat, og
> så er jeg nødt til at indeksere siden igen.
>
> Hvad kan jeg gøre for at få det til at køre hurtigere? Jeg har skitseret
> nogle løsningsmodeller, men vil gerne lige høre jeres kommentar:
>
> - Jeg kan i starten af scriptet lave en midlertidig tabel
> ("ordmidlertidig"), hvori jeg smider alle ord ned (altså uden at køre en
> SELECT for at tjekke, om ordet findes i tabellen i forvejen). I
> slutningen af scriptet SELECT'er jeg så DISTINCT fra ordmidlertidig for
> at få dannet/ændret "ord"-tabellen.
>
> - Jeg kan holde alle dataerne i PHP indtil jeg har indekseret alle sider
> (dvs., at jeg kører alle siderne igennem, gemmer ordene i et array og
> først skriver ned i databasen til allersidst i scriptet).
>
> - Jeg kan skifte til MySQL. Jeg må indrømme, jeg ikke tror, det ændrer
> så meget, og desuden får jeg muligvis brug for PostgreSQLs avancerede
> SELECT-muligheder til forenden, der skal søge i dataerne, så det er nok
> ikke den bedste løsning.
Nu har jeg læst en lang række svar igennem, og ingen har foreslået en
mere drastisk omstrukturering. Det vil jeg så gøre.
Opret temp-tabeller T1, T2 .....
For hver uri:u
delete from T1, T2 ...
-- datafangst
for hvert ord:o på u
insert into T1 values(u,o)
-- Summation
insert into T2
select u, o, count(*)
from T1
group by o
-- Find ord, der allerede er registreret
update T2
set id = (select * from ord where ord.ord = T2.ord)
-- Tilføj nye ord til ord.
for hver forekomst i T2, hvor id er NULL, opret en forekomst i ord.
-- Opdater, hvor kombinationen ord, uri findes i forvejen
update ordoguri
set antalforekomster = T2.antal
from T2
where ordoguri.ord = T2.ord
and ordoguir.uri = T2.uri
-- Indsæt, hvor kombinationen ord, uri ikke findes i forvejen
insert into ordoguri
select ord, uri, antal
from T2
where not exists
(select * from ordoguri O
where T2.ord = O.ord
and T2.uri = O.uri)
Fordelene ved dette er, at dine select's i basen er få men store.
Yderligere er alle insert og update på tabeller med eksisterende data
også få men store. Det bør enhver velindekseret database kunne lide.
Du får på denne måde een insert pr. (ord, uri) samt. En insert (og
muligvis en select, afhængig af hvordan du danner unike id'er) pr. nyt
ord. Samt dertil 4 sql-forespørgsler pr. uri. (Det kan naturligvis
reduceres til 4 forespørgsler i alt, men så bliver T1 meget stor.)
--
Kristian Damm Jensen | Feed the hungry at www.thehungersite.com
kristian-damm.jensen@cgey.com | Two wrongs doesn't make a right,
ICQ# 146728724 | but three lefts do.
| |
|
|