Модераторы: skyboy, MoLeX, Aliance, ksnk
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как взять id последнего сообщения с 100% гарантией, Нужно ли использовать транзакции? 
V
    Опции темы
linuxoid
Дата 5.7.2010, 21:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Здравствуйте коллеги! У меня такой вопрос: на сайте много пользователей, которые пишут сообщения. Если все ОК с валидацией, то заносим сообщение пользователя в базу. Затем мне нужен id этого сообщения из базы. Я просто беру max(id). Проблема следующая: если одновременно много пользователей добавляют сообщения, то по случайности может оказаться, что этот max(id) является id сообщения другого пользователя. Т.к. он успел написать новое сообщение и добавить его именно в тот момент, когда в базу добавлялось еще предыдущее (или любая другая причина). Т.е. по идее есть вероятность того, что я получу неверный id. Как нужно правильно сделать, чтобы я 100% был уверен, что id взят именно того сообщения, которое сейчас было вставлено. Нужно использовать транзакции? Если да, то можно ли оставить выборку max(id) как есть? Т.е. текущий код абсолютно тот же, но просто разместить все в одной транзакции. 
Благодарствую за внимание.
PM MAIL   Вверх
DimW
Дата 6.7.2010, 08:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1330
Регистрация: 24.2.2005
Где: Орёл

Репутация: нет
Всего: 44



Цитата(linuxoid @  5.7.2010,  21:45 Найти цитируемый пост)
по идее есть вероятность того, что я получу неверный id

причем стопроцентная. 

Цитата(linuxoid @  5.7.2010,  21:45 Найти цитируемый пост)
Нужно использовать транзакции?

т.е. вы полагаете что при добавлении сообщений вы транзакции не используете?


Цитата(linuxoid @  5.7.2010,  21:45 Найти цитируемый пост)
Я просто беру max(id).

linuxoid, вы про автоинкрименты, генераторы, последовательности что нить слышали?
PM MAIL ICQ   Вверх
skyboy
Дата 6.7.2010, 10:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 14
Всего: 260



