Black Friday = Pricewatch Bekijk onze selectie van de beste Black Friday-deals en voorkom een miskoop.

[MySQL] Domein tabel

Pagina: 1
Acties:

  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Probleem soort: optimalisatie
Probleem beschrijving: LAMP-opstelling gebruikt een MySQL tabel voor het opslaan van de domeinen waarop een virtualhost moet reageren. Echter de query is niet (goed?) geoptimaliseerd voor het opslaan van subdomein-afvanging... (*.mijndomein.nl)

Voorbeeld database 'Domain':
idnamesiteId
1test.nl1
2nu.test.nl2
3test2.nl1

Zoals je ziet test.nl en test2.nl komen op dezelfde site uit, maar nu.test.nl komt op een ander uit. Om dit te query-en voor bijvoorbeeld de url: `www.test.nl` wordt de volgende query toegepast:
SQL:
1
2
3
4
5
6
7
SELECT
      `domain`.`siteId`
FROM
        `domain`
WHERE
        '.www.test.nl' LIKE CONCAT('%.', `domain`.`name`))
ORDER BY LENGTH(`domain`.`name`) DESC;


Nu zie je het al: die where is vervelend... De reden dat het zo staat is zodat www.test.nl direct al wordt afgevangen door record #1, maar dat nu.test.nl wel netjes naar een ander gaat (en direct *.nu.test.nl ook)

Mijn vraag is dus eigenlijk zijn hier niet betere truucen voor die ervoor zorgen dat die `LIKE CONCAT()` eruit kan waardoor de query een stuk beter te cachen is... ? Hij kan nu namelijk niet de index gebruiken die op domein.name staat...

(de reden voor de ORDER BY is ivm 'meest specifiek ingevoerd domein wat matcht geldt' ... )

Gekkie is a proud member of TheBenny!


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
De truuk die hier voor gebruikt wordt (FireFox 3 doet dat ook in z'n places.sqlite db bijvoorbeeld) is door de domeinnaam in reverse op te slaan.

gathering.tweakers.net wordt dus ten.srekaewt.gnirehtag. Daarmee kun je wél je indexen gebruiken en hoef je in je interface enkel bij het weergeven en opslaan de string te reversen. Vervolgens kun je de wildcard gewoon lekker achteraan plakken.
Je kunt er overigens ook gewoon een extra veld voor gebruiken (dus 1 keer 'gewoon' opslaan en 1 keer reversed en dan je index op de reversed zetten (of 2 indexes...) of....).

[ Voor 26% gewijzigd door RobIII op 25-06-2008 16:15 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Hmmmzz... dat klinkt wel koel :)

Betekend dus dat de query min of meer hetzelfde blijft, maar dat de string omgedraaidt wordt:
SQL:
1
2
3
4
5
6
7
SELECT
      `domain`.`siteId`
FROM
        `domain`
WHERE
        `domain`.`name_rev` LIKE 'ln.tset.www%'
ORDER BY LENGTH(`domain`.`name`) DESC;


Vraag die dan overblijft (of juist ontstaat) moet de '.' er dan nog achteraan? (in de LIKE én in de `domain`.`name`)

of begrijp ik het niet?

Gekkie is a proud member of TheBenny!


  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
Ja, want je wil toch subdomeinen vinden? Anders vindt je bij het zoeken op tweakers.net ook wij.zijncooletweakers.net en dat is natuurlijk iets anders :)

  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Dus de truuc is dan al in mn reversed column ook de extra '.' meenemen en in mn like deze er gewoon in zetten met mn PHP :)

Prima prima!

Gekkie is a proud member of TheBenny!


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Ik krijg het maar niet goed voor elkaar... Wat doe ik fout:
- ik heb de tabel aangepast zodat er nu een extra kolom 'name_rev' erbij is gekomen die de naam reversed + een extra '.' bevat: "test.nl" wordt "ln.tset."
- ik heb de where in de query aangepast naar "domain`.`name_rev` LIKE 'ln.tset.%'"

