SQLinfo.ru - Все о MySQL Webew.ru: теория и практика веб-технологий

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 22.04.2010 14:11:33

cooler
Завсегдатай
Зарегистрирован: 14.01.2010
Сообщений: 52

Процедура

Здравствуйте. Вопрос собственно в комментарии.

DELIMITER //
CREATE PROCEDURE `buy_item`(IN user_id INT, IN item_id INT)
BEGIN
    DECLARE cost INT;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
    START TRNSACTION
        SELECT `cost` INTO cost FROM `item` WHERE `item_id`=item_id;
        UPDATE `user` SET `money`=`money`-cost WHERE `user_id`=user_id AND `money` >= cost; 
      /* Как здесь узнать деньги изменились или нет (нехватило денег) ?*/
        INSERT INTO `inventory` (`item_id`, `user_id`) VALUES(item_id, user_id);
    COMMIT;
END

Неактивен

 

#2 22.04.2010 14:55:48

cooler
Завсегдатай
Зарегистрирован: 14.01.2010
Сообщений: 52

Re: Процедура

DELIMITER //
CREATE PROCEDURE `buy_item`(IN user_id INT, IN item_id INT)
BEGIN
    DECLARE cost INT;
    DECLARE money INT;   
    DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
    START TRNSACTION
        SELECT `cost` INTO cost FROM `item` WHERE `item_id`=item_id;
        SELECT `money` INTO money FROM `user` WHERE `user_id`=user_id; 
        IF(money >= cost)
          UPDATE `user` SET `money`=`money`-cost WHERE `user_id`=user_id AND `money` >= cost; 
          INSERT INTO `inventory` (`item_id`, `user_id`) VALUES(item_id, user_id);
        END IF;
    COMMIT;
END

Если я сделаю так? Есть вероятность, что `user`.`money` изменится между селектом и апдейтом `money`, другим запросом. Или запись заблокируется после SELECT на время транзакции?

Неактивен

 

#3 22.04.2010 15:00:15

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: Процедура

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

Неактивен

 

#4 22.04.2010 15:11:14

cooler
Завсегдатай
Зарегистрирован: 14.01.2010
Сообщений: 52

Re: Процедура

Тогда вопрос. Как mysql понимает какую именно таблицу или запись блокировать? Заранее спасибо.

Неактивен

 

#5 22.04.2010 18:19:11

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Процедура

Зависит от уровня изоляции. Для этого случая подозреваю, что нужен уровень
SERIALIZABLE, но его, к сожалению, сломали в 5.1 и бага до сих пор открыта sad

Как рабочее решение — предлагаю Вам воспользоваться явным LOCK TABLES
перед соответствующими SELECT и UPDATE.

Неактивен

 

#6 22.04.2010 19:09:18

cooler
Завсегдатай
Зарегистрирован: 14.01.2010
Сообщений: 52

Re: Процедура

Хорошо допустим LOCK TABLES. Как это отразится если скрипт юзают около 1 млн.? Может быть лучше использовать GET_LOCK()?

Отредактированно cooler (22.04.2010 19:21:13)

Неактивен

 

#7 22.04.2010 19:25:11

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Процедура

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

Неактивен

 

#8 22.04.2010 19:26:40

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Процедура

GET_LOCK по id записи? Можно попробовать. Но тогда бы я лучше брал mutex внутри
приложения — быстрее получится.

Неактивен

 

#9 22.04.2010 19:32:28

cooler
Завсегдатай
Зарегистрирован: 14.01.2010
Сообщений: 52

Re: Процедура

А можите привести пример как в моем случае обходить баг в транзакции? буду очень признателен.

Неактивен

 

#10 22.04.2010 19:46:07

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Процедура

Ну так в Вашем коде дописать только явный уровень нужно:

    SELECT `cost` INTO cost FROM `item` WHERE `item_id`=item_id;

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    BEGIN;
    SELECT `money` INTO money FROM `user` WHERE `user_id`=user_id;
    IF(money >= cost)
       UPDATE `user` SET `money`=`money`-cost WHERE `user_id`=user_id AND `money` >= cost;
       INSERT INTO `inventory` (`item_id`, `user_id`) VALUES(item_id, user_id);
    END IF;
    COMMIT;

Неактивен

 

#11 22.04.2010 19:59:13

cooler
Завсегдатай
Зарегистрирован: 14.01.2010
Сообщений: 52

Re: Процедура

Спс большое. И еще вопрос вот 2 запроса
SELECT `cost` INTO cost FROM `item` WHERE `item_id`=item_id;
SELECT `cost` INTO cost FROM `item` WHERE `item_id`=item_id LIMIT 1;
Второй запрос выполнится быстрее?

Неактивен

 

#12 22.04.2010 22:10:37

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Процедура

Если в первом возвращается всего одна строка, то нет, очевидно smile

Если больше одной строки — то да. Даже планы выполнения запросов могут быть
разные — в зависимости от количества строк и индексов.

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson