一直以来我都没有考虑过些SQL语句是需要自己处理锁的问题。最近一段和钱有关的代码收到攻击才让我意识到问题的严重性。
受攻击的代码大概是这样子的:
SQL查询余额
if (余额 > 0) {
执行提现操作
减去余额
}
攻击者同时发送了多个提现请求,提现请求在判断是否有余额的时候都显示有余额,然后都允许提现。事实上,这里面其中一个请求提现完之后就不够余额了,但是其它进程都不知道这个情况。要避免这种情况的发生,需要对查询余额和扣减余额的操作做成原子操作,在查询和扣减操作之间不允许余额再发生变动。
MySQL中有"SELECT ... FOR UPDATE"去解决这种情况,需配合Transacion来实现,参考:http://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
要验证锁是否生效不需要模拟同时发请求的情况,模拟了也不确定是否成功。可以在查询之后sleep 60秒再更余额,在60秒内发送两个请求就能重现问题了。
评论