资讯专栏INFORMATION COLUMN

余额,危险的操作,给996留点福报

QLQ / 3384人阅读

摘要:对单一余额的高并发操作自然不是正常人发起的,系统正在承受攻击,或者自以为是的使用了。类似于当然,你也可以通过加入版本号概念,而不是余额字段来控制这个过程,但都类似。变种版本号通过在表中加一个额外的字段,来控制并发。

真的很危险,有人因此进了局子;也有公司因此损失上亿。

想象一下你在一个月黑风高的夜晚,大概是10点多钟的样子,加班归来,打算到小卖部弄盒烟抽。
夜凉风急,你用力裹了下被风鼓起的外套。
那里有你暗恋的收银姑娘。
没日没夜的工作,只有这十几分钟,能让你感到些许生活的意义。
从羞涩的钱包里翻出仅存的一张百元大钞,结账。然后用颤抖的双手接过收银员的找零。
不是因为轻触到了她的指尖。
也并非因她如花的笑靥。

只因为,脑海里竟然不争气的浮现出这样的过程。

balance = dao.getBalance(userid)
balance = balance - cost
dao.setBalance(userid,balance)

还真是狗改不了吃屎啊,果然还是一个码畜。提醒着自己,自卑的埋下了脸,快步走开。

这是什么?这是我们送给996公司的一点福报。

一波麻6的操作

余额修改,是交易系统里最常见的操作。上面的伪代码,大意是先取出余额,然后扣掉消费,然后再回写余额。通常情况下这不会发生问题。

除非是高并发,与你是否单机无关。

对单一余额的高并发操作自然不是正常人发起的,系统正在承受攻击,或者自以为是的使用了MQ。在攻击面前,上面的操作显得不堪一击。

拿一个最严重的例子说明:同时发起了一笔消费20元和消费5元的请求。在经过一波猛如虎的操作之后,两个请求都支付成功了,但只扣除了5元。相当于花了5块钱,买了25的东西。

划重点:把以上操作扩展到提现操作上,就更加的恐怖。比如你发起了一笔100元的提现和0.01元的提现,结果余额被扣减0.01元的提现给覆盖了。这相当于你有了一个提款机,非要薅到平台倒闭为止。

防护办法 通过SQL解决
update user set balance = balance - 20
    where userid=id

这条语句就保险了很多,如果考虑到余额不能为负的情况,可以把sql更加精进一点。

update user set balance = balance - 20
    where userid=id and balance >= 20

以上sql,就可以保证余额的安全,高并发下的攻击就变得意义不大了。​但会有别的问题,比如重复扣款。

通过锁解决

现实中,这种直接通过sql扣减的应用,规模都比较小。当你的业务逐渐复杂,又没有进行很好的拆分的情况下,先读再设值的情况还是比较普遍的。比如某些营销操作、打折、积分兑换等。

这种情况,可以引入分布式锁。简单点的,只需要使用redis的setnx或者zk来控制就可以;复杂点的方案,可以使用二阶段提交之类的。

分布式事务的业务粒度,要足够粗,才能保护这些余额操作;加锁的粒度,要足够细,才能保证系统的效率。

begin transition(userid)
    balance = dao.getBalance(userid)
    balance = balance - cost
    dao.setBalance(userid,balance)
end
类CAS方式解决

java的朋友可以回想下concurrent包的解决方式。那就是引入了CAS,全称Compare And Set

扩展到分布式环境下,同样可以采用这一策略。即先比较再设值。如果初始值已经变化了,那么不允许set设值。

cas一般通过循环重试的方法进行状态更新,但余额操作一般都是比较单一的,你也可以直接终止操作,并预警风险。

sql类似于:

update user set balance = balance - 20
    where userid=id 
    and balance >= 20
    and balance = $old_balance

当然,你也可以通过加入版本号概念,而不是余额字段来控制这个过程,但都类似。

变种:版本号

通过在表中加一个额外的字段version,来控制并发。这种方式不去关注余额,可扩展性更强。

version的默认值一般是1,即记录创建时的默认值。

操作的伪代码如下:

version,balance = dao.getBalance(userid)
balance = balance - cost
dao.exec("
    update user 
    set balance = balance - 20
    version = version + 1
    where userid=id 
    and balance >= 20
    and version = $old_version
")

上面的并发攻击,将会只有一个操作能够成功,我们的余额安全了。

End

赶紧看一下你的余额操作,是否也暴露在风险之下。你可以选择接受福报继续当兄弟,当然也可以将福报还给资本家。

一念成佛,一念成魔。你才是自己的主人。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74112.html

相关文章

  • 马云也谈996,对开发者真是一福利吗?

    摘要:在前几天的阿里内部交流活动上,我们的马总也与其员工讨论了话题,包括自己创办阿里的成长过程。马总的一番话点醒了一批互联网行业的我们。马总说马云提到,能做是一种巨大的福气,很多公司很多人想都没有机会。 最近的一个996话题在互联网业界,可以说是非常的火热。身为互联网人,也在时时关注的,毕竟和我们的生活息息相关。在前几天的阿里内部交流活动上,我们的马总也与其员工讨论了996话题,包括自己创办...

    OnlyLing 评论0 收藏0
  • 进入阿里做测试员遥不可及?这里或许有你想要答案

    摘要:更进一步,马云的好朋友兼董事的杨致远被资本联合踢出董事会,这也意味着马云逐渐丧失对阿里巴巴的控制权,甚至面临出局的危险。阿里的招聘阿里的招聘在初中级岗位中的要求如下阿里软件测试工程师招聘需求需要我们注意的是岗位需求中对技术的要求会更加严格。 ...

    stefan 评论0 收藏0
  • 程序员如何在996之外获得收入

    摘要:因为网站建设一般项目比较小,我一个人是可以完成前后端开发的,如果做成成品当然得加上小苏设计师。关键词选择因为面向的是单个城市业务,在城市选择上犹豫了不少时间,首先得是一个大城市,客户量足,当时我在北京,小苏是在一个省会城市。 我技术之外的资本是零,如果你也是这样,那这篇文章适合你! 这是我的故事之一,希望对你有启发。如果你每天下班后就是躺在床上刷刷斗音,看看微博。但是又总想摆脱黑暗迷乱...

    siberiawolf 评论0 收藏0
  • 程序员如何在996之外获得收入

    摘要:因为网站建设一般项目比较小,我一个人是可以完成前后端开发的,如果做成成品当然得加上小苏设计师。关键词选择因为面向的是单个城市业务,在城市选择上犹豫了不少时间,首先得是一个大城市,客户量足,当时我在北京,小苏是在一个省会城市。 我技术之外的资本是零,如果你也是这样,那这篇文章适合你! 这是我的故事之一,希望对你有启发。如果你每天下班后就是躺在床上刷刷斗音,看看微博。但是又总想摆脱黑暗迷乱...

    TZLLOG 评论0 收藏0
  • 程序员如何在996之外获得收入

    摘要:因为网站建设一般项目比较小,我一个人是可以完成前后端开发的,如果做成成品当然得加上小苏设计师。关键词选择因为面向的是单个城市业务,在城市选择上犹豫了不少时间,首先得是一个大城市,客户量足,当时我在北京,小苏是在一个省会城市。 我技术之外的资本是零,如果你也是这样,那这篇文章适合你! 这是我的故事之一,希望对你有启发。如果你每天下班后就是躺在床上刷刷斗音,看看微博。但是又总想摆脱黑暗迷乱...

    YFan 评论0 收藏0

发表评论

0条评论

QLQ

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<