Вот два потенциальных рабочих процесса, которые я хотел бы выполнить в веб-приложении.

Вариант 1

  • пользователь отправляет запрос
  • сервер читает данные
  • сервер изменяет данные
  • сервер сохраняет измененные данные

Вариация 2:

  • пользователь отправляет запрос
  • сервер читает данные
  • сервер отправляет данные пользователю
  • пользователь отправляет запрос с изменениями
  • сервер сохраняет измененные данные

В каждом из этих случаев мне интересно: каковы стандартные подходы к обеспечению того, чтобы одновременный доступ к этой службе давал нормальные результаты? (т. е. ничья правка затирается, значения соответствуют некоторому порядку правок и т. д.)

Ситуация гипотетическая, но вот некоторые детали того, где мне, вероятно, придется иметь дело с этим на практике:

  • веб-приложение, но не указан язык
  • потенциально, используя веб-фреймворк
  • хранилище данных - это реляционная база данных SQL
  • задействованная логика слишком сложна, чтобы ее можно было хорошо выразить в запросе, например значение = значение + 1

Я чувствую, что предпочел бы не пытаться изобретать велосипед здесь. Конечно, это хорошо известные проблемы с хорошо известными решениями. Пожалуйста, порекомендуйте.

Спасибо.

7
Ming 29 Авг 2011 в 00:51

3 ответа

Лучший ответ

Насколько мне известно, общего решения проблемы нет.

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

Я знаю три основных подхода:

  1. Когда пользователь читает базу данных, заблокируйте запись и не отпускайте, пока пользователь не сохранит какие-либо обновления. На практике это совершенно непрактично. Что, если пользователь откроет экран, а затем перейдет на обед без сохранения? Или едет домой на день? Или он так расстроен, пытаясь обновить эту дурацкую запись, что уходит и больше не возвращается?

  2. Выражайте свои обновления как дельты, а не как пункты назначения. В качестве классического примера предположим, что у вас есть система, которая учитывает запасы на складе. Каждый раз, когда происходит распродажа, вы должны вычесть 1 (или более) из подсчета запасов.

Предположим, что в наличии имеется 10. Пользователь A совершает продажу. Текущее количество = 10. Пользователь Б создает продажу. Он также получает текущее количество = 10. Пользователь A вводит, что две единицы проданы. Новое количество = 10 - 2 = 8. Сохранить. Пользователь Б вводит одну проданную единицу. Новое количество = 10 (значение, которое он загрузил) - 1 = 9. Сохраните. Ясно, что что-то пошло не так.

Решение: вместо того, чтобы писать «обновить количество набора инвентаря = 9, где itemid = 12345», напишите «обновить количество набора инвентаря = количество-1, где itemid = 12345». Затем позвольте базе данных поставить обновления в очередь. Это сильно отличается от стратегии №1, поскольку базе данных достаточно заблокировать запись только на время, достаточное для ее чтения, обновления и записи. Не нужно ждать, пока кто-то смотрит на экран.

Конечно, это можно использовать только для изменений, которые могут быть выражены как дельта. Если вы, скажем, обновляете номер телефона клиента, это не сработает. (Например, старый номер 555-1234. Пользователь A говорит, что нужно изменить его на 555-1235. Это изменение на +1. Пользователь B говорит, что нужно изменить его на 555-1243. Это изменение на +9. Таким образом, общее изменение +10, новый номер клиента - 555-1244. :-)) Но в подобных случаях «побеждает последний пользователь, щелкнувший клавишу ввода», вероятно, лучшее, что вы можете сделать в любом случае.

  1. При обновлении убедитесь, что соответствующие поля в базе данных соответствуют значению "от". Например, предположим, что вы работаете в юридической фирме, которая ведет переговоры с клиентами по контрактам. У вас есть экран, на котором пользователь может вводить заметки о переговорах. Пользователь A вызывает запись контракта. Пользователь B открывает ту же запись контракта. Пользователь А заявляет, что он только что разговаривал с другой стороной по телефону, и они согласны с предложенными условиями. Пользователь B, который также пытался дозвониться до другой стороны, заявляет, что они не отвечают на телефонные звонки, и подозревает, что они препятствуют этому. Пользователь A нажимает кнопку "Сохранить". Хотим ли мы, чтобы комментарии пользователя B перезаписывали комментарии пользователя A? Возможно нет. Вместо этого мы отображаем сообщение, указывающее, что примечания были изменены с тех пор, как он прочитал запись, и позволяющее ему увидеть новое значение, прежде чем решить, продолжить ли сохранение, прервать или ввести что-то другое.

[Примечание: форум автоматически меняет нумерацию моих нумерованных списков. Я не знаю, как это изменить.]

6
Jay 29 Авг 2011 в 14:33

Если у вас нет транзакций в mysql, вы можете использовать команду обновления, чтобы убедиться, что данные не повреждены.

UPDATE tableA  SET status=2  WHERE status = 1

Если статус равен единице, то только один процесс может получить результат, что запись была обновлена. В приведенном ниже коде возвращает -1, если обновление НЕ было выполнено (если не было строк для обновления).

PreparedStatement query;
query = connection.prepareStatement(s);
int rows = -1;
try
{
    rows = query.executeUpdate();
    query.close();
}
catch (Exception e)
{
   e.printStackTrace();
}
return rows;
0
Milhous 28 Авг 2011 в 21:11

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

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

У транзакций есть набор свойств - ACID, которые «гарантируют, что транзакции базы данных обрабатываются надежно».

0
Bozho 29 Авг 2011 в 12:03