1、加快单机的速度,例如使用Redis,提高数据访问频率;增加CPU的内核数,增大内存;
2、增加服务器的数量,利用集群。
应用本身没有状态,状态全部通过配置文件或者集群的服务端提供并与之同步。比如不同的机房需要读取不同的数据源,那么他们直接通过配置文件或者中心来指定。
进一步,在分布式集群中,如果数据请求的节点可以做到没有状态,意味着任意节点都可以被请求,结合去中心化,就能有效避免单节点的访问瓶颈。
系统设计初期,不要按功能模块来进行拆分,只需要尽快保证每个开发的单元能独立运行,即按照多进程模式可以运行。例如搜索引擎的业务会分为地理信息、类别、文本等多个搜索,在初期所有搜索可以继承开发在一个http请求的响应处理模块里,初期就按照业务进行拆分,会带来极大的工作量,各个模块能独立并行运行即可。
系统拆分原则:
功能及业务:例如搜索模块、地图模块、用户信息模块
子功能拆分:例如搜索模块里,图数据库与文本搜索可以分开
读写分离:根据读写的特性在进行拆分,例如淘宝的商品浏览与编辑,编辑是一个纯写的功能,流量是一个纯读的功能,因此可以对读写的实现进行拆分,一个集群实现商品读取的功能,而另一个规模小的多的集群提供商品写入的功能。
代码模块:一般情况,与上图类似,一个系统可以按照Web、Service及DAO来划分,有专门负责Web请求的,有提供进一步数据服务的模块,以及专门的数据库HA管理模块。
如果单机不行,需要考虑使用集群,使用集群就意味着一个负载均衡服务提供在最前端。
负载均衡算法:
(1)轮询:以轮询的方式把请求发到上游服务器,配合weight配置实现基于权重的轮询
(2)IP hash:根据客户的IP做负载均衡,使得胸痛IP均衡到同一个upstream server。
(3)hash key:对客户端的一个key进行hash,建议使用一致性Hash实现负载均衡。普通Hash算法,当添加或删除服务器的时候,很多key都会被重新分配到不同的服务器,使用一致性hash,使得只有极少数的key会被重新分配服务器。
(4)least connection:把最新的请求负载均衡到活跃连接最少的服务器,依然可以与权重相结合。
一致性hash:(如下左图)
一致哈希将每个对象映射到圆环边上的一个点,系统再将可用的节点机器映射到圆环的不同位置。查找某个对象对应的机器时,需要用一致哈希算法计算得到对象对应圆环边上位置,沿着圆环边上查找直到遇到某个节点机器这台机器即为对象应该保存的位置。当删除一台节点机器时,这台机器保存的所有对象都要移动到下一台机器。添加一台机器到圆环边上的某个点时,这个点的下一台机器需要将这个节点前对应的对象移动到新机器上。更改对象在节点机器上的分布可以通过调整节点机器的位置来实现。
一致性hash的平衡性:(如上右图)
通过增加虚拟节点,使得节点的分布及hash算法能实现平衡。
当服务器集群设计为主从架构的时候,意味着需要有多个主服务器提供备份和容灾,这样就要考虑使用服务的自动注册和发现,比如使用ZooKeeper。
消息队列:
消息队列用来解耦一些不需要同步调用的服务或者订阅一些通知以及系统的变化。使用消息队列可以实现服务解耦、异步处理、流量缓冲等。案例:
我们可以通过databus来实现MySQL数据与redis的更新操作,把MySQL的数据变化同步到redis。
把一些联合查询的数据,直接组合成一张结果表,这样在读取的时候能更快速地取回结果。通常这样的表会比较大,因此需要按照主键做分库分表来增加读取的性能,带来的问题:
(1)数据同步
(2)某一项服务的终止导致整体服务的终止
(3)聚合查询
数据异构的实现:
(1)通过MQ机制接收数据变更,然后原子化存储
(2)在任务队列里,聚合数据源并更新
(3)聚合的数据,根据类型、实例进行分割,分割的数据还可以考虑分片
缓存不仅仅存在于服务端,它可以被用在从最前端的任意节点,只要缓存被命中,对应的数据查询就可以得到量级的提升。
(1)客户端缓存:浏览器的cookie、storage,APP的本地数据存储
(2)网络层:CDN、镜像服务器、P2P技术等
(3)服务前端:接入层的缓存,应用层缓存
(4)分布式缓存:利用redis数据库实现分布式的缓存系统
(5)数据库缓存:当所有结果都没命中的情况下,可以依赖数据库的缓存提高检索排序效率
1.保护资源,以防被其他资源拖垮
2.把不同的数据资源隔开
当海量请求发送到前端的时候,单机的故障率会极大提高,此时一旦一台服务器出故障停止响应,前端反向代理会把请求发送到集群的其他节点,导致整体负载的进一步提升,使得所有的服务请求都被拖累,使得服务器节点如雪崩一样纷纷过载从而导致服务不够用。
因此需要考虑从以下几个维度对访问进行隔离:
(1)资源的隔离:比如js、CSS这类访问量极大的文件放在CDN上
(2)热点的隔离:一些高频的请求和低频的请求分开
(3)读写分离:分布式系统总必用的一点(保证系统高可用很重要的一点)
MySQL中的存储引擎InnoDB:行级锁,事务(原子性),写快
MySQL中的存储引擎myisam:标记锁,纯读性能高
对于NoSQL的数据库,往往天然支持分布式部署,例如HBASE、MongoDB,因此直接可以用集群,通过分片及复制来提高响应能力
对于RDBMS,不能进行分片,所以当容量增长的时候,需要考虑分表,把旧的内容独立出来,新建一张表存储;当业务增长很快,可以进一步考虑把旧的历史数据存储到单独的服务器上的一个数据库,由应用层根据请求来判断到那个库上读取数据,这样能使得热点数据的总量以及总访问量是一定的
分库分表的策略:
(1)取模:按照主键取模后做分库分表,缺点是按照非主键查询的时候需要跨库跨表的查询,扩容需要建立新的集群并进行数据迁移
(2)分区:例如2B的系统,每个大的客户一张表,每个客户下的分店可以考虑分表;根据时间或地区,进行表或数据库的切割
HTTP的请求全部发送到任务队列中,当队列满的时候,直接返回服务器超时;队列的大小按照并发处理能力来设置,使得进入队列的请求都能在一定时间内响应
降级是指在服务并发突然增大的时候,启动限制性措施,降低服务提供的能力从而保护系统性能。降级一般是在系统的接口之间,设置降级开关,在特定的时候启用。例如前面讲到为限流而做的任务缓冲队列,可以通过降级来启动或停用:平峰或低估期请求通过代理层直达后端;高峰期通过降级,请求都进入到队列,由worker从队列获取数据来请求。
降级措施还包括针对服务级别的,例如在高峰情况下只读缓存,缓存失效的情况下直接返回失败,使得热点的访问能被响应,而非热点数据请求被拒绝;微博打开个人主页的时候,还回出现热点推荐、广告等,如果出现流量暴涨,可以关闭这些功能的服务接口。
降级的启动方式:
(1)超时:数据库、HTTP服务或者远程调用超时后,可以自动触发降级。例如淘宝的商品主页会异步加载价格、评论、推荐,而抢购活动时,评论和推荐都可以临时不展示,通过超时来自动降级
(2)统计失败次数:调用外部服务,例如数据来源于图数据库的情况,失败次数达到一定次数的时候自动降级
(3)故障:远程服务出错,可以直接降级并报警
(4)限流:流量监控,超过峰值后自动开启降级,把所有请求排队
自动化限流降级: