写点什么

8 应用服务器性能优化

  • 2022 年 4 月 17 日
  • 本文字数:3900 字

    阅读完需:约 13 分钟

如果缓存中保存的是频繁修改的数据,就会出现数据写入缓存后,应用还来不及读 取缓存,数据就已失效的情形,徒增系统负担。一般说来,数据的读写比在 2:1 以上,即 写入一次缓存,在数据更新前至少读取两次,缓存才有意义。实践中,这个读写比通常 非常高,比如新浪微博的热门微博,缓存以后可能会被读取数百万次。


没有热点的访问


缓存使 Java 开源项目【ali1024.coding.net/public/P7/Java/git】 用内存作为存储,内存资源宝贵而有限,不可能将所有数据都缓存起来,只 能将最新访问的数据缓存起来,而将历史数据清理出缓存。如果应用系统访问数据没有 热点,不遵循二八定律,即大部分数据访问并没有集中在小部分数据上,那么缓存就没 有意义,因为大部分数据还没有被再次访问就已经被挤出缓存了。


数据不一致与脏读


般会对缓存的数据设置失效时间,一旦超过失效时间,就要从数据库中重新加载。


因此应用要容忍一定时间的数据不一致,如卖家已经编辑了商品属性,但是需要过一段 时间才能被买家看到。在互联网应用中,这种延迟通常是可以接受的,但是具体应用仍 需慎重对待。还有一种策略是数据更新时立即更新缓存,不过这也会带来更多系统开销 和事务一致性的问题。


缓存可用性


缓存是为提高数据读取性能的,缓存数据丢失或者缓存不可用不会影响到应用程序 的处理——它可以从数据库直接获取数据。但是随着业务的发展,缓存会承担大部分数 据访问的压力,数据库已经习惯了有缓存的日子,所以当缓存服务崩溃时,数据库会因 为完全不能承受如此大的压力而宕机,进而导致整个网站不可用。这种情况被称作缓存 雪崩,发生这种故障,甚至不能简单地重启缓存服务器和数据库服务器来恢复网站访问。


实践中,有的网站通过缓存热备等手段提高缓存可用性:当某台缓存服务器宕机时, 将缓存访问切换到热备服务器上。但是这种设计显然有违缓存的初衷,缓存根本就不应 该被当做一个可靠的数据源来使用。


通过分布式缓存服务器集群,将缓存数据分布到集群多台服务器上可在一定程度上 改善缓存的可用性。当一台缓存服务器宕机的时候,只有部分缓存数据丢失,重新从数 据库加载这部分数据不会对数据库产生很大影响。


产品在设计之初就需要一个明确的定位:什么是产品要实现的功能,什么 不是产品提供的特性。在产品漫长的生命周期中,会有形形色色的困难和诱惑 来改变产品的发展方向,左右摇摆、什么都想做的产品,最后有可能成为一个 失去生命力的四不像。


缓存预热


缓存中存放的是热点数据,热点数据又是缓存系统利用 LRU (最近最久未用算法) 对不断访问的数据筛选淘汰出来的,这个过程需要花费较长的时间。新启动的缓存系统 如果没有任何数据,在重建缓存数据的过程中,系统的性能和数据库负载都不太好,那 么最好在缓存系统启动时就把热点数据加载好,这个缓存预加载手段叫作缓存预热(warmUp )o 对于一些元数据如城市地名列表、类目信息,可以在启动时加载数据库中全部数据 到缓存进行预热。


缓存穿透


如果因为不恰当的业务、或者恶意攻击持续高并发地请求某个不存在的数据,由于 缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成很大压力,甚至 崩溃。一个简单的对策是将不存在的数据也缓存起来(其 value 值为 null)。


  1. 分布式缓存架构


分布式缓存指缓存部署在多个服务器组成的集群中,以集群方式提供缓存服务,其 架构方式有两种,一种是以 JBoss Cache 为代表的需要更新同步的分布式缓存,一种是以 Memcached 为代表的不互相通信的分布式缓存。


JBoss Cache 的分布式缓存在集群中所有服务器中保存相同的缓存数据,当某台服务 器有缓存数据更新的时候,会通知集群中其他机器更新缓存数据或清除缓存数据,如 图 4.9 所示。JBoss Cache 通常将应用程序和缓存部署在同一台服务器上,应用程序可从本地快速获取缓存数据,但是这种方式带来的问题是缓存数据的数量受限于单一服务器 的内存空间,而且当集群规模较大的时候,缓存更新信息需要同步到集群所有机器,其 代价惊人。因而这种方案更多见于企业应用系统中,而很少在大型网站使用。



大型网站需要缓存的数据量一般都很庞大,可能会需要数 TB 的内存做缓存,这时候


就需要另一种分布式缓存,如图 4.10 所不。Memcached 采用一种集中式的缓存集群管理, 也被称作互不通信的分布式架构方式。缓存与应用分离部署,缓存系统部署在一组专门 的服务器上,应用程序通过一致性 Hash 等路由算法选择缓存服务器远程访问缓存数据, 缓存服务器之间不通信,缓存集群的规模可以很容易地实现扩容,具有良好的可伸缩性。


  1. Memcached


Memcached 曾一度是网站分布式缓存的代名词,被大量网站使用。其简单的设计、优异的性能、互不通信的服务器集群、海量数据可伸缩的架构令网站架构师们趋之若鹜。


