很久以前我是学J2EE的,后来做了J2ME,又后来做了一点Android,再后来就学了PHP,励志要用PHP在Web开发上有所发展。我上家公司的老板告诉我说:“你的方向错了,现在是移动互联网的时代,去搞PHP木有前途”。这话想来不错,可是我觉得,再是移动互联网的时代,它也得联网不是,要联网就得用PHP or RoR or Python or J2EE or ASP.NET等等写联网,所以,我学习PHP,是为了有个好基础,有平台,其他才能做得起来。至于,选择什么样的技术实现,各有所爱,我觉得在我们这个小地方,用Java不合时宜,都是大公司用啊,.NET我不喜欢,毕竟是从Java阵营里出来的,这两货老死不相往来,RoR用的人好少,在我们这个小地方就更少了,不好就业,Python用的人也不多,也不好找工作,我对Python还是很有好感的,列在学习计划之列,前段时间学了一点,但是老是被中断,工作上的事情一打扰,就不能够有持续性,这样学习的效果也不好,而且,我深刻体会到,要学以致用,才能记得牢,才能深刻理解内涵,我学了不用,而且断断续续,所以,学习的效果并不理想,看来,还得找个项目做做,才能基本掌握。话说,我学PHP,可是看书自学了半年,又在一家做网站的公司,也就是我的上家,用半生不熟的PHP写了一个又一个具有实验性的东西,才完全入门了。终于,跳槽到现在这家公司,从事真正的PHP开发工作了。
废话真多,书归正传。
我们公司是做网贷平台的,到目前为止,我觉得我最大的贡献,是解决了平台高并发请求时引发的一些问题。虽然我也做了很多其他的并不轻松的工作,但这对这个问题的解决,我还是很自豪的。因为,在我之前,这个问题没有人真正解决掉,直到有一天,我研究投标模块代码的时候,想到了解决方法。
我们每天上午10点开始投标,虽然用户不是很多,但瞬间服务器的压力还是有点的。这类似于秒杀,在那一时刻,会出现高并发的情况。每个标的都有一个总额,所有人的投标金额达到标的总额的时候,即是满标。但由于高并发,会导致在满标的一刹那,出现投标总金额超出标的总额的情况,然后会引发一系列其他问题。投标的流程是判断是否满标,如果没满,则该用户可以投标;如果满了,则中断,并提示现在已满标。问题的根本原因在于,在最后快满标的那一秒,有多个用户同时投标,当然不是绝对的同时,之间相差个几毫秒,但你可以想象成同时。然后,每个请求同时判断是否满标,这时发现还没有满,则同时投标,投标数据进数据库,并扣除用户账户资金、发放奖励、续投奖励等一系列操作(所有用户奖励都会多发)。但实际上,标的不需要这么多资金了,这些同时投上的资金已然成为有效投资,最终导致超标的情况发生。出现这种情况后,我们就需要做一系列善后事宜,挨个给客户打电话,说抱歉啦,金额超了,要扣除多发的奖励啦。然后,技术再哼哧哼哧的改数据,烦死了,当然不是我悲催的改数据,是另外一位程序媛MM或我们技术部总监亲自上阵。
之前我们研究过解决方案,想是不是可以用队列机制来解决。可是,PHP的运行机制,不太好搞队列(当然有方法,但我们都对于此方法没有经验,不能贸然尝试)。后来,我研究出可以用文件锁的方式,类似于Java的同步锁,可以写同步代码块,这样给判断满标的时候开始加锁,到最后扣除账户金额、发完奖励再释放,就应该可以解决问题。可是,因为以前没有用过这种方式,最后领导没有采纳,让程序媛MM写了一套代码另外处理了,最后证明无效。关于文件锁的使用,请自行google,以后我再写一篇关于PHP文件锁的技术文吧。
后来我想,我们都是在投标之前去判断是否满标,这样如果多个请求同时进来,那么就会同时判断,必定会出现超标的情况。何不在投标数据进数据库之后,我们再去判断呢?于是,一道曙光照进了我的心田。
搞PHP的,数据库大多都用Mysql,上次我还特意研究了一下Mysql的锁机制。很好的一点是,Mysql是自动加锁的,当有一个insert or update请求进来的时候,会先锁表,然后去执行insert or update操作,再释放锁,这样就会避免多个请求同时修改表,造成数据混乱了。如果一个select和一个insert or update请求同时进来,则会先执行insert or update,再执行select。有了这个,我们就放心了。在一个请求的投标记录进入数据库之后,回返回该条记录的id,我们立刻去根据此id查询这条刚刚插进数据库之前的记录,获得到之前的投标数据总金额,看看是否超标了,如果没超,ok,程序继续往下走,本条投标是有效的,只不过还得再判断用户的投标金额+之前的投标总额是否会超出标的总额,再做一下微处理;如果超了,那么,本条投标完全无效,程序立刻终止,提示用户现在已满标了。ok,问题解决。
核心思想就在于,投标数据进数据库之后,我们就可以把数据库作为基础,围绕着它做文章,把数据库中的数据作为唯一的参照标准,去分析在库的数据。因为,Mysql有很好的锁机制,我们可以很放心在库的数据,从而能得到正确的分析结果。这样,即使很多很多的请求同来到达,没有问题,实际上每个请求只分析自己这条入库的数据之前的记录,然后只处理自己的记录,从而不会影响别的请求,不会篡改别的请求写入的数据,保证了正确的结果。
代码实现上,就很简单了,加上花括号也不超过10行,思想是最重要的。
现在,修复后的程序,跑了快两个月了吧,天天投标,还没有出现过超标的情况,并且,我很自信的说,今后也不会出现。当然像手机版等地方我还没打补丁,如果用户在那些地方投标,就可能超标,回头还是要给补上。
以前确实是没怎么整过web,更别提这个高并发情况的处理,现在在这家公司,真的是积累了一些宝贵的经验,还是很有收获的。
评论:
这种数据库级别加锁来保证并发的方式还是很常见的,跟语言什么的没关系了。
用乐观锁就可以了。
UPDATE的时候用WHERE条件做限制,判断有没有更新到记录,没更新就回滚。
|