Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Счетчик посещений сайта на postgres / 25 сообщений из 26, страница 1 из 2
11.02.2014, 20:06:38
    #38557673
trom
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
Пишу самый простой счетчик посещений сайта используя куки, сохраняю все данные просто обновляя значения посещений в БД, если правильно понимаю нужна блокировка, чтобы не было параллельных обновлений и не терялись записи о посетителях.
Саму блокировку сделал, но непонятно что делать с первой записью за день, когда делаю 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
11.02.2014, 21:30:10
    #38557783
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
11.02.2014, 22:13:52
    #38557832
Warstone
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
12.02.2014, 09:36:15
    #38558061
trom
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
Ёш ,
автор$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
12.02.2014, 10:25:27
    #38558120
qwwq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
12.02.2014, 11:15:29
    #38558179
trom
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
qwwq,

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

Но это не ответ на главный вопрос как не допустить возможного дублирования insert в начале работы счетчика.
...
Рейтинг: 0 / 0
12.02.2014, 11:27:10
    #38558190
qwwq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
12.02.2014, 11:27:16
    #38558191
Гость_0
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
trom, Warstone же Вам написал как, ловите ошибку при дублировании и делайте после неё вместо insert - update.
...
Рейтинг: 0 / 0
12.02.2014, 11:28:37
    #38558196
qwwq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
12.02.2014, 11:52:17
    #38558235
trom
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
12.02.2014, 12:01:34
    #38558253
qwwq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
12.02.2014, 13:48:15
    #38558429
trom
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
qwwq,

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

Нужно создать хранимую процедуру которая будет вызываться каждый раз когда нужно обновить данные счетчика и она сама будет решать сделать ли инсерт(когда записей за день нет) или update когда записи есть так или нет ?
И тогда pg_try_advisory_lock вообще не нужна ?
...
Рейтинг: 0 / 0
12.02.2014, 14:38:28
    #38558509
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
12.02.2014, 16:57:17
    #38558794
trom
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
Ёш,

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

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

Код: 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
12.02.2014, 18:15:54
    #38558905
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
tromкакой вариант более быстрый ?Скорее все Ваш вариант с delete более быстрый, поскольку не будет бросать исключение и блокироваться на unique индексе для проверки. Нужно только починить ещё суммирование просмотров из удаляемых строк. Ещё для целостной картины не видно где UPDATE идёт.

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

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

авторНужно только починить ещё суммирование просмотров из удаляемых строк.
так я же делаю 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
12.02.2014, 19:10:19
    #38558948
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
tromавторPS: Вы кстати, почти придумали второй стандартный вариант счётчиков, осталось избавиться от UPDATE :)
Это намек на хранимую процедуру ?Не, как написал qwwq, «подбирание в кучки джобом». То есть делаем только INSERT, а потом например раз в час суммируем и удаляем просуммированное. Это позволяет обойтись вообще без блокировок при инкременте.
...
Рейтинг: 0 / 0
12.02.2014, 19:40:53
    #38558979
SmeL_md
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
может так?
Код: 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
12.02.2014, 20:51:50
    #38559041
trom
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
Ёш,

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

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

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

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

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

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

ну и в чем проблема как надо сделать ?
...
Рейтинг: 0 / 0
13.02.2014, 10:51:41
    #38559485
qwwq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
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
13.02.2014, 12:22:53
    #38559634
SmeL_md
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
tromSmeL_md,
Ну вроде выяснили что unique_violation медленнее чем то что я придумал
То что вы придумали намного медленнее моего варианта :)
...
Рейтинг: 0 / 0
14.02.2014, 00:49:19
    #38560525
Sasha Alias
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Счетчик посещений сайта на postgres
Для счетчиков иногда удобно использовать sequences, потому что они инкрементируются независимо от состояния транзакции.
...
Рейтинг: 0 / 0
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Счетчик посещений сайта на postgres / 25 сообщений из 26, страница 1 из 2
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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