应用服务器性能优化 (分布式缓存)
应用服务器就是处理网站业务的服务器,网站的业务代码都部署在这里,是网站开发最复杂,变化最多的地方,优化手段主要有缓存、集群、异步等。
1. 分布式缓存
当网站遇到性能瓶颈时,第一个想到的解决方案就是使用缓存。在整个网站应用中,缓存几乎无所不在,既存在于浏览器,也存在于应用服务器和数据库服务器;既可以对数据缓存,也可以对文件进行缓存,还可以对页面片段缓存。合理使用缓存,对网站性能优化意义重大。
------------------------------------------------------------------------------------------------------
网站性能优化第一定律: 优先考虑使用缓存优化性能
------------------------------------------------------------------------------------------------------
1.1 缓存的基本原理
缓存指将数据存储在相对较高访问速度的存储介质中,以供系统处理。一方面缓存访问速度快,可以减少数据访问的时间,另一方面如果缓存的数据是经过计算处理得到的,那么被缓存的数据无需重复计算即可直接使用,因此缓存还起到减少计算时间的作用。
缓存的本质就是一个内存Hash表,网站应用中,数据缓存以一对Key、Value的形式存储在内存Hash表中。Hash表数据读写的时间复杂度O(1)。
计算KV对中Key的hashCode对应的Hash表索引,可快速访问Hash表中的数据。许多语言支持获取任意对象的hashCode,可以把HashCode理解为对象的唯一标志符,Java语言中HashCode方法包含在根对象Object中,其返回值是一个Int。然后通过HashCode计算Hash表的索引下标,最简单的是余数法,使用Hash表数组长度对HashCode求余,余数即为Hash表索引,使用该索引可直接访问得到Hash表中存储的KV对。Hash表是软件开发中常用到的一种数据结构,其设计思想在很多场景下都可以应用。
缓存主要用来存放哪些读写比较高、很少变化的数据,如商品的类目信息,热门词的搜索列表信息,热门商品信息等。应用程序读取数据时,先到缓存中读取,如果读取不到或数据已失效,再访问数据库,并将数据写入缓存。
1.2 合理使用缓存
- 频繁修改的数据
- 没有热点的访问
- 数据不一致与脏读
- 缓存可用性
- 缓存预热
缓存中存放的是热点数据,热点数据又是缓存系统利用LRU(最近最久未用算法)对不断访问的数据筛选淘汰,这个过程需要花费较长时间。
- 缓存穿透
如果因为不恰当的业务、或者恶意攻击持续高并发的请求某个不存在的数据,由于缓存没有保存该数据,所有请求落在数据库上,会对数据造成很大压力,甚至崩溃。一个简单的对策是将不存在的数据也缓存起来(其value值为null)。
1.3 分布式缓存架构
分布式缓存指缓存部署在多个服务器组成的集群中,以集群的方式提供缓存服务,其架构方式有两种,一种是以JBoss Cache为代表的需要更新同步的分布式缓存,一种是以Memcached为代表的不互相通信的分布式缓存。
JBoss Cache的分布式缓存在集群中所有服务器中保存相同的缓存数据,当某台服务器有缓存数据更新时,会通知其他机器更新缓存数据或清除缓存数据。JBoss Cache通常将应用程序和缓存部署在同一台服务器上,应用程序可从本地快速获取缓存数据,但是这种方式带来的问题是缓存数据的数量受限于单一服务器的内存空间。当集群规模较大是,缓存同步带来的代价惊人。
1.4 Memcached
Memcached 曾一度是网站分布式缓存的代名词,被大量网站使用。其简单的设计、优异的性能、互不通信的服务器集群、海量数据可伸缩的架构令网站架构师趋之若鹜。
简单的通信协议
远程通信设计需要考虑两方面的要素,一是通信协议,即选择TCP协议还是UDP协议,抑或HTTP协议;一是通信序列化协议,数据传输的两端,必须使用彼此可是别的数据序列化才能使通信得以完成,如XML、JSON等文本序列化协议,或者Google Protobuffer 等二进制序列化协议。Memcached使用TCP协议(UDP也支持)通信,其序列化协议则是一套基于文本的自定义协议,以一个命令关键字开头,后面是一组命令操作数。例如读取数据 get<key>。Memcached以后,许多NoSql产品都借鉴获支持这套协议。
丰富的客户端程序
Memcached通信协议非常简单,只要支持该协议的客户端都可以和Memcached服务通信,java、C/C++/C#、Perl、Python等。
高性能的网络通信
Memcached服务端通信模块基于Libevent,一个支持时间触发的网络通信程序库。Libevent的设计和实现有许多值得改善的地方,但它在稳定的长连接方面的表现确实Memcached需要的。
高效的内存管理
内存管理中令人头痛的问题就是内存碎片管理。Memcached使用了一个非常简单的办法--固定空间分配。Memcached将空间分为一组slab,每个slab离又包含一组chunk,同一个slab里的每个chunk的大小是固定的,拥有同样大小chunk的slab被组织在一起,叫做slab_class。存储数据时根据数据size大小,寻找一个大于size的最小chunk将数据写入。
互不通信的服务器集群架构
正是这个特性使得Memcached从JBoss cache、OSCache等众多分布式缓存产品中脱颖而出,满足网站对海量数据缓存的需求。