![在这里插入图片描述](https://img-blog.csdnimg.cn/202105081416125.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 9ibG9nLmNzZG4ubmV0L1dBTlRBV0FZMzE0,size_16,color_FFFFFF,t_70)


简单的通信协议


远程通信设计需要考虑两方面的要素,一是通信协议,即选择 TCP 协议还是 UDP 协 议,抑或 HTTP 协议;一是通信序列化协议,数据传输的两端,必须使用彼此可识别的 数据序列化方式才能使通信得以完成,如 XML、JSON 等文本序列化协议,或者 Google Protobuffer 等二进制序列化协议。Memcached 使用 TCP 协议(UDP 也支持)通信,其序 列化协议则是一套基于文本的自定义协议,非常简单,以一个命令关键字开头,后面是 一组命令操作数。例如读取一个数据的命令协议是 get o Memcached 以后,许多 NoSQL 产品都借鉴了或直接支持这套协议。


丰富的客户端程序


Memcached 通信协议非常简单,只要支持该协议的客户端都可以和 Memcached 服务 器通信,因此 Memcached 发展出非常丰富的客户端程序,几乎支持所有主流的网站编程 语言,Java、C/C++/C#、Perl. Python. PHP、Ruby 等,因此在混合使用多种编程语言的 网站,Memcached 更是如鱼得水。


高性能的网络通信


Memcached 服务端通信模块基于 Libevent, 一个支持事件触发的网络通信程序库。


Libevent 的设计和实现有许多值得改善的地方,但它在稳定的长连接方面的表现却正是


Memcached 需要的。


高效的内存管理


内存管理中一个令人头痛的问题就是内存碎片管理。操作系统、虚拟机垃圾回收在 这方面想了许多办法:压缩、复制等。Memcached 使用了一个非常简单的办法——固定 空间分配。Memcached 将内存空间分为一组 slab,每个 slab 里又包含一组 chunk,同一个 slab 里的每个 chunk 的大小是固定的,拥有相同大小 chunk 的 slab 被组织在一起,叫作 slab class,如图 4.11 所示。存储数据时根据数据的 Size 大小,寻找一个大于 Size 的最小 chunk 将数据写入。这种内存管理方式避免了内存碎片管理的问题,内存的分配和释放都是以 chunk 为单位的。和其他缓存一样,Memcached 采用 LRU 算法释放最近最久未被访 问的数据占用的空间,释放的 chunk 被标记为未用,等待下一个合适大小数据的写入。


当然这种方式也会带来内存浪费的问题。数据只能存入一个比它大的 chunk 里,而一


个 chunk 只能存一个数据,其他空间被浪费了。如果启动参数配置不合理,浪费会更加惊


人,发现没有缓存多少数据,内存空间就用尽了。


互不通信的服务器集群架构


如上所述,正是这个特性使得 Memcached 从 JBossCache、OSCache 等众多分布式缓存产品中脱颖而出,满足网站对海量缓存数据的需求。而其客户端路由算法一致性 Hash 更成为数据存储伸缩性架构设计的经典范式(参考本书第 6 章)。事实上,正是集群内服 务器互不通信使得集群可以做到几乎无限制的线性伸缩,这也正是目前流行的许多大数


据技术的基本架构特点。


虽然近些年许多 NoSQL 产品层岀不穷,在数据持久化、支持复杂数据结构、甚至性 能方面有许多产品优于 Memcached,但 Memcached 由于其简单、稳定、专注的特点,仍 然在分布式缓存领域占据着重要地位。



[](()2 异步操作

使用消息队列将调用异步化,可改善网站的扩展性(参考本书第 7 章内容)。事实上, 使用消息队列还可改善网站系统的性能,如图 4.12 和图 4.13 所示。



在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,


会对数据库造成巨大的压力,同时也使得响应延迟加剧。在使用消息队列后,用户请求 的数据发送给消息队列后立即返回,再由消息队列的消费者进程(通常情况下,该进程 通常独立部署在专门的服务器集群上)从消息队列中获取数据,异步写入数据库。由于 消息队列服务器处理速度远快于数据库(消息队列服务器也比数据库具有更好的伸缩 性),因此用户的响应延迟可得到有效改善。


消息队列具有很好的削峰作用一即通过异步处理,将短时间高并发产生的事务消 息存储在消息队列中,从而削平高峰期的并发事务。在电子商务网站促销活动中,合理 使用消息队列,可有效抵御促销活动刚开始大量涌入的订单对系统造成的冲击。如图 4.14 所示。



需要注意的是,由于数据写入消息队列后立即返回给用户,数据在后续的业务校验、 写数据库等操作可能失败,因此在使用消息队列进行业务异步处理后,需要适当修改业 务流程进行配合,如订单提交后,订单数据写入消息队列,不能立即返回用户订单提交 成功,需要在消息队列的订单消费者进程真正处理完该订单,甚至商品岀库后,再通过 电子邮件或 SMS 消息通知用户订单成功,以免交易纠纷。


任何可以晚点做的事情都应该晚点再做。



[](()3 使用集群

在网站高并发访问的场景下,使用负载均衡技术为一个应用构建一个由多台服务器 组成的服务器集群,将并发访问请求分发到多台服务器上处理,避免单一服务器因负载 压力过大而响应缓慢,使用户请求具有更好的响应延迟特性,如图 4.15 所示。

最后



用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
8 应用服务器性能优化_Java_爱好编程进阶_InfoQ写作平台