关于高并发及海量数据处理,个人浅显理解

高并发海量数据处理,在我经历中,这是两个事情但又相关连的。
高并发怎么处理,这个问题应该是具体问题具体分析,应该是找到瓶颈再做针对处理。不可能全部做升级,那样成本太高了。一般是一步步的解决。
在说具体的方案之前,我说下之前的经历,一个典型的案例。朋友所在的公司是一家互联网保险公司,遇到的问题是:某款产品(打折较多的)会出现加载不成功,然后用户下单时报数据库异常,应该是并发导致事务异常插入不成功。用工具测试:QPS在100到500之间,硬件资源高峰不到30%。这个数据说明我们可以从应用本身做优化。
我建议他第一步把PV高的页面做页面缓存(因为是产品,改动不大),他们的主站是net开发的,用的框架是4.0,且用的是MVC。所以很简单的在action中加上outputcache特性(net称特性,java 叫注解,有意思不?)就可以了。当然还要设置两个比较重要的参数的,这个产品详情页是公用的也就是说所有产品都是打开在这个页面展示。所以我们得设置按产品id作为参数缓存,并且缓存过期时间设置比峰值访问大一些。
第二步,使用消息队列异步下单,这个改动比较大的。这个方案,是我之前设计过一个秒杀架构总结出来的。下单的服务端(消息消费者)用的是java,因为他们公司有意向把主站改为java的。客户端(消息产生者)即net下单站点。消息队列使用的是rabbitmq,选择其有几个比较的指标的,rabbitmq采用的是amqp协议,无需装其他插件只需装erlang环境,跨平台比较泛(手持设备需要下单),开源,收发消息比较平均,且队列可以持久化,这就不怕消息丢失,解决下单不成功时可以再次下单;还有个优点可以设置出错的消息可以重复投递,我建议他设置重复投递3次,3次不成功人工干预,放到另一个暂存队列,并通知管理员人工处理。3次基本可以杜绝并rabbitmq发引起的悲观锁,若都不成功,证明数据有问题的。
其他MQ优势不大,MSMQ微软自己的,不能跨平台,消息大小限制4M。ActiveMQ,JAVA自己的MQ,不过性能不是那么理想跟MSMQ差不多的性能。ZeroMQ,这个性能超乎想象的,发送消息比其他可以甩几条街的,可惜不能持久化,用来做即时通讯是不错的。
按这个改动朋友公司跑了半年后,他又找到我,现在有个这样的问题:C#高并发产生消息有瓶颈,至少比java少一半性能,也就是说c#产生100条消息,java可以产生200条,是不是C#性能比java差?对于这个问题我顿时有点蒙的,两者性能不会相差这么大的。两者性能之争一直是两大开发团队讨论的焦点矛盾,咱不纠结这个问题,以我的经验,两种语言我都有用过,性能不会相差这么大的,一定是代码有问题的。于时我去他公司,用他的电脑,做了次代码走查,当然我只看发送消息那部份,业务我可不关心的。哥们的代码规范做得不错的,定义了接口ISender,再定义RabbitMQSender来实现,用的类库RabbitMQ .NET来连接MQ。且自己写了个均衡策略,开始我原以为问题出个这里,代码看了几遍也没有可疑的地方,注释掉跑也没有解决问题。没办法只有出大招了,VS自带有个性能探测工具,感谢VS IDE有个这么好的工具,解决很多代码失误引起的性能问题。哥们装的是vs2010,没有vs自带性能探测工具,我给他装了个vs2013。多说几句,我比较喜欢VS这个IDE,且最喜欢这个性能测试工具,这是其他IDE没有的。这个工具可以测试每一层每个方法调用的开销,还有个web性能测试工具也不错的。要是这的个IDE能支持JAVA,那么真可以说一统江湖了。好了废话不说了,运行起来,正常的跑一次流程,抽丝剥茧的,使用次数多了也有经验了,马上发现一个异常,发送完后还有很大性能开销,占了86%,展开代码进去看到,重写了析构函数,做一些变量回收工作,且写一句GC.Collect()。哥们的思路了发送一次完后,清理一下内存碎片,给下次得到更多的内存使用。咋一看,这不能说有错,很正常,很多人都会这样写的,特别是有IO文件流操作的时候。可是GC.Collect大家要知道一点,他一回收,我们原先创建连接到MQ那些路由都会给回收掉了,导致每次都要重新寻址创建握手链接的,这个很消耗时间的,几乎需要500-800ms完成这个过程(机器不一样,内网的防火墙路由策略不一样会有影响这个时间)。注释这句代码,跑起来一切正常了。在这里提醒一下大家,GC.Collect这个方法不需要随便调用的,这个交给系统处理就行了,现在内存那么便宜。我们使用是的C#是托管语言,内部有机制去管理内存的,他会在觉得内存不够用的时候适时出手清理掉已过期的数据的,要想具体了解原理实现过程,可以看一下这本书《CLR VIA C#》。
下面说一下我这些经历所使用的招数,再说一下,做性能优化都是具体问题具体分析,一定要找到瓶颈在哪才能针对处理的,都是分步处理的。
一般地我们会利用各种工具得到几个重要的性能指标:
1.QPS,每秒的响应请求数量
2.响应时间:从请求发出到收到响应花费的时间
3.吞吐量:单位时间内处理的请求数量(通常由QPS与并发数决定)
4.峰值每秒请求数(QPS)=(总PV数*80%)/(6小时秒数*20%)
    利用这些数据我们得到某个页面或某个请求有瓶颈,再做具体的处理。比如我们有个公共的查询页面频繁的调用,但里面的数据变动不大,比如部门选择控件。那么我们可以考虑静态化这个控件或对其做页面缓存,缓存的时间就是峰值维持的时间2倍。这样我们就可以大量节省流量到后端去。如果使用的是net,就可以使用outcache。
    利用单位的响应时间及吞吐量再结合硬件性能使用情况,我们可以计算出硬件是否满足现状,是否增加集群。
我一般先从应用程序级做优化再到硬件优化。
第一步:静态资源(js图片CSS等)与应该分开不同的服务器(或不同的站点,即建设独立的文件服务器)
第二步:调用高频但更新低做静态化或页面缓存。
第三步:某个查询频繁,我们可以对其缓存结果,可以按参数缓存结果,C# outputcache可以配置参数缓存。比如ORM:mybatis 可以按查询参数把查询结果缓存至单独的缓存服务器,不进入关系型数据库查询。
第四步:某个操作需频繁的查询,做读写分离,分析出读多还是写多,如果是SQLSERVER我们就可以利用主从技术做分离。目前SQL支持8个slave。其实也相当于建立一个小小的数据库集群。
第五步:消息队列异步写操作。如果读写分离还不理想,比如写操作多,网店的秒杀都是是集中下单。这种高峰值的写操作,关系型数据库没办法满足要求。下单数据放入消息队列中直接回成功给用户,再异步写入异步数据库。
第六步:业务分离,分布式架构,这个需要软件本身就支持。现在流行的微服务架构,SOA都是把不同的业务分到不同的机器上分散处理。
第七步:负载均衡(HAProxy)建立集群
若硬件性能超出负荷,一般cpu状态70%就需要增加硬件资源。集群,多台服务器具有相同的功能,主要起分流的作用。分布式,将不同的业务放到不同的服务器中,处理一个请求可能需要多台服务器,进而提高一个请求的处理速度。又分为静态资源集群和应用程序集群。后者较复杂,经常要考虑session同步等问题。
第八步:做反向代理nginx 
       客户端直接访问的服务器并不是直接提供服务的服务器,它从别的服务器获取资源,然后将结果返回给用户。
  代理服务器和反向代理服务器:
  代理服务器是代我们访获取资源,然后将结果返回。例如,访问外网的代理服务器。反向代理服务器是我们正常访问一台服务器的时候,服务器自己调用了别的服务器。
  代理服务器我们主动使用,是为我们服务的,不需要有自己的域名;反向代理是服务器自己使用的,我们并不知道,有自己的域名。
第九步:做CDN镜像 cdn其实是一种特殊的集群页面缓存服务器,他和普通集群的多台页面缓存服务器相比,主要是它存放的位置和分配请求的方式有点特殊。CDN 服务器是分布在全国各地的,当接收到用户请求后会将请求分配到最合适的CDN服务器节点获取数据。比如联通的用户分配到联通的节点,上海的用户分配到上海的节点。

海量数据处理:
关于海量数据处理有点老生常谈的话题了,无论在网络型公司(高并发带来的海量数据)还是管理型(企业工厂类的)的软件系统,日积月累下来都会有海量的数据沉淀,设计不好的系统就会有越来越慢的感觉了,开始力不从心,于是做数据的切分,纵横扩展,再到加硬件,加到硬件不了问题了,就这么个套路。很多人发现软件运行到3-5年就会有一个瓶颈,软件开发不规范的公司只能推倒重来,因为他们发现不是硬件能解决的问题了,软件架构的不合理,扩展性不强,导致跟不上业务的变化了。简单举个例子说,以前的框架只是个MVVM模型,没有做业务分离等,现在需求需要做手持设备的应用。怎么办呢?只能重新开发个服务端罗,然后就会有两套业务逻辑,然后改来改去后,两套逻辑可能不一样了,就会导致数据的不一致了,然后数据都追溯不出来到底是哪引起的问题。说这些我个觉得还是要重视一下软件架构的,有些东西不是重构可以解决的,当然你说推倒重构那当我没说,呵呵。。。。。。。
下面说一下数据处理的套路吧,一般地我是从页面缓存做起的,解决大量访问的先,然后去拆分做集群处理。
1.应用程序级缓存,如java的ConcurrentHashMap,C#的outputcache
2.缓存架构,Ehcache,Memcache,Redis。最关键的问题是:什么时候创建缓存,以及其失效机制。对于空数据的缓冲:最好用一个特定的类型值来保存,以区别空数据和未缓存的两种状态。
mybatis 可以将关系型数据转换成内存数据。
sql2014、Oracle、mysql(5.5+ENGINE)内存表
3.数据库优化:
  1,表结构优化。
  2,SQL语句优化,语法优化和处理逻辑优化。可记录各语句执行时间,有针对性的分析。
  3,分区
  4,分表
  5,索引优化
  6,使用存储过程代替直接操作
4.分离活跃数据
  例如用户,可以分为活跃用户和不活跃用户。
5.批量读取和延迟修改
  高并发情况可以将多个查询请求合并到一个。
  高并发且频繁修改的可以暂存缓存中。
6.读写分离
  数据库服务器配置多个,配置主从数据库。写用主数据库,读用从数据库。
7.分布式数据库
  将不同的表存放到不同的数据库中,然后再放到不同的服务器中。有些复杂问题,如:事务处理,多表查询。
7.NoSql和Hadoop
  NoSql,not only SQL。没有关系型数据库那么多限制,比较灵活高效。
  Hadoop,将一个表中的数据分层多块,保存到多个节点(分布式)。每一块数据都有多个节点保存(集群)。集群可以并行处理相同的数据,还可以保证数据的完整性。

1、HTML静态化 
2、图片服务器分离  应用和静态资源分离
CDS加速
3、数据库集群和库表散列 
4、缓存 页面缓存
5、镜像 
6、负载均衡 (osi硬件四层或软四层 LVS)

猜你喜欢

转载自blog.csdn.net/luochengbang/article/details/84964141