做为拥有世界上最多的互联网用户群体国家,尤为是移动互联网的大热,作到一个百万级的应用几乎是分分钟的事情。相应对技术的压力,和要求也是很是高的。php
要应付这种大并发须要高性能系统的开发,先从经常使用的MySQL数据库碰到的性能瓶颈,来作分析。由于一般一个小项目刚开始通常都会只用mysql作为数据存储,当用户量增长的时候,就会出现数据库负载太高的问题,也就是所谓的慢查询。解决慢查询的问题通常来讲,解决方案是优化SQL查询,读写分离和主从数据库,不断地切库分表。html
1.SQL优化,最多见的方式是,优化联表查询,以及优化索引。这里面包括,尽可能使用left join 替代 where联表;当碰到,频繁查询字段A和字段B,以及AB联合查询的状况时,对AB作联合索引,可以有效的下降索引存储空间,提高查询效率。在复杂联表的状况下,能够考虑使用 Memory中间表。前端
2.主从数据库和读写分离,主从分库是用来应对访问量增长,带来频繁读写致使数据库的访问和操做性能降低的问题。对数据库的操做,为了保证数据的完整性,一般涉及到锁的机制的问题。MySQL的InnoDB引擎支持行级锁,而MyIsAM只支持表锁定。这样的话,若是读写集中在一个表中的状况下,当访问量增长,就会形成明显的性能降低。所以,经过主从数据库的方式能够实现读写分离。通常来讲,使用InnoDB来做为写库,使用MyISAM做为读库。这种方式是缺点固然是,数据库的维护难度增长,可是一般都会有专门的DBA这个职位来负责。并且几乎是必须的解决方案,算是基础设施了。mysql
3.数据库分库和分表.有的时候会出现,某个表变得愈来愈庞大,好比存放message信息表,这样会形成读取性能的增长。这种状况下,你能够经过分表的方式来解决。将一个大表切分红若干个表。ajax
一种简单算法是:redis
设定表的大小为M,用户访问记录的ID和表的实际ID有差别,这个时候就须要作下换算,表的id = 用户访问的id 对M 进行求余;这种算法简单实现容易,单向扩展简单,可是缺点很明显,他是按照数量进行分配,可是每每实际状况会出现,访问量会集中在某几个表,而其余表访问不大,这样这种算法实际上实际上没太大的效果。并且从新调整的话,就比较困难。算法
相似分表的算法,还有不少,具体能够参考百度。sql
4.使用存储过程.将一些操做,直接经过存储过程的方式,预先设置在MySQL,客户端只须要调用存储过程就能够操做数据。在平常实践中,常常会出现,DBA在备份和恢复数据库的时候,遗忘了存储过程的状况。其次,业务调整的过程时,要对线上的存储过程进行调整,容易出现意想不到的问题,增长运维的成本。所以不少的团队不太愿意使用存储过程.数据库
访问压力增大以后,最容易想到的解决方案就是,使用缓存了。实际上现实中,最经常使用的缓存无处不在。可是缓存细细说来其实仍是比较复杂。后端
首先分为前端缓存,和后端缓存,两种技术解决方案。
先说前端页面的缓存,又能够分为PC端页面和移动客户端的缓存技术方案。
对PC端来讲,对静态资源进行缓存,包括JS,CSS,图片等资源文件的缓存。
1. 通常来讲,资源文件的缓存都是经过专门的CDN缓存加速,因为国内线路不通访问速度也不一样,因此有专门的服务商提供专门的静态资源加速缓存服务保证不一样的线路,以及用户能够就近快速访问。
2.对动态页面进行缓存,好比网站首页,内容页等这种改动不会太大的页面或者几乎不太会改变的页面,有两种方式:1)直接生成静态html页面,须要更新时经过后台成生成新页面进行覆盖。而后能够把静态页存放到本地硬盘或者同步到CDN上。2)使用vanish服务器做为方向代理,对php生成的动态页面进行缓存,因为vanish可使用内存做为缓存,所以访问速度更快,且对生成页面的php代码不须要作任何的修改,就能够实现静态页面缓存。因此能够很好地解决由于一直遗留下来的问题致使代码修改为本高的状况。缺点也是,提高了运维成本。
对页面上某些内容常常变化的页面,好比用户中心页,其实也可使用Ajax的方式来处理,将页面的基本内容缓存成静态页,使用ajax动态加载服务器端的动态数据。
浏览器缓存
经过Cache-Control,以及Last-Modified等控制缓存头的设置来告诉浏览器缓存页面。这样没必要每次,都从服务器端重复请求新文件,也能够防止用户频繁刷新页面。
在过去带宽有限的状况,下浏览器缓存很是的重要,可是目前来讲,因为带宽的提高,服务器性能提高,相对来讲,这方面的浏览器的缓存要求相对降低了。使用浏览器缓存有的时候反而使得业务变得更加复杂,所以不少状况下,很多业务干脆不用浏览器缓存。
后端缓存
1.代码缓存:使用的各类PHP框架,自己会生成各类代码缓存,好比使用模板的视图文件,配置文件,都会解析成相应的php代码。经过这些方式,提高框架的运行效率。
2.数据缓存:这一类缓存其实是最为复杂的一类的,也是常常会碰到,须要根据业务进行不断地调整一类的缓存。一般,咱们常见的是对原先存储在MySQL数据的数据进行缓存加速。这也是前面涉及到的数据库性能瓶颈的一般解决方案之一。通常来讲,过去经常使用的缓存服务器是memcached,可是随着Redis的出现以后,不少的创业公司,和项目,直接绕开memecached使用Redis作为缓存服务器,并且Redis不仅是缓存服务,还有更多的高级特性。这里放在专门的段落来讨论,使用NoSQL替代MySQL的话题。
使用缓存最大的问题,是出现当缓存失效以后,如何解决惊群效应带来的服务器忽然压力上升问题。当某个缓存失效以后,通常的作法是,再从后端的数据库中查找新的数据而后再重建缓存,可是在这个过程当中,若是这个缓存内容同时有不少并发请求,就会出现,在重建新的缓存的时间段内,大量涌向后端数据库的访问,引发慢查询,致使数据库崩溃。通常来讲,咱们采起的方案是,主动更新缓存内容,同时延长缓存的时间,实际失效时间会比告知客户端的约定失效时间要长一些。好比实际失效时间是30s,约定失效时间是20s,后端的worker会对缓存进行主动更新,通常会使用两个key1,key2的方式进行轮流缓存和访问,好比,客户端访问的时候,先访问key1若是key1不存在则访问key2,缓存更新时,先生成key2,而后再删除key1。反之,亦然。
再来讲说所谓惊群效应,来源于一个颇有意思的场景,在广场上,有一大群的鸽子,游人过来抛洒食物的时候,本来平静的广场,忽然一大群鸽子都拥过来争抢游客手中的食物,当食物被吃完以后,又恢复了以前的平静。等到下一次食物到来的时候,又出现相同的情景。这种现象,咱们叫作惊群效应,其实在生活中这种现象随处可见,好比:商场大促销,能够看见一大堆的人早已等候在门外,等商场开门的时候,大量的人如潮水通常涌入。还有线上的一些电商促销活动,好比淘宝双十一,各类秒杀活动等。都是常见的“惊群效应”现象。
在技术开发中,咱们常常也要考虑惊群效应出现的场景。
咱们最熟悉的一个业务场景就是,线上的秒杀促销活动的业务开发,在这个业务中就能够看到惊群效应的影响。咱们能够假定一个业务场景,好比小米手机开放抢购。只有一万台手机,实际上参与秒杀的人,超过100万,假定时间是上午12点开始抢购,可是实际上真正的秒杀过程就是1两秒钟最多了,确定被抢完了。
惊群效应会致使,大量的服务器资源浪费,在服务器访问压力图表中,看到大多数状况下服务器是处于闲置状态的,一旦压力增长以后,服务器资源迅速被消耗殆尽,甚至致使崩溃,整个系统瘫痪。
高并发状态下,惊群效应是常常出现,尤为是在基于社交的移动互联网产品中,几乎是屡见不鲜。咱们常常处理的技术解决方案是,使用队列来进行处理,这也是NoSQL数据库常被用到的地方。将用户全部的请求,写入到队列中,而后经过后端的worker对队列的请求进行处理,这是一个生产者-消费者模型的经典使用场景。当处理完获取到规定数量级的结果以后,通知请求代理服务器,关闭请求通道,并重定向到别的页面,告知用户服务已经完结。好比,秒杀的时候,前端代理服务器负责将用户的请求发送到Redis队列服务器,而后后端的worker进程,消费队列的数据,当发现,秒杀的产品数量已经被抢光以后,则通知前端代理服务器,关闭秒杀请求的通道,重定向用户到一个静态提示页面,告知用户秒杀结束。这样能够保证,不会出现库存和订单不一致的状况,出现用户多抢的状况。包括抽奖也是同样,在高并发的状况下常常出现,用户抢到超出库存设定的相同商品。
这里谈谈使用NoSQL在大型项目中的使用。
这里说说咱们经常使用的NoSQL开源项目,Redis,MongoDB,CounchBase等等,包括甚至一些新的语言,如,Node.JS等,这些新的技术产品,不少生来就是为,移动互联网大数据服务的。过去咱们不少人都认为,所谓高并发,大数据,等名词都出如今BAT等少数公司里。可是随着移动互联网时代的到来,其实不少的新兴的移动互联网创业公司,很容易的,就能出现一晚上间到上千万甚至过亿的用户,好比,以前在朋友圈风靡的,疯狂猜图,神经猫,这类小应用。因此在这些新兴的移动互联网产品中,NoSQL的使用几乎是基础服务。尤为是在社交类的应用中,对Feed数据的处理,消息推送,基于社交关系链的维护,用户社交行为的统计,都使得对代码的质量,数据的优化存储,业务的架构的设计等等都会有更高的要求。
NoSQL具体业务应用场景有如下几个方面:
1)队列服务。前面所提到的秒杀,抽奖,各类道具的交易等都会用到队列服务。其次是,消息推送,粉丝关注列表,等,都会用到队列服务。消息队列开源软件有不少,相似RabbitMQ,ZeroMQ,Redis等等,可是经常使用的仍是Redis比较多。
对Feed的处理,是队列经常使用到的场景。好比微博的fee的消息,一般都是采用push和pull的策略,对活跃用户,通常经过缓存算法,LRC,LRF等进行计算,系统维护一个活跃用户的缓存池。而后为每个活跃用户,生成他所关注的对象列表的消息队列,当用户上线的时候,主动推送这些消息队列。对不常活跃的用户,只有当他登陆的时候,才会从后端的数据库去查询数据,生成结果。
这种方式能够有效的提高性能,同时节约存储空间。
2)计数器和限速器。移动社交应用中,用户的点赞行为很是常见,为了保存用户的点赞数据,咱们常常会用到计数器服务,用被点赞的记录id做为key,对应的结果值为点赞次数。redis的incr,incrby等命令就常常被使用到,还能够经过expire来配合使用,在指定时间里面记录用户的数据。另外为了防止用户频繁访问API接口,尤为是恶意访问服务器数据服务,致使服务器过载,能够对用户的访问进行限速。
3)Top Rank,尤为是在游戏中常常会出现排名榜,电商网站也会有对商品的购买量进行排行,或者是会员等级排名等等。redis的ZSet常常被使用到,能够把记录的id存入zeset,设置对应的score为排名的依据的值,而后使用zRank来读取用户的排名,也可使用zRange命令来获取到前指定位数的用户排行榜信息。
4)消息订阅。redis的pub/sub服务,能够实现消息订阅的功能,一般是用在用户的消息推送中,向指定用户推送相应的消息。其次是,任务分发,将用户产生的行为或者请求,发送到后端,在后端服务器中由后台worker进行处理,这样好处是,下降前端服务器的处理请求的压力,减小用户等待时间,同时减轻带宽的压力,也大大的下降服务器带宽成本,节约资金。
5)存储session。普通的网站使用file文件的方式存储session,使用redis做为session进行用户登陆存储,好处很明显,一方面是访问性能提高,另外,redis使用set来存储在线用户的uid数据,这样能够很方便的使用sCard命令统计当前在线用户数,防止用户重登陆。其次是避免,分布式环境下,带来用户登陆访问不一致的问题。由于用户session信息,统一存储在独立的NoSQL数据库中通常来都是,使用redis服务器,这样,除了提高性能以外,也下降了代码处理的复杂度,甚至避免了过早使用大型的SSO系统,致使开发成本增长。
6)地理信息位置。MySQL其实也有存储地理位置信息,可是不如MongoDB,CounchBase等新兴数据库功能强大。基于地理位置的社交服务,用户会带着手机客户端,不断移动位置,因此用户的位置会不断地上报到服务器,而后根据服务器实时的地理信息位置,快速计算出附近附近的人,这对服务器的访问压力要求比较高。
经过代码,和各类软件的使用,来提高性能以外,整个系统架构的设计就提上日程了,最重要也是最通用的方式,天然是,堆服务器了,换个高大上的叫法,也就是所谓的分布式,系统集成。在高大上一点,就是所谓的云服务,各类所谓的云。
1)反向代理模式。所谓反向代理模式,用户全部的请求都统一发送到反向代理服务器,而反向代理服务器自己并不处理具体的请求,他只负责将请求发送到后端,得到结果以后,再传递给用户。这样的方式,可以加快整个系统的服务器响应,即便后端某个系统的崩溃,也不影响整个服务系统的奔溃。另外,这种反向代理服务器,通常来是应用服务器,自己也是分布式的,用户访问请求是被分发到不一样的服务器上,这样可以支撑更大的并发请求。后端服务器,并不须要接入到互联网中,这样就不须要占用宝贵的带宽,只须要在本地网络使用本地线路进行链接,专一各类数据的运算服务,这样可以大大的下降成本,提升系统的安全性,同时提高整个系统的性能。
2)过滤器中间件。不少的请求并不是都是当即被执行,或者是都会被请求。并不是全部的客户的无理要求都必须知足同样,有的餐厅就并不必定提供住宿服务同样。当用户请求,被传递到中间层中,会有各类中间件会对用户的请求进行过滤,好比秒杀或者抽奖时,用户的频繁刷新请求,会被过滤器进行过滤,合并或者拒绝用户的请求。这样真正传递到后端的请求就会减小不少,有效地下降了后端服务器的压力。
3)数据视图。用户的不少重要数据都最终存在数据库中,可是用户的每次请求,并不须要一次性访问全部的数据。好比,断定用户是否登陆,只须要访问用户的是否登录的标记。这些数据能够存在redis的key中,并不须要经过数据库去保存。再有,一些信息页的数据,因为变更不会太大,由后台编辑生成,而并不是是用户生成的信息数据,每每是树状结构,每次经过数据库查询,须要大量重复的join或者屡次的sql查询才能读取。一方面增长了,代码的逻辑设计和维护的成本,另一方面,运行成本也大大提高,即便经过缓存的方式,也会有惊群效应的问题须要解决。因此不如经过主动视图的方式来解决,这一类的数据,能够经过使用后台使用mysql进行存储和编辑,而面向用户的前台数据,使用MongoDB等高性能的NoSQL的数据库存储,至关于,MongoDB做为MySQL的视图。
4)缓存策略。
对于用户产生的UGC内容,它的特色是,有的用户行为读写频繁,其次是每每出现不可预测,忽然一会儿并发提高。好比,用户的内容分享,转发,收藏,点赞。有的帖子,会忽然出现被病毒式传播的现象,大量的用户分享,转发。可能这篇文章的访问量,就会忽然增长,相应的访问压力也提高,对数据库的读取压力也增长。
然而并不是全部的帖子都是热门帖子,都会被访问,每每在一个系统中,大部分的帖子都处于冷门或者无人问津的状态下,只有少部分的帖子汇出现热门的状态。
所以并不是全部的内容都应该被缓存,提供统一的,缓存策略服务就显得颇有必要。缓存策略服务器系统,使用不一样的算法策略,维持一个缓存数据的池,并不是是简单地缓存数据。使用有效地缓存策略,对低访问压力的请求直接穿透缓存使用后端访问,高访问压力使用缓存服务。
缓存策略的算法有不少种,常见的有:
Least Frequently Used(LFU)
Least Recently User(LRU)
Least Recently Used 2(LRU2)
Two Queues(2Q)
具体还能够百度查询,翻阅更多的详细介绍。
5)任务系统。将不少的耗费性能的服务,集成为一个专门的服务系统,好比,定时任务服务,图像处理,分布式文件同步等等。
以上在高并发,高访问量的应用中,一些技术问题的解决方案的汇总。