David Steinsland – informatikkstudent og webutvikler

Innlegg fra kategorien «MySQL»

Oppgraderingsdag

| Ingen kommentarer »

I disse «sene» juledager har jeg fått oppgradert serveren min, deriblant PHP, MySQL og Apache til aller siste versjon. Mest hyggelig er det jo med en etterlengtet MySQL-oppgradering, som såvidt har blitt oppdatert Oracle kjøpte dem opp.

Anbefaler også at du gjør det samme, og da spesielt MySQL 5.5-oppgraderingen hvor InnoDB endelig er satt som standard og som kan skiltes som 540 % mer effektiv (Windows)!

Så nå kjører jeg altså PHP 5.3.4, MySQL 5.5 og Apache 2.2.17, på en Windows 7-maskin, som tok meg i underkant av 15 minutter å gjøre.

PS:

MySQL Query Cache

| Ingen kommentarer »

Når du bygger applikasjoner for nettet, vil du komme til et punkt hvor enkelte komponenter kan dra ned ytelsen eller lastetiden. Dette kan for eksempel være krevende PHP-kode som store løkker, behandling av store mengder data, eller krevende MySQL-spørringer.

Du tenker da kanskje å implementere et mellomlager. Dette kan du gjøre på applikasjonsnivå og på servernivå.

La oss si at du har bygget deg en RSS-leser, som henter XML-data fra nyhetsstrømmer fra nettet. Over tid vil serveren bruke mye ressurser på å laste disse ned fra nettet hele tiden, og som mottiltak kan du lagre XML-dataene på din egen server (som blir oppdatert jevnlig). På den måten har du implementert et mellomlager på applikasjonsnivå, hvor du lagrer data i en gitt periode før du oppdaterer mellomlageret med ny data (for eksempel hver 30. minutt).

Du kan også implementere løsninger på servernivå, som kan bedre ytelsen til PHP ved at den kompilerer koden og lagrer den på RAM. Her har du eksempler som PHP-Accelerator, eAccelerator og Zend Optimizer.

Men hva når det kommer til MySQL-spørringer?

Her kan du også implementere et mellomlager på applikasjonsnivå, men da må du sette opp dine egne metoder som kjører spørringer etc. Du følger da samme tankegangen som med RSS-leseren:

  • Når spørringen blir kjørt for første gang, lagrer du resultatet som spørringen gir i en tekstfil.
  • Neste gang spørringen blir kjørt, returnerer du dataene fra tekstfilen istedenfor å kjøre spørringen på nytt.

Men dette kan være uønskelig i flere tilfeller:

  • du må sette opp egne metoder, da PHPs innebygde MySQL-bibliotek ikke støtter caching
  • dataene kan være unøyaktige, og du bør hele tiden sørge for at tekstfilene inneholder oppdatert informasjon

Typisk implementering ville vært noe slik:

class MySQL
{
	public function query ($query)
	{
		$cache = 'cache/' . md5 ($query) . '.mysql';

		if ( file_exists ($cache))
		{
			$data = unserialize (file_get_contents ($cache));
		}
		else
		{
			$query = mysql_query ($query);

			$data = array ();

			while ($row = mysql_fetch_assoc ($data))
			{
				$data[] = $row;
			}

			file_put_contents ($cache, $data);
		}

		return $data;
	}
}

$mysql = new MySQL ('localhost', 'user', 'pw', 'db');

$data = $mysql->query ('SELECT foo FROM bar');

MySQL Query Cache

Men en bedre løsning vil være å dra nytte av MySQLs egen Query Cacher. Denne returnerer mellomlagret data dersom det er forblitt uforandret, og oppdaterer så snart det er ny data. I tillegg blir spørringene utført på tilnærmet 0 sekunder. Altså en veldig grei sak, dette her!

I de fleste tilfeller er Cache-muligheten skrudd av som standard, og du kan sjekke dette ved å kjøre følgende kommando til MySQL:

mysql> show variables like 'query%';
+------------------------------+---------+
| Variable_name                | Value   |
+------------------------------+---------+
| query_alloc_block_size       | 8192    |
| query_cache_limit            | 1048576 |
| query_cache_min_res_unit     | 4096    |
| query_cache_size             | 0       |
| query_cache_type             | ON      |
| query_cache_wlock_invalidate | OFF     |
| query_prealloc_size          | 8192    |
+------------------------------+---------+

Her ser vi at query_cache_size er satt til 0, noe som fører til at Cache-funksjonaliteten er skrudd av. For å aktivere denne, kjør følgende kommando:

mysql> set global query_cache_size=50000000;

Det er i bunn og grunn alt du trenger å gjøre! Og dette fungerer flott som fjell.

