架构高性能网站秘笈(六)——构建数据缓冲区

到此为止,一共介绍了四种服务器性能优化的方法,分别是:动态内容缓存、浏览器缓存、反向代理缓存、Web组件分离。我们发现在这四种方法中,“缓存”占了大头!确实如此,“缓存”是服务器性能优化的核心思想,我们提出的各种优化方法本质上只是把“缓存”用在了不同的地方,并根据使用位置的不同,个性化定制缓存的使用方法。接下来又要介绍一种缓存的新用法——数据缓冲区。

之前介绍的动态内容缓存、浏览器缓存都是将整个静态页面进行缓存,这种方式有个弊端:由于缓存了整体页面,因此缓存的数据较为笨重,缺乏灵活性。为了解决这个问题,我们可以只缓存数据库中的数据,当用户请求某一页面时,再根据用户的需求从数据缓存中抽出需要的数据,组装成页面返回给用户,从而提升了数据使用的灵活性

什么是数据缓冲区?数据缓冲区有啥好处?

我们可以在数据库之前开辟一块内存缓冲区,我们把这块区域称为数据缓冲区。所有从数据库出来和进入的数据都要经过该缓冲区。那么,数据想要进入数据库,首先需要进入缓冲区,当缓冲区存满时,一次性地写入数据库,从而降低了数据库操作的频率;同理,从数据库出来的数据也会进入该缓冲区,那么下次需要相同数据的时候直接从缓冲区中取即可。要知道,从内存中取数据要比从数据库中取数据快多了,因此缓冲区能大大提升数据插入和查询的性能。

如何构建数据缓冲区?

根据刚才对缓冲区的介绍,我们可以将数据缓冲区分为:读缓冲和写缓冲。

  • 读缓冲:用于存放即将被存入数据库的数据
  • 写缓存:用于存放最近一段时间访问频率较高的数据

使用Memcache实现数据缓冲区

这里我们使用memcache来实现数据缓冲区。具体的Memcache的介绍请自行百度吧,这里简单介绍下Memcache的几个优点:

  1. 查询效率高 Memcache采用健值对的形式存储数据,并且采用优化了的基于Key的Hash算法,因此不管Memcache中存储了多少数据,根据key查询value的时间复杂度永远是O(1)!
  2. 网络并发能力强 Memcache采用了libevent函数库来实现TCP通信,因此在较高并发数的情况下仍然能高效工作。大家不用担心它的效率。

关于Memcache的使用请移步至:在Linux上安装Memcached服务

1. 构建写缓冲区

场景假设:实现点击量的记录

最Low的做法是每有一个用户点击,就把数据库中的相关值加1.但这种每次更新数据库的做法显然不够高效,当访问量很大时,需要不断更新数据库,大大降低服务器的整体性能。因此,访问量的登记完全可以存入写缓存中,当访问量存到1000时,一次性写入数据库,从而数据库更新频率从1000次降低到1次,大大节省了开销。

当然,使用缓存随之会带来数据实时性降低的问题。但对于像访问量这种无关紧要的数据来说,用降低实时性来换取服务器性能开销,还是相当划算的。 注意:小心线程安全问题! 要实现缓存中的指定页面的访问量加一,一共需要三步:

  1. 将指定页面的当前访问量取出来
  2. 访问量加一
  3. 更新缓存中该页面的访问量

因此,若多线程同时访问,会出现线程安全问题。 因此我们需要使用memcache的原子加一操作(increment)来避免线程安全问题。

2. 构建读缓冲区

场景假设:检查用户是否登录


补充知识:如何判断用户是否登录?

用户点击“记住密码”后就不需要再输入密码,那么当用户再次访问网站时,服务器该如何判断该用户是否已经登录呢?

在很久以前,用户登录之后服务器会在用户的Cookie中存放该用户的id,若用户再次访问网站时,如果请求中包含用户id就认为他已经登录,否则就需要重新登录。

但这种方法会引起安全隐患,由于id具有规律性,黑客往往会篡改他本地的Cookie来冒充其他用户登录。

为了避免这种情况的发生,在用户注册的时候,服务器会为每个用户生成一个无规律的随机字符串ticket,用于标示该用户,并将其存入用户的Cookie。由于字符串随机生成,没有了规律性,因此黑客没办法猜到其他用户的ticket。当用户每次访问网站时,如果请求中携带了ticket,我们就查询数据库中是否存在该ticket,若存在则表示该用户已经登录,否则需要重新登录。


那么问题来了,如果每次判断ticket是否存在都需要查询数据库的话,那么当用户量很大的时候会影响服务器整体性能,因此我们可以将所有的ticket存入读缓存,并每隔一段时间更新,确保ticket的实时性。从而当用户访问网站时,只需要从读缓存中查询ticket是否存在即可,无需查询数据库,从而节约了数据库开销。

如何构建分布式数据缓冲区?

当一个memcache存不下所有缓存的时候,我们需要使用多个memcache来实现分布式数据缓冲区。

“分布式数据缓冲区”看似高大上,其实很简单。 假设现在有三台服务器上运行Memcache,IP分别是: - 10.20.100.101 - 10.20.100.102 - 10.20.100.103

在存储之前,我们需要确定究竟把数据存储在哪台缓存服务器上。我们希望数据能够平均地存储在三台服务器上,从而实现负载均衡。可以采用随机分配的方法:

  1. 将每个请求的URL进行MD5运算,得到32字节的16进制数;
  2. 取前5位,模以3
  3. 得到的结果就是缓存服务器的ID

注:通过概率论可以证明,当访问量很大的时候,采用随机分配的方式能够保证每台缓存服务器被选中的次数是一样的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏desperate633

浅谈servlet的生命周期servlet的生命周期详解servlet生命周期中三大重要的时刻

servlet从被加载到销毁经历了多个阶段,其中需要我们十分了解每个阶段的意义作用,才能更好地编写相关的servlet程序。

542
来自专栏逸鹏说道

现在无法开始异步操作。异步操作只能在异步处理程序或模块中开始,或在页生存期中的特定事件过程中开始

异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 这篇没啥技术含量,用来小记一番 错误信息 ...

3875
来自专栏Java Edge

大道缓存1 缓存特征2 缓存介质3 缓存分类和应用场景缓存实战

用户请求从界面(浏览器/App)到网络转发、应用服务再到存储(数据库或文件系统),然后返回到界面呈现内容。

561
来自专栏技巅

Glusterfs之rpc模块源码分析(中)之Glusterfs的rpc模块实现(2)

1295
来自专栏java一日一条

Java Socket 编程原理及教程

对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket。服务端和客户端之间通过Socket建立连接,之后它们就可以进...

441
来自专栏程序你好

MySQL数据优化总结-查询备忘录

1152
来自专栏Java架构

我是这样手写Spring的,麻雀虽小五脏俱全

人见人爱的Spring已然不仅仅只是一个框架了。如今,Spring已然成为了一个生态。但深入了解Spring的却寥寥无几。这里,我带大家一起来看看,我是如何手写...

451
来自专栏PHP在线

php基础知识点回顾

定义和用法 strrev() 函数反转字符串。 语法 strrev(string) 参数 描述 string 必需。规定要反转的字符串。 echo和...

2814
来自专栏小二的折腾日记

面试总结-链接

https://blog.csdn.net/CCUTwangning/article/details/70153589 天下无难试之Redis面试题刁难大全 h...

501
来自专栏Java帮帮-微信公众号-技术文章全总结

request;response 对象

response ? 1. response简介 response的类型为HttpServletResponse,它是Servlet的service()方法的参...

3017

扫码关注云+社区