powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Счетчик посещений сайта на postgres
25 сообщений из 26, страница 1 из 2
Счетчик посещений сайта на postgres
    #38557673
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Пишу самый простой счетчик посещений сайта используя куки, сохраняю все данные просто обновляя значения посещений в БД, если правильно понимаю нужна блокировка, чтобы не было параллельных обновлений и не терялись записи о посетителях.
Саму блокировку сделал, но непонятно что делать с первой записью за день, когда делаю Insert первых значений для счетчка, этот инсерт тоже нужно как то защитить от проблем с параллельными запросами но немогу понять как, подскажите решение.

Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
$create_date = date('Y-m-d H:i:s');
$text_zaprosa="select id, hosts, views  from enumerator where site_id=$site_id and id_iz_users=$id and create_date='$create_date';";
$res = pg_query($pgsql_conn, $text_zaprosa);
$rows = pg_num_rows($res);
if($rows==0){
      $res = pg_query($pgsql_conn, "INSERT INTO enumerator VALUES (default, $id, $site_id, '$create_date', 1, 1);");
}

while(1){
$text_zaprosa="select id, hosts, views  from enumerator where site_id=$site_id and id_iz_users=$id and create_date='$create_date' And pg_try_advisory_lock(id) limit 1;";
$res = pg_query($pgsql_conn, $text_zaprosa);
$rows = pg_num_rows($res);
if($rows>0){
  $id_iz_enumerator=pg_fetch_result($res, 0, 0);
  $hosts = pg_fetch_result($res, 0, 1);  
  $views = pg_fetch_result($res, 0, 2);  
  
  if($net_host==0){$hosts=$hosts+1;}
  $views=$views+1;
  pg_query($pgsql_conn, "UPDATE enumerator SET hosts=$hosts, views=$views  where id=$id_iz_enumerator;");
  break;
}
}
pg_query($pgsql_conn, "select pg_advisory_unlock_all();");



структура БД

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
CREATE TABLE enumerator (
id bigserial PRIMARY KEY,
id_iz_users   integer,
site_id integer,
create_date  timestamp without time zone,
hosts   integer,
views   integer
);
CREATE INDEX ON enumerator (id);
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38557783
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
trom, ну кто так пишет? Вот так надо:

Код: php
1.
2.
3.
4.
5.
$res = pg_query_params(
  $pgsql_conn,
  'select enumerator.add($1, $2, $3)',
  [$site_id, $id, $create_date]
);
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38557832
Фотография Warstone
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
trom
Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
$create_date = date('Y-m-d H:i:s'); // С точностью до секунды... Ага...
$text_zaprosa="select id, hosts, views  from enumerator where site_id=$site_id and id_iz_users=$id and create_date='$create_date';"; // И тут счетчик посещений в секунду.
$res = pg_query($pgsql_conn, $text_zaprosa);
$rows = pg_num_rows($res); // Почти всегда true
if($rows==0){
      $res = pg_query($pgsql_conn, "INSERT INTO enumerator VALUES (default, $id, $site_id, '$create_date', 1, 1);"); // Может свалиться, если кто-то еще успел раньше.
}

while(1){
$text_zaprosa="select id, hosts, views  from enumerator where site_id=$site_id and id_iz_users=$id and create_date='$create_date' And pg_try_advisory_lock(id) limit 1;"; // А если создали запись, то бузет сразу 2, да?..
$res = pg_query($pgsql_conn, $text_zaprosa);
$rows = pg_num_rows($res);
if($rows>0){
  $id_iz_enumerator=pg_fetch_result($res, 0, 0);
  $hosts = pg_fetch_result($res, 0, 1);  
  $views = pg_fetch_result($res, 0, 2);  
  
  if($net_host==0){$hosts=$hosts+1;}
  $views=$views+1;
  pg_query($pgsql_conn, "UPDATE enumerator SET hosts=$hosts, views=$views  where id=$id_iz_enumerator;"); // А все это говно в одном запросе сделать, видимо нельзя.
  break;
}
}
pg_query($pgsql_conn, "select pg_advisory_unlock_all();");

КГ/АМ.

Короче делается это так:
INSERT INTO enumerator ....
Если не сработало, то:
UPDATE enumerator SET views = views + 1, hosts = hosts + 1 WHERE id = ...

Это в тупую.
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558061
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ёш ,
автор$res = pg_query_params(
$pgsql_conn,
'select enumerator.add($1, $2, $3)',
[$site_id, $id, $create_date]
);

Выдает ошибку Warning: pg_query_params(): Query failed: ОШИБКА: схема "enumerator" не существует
pg_query_params лучше чем pg_query но грамотно составить синтаксис запроса я не смог.