Lykke til!

Daglig backup av MySQL med PHP (Windows)

| Ingen kommentarer »

PHP tips og triks

Dersom du drifter din egen server eller har en utviklingsserver på PC-en din, har du nok vært i situasjoner hvor du tenker: «Hvor er sikkerhetskopien når jeg trenger den?».

Jeg var der selv for omtrent en úke siden, hvor jeg ved en feiltakelse kjørte en gammel PHP-fil som overskrev hele Postnummer-databasen min. Heldigvis hadde jeg mange sikkerhetskopier av databasen, så det gikk bra.

Men hva om jeg ikke hadde hatt kopier? Da hadde faktisk hele arbeidet mitt vært ødelagt! Mangfoldige timer bortkastet, bokstavelig talt.

Hver dag (midnatt) blir livredderen utført: en planlagt oppgave i Windows kjører et PHP-skript som tar backup av MySQL.

I mine siste artikler har jeg skrevet litt om planlagte oppgaver i Windows, og vist flere ting du kan gjøre.

Teori

Når du installerte MySQL, fikk du med et program som heter mysqldump. Programmet brukes til å ta backup av MySQL; du kan selv spesifisere én eller flere databaser, eller om du ønsker å ta backup av alle.

Videre kommer jeg til å ta i bruk  PHP fra kommandolinjen, som muliggjør at vi kan bruke PHP-tolkeren til å kjøre en bestemt PHP-fil. Dette programmet ligger i installasjonsmappen til PHP, og heter php.exe

Jeg refererer til disse programmene som henholdsvis mysqldump og php fra kommandolinjen. Dette er fordi jeg har lagt til PHPs installasjonsmappe og bin-mappen til MySQL til miljøvariabelen PATH. Dette forteller Windows at når du skriver inn et program i CMD, skal den også leite etter det programmet i de plasseringene.

Dersom du ikke har gjort dette, må du spesifisere til programmene med full sti, eks: c:\php\php eller skrive inn følgende i CMD:

SET PATH = %PATH%;C:\PHP;C:\MySQL\bin

(jeg antar PHP og MySQL er installert i C:\).

Ved default ligger «.EXE» i miljøvariabelen PATHEXT, som medfører at du slipper å skrive «.exe» bak programnavnet.

PHP-filen

Gjør klar en PHP-fil som du navngir backup.php og plasser denne utenfor ServerRoot (altså en plass den ikke kan nåes via en nettleser).

ServerRoot hos meg er \www\public_html\, slik at jeg har plassert PHP-filen ett steg opp, i \www\ (Med baklengs-skråstrek foran stien, tolker Windows dette som C:\).

Lag så en mappe du kaller for backup i samme mappe hvor du har plassert PHP-filen. Dette er plassen hvor backupene våre kommer til å ligge.

Det første vi gjør i PHP-filen vår er å definere et par runtime-innstillinger, som databasetilkoblingen, plasseringen til backup-filene og hvordan filnavnet skal utformes.

// MySQL host
define ('HOST', 'localhost');
// MySQL username
define ('USER', 'username');
// MySQL password
define ('PW', 'password');

define ('DUMP_FILENAME', date ('Y-m-d-H-i') . '.sql'); // i.e. 2010-10-30-23-00.sql

define ('SELF_DIR', __DIR__ . DIRECTORY_SEPARATOR);
define ('BACKUP_DIR', 'backup' . DIRECTORY_SEPARATOR);

Backupfilene våre får navnet sitt basert på dagens dato og tidspunktet for når filen blir kjørt.

Neste steg er å kalle opp mysqldump:

passthru (sprintf ('mysqldump -h %s -u %s -p%s -A -r "%s"', HOST, USER, PW, SELF_DIR . BACKUP_DIR . DUMP_FILENAME));

Her trenger du ikke endre noe, bortsett fra stien til mysqldump om du ikke har endret PATH i Windows.

Argumentforklaring:

-h
Spesifiserer host-adressen til MySQL
-u
Brukernavnet til MySQL
-p
Passordet ditt MySQL-brukeren (-p og passordet skal ikke ha mellomrom mellom hverandre).
-A
Spesifiserer at vi skal ta backup av alle databasene. Alternativt –all-databases.
-r
Den fulle stien (plassering + filnavn) til hvor backupen ligger

Det neste jeg har valgt å gjøre, er å komprimere SQL-filen i et Zip-arkiv, som medfører at filstørrelsen blir endel mindre (noe som er bra!).

