mysql 如果存在修改_mysql如存在并发修改可能,一定要注意保证数据一致性

近日,因人员调整接手了一个其他部门负责的项目。随后发现其中的很多关键环节是没有考虑mysql并发操作的,现列出存在的一例问题 并分享如何解决的。

问题描述:

用户账户余额转移赠送 (用户A将自己的账户剩余金额赠送给用户B),同一时刻还可能存在用户A消费操作(例如赠送操作在app,消费操作在手机站)。

PHP 代码:

$sql_select = 'select amount from user where id = 1';

/***

* 从mysql查询出的用户A余额,放入php 变量$amount

* 做一些逻辑处理,例如A用户是否有权限操作,此处代码省略

*

* 用户A账户清零

* $sql_deduct = ' update user set amount = 0 where id = 1';

*/

//将 用户A金额转移给用户B

$sql_transfer = sprintf (' update user set amount = amount+%d where id = 2', $amount );

此种写法存在问题,因:

1.$sql_select执行查询后,有可能存在并发操作,例如刚好此时 用户A有其他消费,mysql  账户余额amount 被扣减。

此时php 变量$amount 与 mysql amount 内容已经不一致。

2. $sql_transfer 使用了php变量:$amount(值已经不是最新的) 进行账户增加,会导致增加与扣除的金额不一致。

解决方案:

处理并发修改一般是要进行加锁防止其他mysql session 修改同一条内容,mysql 也提供了独占锁机制解决并发更新问题。

方案1:

InnoDB 引擎可以使用select … for update , 对要修改的表中的某一行加锁。另外:此方案对MyISAM引擎无效。MyISAM可以考虑使用方案2.

SET autocommit = 0;

$sql_select = 'select amount from user where id = 1 for update ';

//这里可以写修改db 的业务逻辑

COMMIT;

1.执行$sql_select后,user id = 1 的这一行会被mysql 锁定,其他mysql session 只能读取锁定前的数据,其他mysql session要加锁或者修改涉及此行,都会被阻塞(例如修改,删除此行,修改表结构等),直到锁定释放 (commit提交事务 或 mysql session 结束)

2.必须将mysql 自动提交关闭(SET autocommit = 0;),否则锁定无效

3.特定的行进行加锁仅针对“特定的索引 ” 有效,例如id = 1 的select查询,是行锁因id 是主键索引。

3.1 如果查询条件是 id>1,那么mysql 会锁定整个表.

3.2 如果查询条件没有使用索引,那么mysql 也会锁定整个表.

建议大家尽可能使用行锁,以提高mysql并发性能。

另外, 需要注意的是 ,此方案可能会造成死锁。这个还要从业务方面尽可能避免,以后会继续讨论如何避免死锁。

方案2:

2. 使用LOCK TABLES … READ 锁表

LOCK TABLES user READ

//这里可以写修改db 的业务逻辑

UNLOCK TABLES;

此方案比较粗暴,会锁定整个表的写操作,但如果在MyISAM 引擎下,也就只能选择这种方式了。

结论:

mysql 加锁本质上都是通过牺牲并发性能换取数据的一致性,所以在业务需求分析设计时,就要考虑哪些可能存在并发写入,进行规划尽可能减少锁的次数、时间。

394ca6af6e9f2d8e239b9d5ba1c3f56b?s=42&d=mm&r=g

作者: 白金马桶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值