数据库同时更新问题

8.2.3. 解决更新冲突

CREATE TABLE `account` (
	`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
	`user` VARCHAR(50) NOT NULL DEFAULT '0',
	`cash` FLOAT NOT NULL DEFAULT '0',
	`point` INT(10) UNSIGNED NOT NULL DEFAULT '0',
	PRIMARY KEY (`id`),
	UNIQUE INDEX `user` (`user`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
INSERT INTO `test`.`account` (`user`, `cash`,`point`) VALUES ('neo', 10,10);

下面通过account表,我来模拟一个返点场景,例如电商网站经常会用到“返点”,购买一定数量的商品赠送一定的点数,可以通过点数买东西,这样涉及到点的加于减操作。

表 8.1. 更新丢失演示

Session A Session B
select point into @point from account where user='neo';
select point into @point from account where user='neo';
update account set point=@point+20 where user='neo';
update account set point=@point+50 where user='neo';

看看最后用户有多少点?

mysql> select point from account where user='neo';
+-------+
| point |
+-------+
|    30 |
+-------+
1 row in set (0.00 sec)

傻了吧,老板发火,测试不能重现,运维说这是程序计算错误,程序员说程序没有错误,这样的场景国内很多公司都出现过吧?

问题出在哪里呢?出在并发上,很多web程序员很少考虑并发是产生的问题,怎么解决?很多方案,在我的职业生涯过程就见过很多奇葩方案,都能解决问题但不太完美。

如果更新语句改为 update account set point=@point+50 where user='neo' and point=@point ; 会更保险,但仍然不能解决同意时间所产生的更新操作

下面是通过事务与锁彻底解决上面的问题。

表 8.2. 防止更新丢失加锁演示

Session A Session B
begin;
select point into @point from account where user='neo' for update;
begin;
select point into @point from account where user='neo' for update;

执行到此处会挂起

update account set point=@point+20 where user='neo';
commit;
update account set point=@point+50 where user='neo';
commit;

上面解决更新覆盖问题,但从数据库设计角度是不应该这样设计表的。仅供参考

CREATE TABLE `account` (
	`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
	`user` VARCHAR(50) NOT NULL DEFAULT '0',
	`cash` FLOAT NOT NULL DEFAULT '0',
	`point` INT(10) NOT NULL DEFAULT '0',
	PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

每一次数据变化新增一条数据

INSERT INTO `test`.`account` (`user`, `point`) VALUES ('neo', -10);
INSERT INTO `test`.`account` (`user`, `point`) VALUES ('neo', -5);
INSERT INTO `test`.`account` (`user`, `point`) VALUES ('neo', 30);
INSERT INTO `test`.`account` (`user`, `point`) VALUES ('neo', -20);

计算剩余点数

select sum(point) as point from account where user='neo';

转载于:https://my.oschina.net/neochen/blog/178323

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值