Wednesday, April 24, 2019

eCommerce Misc



https://mp.weixin.qq.com/s/wm5osOYgWj83XAtwZ00VdA
balance = dao.getBalance(userid)
balance = balance - cost
dao.setBalance(userid,balance)
余额修改,是交易系统里最常见的操作。上面的伪代码,大意是先取出余额,然后扣掉消费,然后再回写余额。通常情况下这不会发生问题。
除非是高并发,与你是否单机无关。
对单一余额的高并发操作自然不是正常人发起的,系统正在承受攻击,或者自以为是的使用了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
")
上面的并发攻击,将会只有一个操作能够成功,我们的余额安全了。

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts