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

到此为止,一共介绍了四种服务器性能优化的方法,分别是:动态内容缓存、浏览器缓存、反向代理缓存、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 条评论
登录 后参与评论

相关文章

来自专栏喵了个咪的博客空间

[PhalApi实战篇(1)]Redis队列处理异步任务

[PhalApi实战篇(1)]Redis队列处理异步任务 ? 前言 先在这里感谢phalapi框架创始人@dogstar,为我们提供了这样一个优秀的开源框架. ...

3104
来自专栏何俊林

一种在Java层实现的守护进程方式

守护进程是一个黑色地带的产物,无论是通过native的方式在linux中fork进程达到,还是在java层通过两个service守护的方式,都是不太友好的做法,...

1796
来自专栏zhangdd.com

zabbix监控-清理zabbix 历史数据

zabbix运行一段时间之后,会留下大量的历史 数据,会发现zabbix的数据库一直在增大。运行3个月后笔者的数据库达到了5.7G,可能造成系统性能下降,查看历...

581
来自专栏喵了个咪的博客空间

基于PhalApi的DB集群拓展 V0.1bate

#基于PhalApi的DB集群拓展 V0.1bate ? ##前言## 先在这里感谢phalapi框架创始人@dogstar,为我们提供了这样一个优秀的开源框架...

2827
来自专栏散尽浮华

全量备份/增量备份/差异备份说明

作为一名运维工程师,在日常工作中会时常对各类重要数据进行备份,为了方便管理,运用何种备份方案是至关重要的。 今天在此简单说明下Linux运维中的备份种类:全量备...

2257
来自专栏皮振伟的专栏

[linux][memory]mlock技术分析和使用以及问题

前言: 使用了mlock,会把内存lock在内存中,不会被交换,在一定场景下,可以提高性能。 虚拟化场景下,qemu也可以选择lock住一部分内存,来提高Gue...

35211
来自专栏互扯程序

MyCat安装与测试教程 超详细!

MyCat基础知识 一、什么是MYCAT? 1. 一个彻底开源的,面向企业应用开发的大数据库集群 2. 支持事务、ACID、可以替代MySQL的加强版...

4506
来自专栏Golang语言社区

设计Go API的管道使用原则

管道是并发安全的队列,用于在Go的轻量级线程(Go协程)之间安全地传递消息。总的来讲,这些原语是Go语言中最为称道的特色功能之一。这种消息传递范式使得开发者可以...

3476
来自专栏风火数据

hadoop大数据面试题

以下资料来源于互联网,很多都是面试者们去面试的时候遇到的问题,我对其中有的问题做了稍许的修改了回答了部分空白的问题,其中里面有些考题出的的确不是很好,但是也不乏...

902
来自专栏逸鹏说道

Stack Overflow 2016最新架构探秘

这篇文章主要揭秘 Stack Overflow 截止到 2016 年的技术架构。   首先给出一个直观的数据,让大家有个初步的印象。   相比于 2013 ...

2857

扫描关注云+社区