多线程与高并发基础一(超发--悲观锁,乐观锁)

线程,同步,单例,高并发,高访问,死锁前端

1、大规模并发带来的挑战

在过去的工做中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程当中,整个Web系统遇到了不少的问题和挑战。若是Web系统不作针对性的优化,会垂手可得地陷入到异常状态。咱们如今一块儿来讨论下,优化的思路和方法哈。java

1. 请求接口的合理设计

一个秒杀或者抢购页面,一般分为2个部分,一个是静态的HTML等内容,另外一个就是参与秒杀的Web后台请求接口。mysql

一般静态HTML等内容,是经过CDN的部署,通常压力不大,核心瓶颈实际上在后台请求接口上。这个后端接口,必须可以支持高并发请求,同时,很是重要的一点,必须尽量“快”,在最短的时间里返回用户的请求结果。为了实现尽量快这一点,接口的后端存储使用内存级别的操做会更好一点。仍然直接面向 MySQL之类的存储是不合适的,若是有这种复杂业务的需求,都建议采用异步写入。android

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

固然,也有一些秒杀和抢购采用“滞后反馈”,就是说秒杀当下不知道结果,一段时间后才能够从页面中看到用户是否秒杀成功。可是,这种属于“偷懒”行为,同时给用户的体验也很差,容易被用户认为是“暗箱操做”。nginx

2. 高并发的挑战:必定要“快”

咱们一般衡量一个Web系统的吞吐率的指标是QPS(Query Per Second,每秒处理请求数),解决每秒数万次的高并发场景,这个指标很是关键。举个例子,咱们假设处理一个业务请求平均响应时间为100ms,同时, 系统内有20台Apache的Web服务器,配置MaxClients为500个(表示Apache的最大链接数目)。web

那么,咱们的Web系统的理论峰值QPS为(理想化的计算方式):redis

20*500/0.1 = 100000 (10万QPS)算法

咦?咱们的系统彷佛很强大,1秒钟能够处理完10万的请求,5w/s的秒杀彷佛是“纸老虎”哈。实际状况,固然没有这么理想。在高并发的实际场景下,机器都处于高负载的状态,在这个时候平均响应时间会被大大增长。sql

就Web服务器而言,Apache打开了越多的链接进程,CPU须要处理的上下文切换也越多,额外增长了CPU的消耗,而后就直接致使平均响应时间 增长。所以上述的MaxClient数目,要根据CPU、内存等硬件因素综合考虑,绝对不是越多越好。能够经过Apache自带的abench来测试一 下,取一个合适的值。而后,咱们选择内存操做级别的存储的Redis,在高并发的状态下,存储的响应时间相当重要。网络带宽虽然也是一个因素,不过,这种 请求数据包通常比较小,通常不多成为请求的瓶颈。负载均衡成为系统瓶颈的状况比较少,在这里不作讨论哈。数据库

那么问题来了,假设咱们的系统,在5w/s的高并发状态下,平均响应时间从100ms变为250ms(实际状况,甚至更多):

20*500/0.25 = 40000 (4万QPS)

因而,咱们的系统剩下了4w的QPS,面对5w每秒的请求,中间相差了1w。

而后,这才是真正的恶梦开始。举个例子,高速路口,1秒钟来5部车,每秒经过5部车,高速路口运做正常。忽然,这个路口1秒钟只能经过4部车,车流量仍然依旧,结果一定出现大塞车。(5条车道突然变成4条车道的感受)

同理,某一个秒内,20*500个可用链接进程都在满负荷工做中,却仍然有1万个新来请求,没有链接进程可用,系统陷入到异常状态也是预期以内。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

其实在正常的非高并发的业务场景中,也有相似的状况出现,某个业务请求接口出现问题,响应时间极慢,将整个Web请求响应时间拉得很长,逐渐将Web服务器的可用链接数占满,其余正常的业务请求,无链接进程可用。

