• 如何构建高性能web网站:分布式缓存


    一、数据库前端缓冲区

    要清除数据库前缓冲区,首先必须明确什么是文件系统的内核缓冲区(Buffer Area):它位于内核的物理内存地址空间,除了使用O_DIRECT比其他标签中打开文件,所有的磁盘的读写操作,所有人都必须通过它去,所以。可以把它看作是一个正面的磁盘设备。
    这块内核缓冲区也称为:页快速缓存(Page Cache),实际上它包含两部分:
    1、读缓存区
    2、写缓存区

    读缓存区

    读缓冲区保存着系统近期从磁盘上读取的数据,一旦下次须要这些数据,那么内核将直接在读缓存区中获得,而不须要再次读取磁盘。

    写缓存区

    写缓存区的主要作用是降低对磁盘的物理写操作,通常情况下。向磁盘的写操作并不着急,程序的执行并不会由于磁盘的写操作而等待。内核缓冲区能够将多个写操作指令累积起来,通过一次磁盘的磁头移动来完毕,当然,写缓存区会导致数据的实际写入延长几秒,在实际写入之前。这些数据被称为Dirty Page 。脏页。


    实际上。从职能上来看。将写缓存区叫做缓冲区更加形象。缓冲区的样例在生活中随处可见。比如城市的十字路口,它就像一个写缓冲区。红灯亮起的时候,车辆都停在缓冲区,当绿灯时,车辆依次前进,就像内核中的数据积累到一定程度时被写入磁盘。相同,我们能够在数据库和动态内容之间建立一层缓存区,它能够独立部署在server上。用于加速数据库的读写操作,这个缓存区实际上是由动态内容来控制的。

    这个时候就使用到了memcached。

    二、使用memcached

    key-value  

    首先为了实现快速缓存,我们不会将缓存内容放在磁盘上。基于这个原则,memcached使用物理内存来作为缓存区,当我们启动memcached的时候,须要指定分配给缓存区的内存大小,比方分配4G
    s-colin:~ # memcached -d -m 4086 -l 10.0.1.12 -p 11711

    memcached 使用key-value 的方式来存储数据,每一个数据之间相互独立,每一个数据都已key作为唯一的索引。

    数据项过期时间

    因为缓存区是有限制大小的,一旦缓存区没有足够的空间存储新的数据项的时候,memcached便会想办法淘汰一些数据项来腾出空间,淘汰机制基于LRU(Least Recently Used)算法,将近期不常訪问的数据项淘汰掉。一般都是为其设置过期时间,过期时间的取值,须要依据自己网站来平衡把握。

    网络并发模型

    作为分布式系统,memcached能够独立执行在server上,动态内容通过 TCP  Socket 来訪问它,这样一来。memcached 本身的网络并发处理能力尤为重要。memcached 使用libevent 函数库来实现网络并发模型。当中包含epoll,所以在较大并发用户数的情况下,我们仍然能够放心使用memcached。

    对象的序列化

    我们能够在memcached上存储什么样的数据呢?这须要考虑到网络传输。意思就是,我们能够在网络传输中传输什么样的内容?哈哈,自然是二进制了,那么,对于数组或者对象这种抽象数据类型而言,能否够存储在memcached中呢?
    基于序列化机制,没有能够将更好层次的抽象数据类型转化为二进制字符串。以便通过网络进入缓存server,同一时候。在读取这些数据的时候。二进制字符串又能够转换回原来的数据类型。



    有点须要注意的是,我们在试图将一个类的对象进行序列化时。对象的成员函数是不会被序列化的。被序列化的知识对象的数据成员。当须要从持久化数据中恢复对象时(也就算反序列化时),我们会首先实例化一个新的对象。然后将之前持久化的数据成员依次赋值给新对象的对应数据成员。顺便提下。JSON格式。能够非常好的应用在序列化中,不论什么数组和对象都能够非常easy的与json格式的字符串互相转换,且计算量不大。

    三、读操作缓存

    反复的身份验证

    非常早曾经,我们喜欢将用户ID直接写入浏览器的cookies。并以此为荣。可是黑客非常easy的篡改本地cookies来冒充其它用户,这时候新的方法出现了,那就是在用户登录的时候,我们会给其一个ticket字符串。 并将它写入浏览器cookies,随后的每次请求,我们都检查这个ticket,因为客户不知道我们发给其它用户的ticket,所以无法冒充其它用户。

    缓存用户登录状态

    可是有一点即使我们的查询效率非常高,查询本身还是存在开销的。这非常大程度上在于数据库的I/O操作,这个时候。就须要memcached了。这个时候应该充分的利用emcached的序列化,而且设置缓存数据的有效期,可是提醒一点,假设是用户注销了登录的话。记得清楚memcached中的登录状态缓存。

    四、写操作缓存

    对于一个数据库写操作频繁的网站来说,通过引入写缓存来降低写数据库的次数显得至关重要。

    通常的数据写操作包含插入、更新、删除,这些操作的同一时候可能又伴随着条件查找和索引的更新。所以这东西的开销往往是让人印象深刻的。
    这个时候,就会用到memcached的原子递增操作,其实,也正是由于它,我们才会考虑在訪问量递增更新的应用中引入写缓存,写缓存的本质是,在对数据库的写入操作累积到你程序定义的数量时,它在一次行的去运行这些操作,尽管实际的写入往往会延迟几秒钟。可是通常情况下,我们对写入的操作也不是要求实时的,这样既提高了效率又满足了我们业务的需求,何乐不为?


    五、监控状态

    作为一个分布式系统,尽管memcached可以出色的完毕你交给它的任务,可是并非代表你能对它放任无论,相反,我们须要知道它的执行状况,memcached提供了这种协议。可以让你获得对它的实时状态。


    来看个包含缓存状态的数组:

    真的非常全面,比方 uptime表示memcached持续执行的时间;cmd_get表示读取数据项的次数;cmd_set表示更新数据项的次数;get_hits表示缓存命中的次数;bytes_read表示读取字节的总数;bytes表示缓存区已经使用的大小;limit_maxbytes表示缓存区的总大小。

    以上这么多的信息。我们能够从三个方面去看:

    空间使用率

    持续关注缓存空间的使用率。能够让我们知道何时须要为缓存系统扩容,以避免因为缓存空间已满造成的数据被动淘汰,有些数据项在过期之前被LRU算法淘汰可能会造成一定不良后果。


    缓存命中率

    终端用户訪问加速节点时。假设该节点有缓存住了要被訪问的数据时就叫做命中。假设没有的话须要回原server取。就是没有命中。取数据的过程与用户訪问是同步进行的,所以即使是又一次取的新数据。用户也不会感觉到有延时。 命中率=命中数/(命中数+没有命中数), 缓存命中率是推断加速效果好坏的重要因素之中的一个。很教科书式的解释,大家自行消化。

    I/O流量

    我们还须要关注memcached中数据项读写字节数的增长速度,这反应了它的工作量,我们从中能够得知memcached是忙还是空暇。同一时候我们也可能希望在监控系统中集成对memcached的监控,比如cacti监控系统,后面学习。

    缓存扩展

    有非常多理由让我们不得不扩展memcached的规模,包含并发处理能力和缓存空间容量等,不论哪个达到了极限,扩展都在所难免。对于缓存空间的容量,扩展既意味着添加物理内存。这有点不切实际,而对于并发处理能力。这正是memcached的特长所在,所以。我们仅仅能添加缓存server来达到扩展的目的。


    当有多台缓存server的时候。我们面临的是怎样将缓存数据均衡的分布在多台server上,
    一种是我们能够以商业逻辑来划分设计,第二种是基于key的划分方式

    以商业逻辑来划分设计

    比方一台用来缓存用户登录状态,一台用来缓存訪问量统计:
    10.0.1.12-->用户登录状态缓存
    10.0.1.13-->訪问量统计缓存
    有两个问题出现:
    一是这两台server的工作量均衡么?
    二是假设两台仍不能满足须要,那怎样继续扩展呢?
    对于第一个问题,严格来说,要想达到真正的均衡是不现实的,因为它们的职责所在不同。它们的开销和訪问率也不尽同样。所以有的仅仅是相对的均衡。
    对于第二个问题。增加訪问量统计缓存须要扩展,那么我们准备一台新的server:10.0.1.14.然后将訪问量统计缓存再次划分。相同基于业务逻辑为基础。比如将子网站划分为两部分,让它们分别存储在两台server上,这样就形成了:
    10.0.1.12-->用户登录状态缓存
    10.0.1.13-->訪问量统计缓存 group1
    10.0.1.14-->訪问量统计缓存 group2

    基于key的划分方式

    为此,我们须要设计一种不须要依赖数据项内容的散列算法,将全部数据项的key均衡分配在这三台缓存server上。一个简单并且有效的方法是取余运算,就像是打扑克时的发牌。让全部的数据项依照一个顺序在不同的缓存server上轮询,这样能够达到一个较好的均衡。
    举个样例:
    取余之前,我们要做一些准备工作,目的是让key变成整数。并且尽量唯一。比方这个key是:jessonlv-1986.htm
    我们先对它进行md5运算,得到一个32字节的字符串比方是:e6e87fc57lkji1245lki1547iuhgt632,同一时候也是一个十六进制的长整数,为了节省开销,我们取这个字符串的前5个字节,然后将其转化为十进制数:比方945689,这个时候我们再将这个数字进行“模3”的运算。取余的结果就是我们server的编号,server的编号从0開始。
    这里有个问题或许你一直在思考,那就是我们扩展缓存系统后,由于分区算法的改变,会涉及缓存数据须要从一台缓存server迁移到还有一台缓存server的问题,怎样迁移呢?其实,根本不须要考虑迁移的问题。由于是缓存,它应该具备关键时刻牺牲自己的勇气,你必须明确缓存不是持久性存储,而且从引入分布式缓存时就得时刻提醒自己。


    没错。当调整缓存区算法后。我们须要时间来等待缓存的重建和预热。但这往往并不影响网站的正常运转,前提是你要依照文章前面堵缓存和写缓存的设计理念来进线路设计。。


    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    【20211012】以确定去适应不确定
    【20211011】工作之余
    【20211009】二孩的心思
    【20211010】幸福声响
    【20211007】连岳摘抄
    【读书笔记】《创新与企业家精神》
    【20211006】连岳摘抄
    【20211008】生活规律,事情也好解决
    ASP.NET中DataGrid的简单用法
    无限级分类的实现
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4829581.html
Copyright © 2020-2023  润新知