Prevenire inserare multiplă

Cum aș putea să previn adăugarea multiplă de accesări de pe același ip, dar fără să stochez toate ip-urile în baza de date…

$pdo->prepare("update articole set accesari = accesari + 1 where id=?")->execute([$id]);

INSERT INTO table 
  (a, counter_elem) 
VALUES 
  (1, 1),
  (2, 1)
ON DUPLICATE KEY UPDATE counter_elem = counter_elem + 1;

Cred că problema nu e de SQL, ci mai degrabă de arhitectură: Cum poți să incrementezi accesările numai o singură dată pentru un IP fără să salvezi IP-urile în baza de date…

De menționat:

  • un IP poate fi folosit de mai multe persoane, poți să ai 10 utilizatori diferiți în spatele fiecărui IP și să-i numeri pe toți ca și cum ar fi numai unul.
  • poți să setezi un cookie pentru fiecare client în care salvezi articolele vizitate. înainte să incrementezi counter-ul pentru un articol vezi dacă a fost deja vizitat (numărat) folosind lista de id-uri din cookie:
    • consumi bandwidth (cookieurile se trimit la fiecare request)
    • un user poate folosi mai multe browsere, poate folosi incognito mode sau chiar să șteargă manual cookie-urile => e numărat de fiecare dată ca fiind alt utilizator (poate fi folosit pentru a exploata statisticile) (în cele mai multe cazuri cred că e de preferat așa decât să identifici userii pe bază de IP)

Recomandările mele:

Incremeentează toate accesările, chiar de mai multe ori pe user (dacă cifrele sunt destul de mari, refresh-urile de pagină sau revizitările de articol o să fie nesemnificative).

A doua opțiune ar fi să folosești cookie-urile, și atunci filtrezi duplicatele mai ușor. Tot poți să mai scapi, dar mult mai puține decât prima variantă. Dezavantajul e bandwidth-ul. Avantajul e că nu mai încarci baza de date cu IP-uri.

A treia opțiune e să schimbi structura. Ți separat un tabel cu vizite: date identificare utilizator (IP, proxy, user agent, session id etc), articol id, timestamp. Și apoi faci raportarea în funcție de datele din tabelul respectiv. Poți să actualizezi câmpul acesări din tableul articole periodic. Avantajele e că poți face mai multe tipuri de raportări: accesări in ultimele 24 ore, ultima săptămână etc, poți număra separat vizite unice (o accesare pe sesiune/ip/user) sau vizite în general. Dezavantajul e că o să ai un tabel care o să crească încontinuu și o să ajungă la dimensiuni foarte mari. În funcție de trafic poate să devină o problemă de performanță dacă nu e bine configurat și folosit.

4 Likes

Simplu, folosesti login cu SSO prin facebook/google in loc de ip.

Ip-urile nu garanteaza unicitatea utilizatorului si iti ia 5 minute sa faci rost de alt ip cu un vpn/proxy, la digi ip-urile sunt dinamice, iti iei cate ip-uri vrei una dupa alta.

Pentru ip in mod normal iti trebuie un hashmap cu [ip,accesari], ip-ul e cheia dupa care poti lua direct cate accesari are. Iti trebuie o baza de date/cache fiindca php nu ruleaza ca si server (altfel l-ai putea stoca in memorie si sa le salvezi/incarci o data la cateva minute).

  • Poti sa stochezi asta si in memcached/cache/redis, chiar e mult mai bine ca in mysql,
1 Like

Presupunand ca folosesti sessions/cookies, poti improviza ceva de genul:

if(!isset($_SESSION['viewed'][$id])) {
    $pdo->prepare("update articole set accesari = accesari + 1 where id=?")->execute([$id]);
    $_SESSION['viewed'][$id] = true;
}

Nu-i cea mai ok solutie, crawlerii nu tin sesiuni/cookies de obicei, dar isi face treaba pentru un proiect fara multe pretentii.

1 Like