укажи СУБД и целевой язык(Mysql+PHP, Delphi+Paradox, C#+SQL Server). быстрее получишь решение, привязанное к используемым технологиям.
а на абстрактный вопрос получишь абстрактный же ответ
PM MAIL   Вверх
linuxoid
Дата 6.7.2010, 10:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



База postgres или mysql. Все на php. Транзакции я не использую. Вот пример для Postgres'a.


Код

INSERT INTO messages(ref_users_id, ref_sections_id, email, phone, text, publication_date, ip) VALUES('14', '10', '[email protected]', '80100100', 'Test!!!', '1278402306', '130.50.28.33');
SELECT max(id) as max FROM messages LIMIT 1;

Далее с этим id я вставляю дальше в другие таблицы базы различные сведения.

INSERT INTO... (ref_messages_id, ...

Ничего кроме этого кода нет. Т.е. никаких транзакций, никаких инкрементов. Как поменять код, чтобы было бы грамотно и безопасно.


Это сообщение отредактировал(а) linuxoid - 6.7.2010, 10:59
PM MAIL   Вверх
skyboy
Дата 6.7.2010, 11:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 14
Всего: 260



что стоило разместить вопрос в разделе "РНР: БАзы данных"? теперь ждем модератора этого раздела
РНР. хорошо. а какие функции используются? mysql_query? PDO? ADOLite? для семейства функций mysql_* есть функция mysql_last_insert_idmysql_insert_id. в самой mysql есть функция last_insert_id()(но я не помню - надо смотреть, как себя ведет функция в условиях нескольких подключений от имени одного пользователя; скорее всего, работает нормально, но все же лучше убедиться)
а в postgresql, вроде как, сначала получаешь значение sequence при помощи nextval, а потом уже вставляешь запись. при этом полученное значение будет точно уникальным.

Это сообщение отредактировал(а) skyboy - 6.7.2010, 14:32
PM MAIL   Вверх
linuxoid
Дата 6.7.2010, 12:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Спасибо. Становится яснее, что нужно использовать более специализированные функции, которые конкретно связаны с инкрементом. Но как быть с "Далее с этим id я вставляю дальше в другие таблицы базы различные сведения.". Стоит ли мне ознакомиться с транзакциями поближе? Т.е. правильно ли я думаю, что нужно их использовать в моем случае.

P.S. В PHP использую только стандартные функции pg_ и mysql_
PM MAIL   Вверх
skyboy
Дата 6.7.2010, 13:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 14
Всего: 260



транзакции - это другое.
Цитата(linuxoid @  6.7.2010,  11:55 Найти цитируемый пост)
Но как быть с "Далее с этим id я вставляю дальше в другие таблицы базы различные сведения."

получив идентификатор только что вставленной записи при помощи mysql_last_insert_id(), сформировать нужные тебе запросы с этим значением. в чем, собственно, проблема?
PM MAIL   Вверх
linuxoid
Дата 6.7.2010, 13:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Цитата

транзакции - это другое.


Согласен.

Код

<?php
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
    die('Could not connect: ' . mysql_error());
}
mysql_select_db('mydb');

mysql_query("INSERT INTO mytable (product) values ('kossu')");
printf("Last inserted record has id %d\n", mysql_insert_id());
?>



Извиняюсь за назойливость, но т.е. получается, что используя к примеру mysql_insert_id() исключается проблема, что в этот момент другой пользователь вставил сообщение и я могу случайно получить id другого сообщения - т.е. всегда будет именно нужный id? 
PM MAIL   Вверх
skyboy
Дата 6.7.2010, 14:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 14
Всего: 260



Цитата(linuxoid @  6.7.2010,  12:59 Найти цитируемый пост)
т.е. всегда будет именно нужный id?  

да.
PM MAIL   Вверх
linuxoid
Дата 6.7.2010, 15:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Ok, благодарю!
PM MAIL   Вверх
gcc
Дата 7.7.2010, 10:53 (ссылка)    | (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Агент алкомафии
****


Профиль
Группа: Участник
Сообщений: 2691
Регистрация: 25.4.2008
Где: %&й

Репутация: нет
Всего: 17



в Oracle есть по-моиму транзакция SELECT, чтобы выбрать 100% новую запись... (если это то, что надо)


Это сообщение отредактировал(а) gcc - 7.7.2010, 10:59
PM WWW ICQ Skype GTalk Jabber   Вверх
gcc
Дата 7.7.2010, 11:18 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Агент алкомафии
****


Профиль
Группа: Участник
Сообщений: 2691
Регистрация: 25.4.2008
Где: %&й

Репутация: нет
Всего: 17



Цитата

Когда пользователь находит запись, которую нужно изменить (или просто желает добавить запись в таблицу), то он нажимает кнопку добавления/редактирования и в появившемся диалоге заполняет/изменяет поля записи и затем сохраняет/отменяет редактирование.
Как же настроить транзакции для такого приложения?
Для запроса SELECT. ., который читает данные в сетку, следует использовать транзакцию с доступом "только для чтения" с уровнем изоляции READ COMMITED, чтобы получить самые "свежие" данные из таблицы, как только они будут обновлены/добавлены (не надо забывать о том, что наше приложение многопользовательское и одновременно могут работать несколько приложений). Примерный набор параметров такой:

read
read_committed 
rec_version 
nowait

При этом обеспечивается чтение всех подтвержденных другими транзакциями записей, причем без конфликтов с параллельно работающими пишущими и читающими транзакциями.
Такую транзакцию можно длительное время держать открытой - сервер не нагружается версиями записей.
Для запроса на изменение/добавление данных можно использовать транзакцию с уровнем изоляции concurrency. Запрос на обновление в этом случае должен быть очень коротким: пользователь заполняет необходимые поля, запускается транзакция, делается попытка выполнить запрос, и затем, если не возник н> конфликта на запись с другой транзакцией, подтверждение нашей транзакции или откат, если был конфликт (на уровне клиентского приложения конфлнмы проявляются в виде исключений, которые удобно отлавливать с помощью коп струкций try.. .except или try.. .catch)
Параметры такой транзакции будут следующими: 

write
concurrency
nowait

Такой набор параметров позволит нам сразу (nowait) выявить то, что запись редактируется/изменяется другим пользователем (возникнет ошибка), а также предотвратить попытки других пользователей начать изменение записей, трансформированных нашей транзакцией (у претендента возникнет ошибка "update conflict"). Надо отметить, что перед редактированием нужно перечитать запись, потому что она могла быть изменена, а в кеше сетки может все еще находиться старая версия
Для запросов, которые применяются для построения отчетов, однозначно нужно использовать транзакцию с режимом доступа "только для чтения" и с уровнем изоляции concurrency:


read
concurrency
nowait

Такая транзакция будет возвращать строго те данные, что существовали на момент ее запуска, - это очень важная особенность для отчетов, которые строятся за несколько проходов по базе данных.
Для запросов на чтение справочных данных можно использовать транзакцию, аналогичную запросу SELECT для выборки данных в сетку. 


http://www.realcoding.net/article/view/1581
PM WWW ICQ Skype GTalk Jabber   Вверх
krypt3r
Дата 8.7.2010, 13:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 359
Регистрация: 9.6.2009

Репутация: 1
Всего: 16



для постгреса
Код

SELECT LASTVAL();

В php нет вроде функции, аналогичной mysql_insert_id()
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Базы Данных | Следующая тема »


 




[ Время генерации скрипта: 0.0541 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.