更可怕的问题是,是用户的行为特色,系统越是不可用,用户的点击越频繁,恶性循环最终致使“雪崩”(其中一台Web机器挂了,致使流量分散到其余正常工做的机器上,再致使正常的机器也挂,而后恶性循环),将整个Web系统拖垮。

3. 重启与过载保护

若是系统发生“雪崩”,贸然重启服务,是没法解决问题的。最多见的现象是,启动起来后,马上挂掉。这个时候,最好在入口层将流量拒绝,而后再将重启。若是是redis/memcache这种服务也挂了,重启的时候须要注意“预热”,而且极可能须要比较长的时间。

秒杀和抢购的场景,流量每每是超乎咱们系统的准备和想象的。这个时候,过载保护是必要的。若是检测到系统满负载状态,拒绝请求也是一种保护措施。在前端设置过滤是最简单的方式,可是,这种作法是被用户“千夫所指”的行为。更合适一点的是,将过载保护设置在CGI入口层,快速将客户的直接请求返回。

2、做弊的手段:进攻与防守

秒杀和抢购收到了“海量”的请求,实际上里面的水分是很大的。很多用户,为了“抢“到商品,会使用“刷票工具”等类型的辅助工具,帮助他们发送尽可 能多的请求到服务器。还有一部分高级用户,制做强大的自动请求脚本。这种作法的理由也很简单,就是在参与秒杀和抢购的请求中,本身的请求数目占比越多,成功的几率越高。

这些都是属于“做弊的手段”,不过,有“进攻”就有“防守”,这是一场没有硝烟的战斗哈。

1. 同一个帐号,一次性发出多个请求

部分用户经过浏览器的插件或者其余工具,在秒杀开始的时间里,以本身的帐号,一次发送上百甚至更多的请求。实际上,这样的用户破坏了秒杀和抢购的公平性。

这种请求在某些没有作数据安全处理的系统里,也可能形成另一种破坏,致使某些判断条件被绕过。例如一个简单的领取逻辑,先判断用户是否有参与记 录,若是没有则领取成功,最后写入到参与记录中。这是个很是简单的逻辑,可是,在高并发的场景下,存在深深的漏洞。多个并发请求经过负载均衡服务器,分配到内网的多台Web服务器,它们首先向存储发送查询请求,而后,在某个请求成功写入参与记录的时间差内,其余的请求获查询到的结果都是“没有参与记录”。 这里,就存在逻辑判断被绕过的风险。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

应对方案:

在程序入口处,一个帐号只容许接受1个请求,其余请求过滤。不只解决了同一个帐号,发送N个请求的问题,还保证了后续的逻辑流程的安全。实现方案, 能够经过Redis这种内存缓存服务,写入一个标志位(只容许1个请求写成功,结合watch的乐观锁的特性),成功写入的则能够继续参加。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

或者,本身实现一个服务,将同一个帐号的请求放入一个队列中,处理完一个,再处理下一个。

2. 多个帐号,一次性发送多个请求

不少公司的帐号注册功能,在发展早期几乎是没有限制的,很容易就能够注册不少个帐号。所以,也致使了出现了一些特殊的工做室,经过编写自动注册脚 本,积累了一大批“僵尸帐号”,数量庞大,几万甚至几十万的帐号不等,专门作各类刷的行为(这就是微博中的“僵尸粉“的来源)。举个例子,例如微博中有转 发抽奖的活动,若是咱们使用几万个“僵尸号”去混进去转发,这样就能够大大提高咱们中奖的几率。

这种帐号,使用在秒杀和抢购里,也是同一个道理。例如,iPhone官网的抢购,火车票黄牛党。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

应对方案:

这种场景,能够经过检测指定机器IP请求频率就能够解决,若是发现某个IP请求频率很高,能够给它弹出一个验证码或者直接禁止它的请求:

  1. 弹出验证码,最核心的追求,就是分辨出真实用户。所以,你们可能常常发现,网站弹出的验证码,有些是“鬼神乱舞”的样子, 有时让咱们根本没法看清。他们这样作的缘由,其实也是为了让验证码的图片不被轻易识别,由于强大的“自动脚本”能够经过图片识别里面的字符,而后让脚本自 动填写验证码。实际上,有一些很是创新的验证码,效果会比较好,例如给你一个简单问题让你回答,或者让你完成某些简单操做(例如百度贴吧的验证码)。
  2. 直接禁止IP,其实是有些粗暴的,由于有些真实用户的网络场景刚好是同一出口IP的,可能会有“误伤“。可是这一个作法简单高效,根据实际场景使用能够得到很好的效果。

3. 多个帐号,不一样IP发送不一样请求

所谓道高一尺,魔高一丈。有进攻,就会有防守,永不休止。这些“工做室”,发现你对单机IP请求频率有控制以后,他们也针对这种场景,想出了他们的“新进攻方案”,就是不断改变IP。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

有同窗会好奇,这些随机IP服务怎么来的。有一些是某些机构本身占据一批独立IP,而后作成一个随机代理IP的服务,有偿提供给这些“工做 室”使用。还有一些更为黑暗一点的,就是经过木马黑掉普通用户的电脑,这个木马也不破坏用户电脑的正常运做,只作一件事情,就是转发IP包,普通用户的电 脑被变成了IP代理出口。经过这种作法,黑客就拿到了大量的独立IP,而后搭建为随机IP服务,就是为了挣钱。

应对方案:

说实话,这种场景下的请求,和真实用户的行为,已经基本相同了,想作分辨很困难。再作进一步的限制很容易“误伤“真实用户,这个时候,一般只能经过设置业务门槛高来限制这种请求了,或者经过帐号行为的”数据挖掘“来提早清理掉它们。

僵尸帐号也仍是有一些共同特征的,例如帐号极可能属于同一个号码段甚至是连号的,活跃度不高,等级低,资料不全等等。根据这些特色,适当设置参与门槛,例如限制参与秒杀的帐号等级。经过这些业务手段,也是能够过滤掉一些僵尸号。

4. 火车票的抢购

看到这里,同窗们是否明白你为何抢不到火车票?若是你只是老老实实地去抢票,真的很难。经过多帐号的方式,火车票的黄牛将不少车票的名额占据,部分强大的黄牛,在处理验证码方面,更是“技高一筹“。

高级的黄牛刷票时,在识别验证码的时候使用真实的人,中间搭建一个展现验证码图片的中转软件服务,真人浏览图片并填写下真实验证码,返回给中转软件。对于这种方式,验证码的保护限制做用被废除了,目前也没有很好的解决方案。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

由于火车票是根据身份证明名制的,这里还有一个火车票的转让操做方式。大体的操做方式,是先用买家的身份证开启一个抢票工具,持续发送请 求,黄牛帐号选择退票,而后黄牛买家成功经过本身的身份证购票成功。当一列车箱没有票了的时候,是没有不少人盯着看的,何况黄牛们的抢票工具也很强大,即 使让咱们看见有退票,咱们也不必定能抢得过他们哈。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

最终,黄牛顺利将火车票转移到买家的身份证下。

解决方案:

并无很好的解决方案,惟一能够动心思的也许是对帐号数据进行“数据挖掘”,这些黄牛帐号也是有一些共同特征的,例如常常抢票和退票,节假日异常活跃等等。将它们分析出来,再作进一步处理和甄别。

3、高并发下的数据安全

咱们知道在多线程写入同一个文件的时候,会存现“线程安全”的问题(多个线程同时运行同一段代码,若是每次运行结果和单线程运行的结果是一 样的,结果和预期相同,就是线程安全的)。若是是MySQL数据库,能够使用它自带的锁机制很好的解决问题,可是,在大规模并发的场景中,是不推荐使用 MySQL的。秒杀和抢购的场景中,还有另一个问题,就是“超发”,若是在这方面控制不慎,会产生发送过多的状况。咱们也曾经据说过,某些电商搞抢购活动,买家成功拍下后,商家却不认可订单有效,拒绝发货。这里的问题,也许并不必定是商家奸诈,而是系统技术层面存在超发风险致使的。

1. 超发的缘由

假设某个抢购场景中,咱们一共只有100个商品,在最后一刻,咱们已经消耗了99个商品,仅剩最后一个。这个时候,系统发来多个并发请求,这批请求读取到的商品余量都是99个,而后都经过了这一个余量判断,最终致使超发。(同文章前面说的场景)

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

在上面的这个图中,就致使了并发用户B也“抢购成功”,多让一我的得到了商品。这种场景,在高并发的状况下很是容易出现。

2. 悲观锁思路

解决线程安全的思路不少,能够从“悲观锁”的方向开始讨论。

悲观锁,也就是在修改数据的时候,采用锁定状态,排斥外部请求的修改。遇到加锁的状态,就必须等待。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

虽然上述的方案的确解决了线程安全的问题,可是,别忘记,咱们的场景是“高并发”。也就是说,会不少这样的修改请求,每一个请求都须要等待 “锁”,某些线程可能永远都没有机会抢到这个“锁”,这种请求就会死在那里。同时,这种请求会不少,瞬间增大系统的平均响应时间,结果是可用链接数被耗 尽,系统陷入异常。

3. FIFO队列思路

那好,那么咱们稍微修改一下上面的场景,咱们直接将请求放入队列中的,采用FIFO(First Input First Output,先进先出),这样的话,咱们就不会致使某些请求永远获取不到锁。看到这里,是否是有点强行将多线程变成单线程的感受哈。

而后,咱们如今解决了锁的问题,所有请求采用“先进先出”的队列方式来处理。那么新的问题来了,高并发的场景下,由于请求不少,极可能一瞬 间将队列内存“撑爆”,而后系统又陷入到了异常状态。或者设计一个极大的内存队列,也是一种方案,可是,系统处理完一个队列内请求的速度根本没法和疯狂涌 入队列中的数目相比。也就是说,队列内的请求会越积累越多,最终Web系统平均响应时候仍是会大幅降低,系统仍是陷入异常。

4. 乐观锁思路

这个时候,咱们就能够讨论一下“乐观锁”的思路了。乐观锁,是相对于“悲观锁”采用更为宽松的加锁机制,大都是采用带版本号 (Version)更新。实现就是,这个数据全部请求都有资格去修改,但会得到一个该数据的版本号,只有版本号符合的才能更新成功,其余的返回抢购失败。 这样的话,咱们就不须要考虑队列的问题,不过,它会增大CPU的计算开销。可是,综合来讲,这是一个比较好的解决方案。

 

Web系统大规模并发——电商秒杀与抢购 - 徐汉彬Hansion - 技术行者

 

有不少软件和服务都“乐观锁”功能的支持,例如Redis中的watch就是其中之一。经过这个实现,咱们保证了数据的安全。

4、小结

互联网正在高速发展,使用互联网服务的用户越多,高并发的场景也变得愈来愈多。电商秒杀和抢购,是两个比较典型的互联网高并发场景。虽然咱们解决问题的具体技术方案可能千差万别,可是遇到的挑战倒是类似的,所以解决问题的思路也殊途同归。

5、关于并发

经过上面的抢购和秒杀的例子来探讨并发的出现场景及处理高并发的相应技术。

关于多线程和并发的知识:

一、    什么是并发?

在互联网时代,所讲的并发、高并发,一般是指并发访问。也就是在某个时间点,有多少个访问同时到来。(PV(page view)即页面浏览量。)

一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强。服务器的本质工做就是,争取以最快的速度将内核缓冲区中的用户请求数据一个不剩地都拿出来,而后尽快处理,再将响应数据放到一块又可以与发送数据的缓冲区中,接着处理下一拨请求。

二、    衡量服务器并发处理能力?

高并发相关经常使用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等。

响应时间:系统对请求作出响应的时间。例如系统处理一个HTTP请求须要200ms,这个200ms就是系统的响应时间。

吞吐量:单位时间内处理的请求数量。

QPS:每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。

并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通信系统,同时在线量必定程度上表明了系统的并发用户数。

三、    如何提升服务器的并发处理能力?

有两种方式:第一种就是提升单机的性能;

第二种就是互联网的分布式开发架构,增长机器的数量。

其实还能够经过减小无用请求来下降服务器的压力。例如微博的僵尸粉,购票的黄牛。

【1】提升CPU并发计算能力

(1)多进程&多线程

(2)减小进程切换,使用线程,考虑进程绑定CPU

(3)减小使用没必要要的锁,考虑无锁编程

(4)考虑进程优先级

(5)关注系统负载

(6)关注CPU使用率,除了用户空间和内核空间的CPU使用率之外,还要关注I/O wait

【2】减小系统调用

【3】考虑减小内存分配和释放

(1)改善数据结构和算法复制度

(2)使用内存池

(3)考虑使用共享内存

【4】考虑使用持久链接

【5】改进I/O模型

(1)DMA技术

(2)异步I/O

(3)改进多路I/O就绪通知策略,epoll

(4)Sendfile

(5)内存映射

(6)直接I/O

【6】改进服务器并发策略

(1)一个进程处理一个链接,非阻塞I/O,使用长链接

(2)一个进程处理多个链接,异步I/O,使用长链接

【7】改进硬件环境

四、    总结

高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它一般是指,经过设计保证系统可以同时并行处理不少请求。

提升系统并发能力的方式,方法论上主要有两种:垂直扩展(Scale Up)与水平扩展(Scale Out)。前者垂直扩展能够经过提高单机硬件性能,或者提高单机架构性能,来提升并发性,但单机性能老是有极限的,互联网分布式架构设计高并发终极解决方案仍是后者:水平扩展。

互联网分层架构中,各层次水平扩展的实践又有所不一样:

(1)反向代理层能够经过“DNS轮询”的方式来进行水平扩展;

(2)站点层能够经过nginx来进行水平扩展;

(3)服务层能够经过服务链接池来进行水平扩展;

(4)数据库能够按照数据范围,或者数据哈希的方式来进行水平扩展;

各层实施水平扩展后,可以经过增长服务器数量的方式来提高系统的性能,作到理论上的性能无限。

五、    多线程和并发有什么关系?

【1】多线程

(1)java自己就是多线程的;

(2)java的多线程是一种编程思想,一个程序执行以后就成为一个进程,所谓的多线程就是在一个进程中有多个线程存在,在宏观上是多个线程同时运行,能够使进程同时完成多件事,微观上是cpu在多个线程之间切换,在某一时刻仍是执行一个线程。

【2】并发

(3)并发通常指 "并发访问", 如若干应用程序从不一样的客户端同时访问同一个服务器端的数据库,服务器应用必定的承受能力处理并发的请求,并发请求被进程或是进程里面的线程处理,并发访问用并发编程处理其实最好的解决办法就是多线程。

(4)并发编程是多线程编程的一种应用。

(5)不管你是作web开发,仍是数据库开发,仍是桌面开发,都会用到多线程的。

(6)由于有并发的这个实际的需求,因此出现了多线程。

【3】

(1)高并发不是JAVA的专有的东西,是语言无关的广义的,为提供更好互联网服务而提出的概念。举个极端的例子,就是100我的,1人分配1台web服务器,那么服务器资源是他们独占的,他们不须要抢占服务器资源,100个请求被100台服务器并行处理,速度一定很快,这就是高并发。固然这是不可能的,可是,咱们老是努力去作,让少许的服务器也能达到近似的能力。这就须要服务器的HTML画面,后台业务逻辑,db数据存取等等细节上的处理都达到一个并行的极致,以此来实现整个服务器对全部请求的高并行。这是战略上的并行。多线程只是为了达到高并发目的,在某个细节点上,为实现某并发功能而采用的一种具体的实现方法,这种功能也能够由多进程实现,固然,也能够由多进程,多线程一块儿实现。这是战术上的并行。那么能够说,高并发是目的,多线程是某种手段(不是惟一的),高并发能够由多线程实现,可是多线程不表明就是高并发。

(2)并发与多线程之间的关系就是目的与手段之间的关系。并发(Concurrent)的反面是串行。串行比如多个车辆行驶在一股车道上,它们只能“鱼贯而行”。而并发比如多个车辆行驶在多股车道上,它们能够“并驾齐驱”。并发的极致就是并行(Parallel)。多线程就是将本来多是串行的计算“改成”并发(并行)的一种手段、途径或者模型。所以,有时咱们也称多线程编程为并发编程。固然,目的与手段之间经常是一对多的关系。并发编程还有其余的实现途径,例如函数式(Functional programming)编程。多线程编程每每是其余并发编程模型的基础,因此多线程编程的重要性不言而喻。

综合上面几点能够看出:并发,就是服务器同时处理多个请求的能力,而处理并发的最好的办法,就是采用多线程。cpu快速调用不一样的就绪队列,看起来好像是同步进行,实际仍是一个线程一个线程的处理的,但这样,就大大提升了服务器的效率,减小了线程的等待时间。

便可以这么理解:多线程是处理高并发的一种编程方法。即并发须要用多线程实现。

当有海量的请求时,服务器没有链接的进程可用,系统就会产生异常。

六、    在具体项目中,什么状况下用到了多线程?

【1】若是作 java web方面开发的话几乎用不到多线程!由于有多线程的地方 servlet 容器或者其余开发框架都已经实现掉了!通常在网络应用程序中使用多线程的地方很是多!另外,拷贝文件使用多线程,是没有用的!以多线程来提升效率的场景通常在 CPU 计算型,而不是在 IO 读写型。CPU 能够会有多个核心并行处理计算,可是磁盘 IO 就没这功能了,磁头只有一个,根本不可能靠多线程提升效率!通常来讲,磁盘 IO 的并发能力为 0,也就是说没法支持并发!网络 IO 的话因为带宽的限制的,使用多线程处理最多也只能达到带宽的极值。对于磁盘 IO 来讲,多线程能够用于一个线程专门用于读写文件,其余的线程用于对读取数据进行处理,这样才有可能更好地利用 CPU 资源。若是仅仅是单纯的文件复制,使用多线程操做的话,会使用磁头在磁盘上不停地进行寻道操做,使得效率更为低下!

【2】压力测试时,会用到多线程。

【3】服务器编程时,会用到多线程。

【4】使用监听器时,可能会用到多线程。

【5】跑JOB时,可能会用到多线程。

【6】还有一种极为广泛的使用多线程的场景是UI编程,通常UI界面绘制于主线程,为了避免阻塞主线程让用户体验更流畅,须要建立单独的线程处理耗时操做,处理完了再更新主界面,典型的案例就是android应用开发。

【7】一些C/S模式好比说网络游戏(基于socket协议)通常在服务器那边处理的时候一个客户端,一个线程;还有就是一些银行软件,用到了线程同步等等。

七、    高并发事件的出现的场景

(1)     服务器在每秒内处理请求数是必定,当访问量太大时,没有多余的进程来处理这些请求,就会形成系统的瘫痪。系统崩溃,例如前段时间微博的瘫痪,就是短期内流量太大了,致使系统处于异常状态,页面没法显示,用户没法登录等;电商中抢购,秒杀中出现超发的状况,多个用户同时购买同一物品,访问修改同一数据等状况。

(2)     因为购票、查询和浏览的数量激增,12306网站天天访问量比平时增加数十倍,常常出现登陆难的现象,近日甚至出现了付款不出票、系统干脆瘫痪的状况。网络订票本来是一项便民利民的措施,却因为铁道部未正确预估网站并发状况、技术支持不足而致使相反的结果。IT业界人士纷纷指出,12306网站瘫痪最大的缘由是“技术之罪”。并针对高并发网站引起的技术考验,纷纷献技献策、展开讨论,提出了不少具指导意义的技术解决方案。

(3)     这次12306网站难登陆并瘫痪是系统架构规划的问题,致使不能有效支持大并发量集中访问。同时,12306在IT管理上也有问题,未能进行有效的压力测试和运行模拟。

八、    高并发下出现的超发问题的解决方案

项目中:

【1】在点击购物车的提交订单按钮时,会判断库存是否足够,不足,会拒绝提交订单。

【2】当库存只有一件时,两个客户,或多个客户通过在购物车提交订单判断,在订单页面进行支付时判断,同时都知足了购买一件产品的需求,那么,让谁购买成功?岂不是多个用户同时都支付了这同一件商品?这种状况就是超发,下面将解决高并发下的这种超发的问题。

【3】假设 个,B客户购买6个:

(1)在点击提交订单时,两这同时判断库存知足,进入到订单界面;(2)而后 A先支付完成,在支付时,减小库存(此时作判断,若是库存足够,支付成功,不然支付失败)---》PS:跟12306购票系统类似。(3)而后B慢一步也进入到支付,判断,此时库存只有5个,不让支付,支付失败。(4)若是二者同时进行支付呢?怎么改变库存,让谁购买成功?这就涉及到了加锁,同步的问题。下面就讨论这个问题 的解决方案。、

【4】涉及到的知识点技术

(1)    悲观锁:解决线程安全的思路不少,能够从“悲观锁”的方向开始讨论。悲观锁,也就是在修改数据的时候,采用锁定状态,排斥外部请求的修改。遇到加锁的状态,就必须等待。虽然上述的方案的确解决了线程安全的问题,可是,别忘记,咱们的场景是“高并发”。也就是说,会不少这样的修改请求,每一个请求都须要等待“锁”,某些线程可能永远都没有机会抢到这个“锁”,这种请求就会死在那里。同时,这种请求会不少,瞬间增大系统的平均响应时间,结果是可用链接数被耗尽,系统陷入异常。

本身理解:悲观锁就是给要修改的数据加上锁,同步。其余请求须要等待。把数据锁住。

怎么实现?具体怎么在代码中写。须要了解的。

通常意义上的加锁指两个层面:代码层次,如java中的同步锁,典型的就是同步关键字synchronized ;数据库层次,如悲观锁(物理锁)和乐观锁。

悲观锁:经过数据库的forupdate字段实现加锁。

for update要放到mysql的事务中,即begin和commit中,否者则不起做用。

(2)FIFO队列思路

那好,那么咱们稍微修改一下上面的场景,咱们直接将请求放入队列中的,采用FIFO(First Input FirstOutput,先进先出),这样的话,咱们就不会致使某些请求永远获取不到锁。看到这里,是否是有点强行将多线程变成单线程的感受哈。

而后,咱们如今解决了锁的问题,所有请求采用“先进先出”的队列方式来处理。那么新的问题来了,高并发的场景下,由于请求不少,极可能一瞬间将队列内存“撑爆”,而后系统又陷入到了异常状态。或者设计一个极大的内存队列,也是一种方案,可是,系统处理完一个队列内请求的速度根本没法和疯狂涌 入队列中的数目相比。也就是说,队列内的请求会越积累越多,最终Web系统平均响应时候仍是会大幅降低,系统仍是陷入异常。(3)乐观锁思路