maar ik krijg veel meer resultaten dan volgens de eerdere query... ? Als ik voorheen met 'test.nl' inzette kreeg ik alleen 'test.nl' terug. Nu met de reversed oplossing krijg ik bovenaan de meest uitgebreide subdomein specificatie terug... (bijvoorbeeld dit.is.nu.een.test.nl ) wat in dit geval natuurlijk niet de bedoeling is doordat ik op test.nl uit had moeten komen...

Wat doe ik fout?

[ na wat meer testen]

ik heb nu de volgende query:
SQL:
1
2
3
4
EXPLAIN SELECT  `domain` . *
FROM `domain`
WHERE 'ln.test.' LIKE CONCAT( `domain`.`name_rev` , '%' )
ORDER BY LENGTH( `domain`.`name` ) DESC ;


Deze geeft hetzelfde resultaat als de eerdere query, maar gebruikt ook geen index op 'name_rev'... In andere woorden: hier bereik ik hetzelfde resultaat én dus ook hetzelfde probleem mee: geen gebruik van indexen... :(

ik herhaal mezelf dus maar even: wat doe ik dan fout? :)

[ Voor 26% gewijzigd door _Gekkie_ op 27-06-2008 10:31 . Reden: extra testjes ]

Gekkie is a proud member of TheBenny!


  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Zet die procent nou gewoon dmv code zodat je er in je query eenvoudigweg `domain`.`name_rev` LIKE 'ln.test.%' staat. Omdat er dan geen functie over die kolom gegooid wordt is de index tenminste wel bruikbaar. Sowieso had je wel op je klompen aan kunnen voelen dat die concat() nogal suf is.

En als je de kortste domeinnaam terug wel krijgen moet je uiteraard in ASCending order sorteren. ;) Ook hier geldt dat je nu een functie gebruikt, waardoor deze order by clause relatief duur zal zijn (wellicht filesort zichtbaar bij explain van query).

[ Voor 30% gewijzigd door Voutloos op 27-06-2008 11:12 ]

{signature}


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Dat had ik ook wel begrepen, maar nogmaals, deze twee queries leveren niet hetzelfde resultaat:
SQL:
1
2
3
4
5
6
7
SELECT
      `domain`.*
FROM
        `domain`
WHERE
        '.tset.nl' LIKE CONCAT('%.', `domain`.`name`)
ORDER BY LENGTH(`domain`.`name`) DESC;

SQL:
1
2
3
4
5
6
7
SELECT
      `domain`.*
FROM
        `domain`
WHERE
        `domain`.`name_rev` LIKE 'ln.tset.%'
ORDER BY LENGTH(`domain`.`name`) DESC;


Die tweede geeft me namelijk _alle_ resultaten waar test.nl in staat dus ook alle eventuele subdomeinen e.d. .. stel dat er een 'dit.is.een.test.nl' was vind ik die met de 'test.nl' vergelijking... échter in de eerste query vind ik de juiste... namelijk 'test.nl'...

[edit]
sterker nog: beide queries hebben nu dezelfde explain, waar de eerste maar 1 resultaat geeft, en de tweede alle waar 'test.nl' voorkomt (dus ook 'test.nu.is.er.weereen.test.nl') én ze gebruiken beide géén index... ?

[ Voor 13% gewijzigd door _Gekkie_ op 27-06-2008 11:19 ]

Gekkie is a proud member of TheBenny!


  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Ook in de 1e query kan je die wildcard op een fatsoenlijke plek neerzetten en zijn het toch wel heel eenvoudige queries die je echt zelf moet kunnen debuggen. Op het moment dat beide kolommen netjes elkaars reverse zijn en de like clause ook netjes omgedraaid is is het gedrag uiteraard hetzelfde.
én ze gebruiken beide géén index... ?
Is er wel een index? :P En soms wordt een index alsnog genegeerd, afhankelijk van cardinaliteit etc.

Post een je complete tabel structuur en explain.

[ Voor 24% gewijzigd door Voutloos op 27-06-2008 11:24 ]

{signature}


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Dat ik iets niet goed doe, dat begrijp ik ook wel... in inderdaad '- x - = +'... Zover kom ik ook nog... Maar wat het dan wel moet zijn weet ik niet... Dat is juist mijn vraag ;)

