dalsi hlavolam WAS: jak z toho ven
Petr Vileta
petr na practisoft.cz
Sobota Červen 28 01:57:55 CEST 2003
"Petr Vileta" <petr na practisoft.cz> píše v diskusním příspěvku
news:bdg7uv$2rn2$4 na ns.felk.cvut.cz...
> Jsem ve fazi testovani a narazil jsem na dalsi problem. Zadavatel si nedal
> vysvetlit, ze by si login meli urcovat sami soutezici, neakceptoval ani
> pismena a navic si TED vymyslel, ze to musi byt cisla z rozsahu 100001 az
> 999999, ovsem generovana NAHODNE. Je to normalni reklamni trik (jak tvrdi
> zadavatel) nebo podvod (jak tvrdim ja), aby ucastnici nepoznali kolik jich
> vlastne celkem je a tudiz jaka je sance na vyhru. Jenze generovat neco
> takoveho je silenost, tak me napadlo, ze bych si ta cisla vygeneroval
> jednorazove predem do tabulky:
> id mediumint(6), pouzito smallint(1)
> a pak jen delal:
> SELECT id FROM cisla WHERE pouzito=0 limit 1;
> Sice to bude tabulka jako krava, ale bude to rychle pri tom pridelovani
id.
Provedl jsem si testy a dospel k zajimavemym poznatkum, tak tady jsou.
Zkousel jsem vytvorit tabulku
create table cisla (idnum mediumint(6) unsigned not null primary key,
pouzito datetime, karantena date);
a do ni generovat rand() cisla upravena na rozsah 100001 az 999999. Spustil
jsem skript, ktery generoval 800000 zaznamu a na stdout vypisoval cas
potrebny k jednomu zapisu. Vypadal asi nejak takhle
for($pocet=1; $pocet <= 80000; $pocet++)
{
&do_insert;
}
sub do_insert
{
$q=0;
$t1=time;
while($q < 80000)
{
$err=0;
$idnum=genrand(100001,999999);
$sth=$dbh->prepare("insert into cisla set idnum=$idnum");
$sth->execute() or $err=1;
$sth->finish;
if($err==0)
{
print "time - $t1\n";
return;
}
$q++;
}
}
sub genrand
{
return int(($_[1]*1 - $_[0]*1 +1) * rand() + $_[0]*1);
}
Ze zacatku to bezelo samozrejme rychle, ale pak to zacalo postupne
zpomalovat a posledni zobrazena hodnota byla cca 12minut. Takze jsem tento
zpusob zavrhnul a vratil se k memu puvodnimu zameru, ze si vygeneruji
tabulku predem a budu jen "obsazovat" dosud nepouzite zaznamy. Tabulka
obsahuje 899998 cisel a ma i s indexy 30MB. Je vytvorena uplne stejne a
navic ma vytvorene 2 indexy z poli "pouzito" a "karantena".
Ted prideleni idnum probiha takto
# zamkneme, aby nam to nekdo nevzal pod rukama
$sth=$dbh->do("lock tables soutez_cisla write");
# najdeme prvni volne cislo, ktere jeste nebylo nikdy pouzito
$sth=$dbh->prepare("select idnum from soutez_cisla
where isnull(karantena) and isnull(prideleno) limit 0,1");
$sth->execute() or die "can't select free idnum";
$q=$sth->rows*1;
if($q==1)
{
# pokud se cislo naslo, nacteme ho
$row=$sth->fetchrow_arrayref;
$idnum=$row->[0]*1;
$sth->finish;
# a zapiseme, ze uz je pouzite
$sth=$dbh->prepare("update soutez_cisla set prideleno=now() where
idnum=$idnum");
$sth->execute() or die "can't update soutez_cisla";
$sth->finish;
# a sammozrejme odemkneme
$sth=$dbh->do("unlock tables");
# uspech, vracime se
return 0;
}
$sth->finish;
# najdeme prvni volne cislo, ktere je v karantene dele nez 1 rok
$sth=$dbh->prepare("select idnum from soutez_cisla
where not isnull(karantena) and karantena*1 < date_sub(now(), interval
1 year)*1
limit 0,1");
$sth->execute() or die "can't select any idnum";
$q=$sth->rows*1;
if($q==1)
{
# pokud jsme nasli, tak ho nacteme
$row=$sth->fetchrow_arrayref;
$idnum=$row->[0]*1;
$sth->finish;
# a zapiseme, ze uz je pouzite, zaroven nastavime karantenu na null
$sth=$dbh->prepare("update soutez_cisla set prideleno=now(), karantena=null
where idnum=$idnum");
$sth->execute() or die "can't update soutez_cisla";
$sth->finish;
# a sammozrejme odemkneme
$sth=$dbh->do("unlock tables");
# take uspech, tak zpatky
return 0;
}
$sth->finish;
# nepodarilo se tak ani tak, ale musime odemknout
$sth=$dbh->do("unlock tables");
return 14; # neni mozne pridelit dalsi cislo, vraci se error 14
Na tenhle program je odezva na prideleni idnum maximalne 5 sekund.
Testovano (oboji) na singl stroji s jedinym aktivnim klientem.
--
Petr
Další informace o konferenci Test