Mysql
 sql >> база данни >  >> RDS >> Mysql

Цикъл, докато паролата стане уникална

Строго погледнато, вашият тест за уникалност няма да гарантира уникалност при едновременно натоварване. Проблемът е, че проверявате за уникалност преди (и отделно от) мястото, където вмъквате ред, за да "искате" новата си генерирана парола. Друг процес може да прави същото нещо по едно и също време. Ето как става...

Два процеса генерират една и съща парола. Всеки от тях започва с проверка за уникалност. Тъй като нито един процес (все още) не е вмъкнал ред в таблицата, и двата процеса няма да намерят съответстваща парола в базата данни и така и двата процеса ще приемат, че кодът е уникален. Сега, тъй като всеки процес продължава своята работа, в крайна сметка те ще и двата вмъкнете ред в files таблица, използвайки генерирания код - и по този начин получавате дубликат.

За да заобиколите това, трябва да извършите проверката и да направите вмъкването с една "атомна" операция. Следва обяснение на този подход:

Ако искате паролата да е уникална, трябва да дефинирате колоната във вашата база данни като UNIQUE . Това ще гарантира уникалност (дори ако вашият php код не го прави), като откаже да вмъкнете ред, който би причинил дублиран парола.

CREATE TABLE files (
  id int(10) unsigned NOT NULL auto_increment PRIMARY KEY,
  filename varchar(255) NOT NULL,
  passcode varchar(64) NOT NULL UNIQUE,
)

Сега използвайте SHA1() на mysql и NOW() за генериране на вашата парола като част от изявлението за вмъкване. Комбинирайте това с INSERT IGNORE ... (документи ) и повтаряйте, докато редът бъде успешно вмъкнат:

do {
    $query = "INSERT IGNORE INTO files 
       (filename, passcode) values ('whatever', SHA1(NOW()))";
    $res = mysql_query($query);
} while( $res && (0 == mysql_affected_rows()) )

if( !$res ) {
   // an error occurred (eg. lost connection, insufficient permissions on table, etc)
   // no passcode was generated.  handle the error, and either abort or retry.
} else {
   // success, unique code was generated and inserted into db.
   // you can now do a select to retrieve the generated code (described below)
   // or you can proceed with the rest of your program logic.
}

Забележка: Горният пример беше редактиран, за да отчете отличните наблюдения, публикувани от @martinstoeckli в секцията за коментари. Бяха направени следните промени:

  • променен mysql_num_rows() (docs ) към mysql_affected_rows() (docs ) -- num_rows не се отнася за вмъквания. Също така премахна аргумента на mysql_affected_rows() , тъй като тази функция работи на ниво връзка, а не на резултатно ниво (и във всеки случай резултатът от вмъкването е булев, а не номер на ресурс).
  • добавена е проверка на грешки в условието на цикъла и е добавен тест за грешка/успех след излизане от цикъла. Обработката на грешки е важна, тъй като без нея грешките в базата данни (като загубени връзки или проблеми с разрешенията) ще накарат цикъла да се върти завинаги. Подходът, показан по-горе (използвайки IGNORE и mysql_affected_rows() и тестване на $res отделно за грешки) ни позволява да разграничим тези „реални грешки в базата данни“ от уникалното нарушение на ограничението (което е напълно валидно условие без грешка в този раздел на логиката).

Ако трябва да получите паролата, след като е била генерирана, просто изберете записа отново:

$res = mysql_query("SELECT * FROM files WHERE id=LAST_INSERT_ID()");
$row = mysql_fetch_assoc($res);
$passcode = $row['passcode'];

Редактиране :променен горният пример за използване на функцията mysql LAST_INSERT_ID() , а не функцията на PHP. Това е по-ефективен начин за постигане на същото нещо и полученият код е по-чист, по-ясен и по-малко претрупан.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да се свържете с MySQL, работещ на Docker от хост машината

  2. Как да покажа общия брой на последния ред по статус на файла?

  3. Преобразувайте датата, създадена от jquery datepicker, в стандартен формат за дата на mysql

  4. Как мога да задам стойността по подразбиране на поле като '0000-00-00 00:00:00'?

  5. 500 - Възникна грешка - функцията DB не отчита грешки при добавяне на нова статия в Joomla