MySQL Jokeri

Pitanje za milion dolara u nekom popularnom kvizu. Imate samo 15 sekundi za odgovor. Srecni ste jer ste izvukli pitanje iz vase omiljene oblasti 🙂

Kako obrisati sve korisnike ciji nick pocinje sa donjom crtom (‘_’).

I vas odgovor je naravno:

delete from users where username like '_%';

Vec mastate o raskalasnom zivotu daleko od kompjutera, mozda i neka jahtica pride (i sve sto ide u to lol). Glas voditelja vraca vas u surovu realnost. “Zao mi je, ovo nije tacan odgovor”. U neverici gledate vas query i milione koji su otisli u nepovrat.

Verovali ili ne, slicnu nevericu iskusio sam na svojoj kozi pre nekoliko veceri, tacnije 8. aprila. U tabeli sa nekoliko miliona generisanih domena uocen je bug (mala greska u regularnom izrazu) i nekako su generisani i domeni koji pocinju sa znakom “_”. Underscore (donja crta) naravno nije dozvoljen kod domena, bug je ispravljen ali treba obrisati i te nevalidne domene iz baze. Nista lakse, jedan brzi query i sve ce ubrzo biti pocisceno. Otvaram mysql klijent i bez mnogo razmisljanja kucam:

mysql> delete from result_domains where domain like '_%';
Query OK, 9035782 rows affected (9 min 57.35 sec)

WTF!?? Query je obrisao sve domene iz tabele. Ali kaaakooo???

I onda se setim. Donja crta – ‘_’ je poput ‘%’, takodje joker karakter koji za razliku od ‘%’ (koji menja ‘nula ili vise’ karaktera), ‘_’ menja tacno jedan karakter. Ne secam se kada sam ga poslednji put koristio (ako sam ga uopste koristio), ali znam da mi je on dosao glave.

Cimam admina na ICQ … treba mi backup, server taj i taj, tabela ta i ta, poslednji koji imamo … ASAP! Posle 30-tak sekundi admin se javlja “ok, poslednji koji imamo je od 8 marta. Gde da ti stavim?”. FUCK!!!

FUCK!!! FUCK!!! FUCK!!! FUUUUCK!!!

Ustajem od kompjutera i besno setam kroz sobu. Na postoji nacin da objasnim klijentu da sam jednim jedinim kverijem sjebao sate i sate mukotrpnog rada, podatke vredne verovatno hiljade dolara. Da imam utoku verovatno bih pao u iskusenje da pucam sebi u glavu. Mozda pre toga da sredim i admina? Kako god, sta je tu je, ne preostaje nista drugo nego da napisem email i objasnim svom klijentu sta se desilo.

Sedam ponovo za comp, ali umesto da otvorim thunderbird, cimam ponovo admina.

– Ja: “Jebote … kako se desilo da je poslednji backup star mesec dana???”
– Admin: “Kako to mislis? Ovo je backup od sinoc!?”
– Ja: “Rekao si 8 mart???”
– Admin: “Ups. Sorry, 8. April. Sad gledam, kreiran je pre samo par sati. Moze?”

I tako, my ass has been saved. Ali moglo je i biti drugacije. Mnogo drugacije.

Pouka price:

1) Budite ekstremno oprezni kada kucate nesto unutar mysql klijenta (ili phpmyadmina) koji barata sa live podacima
2) Ako morate da brisete/update-ujete nesto, uvek uradite prvo count nad istim podacima i istom where klauzom kako bi ste se uverili da je to bas ono sto ste zeleli.

Npr:

mysql> select count(*) from result_domains where domain like '_%';
+----------+
| count(*) |
+----------+
|  9035782 |
+----------+
1 row in set (5.27 sec)

(ups nesto ne valja, ovo ce obrisati sve domene koje imamo)

mysql> select count(*) from result_domains where domain like '\_%';
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (4.18 sec)

Aha, sada je sve ok 🙂

3) Uvek pravite redovan backup. Ako to za vas rade admini postarajte se da rade svoj posao kako treba. Cak i tada nije losa ideja da s vremena na vreme napravite sopstveni backup.

P.S. Problem sa pocetka price (matchovanje stringa koji pocinje sa ‘_’) resava se jednostavnim escapovanjem specijalnog karaktera. Dakle:

select foo from footable where somefield like '\_%';

ili ako ste ljubitelj regularnih izraza:

select foo from footable where somefield regexp '^_';

13 thoughts to “MySQL Jokeri”

  1. He, he, ala te je iscimao. 🙂

    Ma ništa gore od tog prčkanja na živo i kad nakon pritiska na Enter shvatiš da si zadao pogrešan WHERE uslov, ili ga nisi zadao uopšte a trebalo je… O:)

  2. Uvek backup cele baze pre igranja, posebno ako se nešto briše. Uvek to sebi kažem, nikada ga ne napravim i s vremena na vreme obrišem stvari koje ne bi trebalo da obrišem 🙂

    Nego, zašto ti ovom count() treba 5 sekundi??? Kačiš se na remote server ili?

  3. @Ilija
    Nije ti palo na pamet da je to zato sto ima 9 miliona slogova 🙂 ? Ok, delom je i zbog hardware-a posto je u pitanju single CPU masina 🙂

  4. Ovo ide uz temu (cisto nek se nadje…):

    function mysql_escape($string, $useInLike = false) {
    if (version_compare(phpversion(), “4.3.0”) == “-1”) {
    $string = mysql_escape_string((string)$string);
    } else {
    $string = mysql_real_escape_string((string)$string, $GLOBALS[‘db_link’]);
    }
    if ( $useInLike ) $string = addcslashes($string, ‘%_’);
    return $string;
    }

    🙂

  5. Препоручујем да све то обављаш у оквиру трансакција, и онда немаш никаквих проблема — ако ти каже да си обрисао сувише редова, знаш да брже боље урадиш ROLLBACK 😉

  6. А успут, не верујем да вишепроцесорска машина може помоћи у извршавању једног таквог упита који је по својој природи „атомски“.

  7. Eehehehe nisi jedini, koji se opekao sa tim jokerima…
    A reci mi sto ne koristis InnoDB engine? Btw. da li si razmislio o kombinaciji MyISAM engina (za SELECT) i InnoDB engina (za INSERT, UPDATE i DELETE? Podatke upisujes u InnoDB tabele, potom ih kopiras u (kompresovanu) MyISAM tabelu… Zahtjeva vise administrativnog posla, ali mislim da bi se isplatilo za tu kolicinu podataka…

Comments are closed.