按照使用的资源类型划分,咱们能够把系统分为三大类型:IO密集型、计算密集型,数据密集型。系统的类型反映了系统的主要瓶颈。现实状况中,大部分系统在由小变大的过程当中,最早出现瓶颈的是IO。IO问题体如今两个方面:高并发,存储介质的读写(例如数据库,磁盘等)。随着业务逻辑的复杂化,接下来出现瓶颈的是计算,也就是常说的CPU idle不足。出现计算瓶颈的时候,通常会使用水平扩展(加机器)和垂直扩张(服务拆分)两个方法。随着数据量(用户数量,客户数量)的增加,再接下来出现瓶颈的是内存。数据库
现在,内存的合理使用比以往更加剧要。一方面,大数据理论已经很是普及,用数据驱动产品也已经被广泛接受并落地,同时数据分析也促使产品设计的更加精细,所以系统承载的数量比之前有了很大的变化,系统遇到内存瓶颈的时间也比之前大大缩短了。另外一方面,内存依然是相对昂贵的硬件,不能无限制的使用。即便在Amazon等云服务上,大内存的实例也是很昂贵的,而且大内存的实例每每伴随着高性能型CPU,这对一些数据密集型系统是一个浪费。所以,本文重点探讨数据密集系统如何应对出现的瓶颈。缓存
任何工程上的问题最基本的思路都是“分而治之”。所以,当内存不够时,很天然的想法是将数据拆分到多台机器中,俗称拆库。沿用数据库拆分的术语,拆库又分为“水平拆分”和“垂直拆分”两个派别。服务器
水平拆分是指将同一种数据的不一样记录进行拆分。网络
例如咱们有一亿条商品数据供查询。若是单机没法存储,可使用四台机器,每台机器存储2500万条商品数据。其中,每台机器称为一个“分片”,同一个分片的多台机器组成一个“分组”,从四个分组各选出一台机器组成一个完整的服务。当上游服务进行查询时,同时查询四台机器,并对返回结果作合并。架构
在使用水平拆分的方案时,须要重点考虑如下问题:并发
索引服务负载均衡
如前几篇文章所述,任何大数据量系统中,在启动以前都须要加载索引数据。索引数据通常是预先计算好的,而且以二进制格式持久化的文件。由于服务进行了拆分,每一台机器只须要加载一部分数据,所以须要为每一个分组的机器单独计算索引数据,这样减小了系统启动时处理的数据量,加快启动速度。运维
数据更新高并发
一样,因为每台机器只须要加载一部分数据,那么也只须要处理这部分数据的更新。目前主流的更新数据流都是使用 Mesage Queue 做为传输和持久化系统个,在服务端接收 Message Queue 的数据并持久化到本地,供在线服务按期读取。通常同一类的数据使用一个 Topic 传输,同时 Message Queue 通常都支持 Partition 的机制。即在向 MQ 中发送一条数据时,能够指定将该条数据发送到哪一个 Partition;在从 MQ 中读取数据时,能够指定只读取哪些 Partition 的数据。例如上文的例子,存储商品数据的服务器分了四个组,所以能够将传输商品更新数据的 Topic 划分为四个 Partition,每一个分组的机器只须要订阅其须要的 Partition 便可。在实际操做中,为了保持将来的扩展性,通常 Partition 的数量都会设置为分组数量的若干倍,例如八个或者十六个,这样在将来数据量进一步增加致使分组个数进一步增长时,不须要修改 MQ 的 Partition 配置。性能
利用 MQ 这个机制,可使每台机器只订阅本身须要处理的数据,减小带宽,也减小更新时处理的数据量,避免浪费资源。
服务管理的复杂性
在咱们管理上下游机器时,通常会使用以 ZooKeeper 为核心的服务管理系统。即每一个服务都注册在 ZooKeeper 中,当上游服务须要访问下游服务时,去 ZooKeeper 中查询可用的下游服务列表,并同时考虑负载均衡等因素,选择最合适的一个下游服务实例。
当一个服务出现分组时,管理的难度会增大。服务管理系统须要确保一个服务的每一个分组的实例一样多,而且负载基本保持平衡。另外,当任何一台机器出现 故障致使的宕时,须要启动备用机器。这时,须要判断是哪一个分组的机器发生了故障,并启动相关分组的机器实例,从新注册到 ZK 中。
没法拆分的数据
有不少数据是没法拆分的。一方面有些数据是自然不可拆分的,例如各类策略使用的词典;另外一方面,有些数据即便能够拆分,但和系统中其余数据的拆分规则不一样,那么系统也没法保证全部数据都能被拆分,只能优先拆分主要数据。
在传统关系型数据库的设计上,垂直拆分是指将一种数据的不一样列进行拆分;在对系统架构的设计上,垂直拆分是只将一个服务的不一样计算逻辑拆分为多个服务。在使用垂直拆分的方案时,须要重点考虑如下问题:
增长网络请求次数,增长系统响应时间
若是是对响应时间要求很高的系统,必定会尽量地避免垂直拆分,例如搜索。而有一些对逻辑确实很复杂,对时间又不太敏感的系统,通常都会优先选择垂直拆分,例如支付。
增长系统复杂度
将服务进行了分层,更加了开发成本,对运维的要求也更高。
数据冗余
有一些数据会被拆分过的多个服务使用,会出如今上下游多个服务中,那么数据的分发、更新都会更加复杂,即浪费资源,又进一步增长了系统的复杂度。所以,在垂直拆分的过程当中,必定要尽量将服务的功能作良好的划分,避免一种数据被多个服务使用的状况。
垂直拆分的方案中,有一种状况能够大幅减小机器数量,即:一部分数据的存在并非在处理请求的时候被直接使用,其存在是为了维护被处理请求的逻辑直接使用的数据。
一个典型的例子是检索服务中的正排索引。检索服务在查询时,直接使用的是倒排索引,而倒排索引是根据正排索引生成的。正排索引每每有多种数据,当一条数据发生更新时,会影响其余类别的数据。所以,一条数据的更新信息没法被单独处理,在系统的内存中每每同时维护正排索引和倒排索引,致使内存翻倍。这种状况下,若是咱们把正排索引独立到一台离线机器中,这台机器维护正排索引的所有数据,当正排索引起生更新时,倒排索引的更新信息,并分发给全部在线机器。那么,在线服务就不须要维护正排索引,可以大幅度减小内存的使用。
实际状况中,大型系统每每同时使用水平拆分和垂直拆分两种方案。一方面,水平拆分虽然服务内部进行了分组,但对外仍然是单一的服务,所以从业务逻辑上来说更加简单。另外一方面,垂直拆分能够将很是复杂、计算资源有不一样需求的业务逻辑进行很好的隔离,方便系统中各业务逻辑能够针对本身的特色进行开发和部署。所以,在选择拆分方案时,要结合系统的主要矛盾以及目前团队成员的技术特色,综合考虑作出选择。
俗话说,当上帝为你关上了一扇门,必(可)定(能)为你打开了一扇窗。若是说大数据是上帝为架构师关上的一扇门,那么热点数据就是打开的那扇窗。虽然在现实世界中的数据是海量难以估算的,但幸运的是,有价值或者说值得关注的数据老是少数的。在大型系统中,请永远把二八法则的重要性放在第一位。
通常来讲,计算机的存储系统分为三级:CPU Cache,内存,磁盘。这三者的访问速度依次下降(而且是数量级的下降),单位存储的成本也依次下降(也是数量级的下降)。多级存储的基本思想是,按照被访问频率的不一样给数据分类,访问频率越高的数据应当放在访问速度越快的存储介质中。
三种系统都使用页式存储的结构,页也是其处理数据的最小单位。因为这个特性,咱们通常在编写程序时,尽量地将连续访问的数据放在内存的相邻位置,以提升CPU Cache的命中率,也就是常说的 locality principle。
随着SSD的出现,对磁盘的使用已经出现了新的方法论。机械磁盘的随机读写速度在10ms左右,不太可能供实时系统使用。而SSD磁盘的随机读写速度在100us左右,对于有些秒级响应的系统来讲,已经能够做为实时系统的存储介质。一种典型的状况是系统存在至关数量的冷门数据。系统对于热点数据能够快速地反馈,对于不多被访问的冷门数据能够存储在SSD磁盘中。当冷门数据被访问时,只要latency仍然能够控制在秒级,就能够在保证用户体验只有不多的损害的状况下,大幅减小系统成本。
一种典型的场景是电商的商品信息。常常被访问的商品可能不到商品总量的1%。像淘宝这样规模的电商系统,实际可能比1%还低。
另外一种典型的场景是用户评论。不管按评论发表的前后顺序,仍是按某种规则计算出的评论的质量度排序,老是前100个左右的评论被常常访问,后面的评论几乎不会被访问到。
另外,回想上文提到的检索服务的案例。正排索引除了能够拆分为单独的服务以外,还能够存储在磁盘中。更新正排索引的时候直接从磁盘读取数据,修改后写会磁盘,同时更新内存的倒排索引。若是使用SSD磁盘,虽然更新的延迟会增加,但也会控制在毫秒级,对于系统彻底是能够接受的。要知道,在一条数据到达检索服务以前,都会通过若干次网络传输,由磁盘引发的延迟并非主要因素。
在使用磁盘做为能够提供实时查询功能的存储介质时,很常见的方案是将磁盘做为二级缓存,将最近访问的数据保存在内存中,当访问的数据不在内存中时,从磁盘读取,并放入内存中。这个方案的假设是,最近被访问的数据极可能在接下来仍然被访问。采用这种方案须要重点注意,防止爬虫或者外部的恶意请求短时间内访问大量冷门数据,形成实际的热点数据被换出缓存,致使处理真实请求时有大量的缓存失效。
大数据技术对商业效果的提高已经在愈来愈多的行业中被证实,将来的服务,不管是在线仍是离线,处理的数据都会有数量级甚至几个数量级的增加。同时,咱们看到内存除了访问速度愈来愈快,在存储的数据量和成本上并无太大的变化。所以,将来愈来愈多的系统的主要瓶颈会从计算、IO转移到数据量上,内存密集型系统会变得愈来愈重要,相信其架构在将来几年也会有不少新的方式出现。