转账日志表user_balance_log:
CREATE TABLE `user_balance_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`log_type` tinyint(4) NOT NULL COMMENT '日志类型1代表充值 2代表消费 3代表转账',
`log_des` varchar(200) DEFAULT NULL COMMENT '日志备注',
`log_value` decimal(10,2) NOT NULL COMMENT '金额发生值可以为负',
`log_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
用户充值的步骤:
1、往日志表里面插入记录,插入成功后;
2、更新余额表的值,余额表里面需要判断当前ID是否存在;
3、如果存在则UPDATE 否则 INSERT。
DROP PROCEDURE IF EXISTS `sp_add_usermoney`;
CREATE DEFINER = CURRENT_USER PROCEDURE `sp_add_usermoney`(IN _user_id int,IN _log_type tinyint,IN _log_des varchar(200),IN _log_value decimal(10,2))
BEGIN
DECLARE _error INT DEFAULT 0;
DECLARE usercount INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET _error = 1;
START TRANSACTION;
# 插入用户充值日志表
INSERT INTO user_balance_log(user_id, log_type, log_des, log_value) VALUES (_user_id, _log_type, _log_des, _log_value);
# 代表日志表插入成功
IF ROW_COUNT() > 0 THEN
#判断用户余额表是否存在用户
SELECT count(*) INTO usercount FROM user_balance WHERE user_id=_user_id;
# 代表已经存在用户
IF usercount > 0 THEN
UPDATE user_balance SET user_money = user_money + _log_value WHERE user_id = _user_id;
ELSE
INSERT INTO user_balance(user_id, user_money) VALUES (_user_id, _log_value);
END IF;
END IF;
#模拟服务器很卡
SELECT SLEEP(7), '卡顿结束';
IF _error = 1 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END;
调用示例:
call sp_add_usermoney(3, 1, '用户充值详情', 10)
7秒之后,出现了如下这些结果:
思考题
如果停顿的这7秒出现点什么的话,会怎么样?
这种情况实际上是非常常见的。接下来我们来举个例子演示:
1、运行我们的七秒存储过程:
call sp_add_usermoney(3, 1, '用户充值详情', 10)
新建一个会话,并且迅速在七秒之内往转账表插入一条数据:
INSERT INTO shop.user_balance(user_id, user_money) VALUES (3, 20);
INSERT INTO shop.user_balance(user_id, user_money) VALUES (3, 20);
INSERT INTO shop.user_balance(user_id, user_money) VALUES (3, 20);
按照我们存储过程的逻辑以及需求,如果已有用户存在,那么应该是叠加UPDATE的。那如果操作的过程中有其它会话介入影响了呢?就会出现这种情况: