`
sungyang
  • 浏览: 20090 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

可伸缩性、可扩展性、缓存可扩展性

阅读更多

注:以下内容摘自:http://www.jianshu.com/p/bde93f9d1469

 

一、什么是可伸缩性

可伸缩性是一种对软件系统计算处理能力的设计指标,高可伸缩性代表一种弹性,在系统扩展成长的过程中,软件能够保证旺盛的生命力,通过很少的改动甚至只是硬件设置的添置,就能实现整个系统能力的线性增长,实现高吞吐量和低延迟性能。

 

二、可伸缩性和纯粹性能调优是有区别的:

可伸缩性是高性能、低成本和可维护性等诸多因素的综合考量和平衡,可伸缩性讲究平滑线性的性能提升,更侧重于系统的水平伸缩,通过廉价的服务器实现分布式计算;

而普通性能优化只是单台机器的性能指标优化。他们共同点都是根据应用系统特点在吞吐量和延迟之间进行一个侧重选择,当水平伸缩分区后会带来CAP定理约束

 

三、软件的可扩展性设计注意

软件的可扩展性设计非常重要,但又比较难以掌握,业界试图通过云计算或高并发语言等方式节省开发者精力,但是,无论采用什么技术,如果应用系统内部是铁板一块,例如 严重依赖数据库, 系统达到一定访问规模,负载都集中到一两台数据库服务器上,这是进行分区扩展伸缩就比较困难。

关系数据库是最不可扩展的。

 

四、性能和扩展性

什么是性能问题?如果你的系统对于一个用户访问还很慢,那就是性能问题。

什么是扩展性问题?如果你的系统对于一个用户来说很快的,但是在用户不断增长的高访问量下就慢了。

 

五、可扩展性的目的

延迟和吞吐量是衡量可扩展性的一对指标,我们希望获得低延迟和高吞吐量的系统架构。所谓低延迟,也就是用户能感受到的系统响应时间,比如一个网页在几秒内打开,越短表示延迟越低,而吞吐量表示同时有多少用户能够享受到这种低延迟,如果并发用户量很大时,用户感觉网页的打开速度很慢,这意味着系统架构的吞吐量有待提高。

扩展性的目标是用可接受的延迟获得最大的吞吐量。可靠性(可用性)目标:用可接受的延迟获得数据更新的一致性

 

六、缓存的可扩展性

缓存层的伸缩性,最简单粗暴的方式是什么呢?

趁着半夜量比较低的时候,把整个缓存层全部下线,然后上线新的缓存层。新的缓存层启动起来之后,再等这些缓存慢慢预热。当然这里一个要求,你的数据库能抗住低估期的请求量。如果扛不住呢?取决于缓存类型,下面我们先可以将缓存的类型区分一下。

强一致性缓存:无法接受从缓存拿到错误的数据 (比如用户余额,或者会被下游继续缓存这种情形)

弱一致性缓存:能接受在一段时间内从缓存拿到错误的数据 (比如微博的转发数)。

不变型缓存:缓存key对应的value不会变更 (比如从SHA1推出来的密码, 或者其他复杂公式的计算结果)

 

那什么缓存类型伸缩性比较好呢?

弱一致性和不变型缓存的扩容很方便,用一致性Hash即可;

使用一致性Hash,而不用简单Hash的原因是缓存的失效率。如果缓存从9台扩容到10台,简单Hash 情况下90%的缓存会马上失效,而如果使用一致性Hash情况,只有10%的缓存会失效。

强一致性缓存会有什么问题?

第一个问题是,缓存客户端的配置更新时间会有微小的差异,在这个时间窗内有可能会拿到过期的数据。

第二个问题是,如果扩容之后再裁撤节点,会拿到脏数据。比如 a 这个key之前在机器1,扩容后在机器2,数据更新了,但裁撤节点后key回到机器1,这时候就会拿到脏数据。

要解决问题二比较简单,要么保持永不减少节点,要么节点调整间隔大于数据的有效时间。

问题一可以用如下的步骤来解决:

1、两套hash配置都更新到客户端,但仍然使用旧配置;

2、逐个客户端改为只有两台Hash结果一致的情况下会适用缓存,其余情况从数据库读,但写入缓存;

3、逐个客户端通知使用新配置;

 

Memcache 设计得比较早,导致在伸缩性高可用方面的考虑得不太周到。Redis 在这方面有不少改进,特别是 @ngaut 团队基于 redis 开发了 codis 这个软件,一次性地解决了缓存层的绝大部分问题。推荐大家考察一下。

 

八、缓存穿透,缓存雪崩

缓存穿透:查询一个必然不存在的数据。比如文字表,查询一个不存在的id,每次都会访问DB,如果有人恶意破坏,很可能直接对DB造成影响。解决办法:对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合直接丢弃

缓存失效:如果缓存在一段时间内失效,DB的压力凸显。这个没有完美的解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。

缓存雪崩:当发生大量的缓存穿透,例如对某个失效的缓存的大并发访问就造成了缓存雪崩。比如前端的Cache挂掉,或者比较极端的整个机房断电了,那么在机器重启后,原来Cache机器在内存中的缓存会全部清空,在客户端访问过程中,会百分之百的不命中,这样数据库会在瞬间接受巨大的读压力。

试想如果一个64GB的缓存失效了,在其重建时,假设与数据库连接的千兆网卡,假设其以极限速度100M每秒从数据库取数据过来重建缓存,那么也需要10分钟才能建完。更何况这是理想情况,对于客户端触发式的随机缓存重建,可能会花掉更长的时间。这还是在数据库能提供100M每秒的数据读请求的前提下。

我们经常看到一些网站挂掉后又恢复,恢复后又挂掉,如此反复几次才能真正恢复,原因就在于其第一次Cache倒了,数据库无法承受相应的读压力,在缓存重建了一小部分后被压死。相当于数据库每重启一次,可以恢复部分缓存,直到缓存的非命中率到达数据库可承受的压力时,才能够真正恢复服务。

 

九、如何解决缓存雪崩

第一种方式

概述: 做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期

oschina首页使用了缓存,假设我们前面举的例子中所使用的缓存region(region1),设置了自动失效时间为5分钟,相等于说每5分钟就会发现首页很卡。因此引入第二个缓存region(region2),这个缓存的对象不会自动失效,也就说该区域的数据长期有效。

引入了第二个长效的region后,数据的读取流程这样的:

从 region1 读取数据,有则直接返回

region1 没数据则启动数据更新线程,然后从 region2 读数据,有则返回

region2 也没有数据

针对第3种,这种情况属于系统刚刚启动,缓存还没有填充数据的情况,没办法,这时候肯定会卡,或者你应该在系统启动的时候,自行填充一下数据,很简单,我一般在tomcat启动后,用命令访问下首页就有了缓存数据。

这样做的目的是为了正常的缓存失效后,无需等待重新从数据库中获取数据,而是直接在 region2 中获取数据并返回。因此对用户来讲,不会感觉请求被堵塞。

就这么简单,其实也可以工作,但会有一个问题:假设缓存失效的时候,同时来了100个请求,那么这100个请求会同时启动100个数据更新线程,这100个数据更新线程会到数据库执行同样的SQL语句获得同样的结果,因此这种做法对数据库的压力并没有降低。

所以缓存失效的情况下,保证有且只有一个线程去更新缓存数据。

 

其他方式

对查询结果为空的情况也进行缓存,缓存时间设置短一些,或者该key对应的数据insert了之后清理缓存。

不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

可以用一些可以提供持久化功能的缓存来实现,比如Redis,在未开启aof的情况下,其定期dump出来的rdb文件出能自动恢复出绝大部分数据,当然,在有的时候这可能导致缓存和数据库数据不一致的情况,需要根据应用场景选择性的使用。

 

十、缓存重建

上面是对分布式Cache的问题,而对于很多数据库存储,实际上也几乎都是将热数据尽量放在内存中的。但很多数据库在实现上是自己在内存中实现了Cache机制,这样在数据库重启(非操作系统重启)时,这些Cache可能也就随之被清空了,对于数据库来说,也需要重建缓存,而数据库这时所有的操作可能都落在磁盘IO上,带来了同样的问题。

而MongoDB与上面的方式不太一样,MongoDB采用mmap来将数据文件映射到内存中,所以当MongoDB重启时,这些映射的内存并不会清掉,因为它们是由操作系统维护的(所以当操作系统重启时,MongoDB才会有相同问题)。相对于其它一些自己维护Cache的数据库,MongoDB在重启后并不需要进行缓存重建与预热。

另外,新浪微博的timyang也曾经提出过一种缓存重建加锁的方式,也能部分解决此问题。简单来说就是缓存重建时,当多个客户端对同一个缓存数据发起请求时,会在客户端采用加锁等待的方式,对同一个Cache的重建需要获取到相应的锁才行,只有一个客户端能拿到锁,并且只有拿到锁的客户端才能访问数据库重建缓存,其它的客户端都需要等待这个拿到锁的客户端重建好缓存后直接读缓存,其结果是对同一个缓存数据,只进行一次数据库重建访问。但是如果访问分散比较严重,还是会瞬间对数据库造成非常大的压力。

下面是几点比较实用的知识:

无论使用哪个存储,都最好先搞清楚其缓存重建的过程,如果一次重启就可能导致数据库崩溃,还是小心为好,最好把重启时间选在访问量比较小的时候

重启MongoDB不会导致MongoDB的缓存失效(除非重启服务器)

当你重新mount磁盘时,文件系统的缓存会失效,这和重启机器时一样,MongoDB也无法避免

一个使用MongoDB的小技巧,当MongoDB服务器刚启动时,你可以将其所有文件copy到/dev/null中,这会触发操作系统对这些文件的读操作,从而在内存允许的条件下,会将尽可能多的MongoDB数据文件映射到物理内存中。当然,如果在MongoDB运行过程中,你能够判断哪些文件保存的数据是热数据,也可以将这些文件copy到/dev/null 来为其争取更多的物理内存。

 

十一、缓存一致性

缓存系统与底层数据的一致性。这点在底层系统是“可读可写”时,写得尤为重要。

有继承关系的缓存之间一致性。为了尽量提高缓存命中率,缓存也是分层:全局缓存,二级缓存。他们是存在继承关系的。全局缓存可以有二级缓存来组成。

多个缓存副本之间的一致性。为了保证系统的高可用,缓存系统背后往往会接两套存储系统(如memcache,redis等)

 

十二、缓存数据的淘汰

定时去清理过期的缓存。

当有用户请求过来时,在判断这个请求所用到的缓存是佛过期,过期的话就去底层得到新数据并更新缓存。

两者各有优劣:

第一种的缺点是维护大量缓存的key是比较麻烦的

第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂,具体哪种方案,大家可以根据自己的应用场景来权衡。

缓存淘汰的几种会用到的技巧

预估失效时间。

版本号(必须单调递增,时间戳是最好的选择)。

提供手动清理缓存的接口。

 

十三、高手说可扩展

衡量架构伸缩性的主要标准就是是否可以用多台服务器构建集群,是否容易向集群中添加新的服务器。加入新的服务器后是否可以提供和原来的服务器无差异的服务。集群中可容纳的总的服务器数量是否有限制。

对于应用服务器集群,只要服务器上不保存数据,所有服务器都是对等的,通过使用合适的负载均衡设备就可以向集群中不断加入服务器。

对于缓存服务器集群,加入新的服务器可能会导致缓存路由失效,进而导致集群中大部分缓存数据都无法访问。虽然缓存的数据可以通过数据库重新加载,但是如果应用已经严重依赖缓存,可能会导致整个网站崩溃。需要改进缓存路由算法保证缓存数据的可访问性。

关系型数据库虽然支持数据复制,主从热备机制,但是很难做到大规模集群的可伸缩性,因此关系数据库的集群伸缩性方案必须在数据库之外实现,通过路由分区等手段将部署有多个数据库的服务器组成一个集群。

至于大部分NoSQL数据库产品,由于其先天就是为海量数据而生,因此其对伸缩性的支持通常都非常好,可以做到在较少运维参与的情况下实现集群规模的线性绳索。

《大型网站技术架构核心原理与案例分析》

 

十四、总结

1. 缓存均匀分布,均匀失效。

2. 二级缓存解决缓存雪崩

3. 缓存很重要,但是数据库的查询优化也很重要

4. 情况允许,扩大MySQL临时表的空间,以防缓存雪崩瞬间巨大访问,导致MySQL崩溃

5. 需要在流量少的情况下,切换缓存,切换完毕之后。需要缓存预热

6. 多看书,多试验

分享到:
评论

相关推荐

    可伸缩性模式

    本书包含:构建和开发应用程序,以便它们易于扩展。 了解不同的缩放和分区选项以及组合。 学习加快响应速度的技巧。 深入研究缓存,列族...有效平衡可扩展性,性能,响应能力和可用性,同时最大限度地减少停机时间。

    论文研究-空间数据存储对象的元数据可伸缩性管理.pdf

    基于空间数据面向对象存储思想和云存储可扩展架构,将控制信息集中在元数据服务器集群中管理,而实际的空间数据基于对象存储分布到存储设备集群中,实现控制信息路径与数据传输路径的分离,并缓存热点空间数据对象...

    system-design-notebook:逐步学习系统设计

    可伸缩性因子 线性可伸缩性 亚线性可扩展性 超线性可伸缩性 负可伸缩性 可用性 表现 弹性 微服务架构 整体建筑 快取 分布式缓存 缓存策略(aka替换策略) LRU(最近最少使用) 负载均衡 一致的散列 技巧 循环赛 ...

    网站架构技术

    永无止境:网站的可伸缩性 网站架构的伸缩性设计 不同功能进行物理分离实现伸缩 单一功能通过集群规模实现伸缩 应用服务器集群的伸缩性设计 http重定向负载均衡 DNS域名解析负载均衡 反向代理负载均衡 ...

    通用asp.net mvc5 Easyui开发框架源码

    Rookey.Frame是一套基于.NET的极速开发框架,支持简单逻辑模块零代码编程、支持二次开发,具有高扩展性、高复用性、高伸缩性、高性能 2.框架特点: (1)简单逻辑模块实现零代码编程,通过简单配置即可实现增、删、...

    RocketMQ搭建文档

    事件驱动架构:RocketMQ可以用来将事件异步传输到订阅者,从而实现事件驱动架构,降低系统的耦合度,提高系统的可扩展性和可维护性。 流式计算:RocketMQ可以用来传输实时流式数据,如日志、监控数据、交易数据等,...

    Java并发编程实践 PDF 高清版

    Java 5以及6在开发并发程序取得了显著的进步,提高了Java虚拟机的性能,提高了并发类的可伸缩性,并加入了丰富的新并发构建块。在本书中,这些便利工具的创造者不仅解释了它们究竟如何工作、如何使用,同时,还阐释...

    当前一种先进实用的架构设计

    对用JAVA开发的项目来说,根据“成熟稳定、先进科学、实用可靠“的原则,可以使用这样一种架构,采用多个集群来保证系统的高性能、高可靠性、伸缩性、可维护性和安全的需要,服务器可以线性扩展,使用开源免费软件和...

    J2EE系统设计方案(1).doc

    J2EE体系结构提供中间层集成框架用来满足无需太多费用而又需要高可用性、高可靠性 以及可扩展性的应用的需求。通过提供统一的开发平台,J2EE降低了开发多层应用的费 用和复杂性,同时提供对现有应用程序集成强有力...

    J2EE系统设计方案.doc

    J2EE体系结构提供中间层集成框架用来满足无需太多费用而又需要高可用性、高可靠性 以及可扩展性的应用的需求。通过提供统一的开发平台,J2EE降低了开发多层应用的费 用和复杂性,同时提供对现有应用程序集成强有力...

    oracle biee 11g新功能介绍PPT 中文版本

    经过证明的可伸缩性和灵活性 针对伸缩和性能而构建 优化的原生 SQL 功能传送 并行处理 集群 智能缓存 连接池 支持广泛的数据源 直接支持物理数据源 充分利用现有的 IT 投资 与异构系统的“可热插拔”集成 Oracle...

    中国工商网电子商务购物中心系统EMall v1.0 (全源码)

    易于安装的系统和应用功能 100%的asp.net的代码,没有COM,java或者其他的格式 完全基于MS建议的系统安全设计 最佳的应用程序,数据库和安全设定 MS建议的最佳用户密码管理设定 极强的系统性能与伸缩性 大量应用缓存以...

    中国工商网电子商务购物中心系统EMall v1.0

    没有COM,java或者其他的格式完全基于MS建议的系统安全设计最佳的应用程序,数据库和安全设定MS建议的最佳用户密码管理设定极强的系统性能与伸缩性大量应用缓存以提升系统的性能和浏览速度方便的功能可伸缩设计

    LKImageKit:一种高性能的图像框架,包括一系列功能,例如图像视图,图像下载器,内存缓存,磁盘缓存,图像解码器和图像处理器

    该框架具有高度的可伸缩性。 在此框架中,开发人员可以自定义相框的任何部分,例如:自定义图片显示逻辑,自定义缓存,自定义下载器,自定义解码器,自定义图像处理算法等。 视频 github的: : youtube: : 主要...

    JAVA并发编程实践_中文版(1-16章全)_1/4

    第11章 性能和可伸缩性 第12章 测试并发程序 第4部分 高级主题 第13章 显示锁 第14章 构建自定义的同步工具 第15章 原子变量与非阻塞同步机制 第16章 java存储模型 附录a 同步annotation 参考文献 索引

    Java并发编程part2

    中文完整版的Java并发编程实践PDF电子书 ...第11章 性能和可伸缩性 第12章 测试并发程序 第4部分 高级主题 第13章 显示锁 第14章 构建自定义的同步工具 第15章 原子变量与非阻塞同步机制 第16章 java存储模型

    Java并发编程实践part1

    中文完整版的Java并发编程实践PDF电子书 ...第11章 性能和可伸缩性 第12章 测试并发程序 第4部分 高级主题 第13章 显示锁 第14章 构建自定义的同步工具 第15章 原子变量与非阻塞同步机制 第16章 java存储模型

    menu_module:CFourCourse的餐厅菜单后端微服务-负责从数据库缓存中获取单个餐厅菜单并将其发送给客户端

    由于Cassandra具有高可用性和接近线性的可伸缩性,因此被选为NoSQL DBMS候选对象。 Postgres和Cassandra查询作为已准备好的语句执行。 Cassandra启用了行缓存,并将“复制因子”设置为3。两个数据库都植入了1000万...

    基础电子中的关于服务器性能优化的十条规则

    下面的每一条戒律都将有效地影响代码的性能和可伸缩性。换句话说,尽可能不要照着戒律去做!下面,我将解释如何破坏他们以便提高性能和可伸缩性。  1、应该分配和释放多个对象  你应该尽量避免过量分配内存,因为...

Global site tag (gtag.js) - Google Analytics