Warstone,
Конечно вы правы надо время вызывать так
Код: php
1.
$create_date = date('Y-m-d 00:00:00');

исправил

авторА все это говно в одном запросе сделать, видимо нельзя.
Использовать SELECT FOR UPDATE вместо блокировки pg_try_advisory_lock или о чем речь ? я читал что pg_try_advisory_lock лучше


Но по главному вопросу все так и осталось не понятно!

Код: sql
1.
2.
INSERT INTO enumerator ....
Если не сработало, то:


Как понять что не сработало ?? инсерт всегда будет вставлять запись в таблицу
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558120
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
trom Ёш ,
автор$res = pg_query_params(
$pgsql_conn,
'select enumerator.add($1, $2, $3)',
[$site_id, $id, $create_date]
);

Выдает ошибку Warning: pg_query_params(): Query failed: ОШИБКА: схема "enumerator" не существует
pg_query_params лучше чем pg_query но грамотно составить синтаксис запроса я не смог.

<...>
"м,тд?" (сс)

расшифрую, в таком разе, за йоша:
надо написать хранимку enumerator.add. И передать туда параметры, способом, предотвращающим скл-инжекцию.

если не писать хранимку (что можно, но менее симпатично), то что-то в стиле
Код: php
1.
2.
3.
4.
$res = pg_query_params(
     $pgsql_conn,
     'INSERT INTO enumerator VALUES (default, $1, $2, $3, 1, 1);'
     , [$site_id, $id, $create_date]);
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558179
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
qwwq,

Спасибо работает, теперь я знаю как использовать pg_query_params давно не мог разобраться

Но это не ответ на главный вопрос как не допустить возможного дублирования insert в начале работы счетчика.
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558190
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tromqwwq,

Спасибо работает, теперь я знаю как использовать pg_query_params давно не мог разобраться

Но это не ответ на главный вопрос как не допустить возможного дублирования insert в начале работы счетчика.
1. нопейсать хранимку.
1.0. засунуть адвайзери локинг туда, где он должен быть -- в память на будущее, для организации очередей джобов типа мейлеров [или автономностей].
1.1. а тут навесить нужное уникью и обрабатывать exception insert-a [OR NOT FOUND update-a].
все впоросы в RTFM
1.1.1. ссылки на RTFM отсюда (тут поиск например по "UPSERT" ).

самое простое (но, тащемто, неверное)
Код: sql
1.
2.
WITH up AS (UPDATE blahblahblah RETUTNING key)
INSERT INTO blahblahblah  SELECT blah,blah,blah WHERE (SELECT count(1) FROM up =0);
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558191
Гость_0
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
trom, Warstone же Вам написал как, ловите ошибку при дублировании и делайте после неё вместо insert - update.
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558196
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwqtromqwwq,

Спасибо работает, теперь я знаю как использовать pg_query_params давно не мог разобраться

Но это не ответ на главный вопрос как не допустить возможного дублирования insert в начале работы счетчика.
1. нопейсать хранимку.
1.0. засунуть адвайзери локинг туда, где он должен быть -- в память на будущее, для организации очередей джобов типа мейлеров [или автономностей].
1.1. а тут навесить нужное уникью и обрабатывать exception insert-a [OR NOT FOUND update-a].
все впоросы в RTFM
1.1.1. ссылки на RTFM отсюда (тут поиск например по "UPSERT" ).

самое простое (но, тащемто, неверное)
Код: sql
1.
2.
3.
WITH up AS (UPDATE blahblahblah RETUTNING key)
INSERT INTO blahblahblah  SELECT blah,blah,blah WHERE (SELECT count(1) FROM up) =0;
--поправил
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558235
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
qwwq,

авторWITH up AS (UPDATE blahblahblah RETUTNING key)
INSERT INTO blahblahblah SELECT blah,blah,blah WHERE (SELECT count(1) FROM up) =0;
А можно пример без blahblahblah а с моими данными, чтобы стало понятно так же как с pg_query_params

Гость_0
авторtrom, Warstone же Вам написал как, ловите ошибку при дублировании и делайте после неё вместо insert - update.
update чего делать ?
делать инсерт потом селект если обнаружилось две записи одну удалять, вот это более менее понятная схема, но тоже по моему не очень.
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558253
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tromqwwq,

авторWITH up AS (UPDATE blahblahblah RETUTNING key)
INSERT INTO blahblahblah SELECT blah,blah,blah WHERE (SELECT count(1) FROM up) =0;
А можно пример без blahblahblah а с моими данными, чтобы стало понятно так же как с pg_query_params
можно, но не нужно.
сделайте хотя бы шажок сами.tromГость_0
авторtrom, Warstone же Вам написал как, ловите ошибку при дублировании и делайте после неё вместо insert - update.
update чего делать ?
делать инсерт потом селект если обнаружилось две записи одну удалять, вот это более менее понятная схема, но тоже по моему не очень.
http://www.sql.ru/forum/actualsearch.aspx?search=exception unique_violation INSERT UPDATE FUNCTION&sin=0&bid=7&a=&ma=0&dt=-1&s=1&so=1
и там - хотя бы вот это
http://www.sql.ru/forum/628082/proverka-na-nalichie-row-chtoby-update-ili-insert-into?mid=6645914&hl=exception unique_violation insert update function#6645914
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558429
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
qwwq,

НУ а просто схему можете описать ?

Нужно создать хранимую процедуру которая будет вызываться каждый раз когда нужно обновить данные счетчика и она сама будет решать сделать ли инсерт(когда записей за день нет) или update когда записи есть так или нет ?
И тогда pg_try_advisory_lock вообще не нужна ?
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558509
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tromupdate чего делать ?
делать инсерт потом селект если обнаружилось две записи одну удалять, вот это более менее понятная схема, но тоже по моему не очень.

Код: sql
1.
2.
alter table enumerator
add unique (id_iz_users, site_id, create_date);



Код: sql
1.
2.
3.
4.
5.
6.
INSERT INTO enumerator VALUES (default, $id, $site_id, '$create_date', 1, 1)

если получили  ошибку unique_violation, то

update enumerator set view = view + 1
where site_id=$site_id and id_iz_users=$id and create_date='$create_date'



PS: при большой нагрузке что бы не блокироваться на одном счётчике делают по другому, но можете сделать пока так.
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558794
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ёш,

Спасибо простая и рабочая схема!

Я только уже свой вариант сделал

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
$text_zaprosa="select id, hosts, views  from enumerator where site_id=$site_id and id_iz_users=$user_id and create_date='$create_date' order by id;";
$res = pg_query($pgsql_conn, $text_zaprosa);
$rows = pg_num_rows($res);
if($rows==0){   
   pg_query_params($pgsql_conn, 'INSERT INTO enumerator VALUES (default, $1, $2, $3, 1, 1);' , array($user_id, $site_id, $create_date));    
      }
if($rows>1){    
    for($i=1; $i<$rows; $i++){
        $id_iz_enumerator=pg_fetch_result($res, $i, 0);
        pg_query($pgsql_conn, "delete from enumerator where id=$id_iz_enumerator;");
    }    
}



то есть селект делается с order by id и в случае дублирующих записей все обновления идут на первую запись, ну а если дублирующие записи случаются, то они все удаляются в цикле.
Если сравнивать с
Код: sql
1.
2.
alter table enumerator
add unique (id_iz_users, site_id, create_date);


какой вариант более быстрый ?

Просто нагрузка на счетчик может быть большой, но от самого счетчика кроме самых простых данных ничего не требуется, задача что бы было быстро и просто.
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558905
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tromкакой вариант более быстрый ?Скорее все Ваш вариант с delete более быстрый, поскольку не будет бросать исключение и блокироваться на unique индексе для проверки. Нужно только починить ещё суммирование просмотров из удаляемых строк. Ещё для целостной картины не видно где UPDATE идёт.

PS: Вы кстати, почти придумали второй стандартный вариант счётчиков, осталось избавиться от UPDATE :)
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558913
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ёш <>

PS: Вы кстати, почти придумали второй стандартный вариант счётчиков, осталось избавиться от UPDATE :)подбираете в кучки джобом ?
или у вас счетчики маленькие, без подбора обходитесь ?
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558918
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ёш,

авторНужно только починить ещё суммирование просмотров из удаляемых строк.
так я же делаю order by id все суммироваться будет в первой по id записи если конечно я правильно понимаю и не будет опять каких то проблем с параллельными запросами.

авторЕщё для целостной картины не видно где UPDATE идёт.

вот приведу код всего счетчика, для критики


Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
$create_date = date('Y-m-d 00:00:00');
$text_zaprosa="select id, hosts, views  from enumerator where shop_id=$site_id and id_iz_users=$user_id and create_date='$create_date' order by id;";
$res = pg_query($pgsql_conn, $text_zaprosa);
$rows = pg_num_rows($res);
if($rows==0){   
   pg_query_params($pgsql_conn, 'INSERT INTO enumerator VALUES (default, $1, $2, $3, 1, 1);' , array($user_id, $site_id, $create_date));    
      }