这个时候,咱们就能够讨论一下“乐观锁”的思路了。乐观锁,是相对于“悲观锁”采用更为宽松的加锁机制,大都是采用带版本号(Version)更新。实现就是,这个数据全部请求都有资格去修改,但会得到一个该数据的版本号,只有版本号符合的才能更新成功,其余的返回抢购失败。这样的话,咱们就不须要考虑队列的问题,不过,它会增大CPU的计算开销。可是,综合来讲,这是一个比较好的解决方案。有不少软件和服务都“乐观锁”功能的支持,例如Redis中的watch就是其中之一。经过这个实现,咱们保证了数据的安全。

本身理解:修改版本号,version。

假设数据库中账户信息表中有一个 version字段,当前值为 1 ;而当前账户余额字段( balance )为 $100 。 操做员 A 此时将其读出( version=1 ),并从其账户余额中扣除 $50( $100-$50 )。 2 在操做员 A 操做的过程当中,操做员 B 也读入此用户信息( version=1 ),并 从其账户余额中扣除 $20 ( $100-$20 )。 3 操做员 A完成了修改工做,将数据版本号加一( version=2 ),连同账户扣 除后余额( balance=$50 ),提交至数据库更新,此时因为提交数据版本大 于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。 4 操做员 B 完成了操做,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操做员 B提交的 数据版本号为 2 ,数据库记录当前版本也为 2 ,不知足“ 提交版本必须大于记 录当前版本才能执行更新 “ 的乐观锁策略,所以,操做员 B 的提交被驳回。 这样,就避免了操做员 B 用基于

version=1 的旧数据修改的结果覆盖操做员 A 的操做结果的可能。 从上面的例子能够看出,乐观锁机制避免了长事务中的数据库加锁开销(操做员 A和操做员 B 操做过程当中,都没有对数据库数据加锁),大大提高了大并发量下的系统总体性能表现。

(4)Mysql的数据库的锁机制

(5)关于事务和脏数据

脏数据,不可重复读,幻读

解决:

九、    我对多线程及高并发的了解

我对线程和并发的了解可能比较浅显,理论化,基础化,由于我接触的项目中,并无深刻的处理过并发和多线程的处理,我对于并发的处理只是在分布式开发和负载均衡中提升项目的并发处理能力。多线程的了解就是对同步(就是加锁)和servlet的单例多线程有必定的了解。

谈到高并发,就是服务器端同时处理海量请求的能力。当出现高并发时,可能会出现超发的状况:(详细介绍,结合本身项目中遇到的超发问题)

当请求超过服务器的承受限制,还可能会使服务器崩溃。此时,咱们能够经过分布式开发的架构,将业务细化为多个子业务,这样既能够增长了后台服务器对高访问的业务的请求处理能力,同时还便于后期的升级维护,各个子业务之间的维护升级更加简单,经过远程调用来实现各个子业务之间的联系。此外,还能够经过集群,来提升服务器的并发能力。这种提升服务器的并发能力的方式也成为水平提升服务器的并发能力,这也是将来发展的趋势。

此外,就是经过单机,经过提升cpu等单个物理机器的性能,来提升服务器的并发能力。

PS:web服务器都是已经实现了多线程的。

因此我要了解的就是怎么处理超发的问题,在处理超发的前提下,我要对什么是高并发,高并发和多线程有什么关系搞清楚。

关于实际中编码中我所接触到的对于多线程,和并发访问有四点:

【1】在讲多线程时,关于火车票的简单代码和同步代码块,了解了多线程的底层代码。

 

 

 

 

 

 

【2】在讲servlet时的单例多线程。

双重检验,为何要用多线程?就是为了提升cpu的利用率,(提升并发能力)若是同步方法,跟单线程的区别并不大,因此,同步共同的资源对象,缩小锁定的范围,故用双重检验。

   

 

 

 

 

 

【3】在讲解电商项目时,分布式开发和Nginx的负载均衡的集群创建。

【4】Web服务器都是实现了多线程的,现阶段不须要编写多线程。

相关文章
相关标签/搜索