Подтвердить что ты не робот

База данных SQL Server по ошибке Azure UNIQUE KEY

После запуска промо-акции нашего программного обеспечения вчера мы встретили некоторые странные проблемы с нашей базой данных SQL Server. Эта ошибка возникает, когда пользователи регистрируются на нашем сайте. Когда это был низкий пользовательский трафик, все выглядело нормально и работало правильно, но как только трафик увеличился до ~ 10 подписчиков за раз, все идет вниз. Как только он сбрасывает каждый следующий запрос в базу данных, возникает ошибка. Единственный способ исправить это - перезапустить веб-сайт (до следующего сбоя и т.д.).

Это ошибка:

Нарушение ограничения UNIQUE KEY 'AK_Users_Username. Невозможно вставить дубликат ключа в объект 'dbo.Users. Значение дублирующегося ключа (имя пользователя). Заявление было прекращено.

(Несмотря на то, что мы проверяем имя пользователя перед его добавлением)

Также произошла другая ошибка:

Новая транзакция не допускается, поскольку в сеансе есть другие потоки.

Это фрагмент кода, который добавляет User и его License в базу данных:

var db = new CBEntities();

var user = db.Users.Add(new User
        {
            Username = model.Username,
            Firstname = model.FirstName,
            Lastname = model.LastName,
            Email = model.Email,
            Password = model.Password,
            EmailConfirmed = 0,
            Country = model.Country,
            EmailSubscribed = model.Newsletter != null && model.Newsletter.Value ? 1 : 0,
            Role = "User",
            SignUpDate = DateTime.UtcNow
        });

db.SaveChanges();

//create a initial trial license for user
var lic = db.Licenses.Add(new License
        {
            ExpirationDate = DateTime.UtcNow.AddDays(21),
            UserID = user.ID,
            Key = "",
            PackageID = coupon.ID,
            Status = (int)LicenseStatus.Active,
            Type = (int)LicenseTypes.TRIAL
        });

db.SaveChanges();
user.LicenseID = lic.ID;

db.SaveChanges();

Сайт и база данных работают на Azure-хостинге. Мы действительно масштабировали сайт до плана STANDARD, и он использовал даже несколько экземпляров с 2 ядрами и 3,5 ГБ памяти. Это не решило ошибку. В настоящее время в базе данных имеется STANDARD уровень обслуживания с S2 perf. уровень. (Он связан с веб-сайтом).

Когда нет трафика с высокими пользователями, все работает нормально!

Пожалуйста, помогите нам найти решение

4b9b3361

Ответ 1

Ваш код структурирован следующим образом.

  • Проверить наличие повторяющегося имени пользователя
  • Добавить пользователя
  • Сохранить пользователя в базе данных
  • Добавить лицензию
  • Сохранить лицензию в базе данных
  • Свяжите Лицензию с пользователем

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

Относительно уникальной ошибки ключа

Нарушение ограничения UNIQUE KEY 'AK_Users_Username. Невозможно вставить дубликат ключа в объект 'dbo.Users. Значение дублирующегося ключа (имя пользователя). Заявление завершено.

В приведенной выше последовательности могут быть два разных соединения, которые проверяют одно и то же имя пользователя: "abc.def" и попытайтесь создать пользователя. Однако только один из этих запросов сможет создать новую запись пользователя в базе данных.

Вторая запись завершится с уникальным нарушением ограничения ключа.

Вы можете свести к минимуму такие ошибки, выполнив еще одну проверку для имени пользователя перед тем, как создать своего пользователя. Это не позволит полностью удалить ошибку, но свести к минимуму такие случаи.

Полностью удалить такие ошибки

  • Запустите транзакцию Serializable
  • Еще раз проверьте имя пользователя
  • Выполнять обычную обработку...
  • Свяжите Лицензию с пользователем
  • Зафиксировать транзакцию

Примечание. Использование области Serializable может привести к блокировке, поскольку это наиболее изолированная область.

Ссылка на Serializable

Надеюсь, что это поможет

Ответ 2

Вы не вложили DbContext в оператор using. Его следует утилизировать следующим образом:

using (var db = new CBEntities()) {

  var user = db.Users.Add(new User
          {
              Username = model.Username,
              Firstname = model.FirstName,
              Lastname = model.LastName,
              Email = model.Email,
              Password = model.Password,
              EmailConfirmed = 0,
              Country = model.Country,
              EmailSubscribed = model.Newsletter != null && model.Newsletter.Value ? 1 : 0,
              Role = "User",
              SignUpDate = DateTime.UtcNow
          });

  db.SaveChanges();

  //create a initial trial license for user
  var lic = db.Licenses.Add(new License
          {
              ExpirationDate = DateTime.UtcNow.AddDays(21),
              UserID = user.ID,
              Key = "",
              PackageID = coupon.ID,
              Status = (int)LicenseStatus.Active,
              Type = (int)LicenseTypes.TRIAL
          });

  db.SaveChanges();
  user.LicenseID = lic.ID;

  db.SaveChanges();
}

Устранение DbContext закрывает соединение с базой данных. Это объясняет, почему не удалять DbContext вызывает слишком много ошибок соединений.

Когда у вас мало активности на сайте, тайм-аут соединений объясняет, почему у вас нет ошибки при легкой загрузке.

Ответ 3

Как уже упоминалось в предыдущих ответах, вы всегда должны прилагать DbContext к использованию операторов. Не только в этом фрагменте кода, но и каждый раз, когда вы его используете.

Однако проблема ограничения уникального ключа может быть вызвана различными причинами. Мы не можем видеть код, который вы используете для проверки уже существующих имен пользователей, но я думаю, что он написан на С#. При вставке новой строки SQL Server проверяет ограничение имени пользователя для вас. Есть много различий в сравнении строк в SQL-сервере по сравнению с С#.

Прежде всего SQL Server использует стандартную сортировку БД, если в столбце не задано сопоставление. Стандартная сортировка SQL Azure по умолчанию - SQL_Latin1_General_CP1_CI_AS. CI означает, что без учета регистра AS означает Accent Sensitive. Это означает, что два строковых значения считаются равными, если они отличаются только в корпусе. Таким образом, пользователь123 равен USER123 и User123 и т.д.

Другое, что может вызвать проблемы, - это обработка конечных пробелов. SQL Server сравнивает укусы в основном, сравнивая их символы один за другим. Если одна строка короче другой, она добавляет дополнительные пробелы в конец более короткой, чтобы сделать их длину равной. Этот способ 'User123' равен 'User123 ' и любой строке, начинающейся с User123 и заканчивающейся пробелом. Это означает, что вы должны всегда обрезать имена пользователей перед их сохранением.