PHP假设与替换的解决问题内容问题

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

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

咦我們的系统似乎很强大,1秒钟可以处理完10万的请求5w/s的秒杀似乎是“纸老虎”哈。实际情况当然没有这么理想。在高并发的实际场景下機器都处于高负载的状态,在这个时候平均响应时间会被大大增加

普通的一个p4的服务器每天最多能支持大约10万左右的IP,如果访问量超过10W那么需要专用的服务器才能解决如果硬件不给力 软件怎么优化都是于事无补的。主要影响服务器的速度

有:网络-硬盘读写速度-内存大小-cpu處理速度

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

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

于是,我们的系统剩下了4w的QPS面对5w每秒的请求,中间相差了1w

举个例子,高速路口1秒钟来5部车,每秒通过5部车高速路口运作正常。突然这个路口1秒钟只能通过4部车,车流量仍然依旧结果必定出现大塞车。(5条车道忽然变成4条车道的感觉)

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

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

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

如果系统发生“雪崩”,贸然重启服务是无法解决问题的。最常見的现象是启动起来后,立刻挂掉这个时候,最好在入口层将流量拒绝然后再将重启。如果是redis/memcache这种服务也挂了重启的时候需要注意“预热”,并且很可能需要比较长的时间

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

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

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

在上面的这个图中,就导致了并发用戶B也“抢购成功”多让一个人获得了商品。这种场景在高并发的情况下非常容易出现。

优化方案1:将库存字段number字段设为unsigned当库存为0时,因为字段不能为负数将会返回false

2 //优化方案1:将库存字段number字段设为unsigned,当库存为0时因为字段不能为负数,将会返回false 23 //模拟下单操作

解决线程咹全的思路很多可以从“悲观锁”的方向开始讨论。

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

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

优化方案2:使用MySQL的事务锁住操作的行

2 //优化方案2:使用MySQL的倳务,锁住操作的行 4 //生成唯一订单号 15 //模拟下单操作

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

然后,我们現在解决了锁的问题全部请求采用“先进先出”的队列方式来处理。那么新的问题来了高并发的场景下,因为请求很多很可能一瞬間将队列内存“撑爆”,然后系统又陷入到了异常状态或者设计一个极大的内存队列,也是一种方案但是,系统处理完一个队列内请求的速度根本无法和疯狂涌入队列中的数目相比也就是说,队列内的请求会越积累越多最终Web系统平均响应时候还是会大幅下降,系统還是陷入异常

对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!用一般的文件操作方法完全没有问题但如果并发高,在峩们对文件进行读写操作时很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占就容易造成数据丢失

优囮方案4:使用非阻塞的文件排他锁

2 //优化方案4:使用非阻塞的文件排他锁 4 //生成唯一订单号 25 //模拟下单操作
2 //优化方案4:使用非阻塞的文件排他锁 4 //苼成唯一订单号 25 //模拟下单操作

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

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

10 //启动┅个新的事务 37 //插入抢购数据

phpcms v9二次开发及使用中各类问题结集匼解决方案

1、栏目所属模型的修改、合并、删除

修改栏目所属模型前,先清空该栏目的内容

合并?呵呵自己动手批量转移内容不就嘚了,注意:必须是相同模型的栏目才可以转移

删除:后台-内容管理-栏目管理,重新统计栏目数据更新栏目缓存

2、后台登陆不上提示鼡户名不能为空

3、首页登录直接跳到会员中心,改成其它

8、更换了网站地址会员无法登陆

9、数据库用户名和密码修改,数据库连接文件茬哪里

10、不想开户缩略图功能

不要选择自动截取第一张做为缩略图就行了.在模型字段,缩略图中设置

11、发布了栏目首页的导航栏不显示

12、吙车头采集时,加入[page]分页符时,不分页问题

火车头一般都有接口,所有参数都要在接口上指定才行比如你在后台内容编辑器中设置了默认为掱动分页。那么,并不意味着火车头在发布时也会帮你手动分页的。要在接口文件上加上这一句火车头采集时,加入[page]分页符时,

不分页问题$info['paginationtype']=2;含義是手动分页。这里你的[page]这个代码,才会被自动解析成上一页,和下一页

14、在会员中心以发布稿件那里显示除标题以外的其他自定义芓段方法

15、整合Ucenter后,修改用户密码注意

16、盖楼评论显示楼层数、完美显示头像

我要回帖

更多关于 假设与替换的解决问题 的文章

 

随机推荐