if($rows>1){    
    for($i=1; $i<$rows; $i++){
        $id_iz_enumerator=pg_fetch_result($res, $i, 0);
        pg_query($pgsql_conn, "delete from enumerator where id=$id_iz_enumerator;");
    }    
}
      
      
while(1){
$text_zaprosa="select id, hosts, views  from enumerator where site_id=$site_id and id_iz_users=$user_id and create_date='$create_date' And pg_try_advisory_lock(id) order by id limit 1;";
$res = pg_query($pgsql_conn, $text_zaprosa);
$rows = pg_num_rows($res);
if($rows>0){
  $id_iz_enumerator=pg_fetch_result($res, 0, 0);
  $hosts = pg_fetch_result($res, 0, 1);  
  $views = pg_fetch_result($res, 0, 2);  
  
  if($eto_host==1){$hosts=$hosts+1;}
  $views=$views+1;
  pg_query($pgsql_conn, "UPDATE enumerator SET hosts=$hosts, views=$views  where id=$id_iz_enumerator;");
  break;
}
}
pg_query($pgsql_conn, "select pg_advisory_unlock_all();"); 



авторPS: Вы кстати, почти придумали второй стандартный вариант счётчиков, осталось избавиться от UPDATE :)
Это намек на хранимую процедуру ?
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558948
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tromавторPS: Вы кстати, почти придумали второй стандартный вариант счётчиков, осталось избавиться от UPDATE :)
Это намек на хранимую процедуру ?Не, как написал qwwq, «подбирание в кучки джобом». То есть делаем только INSERT, а потом например раз в час суммируем и удаляем просуммированное. Это позволяет обойтись вообще без блокировок при инкременте.
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38558979
Фотография SmeL_md
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
может так?
Код: php
1.
2.
3.
4.
5.
pg_query_params($conn,'UPDATE enumerator SET view = view + 1 ... WHERE ... RETURNING key',$params);
если нет key то
pg_send_query_params($conn, 'INSERT INTO enumerator ...', $params);
если получили unique_violation, то не расстраиваемся (мы просто потеряли еще +1).
Вернуть его можно, а нужно ли?
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38559041
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ёш,

авторНе, как написал qwwq, «подбирание в кучки джобом». То есть делаем только INSERT, а потом например раз в час суммируем и удаляем просуммированное.
Без нормального примера это сложно понять

SmeL_md,
Ну вроде выяснили что unique_violation медленнее чем то что я придумал
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38559050
йццй
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
tromЁш,

авторНе, как написал qwwq, «подбирание в кучки джобом». То есть делаем только INSERT, а потом например раз в час суммируем и удаляем просуммированное.
Без нормального примера это сложно понять

SmeL_md,
Ну вроде выяснили что unique_violation медленнее чем то что я придумал
нет, выяснили, что
1. вы хернёй маетесь
это раз
2. что ёш хороший психолог и педагог
это два

3. при этом все в проигрыше - и он не достиг целей, и вы в похабе погрязли, дальше некуда
а что будет, если пара ползателей такой сеанс, с открытыми на клиенте наборами, захотят поиметь - и подумать страшно

парадокс, блин
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38559068
trom
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
йццй,

ну и в чем проблема как надо сделать ?
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38559485
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tromйццй,

ну и в чем проблема как надо сделать ?
проблема в вас, очевидно
вам 10 раз сказали, как надо сделать
10 раз сказали, как не надо делать
а вы продолжаете на клиентский (по сути) код надрачивать.

ну даже и так - - реализуйте таки ёшевский "2-й путь" , а не свою кривую альтернативу.
можете "сборку кучек" из под себя запускать, раз джоб не умеете. но она должна "потоконезависимо" быть написана, т.е. не реализовываться через длинную кривую сессию/транзакцию на похабе, а , в идеале, быть одним самосогласованным стейтментом, типа.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
WITH sel AS (SELECT .... FROM enumerator
WHERE {wherekeys} -- не все, а только наши ключи
AND (SELECT count(1) FROM enumerator WHERE {wherekeys})>1 --и их уже кучка
 -- чтобы не перекладывать по 1-й записи
)
, del AS (DELETE FROM enumerator WHERE {keys} IN (SELECT {keys} FROM sel) RETURNING ....)

INSERT INTO enumerator SELECT ....., SUM(...) FROM del ;
-- сколько удалось DELетнуть именно в текущей транзакции, только те и просуммировали
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38559634
Фотография SmeL_md
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tromSmeL_md,
Ну вроде выяснили что unique_violation медленнее чем то что я придумал
То что вы придумали намного медленнее моего варианта :)
...
Рейтинг: 0 / 0
Счетчик посещений сайта на postgres
    #38560525
Sasha Alias
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для счетчиков иногда удобно использовать sequences, потому что они инкрементируются независимо от состояния транзакции.
...
Рейтинг: 0 / 0
25 сообщений из 26, страница 1 из 2
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Счетчик посещений сайта на postgres
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]