$Zip = new ZipArchive;
$Zip->open (SELF_DIR . BACKUP_DIR . DUMP_FILENAME . '.zip', ZipArchive::CREATE);
$Zip->setArchiveComment ('This database dump was automatically taken at ' . date ('H:i, d.m.Y') . ' by a Windows Scheduled Task');
$Zip->addFile (DUMP_FILENAME, SELF_DIR . BACKUP_DIR . DUMP_FILENAME);
unlink (SELF_DIR . BACKUP_DIR . DUMP_FILENAME);
$Zip->close();

Den planlagte oppgaven

Det siste steget vårt, er å opprette oppaven som skal kjøre PHP-skriptet. Jeg har valgt å kjøre denne oppgaven hver dag ved midnatt.

Opprett den planlagte oppgaven ved å skrive dette inn i CMD:

SCHTASKS /Create /SC DAILY /MO 1 /ST 00:00 /TN "MySQL Backup" /TR "php \www\backup.php"

Tips

Uten PHP

Dersom du ikke ønsker å komprimere filene med Zip, trenger du heller ikke anvende PHP til denne jobben. Da kjører du rett og slett bare mysqldump direkte i oppgaven:

SCHTASKS /Create /SC DAILY /MO 1 /ST 00:00 /TN "MySQL Backup" /TR "mysqldump -h <host> -u <brukernavn> -p<passord> -A -r \www\backups\database_dump.sql"

Husk at det ikke skal være mellomrom mellom -p og passordet!

Én eller flere databaser istedenfor alle

Erstatt -A i mysqldump-kommandoen med:

--database <database>

eller

--databases <database 1> <database 2> ... <database N>

Garbage Collector

Det tar ikke lange tiden før backup-mappen blir full av filer; og strengt talt så trenger du ikke backups som ble tatt for måneder siden. Derfor kan det være lurt å sette opp en søppelsamler, som sletter gamle filer.
Da oppretter du en planlagt oppgave som kjører for eksempel \www\garbage.php hver uke. Innholdet i PHP-filen er å loope gjennom backup-mappen, og sjekke datoen på når filene sist ble modifisert (opprettet). Slik har jeg gjort det:

// How long we are keeping each file, since its creation date
// (in seconds)
define ('MAX_FILE_AGE', (60 * 60 * 24 * 7)); // 7 days

$data = glob (__DIR__ . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . '*.zip');

foreach ($data as $file)
{
	if (time() - filectime ($file) > MAX_FILE_AGE)
	{
		unlink ($file);
	}
}

Database med Norges postnummer, poststeder, fylker og kommuner (med koordinater)

| 16 kommentarer »

Norway_counties.svg

Her kommer det er oversikt over absolutt alle postnummer i Norge med tilhørende fylke, kommune og poststed. Hvert postnummer har også en lengde- og breddegrad tilknyttet seg. På denne måten kan du koble opp postnummeret til f.eks Google Maps.

Databasesettet er generert med hjelp av Postnummer-dugnaden Yr.no arrangerte sommeren 2009.

Ved hjelp av lengde- og breddegradene kan du jo også regne deg frem til avstanden mellom to steder, eller finne nærmeste postkontor til en bruker.

For å få lett oversikt over alle postkoder, kjører du følgende spørring:

SELECT
 postnummer, fylkeNavn, kommuneNavn, poststedNavn, kategoriNavn, lat, lon
FROM `postnumre`
INNER JOIN poststed
USING(poststedID)
INNER JOIN kommuner
USING(kommuneID)
INNER JOIN fylker
USING(fylkeID)
INNER JOIN kategorier
USING(kategoriID)
ORDER BY
 postnummer ASC, fylkeNavn ASC, kommuneNavn ASC, poststedNavn ASC

Med enhver sak kan det forekomme feil, så om dere kommer over noe som skulle vise seg til å være en bug: si i fra.

Oppdateringer

Versjon 1.4
- Har innført alle endringer gjort av posten pr. 1. februar 2012
- Det er nye navn på tabeller og kolonner (norsk)
- Koordinater er synkronisert med listene til Erik Bolstad

Versjon 1.3
- Tabellene er gjort om til InnoDb med nødvendige indekser og fremmednøkler
- Jan Mayen og Svalbard er nå plassert inn i to fylker (med samme navn), i henhold til ISO 3166-2:NO. På grunn av at områdene ikke har noe de facto fylke, måtte det gjøres slik.
- Enkelte justeringer på koordinater
- Kommunesentre for hver kommune er lagt inn

Versjon 1.2
- Jan Mayen og Svalbard har klart å falle ut fra oversikten. Det er pga. områdene ikke er del av noen fylker, og det har dermed blitt opprettet et «Svalbard og Jan Mayen»-fylke.

Last ned (versjon 1.4)

Last ned Norske postnummer (2183 nedlastninger) , lisensiert under Creative Commons.