De complete structuur:
SQL:
1
2
3
4
5
6
7
8
9
CREATE TABLE `domain` (
  `id` bigint(20) NOT NULL default '0',
  `name` varchar(255) NOT NULL,
  `name_rev` varchar(255) NOT NULL,
  `siteId` bigint(20) NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `siteId` (`siteId`),
   KEY `name_rev` (`name_rev`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


query:
SQL:
1
2
3
4
5
6
7
SELECT
      `domain`.*
FROM
        `domain`
WHERE
        `domain`.`name_rev` LIKE  'ln.tset.%'
ORDER BY LENGTH(`domain`.`name`) DESC;


levert de volgende explain:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE domain range name_rev name_rev 767 NULL 1 Using where; Using filesort

Problemen met deze oplossing:
- als ik volgens de originele query zoek op een domein die er níet specifiek instaat: 'testje.test.nl' maar 'test.nl' staat er wel in, vind ik test.nl... bij de tweede vind ik niets...
- ze gebruiken beide (originele en nieuwe oplossing) geen index (de explain is voor beide hetzelfde)

[ Voor 74% gewijzigd door _Gekkie_ op 27-06-2008 11:31 ]

Gekkie is a proud member of TheBenny!


  • Voutloos
  • Registratie: Januari 2002
  • Niet online
De key kolom van je explain is gewoon gevuld, dus er wordt een index gebruikt. :> Waarom de filesort nodig is heb ik reeds gezegd. 'using index' in de extra kolom, als je daarnaar op zoek wat is iets anders, dan is namelijk een covering index.

Oftewel: order by kan beter maar verder qua optimalisatie geen echte problemen meer.

{signature}


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Je zou eventueel (nogal dubbelop) de lengte nog in een extra field kunnen toevoegen, scheelt je ook weer een LENGTH call.

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Stoem: zodra ik een niet vermeld domeinnaam gebruik (zoals 'example.org') gebruikt ie wel de index, maar een veelvoorkomende naam gebruikt ie geen index... :(

Wel begrijp ik dat er dus eigenlijk weinig te optimaliseren valt (om het resultaat hetzelfde te houden) Ik moet en zal de where clause hetzelfde moeten laten ... Namelijk de opgevraagde domeinnaam 'test.nl' als basis gebruiken op een wildcard table waarde... :
SQL:
1
... WHERE  'ln.tset.' LIKE CONCAT(`domain`.`name_rev`, '%')


Of de '%' dan aan het einde of aan het begin staat, zou niet uit moeten maken... het is nu alleen de '.' die ik in de concat weg kan laten...

en dus kom ik terug aan het begin:
ik kan dus de originele oplossing laten staan, blijkbaar kan het niet veel beter... ?

Gekkie is a proud member of TheBenny!


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
_Gekkie_ schreef op vrijdag 27 juni 2008 @ 11:49:
Of de '%' dan aan het einde of aan het begin staat, zou niet uit moeten maken...
Tuurlijk wel :X Wat doe je liever? Zoeken in het telefoonboek naar alle namen die eindigen op %dijk of die beginnen met dijk%?
In het geval van %dijk kun je geen index gebruiken en zul je een full-telefoonboek-scan (full table scan) moeten doen en in het geval van dijk% kun je gewoon je index gebruiken (vingertje bij de d - i - j - k en vanaf daar verder lezen).

[ Voor 27% gewijzigd door RobIII op 27-06-2008 11:55 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Leg het probleem eens fatsoenlijk uit, want qua logica moet er gewoon, ongeacht al dan niet reversed opslag van dat veld, vrij simpel uit te komen zijn. Als je gewoon weet welk deel je met een wildcard wil matchen en of je wel of niet dat puntje wil afdwingen is dit verder echt geen rocket science.

{signature}


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Het doel van het systeem is de apache directive '*.domeinnaam' oplossen ... Dat is het belangrijkste.
Alle domeinnamen staan in 1 tabel:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE `domain` (
  `id` bigint(20) NOT NULL default '0',
  `name` varchar(255) NOT NULL,
  `siteId` bigint(20) NOT NULL default '0',
  `mailDomainId` int(11) NOT NULL,
  `_rev` varchar(255) NOT NULL,
  `_length` tinyint(4) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `siteId` (`siteId`),
  KEY `_length` (`_length`),
  KEY `_rev` (`_rev`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Ik gebruik een PHP script als bootstrap om te achterhalen welke site ik moet tonen:
SQL:
1
2
3
4
5
6
7
SELECT
    `domain`.`siteId` as `siteId`,
    `domain`.`id` as `domainId`
FROM
    `domain`
WHERE ('[gezochte domeinnaam]' LIKE CONCAT('%.', `domain`.`name`))
ORDER BY `domain`.`_length` DESC


Ik heb de volgende domeinen in mn tabel:
- test.nl (siteId 1)
- www.test.nl (siteId 1)
- nu.test.nl (siteId 2)
- nu.nl (siteId 3)
- test.tweakers.net (siteId 1)

SItuatieschetsen:
input | gewenst siteId
test.nl | 1
test.test.nl | 1
www2.test.nl | 1
nu.test.nl | 2
www.nu.nl | 3
www.tweakers.net | null *niet gevonden*
etc

Dat kan ik bereiken met de huidige query... (even de _length meegenomen)

Echter gebruikt die query niet (altijd?) de index op 'name'. En dus kan het nogal een onhandige query worden als deze _iedere_ hit op de webserver wordt uitgevoerd :)

De reallife situatie is alsvolgt:
- er staan 1435 domeinen in de tabel
- 1 op de 5 queries op een dedicated MySQL server is zo'n query als hierboven
- volgens mn slow-log analyser waar ik ook de non-indexed queries in opsla, zijn dit de statistieken voor die query:
Count : 15647 (19%)
Time : 88 s total, 0 s avg, 0 s to 1 s max
95% of Time : 0 s total, 0 s avg, 0 s to 0 s max
Lock Time : 11 s total, 0 s avg, 0 s to 1 s max
Rows sent : 1 avg, 0 to 3 max
Rows examined : 1605 avg, 1598 to 1644 max

Puur dus omdat het géén index kan gebruiken (behalve nu dan de _length maar die is minimaal natuurlijk)

Wat kan ik dus doen om het e.e.a. intelligenter te maken en inderdaad die vreemd uitziende CONCAT te vervangen door iets wat database vriendelijker is ...

Gekkie is a proud member of TheBenny!


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
*schaamteloze schop*

Helaas blijft het probleem bestaan dat de queries niet te cachen is vanuit MySQL... Elke keer moet het systeem 1600+ (er zijn onderstussen weer domeinen bijgekomen) rijen bekijken volgens de EXPLAIN-op de query...

Niemand een slimme oplossing?

De geanalyseerde slow log:
Count : 21965 (27%)
Time : 140 s total, 0 s avg, 0 s to 1 s max
95% of Time : 0 s total, 0 s avg, 0 s to 0 s max
Lock Time : 11 s total, 0 s avg, 0 s to 1 s max
Rows sent : 1 avg, 0 to 3 max
Rows examined : 1653 avg, 1646 to 1659 max

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT
     `domain`.`id` as `domainId`
FROM
    `site`,
    `domain`,
    `base` AS `domainBase`,
    `base` AS `siteBase`
WHERE
    (`site`.`id` = `siteBase`.`id`)
    AND (`siteBase`.`deleted` = 0)
    AND (`domain`.`id` = `domainBase`.`id`)
    AND (`domainBase`.`deleted` = 0)
    AND (`domain`.`siteId` = `site`.`id`)
    AND ('.www.example.com' LIKE CONCAT('%.', `domain`.`name`))
ORDER BY LENGTH(`domain`.`name`) DESC;

Gekkie is a proud member of TheBenny!


  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Heb je nou al eens een extra kolom met de reversed string geprobeerd, zodat de wildcard aan het eind staat?

{signature}


  • pedorus
  • Registratie: Januari 2008
  • Niet online
Zomaar een idee: maak twee extra tabellen:
  1. domainEnds(domainEndId,domainEndString)
  2. domainsForDomainEnd(domainEndId,domainLength,domainId)
Deze tabellen kun je aanmaken door sql-opdrachten te gebruiken met SUBSTRING_INDEX erin, totdat er geen nieuwe info meer is gevonden. Met een index op domainEndString en een gecombineerde op (domainEndId, domainLength) zal het zeer snel gaan denk ik zo... En de benodigde ruimte is erg klein met 1600 domeinen.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 02:24

Creepy

Tactical Espionage Splatterer

Een like die begint met een %... zolang je dat blijft houden kan er geen index worden gebruikt. Nooit. .. Zoals al eerder is aangegeven: pas dat eens aan.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • pedorus
  • Registratie: Januari 2008
  • Niet online
Creepy schreef op dinsdag 09 september 2008 @ 20:10:
Een like die begint met een %... zolang je dat blijft houden kan er geen index worden gebruikt. Nooit. ..
Ik vind hier iemand die toch hoopt dat de 'n' van nooit af gaat. (Of dit ziet als een dubbele ontkenning. :))

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
@voutloos: Dat leverde de verkeerde resultaten op... de like staat op de kolom, niet op de waarde...
@pedorus: die begrijp ik niet helemaal?
@Creepy: De %-wildcard móet juist op de tabel, niet op mn string...
@pedorus (2): Dat lijkt er inderdaad wel op, maar daar staat de wildcard ook op de string, niet op de kolom.. ?

Dit heeft allemaal te maken met wat er het beste matcht...
Ik zal morgen wel even met de blogposting van jcole.us aan de gang...
- twee kolommen toevoegen:
`_name` en `_length`
- triggers:
SQL:
1
2
3
4
5
6
7
8
9
CREATE TRIGGER domain_r_in
  BEFORE INSERT ON domain
  FOR EACH ROW
    SET NEW._name = REVERSE(CONCAT(".", NEW.name)), NEW._length = LENGTH(NEW.name);

CREATE TRIGGER domain_r_up
  BEFORE UPDATE ON domain
  FOR EACH ROW
    SET NEW._name = REVERSE(CONCAT(".", NEW.name)), NEW._length = LENGTH(NEW.name);

en als laatste de query aanpassen naar:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT 
     `domain`.`id` as `domainId` 
FROM 
    `site`, 
    `domain`, 
    `base` AS `domainBase`, 
    `base` AS `siteBase` 
WHERE 
    (`site`.`id` = `siteBase`.`id`) 
    AND (`siteBase`.`deleted` = 0) 
    AND (`domain`.`id` = `domainBase`.`id`) 
    AND (`domainBase`.`deleted` = 0) 
    AND (`domain`.`siteId` = `site`.`id`)
    AND REVERSE('.www.example.com') LIKE CONCAT(`domain`.`_name`, '%')
ORDER BY `domain`.`_length` DESC;


Maar schopt die concact nu niet alsnog alles in de war?
Ik ga het morgen proberen!

Gekkie is a proud member of TheBenny!


  • pedorus
  • Registratie: Januari 2008
  • Niet online
_Gekkie_ schreef op dinsdag 09 september 2008 @ 23:12:
@Creepy: De %-wildcard móet juist op de tabel, niet op mn string...
Aha, ik was even in de war gebracht door oa deze post... :)

Tsja, in dat geval zou ik gewoon een query maken van de vorm
SQL:
1
where name in ("iets.www.test.nl","www.test.nl","test.nl")

Ja, dat is niet mooi, maar volgens mij doet mysql niet aan indexen bij queries waar de kolom rechts van like staat...

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 02:24

Creepy

Tactical Espionage Splatterer

Ok duidelijk, maar in het begin van je topic geef je aan wel de reverse te hebben gebruikt maar dat er dan nog steeds geen index wordt gebruikt (wat pedorus ook al aangeeft ;) ). Misschien totaal overbodig maaruh: kan je je complete tabelstructuur (dus inclusief indexen) eens geven dan samen met de EXPLAIN die je hebt gedraait? Dat geeft ons wat meer inzicht.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • pedorus
  • Registratie: Januari 2008
  • Niet online
Probeer anders eens of MySQL wel een index kan gebruiken bij de vorm:
SQL:
1
where _name=left(reverse("www.test.nl"),length(_name))

Het probleem met like is dat er misschien in de kolom wel het teken '%' staat. Er moet daarom iedere keer weer een nieuw like-pattern gecompileerd worden, wat flink tijd zal kosten. Waarschijnlijk is het volgende al wat sneller dan like:
SQL:
1
2
where name=right("www.test.nl",length(name)) and 
    (length(name)=length("www.test.nl") or substr(...)='.')

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Trivia kennis: LEFT() is in mysql _nooit_ beter dan een LIKE met een trailing wildcard.

En ja, LEFT() moet in een aantal gevallen goed te optimaliseren zijn, maar dat gebeurd gewoon niet. :P

{signature}


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
Kijk, ik wist wel dat ik niet achterlijk was...

Zoals gevraagd de structuur:
SQL:
1
2
3
4
5
6
7
8
9
10
CREATE TABLE IF NOT EXISTS `domain` (
  `id` bigint(20) NOT NULL default '0',
  `name` varchar(255) NOT NULL,
  `siteId` bigint(20) NOT NULL default '0',
  `mailDomainId` int(11) NOT NULL,
  `hasMail` tinyint(4) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `siteId` (`siteId`),
  KEY `mailDomainId` (`mailDomainId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


De query:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
     `siteBase`.`show` as `show`,
     `site`.`id` as `siteId`,
     `domain`.`id` as `domainId`
FROM
    `site`,
    `domain`,
    `base` AS `domainBase`,
    `base` AS `siteBase`
WHERE
    (`site`.`id` = `siteBase`.`id`)
    AND (`siteBase`.`deleted` = 0)
    AND (`domain`.`id` = `domainBase`.`id`)
    AND (`domainBase`.`deleted` = 0)
    AND (`domain`.`siteId` = `site`.`id`)
    AND ('.www.example.com' LIKE CONCAT('%.', `domain`.`name`))
ORDER BY LENGTH(`domain`.`name`) DESC;

en de resultaten van de explain:
idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEsiteindexPRIMARYsiteId4NULL85Using index; Using temporary; Using filesort
1SIMPLEdomainrefPRIMARY,siteIdsiteId8site.id1Using where
1SIMPLEdomainBaseeq_refPRIMARY,deletedPRIMARY4domain.id1Using where
1SIMPLEsiteBaseeq_refPRIMARY,deletedPRIMARY4domain.siteId1Using where


Doordat de mysql interne optimzer weet dat er met domain zelf niets te halen valt is de join (en de explain dus) op site, niet op domain...

Ik ben het met jullie eens dat de _name en de _length voordeel zouden moeten hebben, iedere mysql-function in je query zou er een te veel kunnen zijn (ivm de QueryCache)...

Ik ben misschien wel aan het muggeziften, maar de situatie is: iedere hit op de webserver dóet deze query voor de opgevraagde hostnaam... dus hóe sneller de query, hoe sneller álle hits.. ;o)

(of moet ik gewoon afstappen van uberhaupt query-en en een ander caching mechanisme bouwen die nóg sneller is?)

Gekkie is a proud member of TheBenny!


  • pedorus
  • Registratie: Januari 2008
  • Niet online
_Gekkie_ schreef op woensdag 10 september 2008 @ 15:32:
Ik ben misschien wel aan het muggeziften, maar de situatie is: iedere hit op de webserver dóet deze query voor de opgevraagde hostnaam... dus hóe sneller de query, hoe sneller álle hits.. ;o)

(of moet ik gewoon afstappen van uberhaupt query-en en een ander caching mechanisme bouwen die nóg sneller is?)
6+ms onnodige vertraging bij iedere request is echt veel te veel als je het mij vraagt. Ik zou dan testen welke (die met in of right) het snelste is, en die kiezen. Voor caching zou ik eens kijken naar APC. Kijk verder voor MySQL eens naar "LIMIT 1" en SQL_NO_CACHE.

Verder gaat er iets mis in die query lijkt wel; volgens mij moet je even zorgen dat domain als eerste staat en als eerste doorzocht wordt. Daarnaast raad ik INNER JOIN aan.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Topicstarter
de query duurt inderdaad 0.0059 sec... Maar een limit noch het juist uitzetten van de cache heeft daar effect op...

Overigens is APC wel leuk voor het cachen van PHP, maar zorgt er meteen ook voor dat apache het weer af en toe af moet laten weten... (en je daar dus weer oplossingen voor moet verzinnen)

De inner join is ook wel leuk, maar ook dat heeft hier niet zoveel zin ivm het feit dat de where het al 1 op 1 aan elkaar schakelt...

De query kan dus worden: (.0059sec)
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
SELECT SQL_NO_CACHE
`siteBase`.`show`,
`siteBase`.`id` as `siteId`,
`domain`.`id` as `domainId`
FROM
`domain`
INNER JOIN `site` ON `site`.`id` = `domain`.`siteId`
INNER JOIN `base` AS `domainBase` ON `domainBase`.`id` = `domain`.`id` AND `domainBase`.`deleted` = 0
INNER JOIN `base` AS `siteBase` ON `siteBase`.`id` = `site`.`id` AND `siteBase`.`deleted` = 0
WHERE (".www.example.com" LIKE CONCAT('%.', `domain`.`name`))
ORDER BY LENGTH(`domain`.`name`) DESC
LIMIT 1

en met de _name en _length:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
SELECT SQL_NO_CACHE
`siteBase`.`show`,
`siteBase`.`id` as `siteId`,
`domain`.`id` as `domainId`
FROM
`domain`
INNER JOIN `site` ON `site`.`id` = `domain`.`siteId`
INNER JOIN `base` AS `domainBase` ON `domainBase`.`id` = `domain`.`id` AND `domainBase`.`deleted` = 0
INNER JOIN `base` AS `siteBase` ON `siteBase`.`id` = `site`.`id` AND `siteBase`.`deleted` = 0
WHERE "moc.elpmaxe.www." LIKE CONCAT(`domain`.`_name`,'%')
ORDER BY `domain`.`_length` DESC
LIMIT 1


Dan is de query 0.0027sec

Toch lijk ik niet onder die CONCAT() uit te kunnen komen ivm het feit dat "LIKE `domain`.`_name`%" niet mag...

Gekkie is a proud member of TheBenny!


  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Deze join syntax is gewoon mooier, dan enkel ',' operators en zoeken naar join voorwaarden in de where clause.

De huidige query moet gewoon in de query cache kunnen komen hoor. Dus of de query cache staat uit (of demand), of de gebruikte tabellen worden steeds geupdate waardoor deze query uit cache geflushed wordt.

{signature}


  • pedorus
  • Registratie: Januari 2008
  • Niet online
_Gekkie_ schreef op woensdag 10 september 2008 @ 17:23:
De inner join is ook wel leuk, maar ook dat heeft hier niet zoveel zin ivm het feit dat de where het al 1 op 1 aan elkaar schakelt...
Maar wordt er nu idd eerst gekeken in domain, en pas daarna in site? Kijk even met explain.
De query kan dus worden: (.0059sec)
SQL:
1
WHERE (".www.example.com" LIKE CONCAT('%.', `domain`.`name`))

en met de _name en _length:
SQL:
1
WHERE "moc.elpmaxe.www." LIKE CONCAT(`domain`.`_name`,'%')


Dan is de query 0.0027sec
Ik had niet verwacht dat a% zoveel sneller dan %a zou zijn. Waarschijnlijk iets geks met de MySQL implementatie van like (of een toevalligheid).
Toch lijk ik niet onder die CONCAT() uit te kunnen komen ivm het feit dat "LIKE `domain`.`_name`%" niet mag...
Probeer het eens zonder like (zie boven voor in en right-varianten). 1600 keer een like-expressie compileren duurt wel even. En daarnaast kun je de % net zo goed gelijk in _name stoppen

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten

Pagina: 1