2016-07-13 3 views
1

Я новичок в MySQL, и я прочитал много сообщений о транзакциях и блокировке таблицы, которые я нашел здесь и через сеть, поэтому я думаю, что мой вопрос не должен быть избыточным.MySQL Transaction VS Table Lock для регистрации пользователя

Я пытаюсь оптимизировать запросы, в основном для регистрации пользователей и сеансов. Веб-приложение написано на PHP/MySQL (i), и я использую движок InnoDB.

Я не использую $ _SESSIONS для хранения сеансов пользователей, но я использую таблицу в БД, в которой храню некоторую информацию о сеансе пользователя, который хранится в куки. Этот метод требует, чтобы я проверил пользовательский сеанс в БД для каждого запроса пользовательской страницы. Выполняя это, я также присоединяюсь к таблице «users» в запросе сеанса select, чтобы каждый раз получать новую информацию о пользователе.

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

пользователи стол:

user_id (primary, AI) 
username (unique) 
email (unique) 
password 
field 1 
field 2 
... 

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

МЕТОД A: пользователи LOCK TABLE

LOCK TABLES users WRITE; 
// users provided username and email are already taken? 
SELECT COUNT(username) username, (SELECT COUNT(email) FROM users WHERE email = '[email protected]') email FROM users WHERE username = 'batman' 

$res = fetch_array; 
if($res['username'] == 0 && $res['email'] == 0){ 
    INSERT INTO users (username,email,password) VALUES ('batman','[email protected]','imtheonlytruehero') 

    if(affected_rows == 1) 
     $username = null; 
     $email= null; 
     $registration = true; 
    } 
    else { 
     $username = null; 
     $email= null; 
     $registration = false; 
    } 
} 
else { 
    $username = ($res['username'] > 0) ? false : true; 
    $email = ($res['email'] > 0) ? false : true; 
    $registration = false; 
}  
UNLOCK TABLES; 

МЕТОД B: СДЕЛКА с INSERT + UPDATE

START TRANSACTION; 
INSERT INTO users (username,email,password) VALUES ('batman','0','imtheonlytruehero'); 

if(affected_rows > 0) { 
    $username = true; 
    UPDATE users SET email='[email protected]' WHERE username='batman'; 
    if(affected_rows > 0) { 
     $email = true; 
     $registration = true; 
     COMMIT; 
    } 
    else { 
     $email = false; 
     $registration = true; 
     ROLLBACK; 
    } 
} 
else { 
    $username = false; 
    $email = null; 
    $registration = false; 
    ROLLBACK; 
} 

Моя озабоченность использованием метода: что происходит с другими зарегистрированными пользователей (при условии, что их много) при попытке проверить свои сеансы для навигации (помните, что «проверка сеанса» также входит в таблицу пользователей) whe n регистрируется новый пользователь и его запрос блокирует таблицу пользователей? Медлительность? Таймауты? Или запросы (выберите и вставьте) достаточно легкие, чтобы не поставить под угрозу производительность?

Есть ли метод B имеет большие шансы на создание взаимоблокировок?

Что я могу сделать? Выберите один из этих методов? Смешать их? Уничтожить все и начать все заново?

Я ценю любую помощь. Спасибо.

+0

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

+0

Спасибо за ваш ответ. Я уже предоставил для установки уникальный индекс для столбцов имени пользователя и электронной почты, как упоминалось в начале сообщения. Я сомневаюсь, однако, что если я просто сделаю вставку, и пользователь использует как имя пользователя, так и адрес электронной почты, уже установленный в таблице, я не могу сказать ему, если оба они взяты, поскольку ошибка mysql сообщает только о первом дублирующем ключе, поэтому в этом случае я могу Не знаю, также ли используется электронная почта, заставляя пользователя попробовать другую вставку, чтобы знать, что и письмо будет принято. Знаете ли вы какой-либо метод решения этой «проблемы»? Большое спасибо! – cicciopast

+0

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

ответ

0

Блокировка всей таблицы должна использоваться только в исключительных случаях, МЕТОД A будет препятствовать параллелизму. Предполагая, что у вас есть уникальные индексы на обоих пользователя и электронной почты МЕТОД В может быть переписано в приведенном примере ниже.

Этот подход должен работать в обоих READ COMMITTED и REPEATABLE READisolation modes, В MySQL и PostgreSQL (псевдокод):

while (1) { 
    START TRANSACTION; 
    SELECT * FROM users WHERE username = ? OR email = ?; 

    if (something_found) { 
     // figure out what was found — email, login or both 
     // ... 
    } else { 
     try { 
      INSERT INTO users …; 
     } catch (UniqueViolation) { 
      // somebody else could have inserted a record with the same 
      // username/email after our SELECT query 
      ROLLBACK; 
      // try again 
      continue; 
     }   
    } 
    COMMIT; 
    break; 
} 
0

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

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

$result = $db->query('SELECT COUNT(id) as total FROM USER WHERE username = "batman" or email = "[email protected]"'); 
$row = $result->fetch_assoc(); 
if($row['total'] > 0}{ 
    // redirect back to form, exit here 
} 

try{ 
    // no user with email, username exists, so try to insert that user 
    $db->query('INSERT INTO USER ...'); 
    $registration = true; 
} 
catch(Exception e){ 
    // If a different user with same username or email registered in 
    // the mean time then redirect to form. 
} 
Смежные вопросы