Autor Zpráva
slovakCZ
Profil
Ahoj,

řeším problém s občasným dvojitým ukládáním do db.

Mám takovýto skript, ktery zobrazuje kazdemu 10. navstevnikovi nejaky reklamni banner. Ted to testuju a kdyz drzim tlacitko F5, tak se mi naprosto nesmyslne ukladaji do databaze hodnoty do sloupce show. Vysvetleni nize, zde je ukazka:
<?php
	/* vybere posledni zaznam z tabulky. ve sloupci SHOW je ulozeno bud 1 nebo 0. ID se navysuji automaticky o 1 */
	$q = mysql_query("SELECT id, show FROM $dt ORDER BY id DESC LIMIT 1") or die(mysql_error());
	while ($row = mysql_fetch_object($q)) {
		/* kdyz se show nerovna 1 tak pokracujeme, jelikoz nechceme reklamu zobrazi 2x hned po sobe. */
		if($row->show!=1){
			if($row->id%3==0){
				echo 'jelikoz je to 3. navstevnik, tak mu zobrazime reklamu. Reklama se tedy zobrazuje kazde 3. navsteve';
				$showen=1;
			}
		}else{
			$showen=0;
		}
		/* do tabulky ulozime id a hodnotu 1 nebo 0 v zavislosti na tom, zda se navstevnikovi zobrazila reklama ci nikoliv */
		$ins = mysql_query("INSERT INTO $dt VALUES('','$showen')") or die(mysql_error());
	}
?>


V pripade, ze mackam tlacitko F5 (refresh) v intervalu cca 0,3 sekundy, tak skript funguje v poradku. Databaze pak vypada nasledovne:
sloupec ID - slupec SHOW
1 - 0
2 - 0
3 - 1
4 - 0
5 - 0
6 - 1
7 - 0
atd atd

ovsem, kdyz tlacitko F5 drzim, tak se do databaze uklada neco takoveho:
1 - 0
2 - 0
3 - 1
4 - 1
5 - 0
6 - 1
7 - 0
8 - 1
9 - 1
10 - 0
11 -0
12 - 1
13 - 1
atd atd


Vypada to, ze skript nedokaze v te "rychlosti" zjistit spravne ID.. nebo dotaz na aktualni radek pomoci selektu je proveden drive nez operace predchoziho ulozeni..... Jak je to ovsem mozne? Testuju u sebe na localhoste (mozna to je vykonem PC, je to mozne? mam stolni PC, WIN XP, Pentium D 3GHz, 3GHz, 2GB RAM).

Jak necemu takovemu predejit? Problem nastane, kdyz skript nasadim na web, ktery je opravdu zatizen velkou navstevnosti. Na male strance to bude fungovat bez problemu.

Dekuji za rady
Joker
Profil
slovakCZ:
Vypada to, ze skript nedokaze v te "rychlosti" zjistit spravne ID
To ne, prostě následující načtení skriptu udělá SELECT dříve, než to předchozí INSERT.
Čili:
- 3. běh skriptu udělá SELECT, poslední řádek je id=2, show=0.
- 4. běh skriptu udělá SELECT a najde tentýž řádek.
- 3. běh skriptu udělá INSERT a vloží řádek s id=3, show=1
- 4. běh skriptu udělá INSERT a vloží řádek s id=4, show=1

Jak necemu takovemu predejit?
- Minimalizovat dobu mezi dotazy, nebo to rovnou postavit jen na jednom dotazu. Zobrazení každému n-tému návštěvníkovi by šlo udělat jednoduše tak, že se udělá INSERT, přečte se hodnota jeho ID a zkontroluje, zda ID je dělitelné n.
- Udělat nějaké řízení přístupu, jako zámek nebo token (před začátkem té vyhrazené operace si skript rezervuje token/zámek. Má-li token jiná instance, počká a zkusí to znovu. Až získá token, provede tu operaci a na jejím konci token uvolní).
slovakCZ
Profil
Joker:
Ok rozumím, děkuji ;-)
TomášK
Profil
Joker:
Transakce na úrovni databáze vynecháváš úmyslně? Zdají se mi přesně dělané pro tento případ, i jednodušší než vytváření zámků. Je v transakcích nějaký háček nebo jsi je jen neuvedl?
Vrtulnik
Profil *
Transakce myslím umí jen InnoDB tabulky, pokud se nemylim.

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: