但是我们可以想像一个极端一点的案例,1百万用户同时访问,且都是第一次访问,每人下载量需要1M,如果需要在120秒内返回,那么需要,1M * 1M /120 * 8 = 66Gbps的带宽。很惊人吧。所以我估计在当天,12306的阻塞基本上应该是网络带宽,所以你可能看到的是没有响应。后面随着浏览器的缓存帮助 12306减少很多带宽占用,于是负载一下到了后端,后端的数据处理瓶颈一下出来。于是你会看到很多http 500之类的错误。这说明服务器垮了。

  四、前端页面静态化

  静态化一些不常变的页面和数据,并gzip一下。还有一个并态的方法是把这些静态页面放在/dev/shm下,这个目录是内存,直接从内存中把文件读出来返回,这样可以减少昂贵的磁盘I/O。

  五、优化查询

  很多人查询都是在查一样的,完全可以用反向代理合并这些并发的相同的查询。这样的技术主要用查询结果缓存来实现,第一次查询走数据库获得数据,并把数据放到缓存,后面的查询统统直接访问高速缓存。为每个查询做Hash,使用NoSQL的技术可以完成这个优化。(这个技术也可以用做静态页面)

  对于火车票量的查询,个人觉得不要显示数字,显示一个“有”或“无”好了,这样可以大大简化系统复杂度,并提升性能。

  六、缓存的问题

  缓存可以用来缓存动态页面,也可以用来缓存查询的数据。缓存通常有那么几个问题:

  1)缓存的更新。也叫缓存和数据库的同步。有这么几种方法,一是缓存time out,让缓存失效,重查,二是,由后端通知更新,一量后端发生变化,通知前端更新。前者实现起来比较简单,但实时性不高,后者实现起来比较复杂 ,但实时性高。

  2)缓存的换页。内存可能不够,所以,需要把一些不活跃的数据换出内存,这个和操作系统的内存换页和交换内存很相似。FIFO、LRU、LFU都是比较经典的换页算法。相关内容参看Wikipeida的缓存算法。

  3)缓存的重建和持久化。缓存在内存,系统总要维护,所以,缓存会丢失,如果缓存没了,需要重建,如果数据量很大,缓存重建的过程会很慢,这会影响生产环境,所以,缓存的持久化也是需要考虑的。

  诸多强大的NoSQL都很好支持了上述三大缓存的问题。

  后端性能优化技术

  前面讨论了前端性能的优化技术,于是前端可能不是瓶颈问题了。那么性能问题会到后端数据上来了。下面说几个后端常见的性能优化技术。

  一、数据冗余

  关于数据冗余,也是说,把我们的数据库的数据冗余处理,也是减少表连接这样的开销比较大的操作,但这样会牺牲数据的一致性。风险比较大。很多人把 NoSQL用做数据,快是快了,因为数据冗余了,但这对数据一致性有大的风险。这需要根据不同的业务进行分析和处理。(注意:用关系型数据库很容易移植到 NoSQL上,但是反过来从NoSQL到关系型难了)

  二、数据镜像

  几乎所有主流的数据库都支持镜像,也是replication。数据库的镜像带来的好处是可以做负载均衡。把一台数据库的负载均分到多台上,同时又保证了数据一致性(Oracle的SCN)。重要的是,这样还可以有高可用性,一台废了,还有另一台在服务。

  数据镜像的数据一致性可能是个复杂的问题,所以我们要在单条数据上进行数据分区,也是说,把一个畅销商品的库存均分到不同的服务器上,如,一个畅销商品有1万的库存,我们可以设置10台服务器,每台服务器上有1000个库存,这好像B2C的仓库一样。

  三、数据分区

  数据镜像不能解决的一个问题是数据表里的记录太多,导致数据库操作太慢。所以,把数据分区。数据分区有很多种做法,一般来说有下面这几种:

  1)把数据把某种逻辑来分类。比如火车票的订票系统可以按各铁路局来分,可按各种车型分,可以按始发站分,可以按目的地分……,反正是把一张表拆成多张有一样的字段但是不同种类的表,这样,这些表可以存在不同的机器上以达到分担负载的目的。

  2)把数据按字段分,也是竖着分表。比如把一些不经常改的数据放在一个表里,经常改的数据放在另外多个表里。把一张表变成1对1的关系,这样,你可以减少表的字段个数,同样可以提升一定的性能。另外,字段多会造成一条记录的存储会被放到不同的页表里,这对于读写性能都有问题。但这样一来会有很多复杂的控制。

  3)平均分表。因为第一种方法是并不一定平均分均,可能某个种类的数据还是很多。所以,也有采用平均分配的方式,通过主键ID的范围来分表。

  4)同一数据分区。这个在上面数据镜像提过。也是把同一商品的库存值分到不同的服务器上,比如有10000个库存,可以分到10台服务器上,一台上有1000个库存。然后负载均衡。

  这三种分区都有好有坏。常用的还是第一种。数据一旦分区,你需要有一个或是多个调度来让你的前端程序知道去哪里找数据。把火车票的数据分区,并放在各个省市,会对12306这个系统有非常有意义的质的性能的提高。

  四、后端系统负载均衡

  前面说了数据分区,数据分区可以在一定程度上减轻负载,但是无法减轻热销商品的负载,对于火车票来说,可以认为是大城市的某些主干线上的车票。这需要使用数据镜像来减轻负载。使用数据镜像,你必然要使用负载均衡,在后端,我们可能很难使用像路由器上的负载均衡器,因为那是均衡流量的,因为流量并不代表服务器的繁忙程度。因此,我们需要一个任务分配系统,其还能监控各个服务器的负载情况。

  任务分配服务器有一些难点:

  负载情况比较复杂。什么叫忙?是CPU高?还是磁盘I/O高?还是内存使用高?还是并发高?还是内存换页率高?你可能需要全部都要考虑。这些信息要发送给那个任务分配器上,由任务分配器挑选一台负载轻的服务器来处理。

  任务分配服务器上需要对任务队列,不能丢任务啊,所以还需要持久化。并且可以以批量的方式把任务分配给计算服务器。

  任务分配服务器死了怎么办?这里需要一些如Live-Standby或是failover等高可用性的技术。我们还需要注意那些持久化了的任务的队列如何转移到别的服务器上的问题。