谈谈【非】高并发
开篇
大部分程序员都没有机会到互联网公司中接触高并发,即使身在互联网公司中,也不一定能够亲自操刀高并发代码,但是如果真的让你攻坚一个高并发业务的时候,稍有不慎就会造成严重的并发问题。所以,高并发成了爬上技术金字塔尖的拦路猛虎之一。
关于高并发,我将用整个程序员生涯来不断体会理解与分享。
今天这篇文章,讲讲初中级程序员在CURD打怪升级初期,怎么去理解【并发】,为将来的【高并发】打下扎实基础。
问题由来
并发问题,抽象起来就是:资源竞争。虽然短短四个字,但这其中要讲起来,可以开个专栏。在面向Spring编程中(Java程序员已经跳不出这个圈子了),贫血模型发挥到了极致,各种O(VO,DTO等)加上三层(Controller,Service,Dao)是大部分初中级程序员的上班工作对象。在这个环境下,暂且把这些O理解为资源。而每一次的http请求,就是竞争。
具体问题具体分析
1. 非严肃的独有的资源竞争
非严肃的,即状态的改变不依赖上一个状态,直接覆盖,并不会造成错误。比如修改用户昵称,不管你以前叫张三还是李四,都可以直接设置为奥观海或者川建国;用户独享的资源,一般不会出现并发,即使出现并发,也允许直接覆盖。典型的场景就是用户修改自己的信息。资源只会被用户自己操作,一般情况下,不会出现并发。即使用户真的在多个页面同时提交修改,那么我们可以认为,任何一次提交都可以直接覆盖前面的提交。
2. 非严肃的共享的资源竞争
比如多个管理员都在编辑商品信息,比如多个会员同时编辑文档。这类型业务,可做并发控制,也可以不做。要不要做,可以从用户体验上来权衡。
实际上,CRM,OA等项目中大部分都是这种情况,如果要做并发控制的话,可以通过版本号来解决。
3. 严肃的共享的资源竞争
严肃的,即状态的改变依赖上一个状态,比如计数器的+1操作,需要知道当前计数,才能计算出下一个计数;再比如充值100元,得知道当前余额,才能够计算出充值之后的余额。再再比如商品的库存,当你去减库存的时候,得知道当前的库存是多少,才能够计算出新的库存是多少。这些情况下,需要控制并发。这类严肃资源一旦出现并发问题,那么后果很严重。
严肃的共享资源竞争问题,可以这么解决:
- 使用Java的锁(Synchronize,Lock):
//伪代码
lock.lock();
balance = queryDB();
updateDBWithNewBalance(balance - money);
lock.unlock();
基本思想是将1)读取当前状态、2)状态改变、3)新状态持久化这三个步骤同步执行。
这种方案的缺点是无法在集群中使用。当然如果把锁换成分布式锁,就不存在集群扩容的问题了。
- 利用数据库的行级锁:
update set balance = balance - money where userId = ? and balance > money;
注:程序要判断修改的影响行数是不是1。
保证余额不能少于0。这个方案在【非】高并发的情况,简单耐用,实在居家旅行增删改必备技能。
多想一点
上面所讲的,在【非】高并发的情况,已经可以交差上生产了,但是如果是高并发的情况呢?比如秒杀场景,红包场景等高并发场景,如果让流量洪峰瞬间流向数据库,那么你第二天就可以领盒饭了:)
我们需要其他手段来解决流量洪峰,比如削峰填谷的消息中间件。