漫谈Web缓存架构
计算机领域多处地方用到缓存,比如说为了缓解CPU和内存之间的速度不匹配问题,我们往往通过增加一级、二级、三级缓存,CPU先从缓存中取指令,如果取不到,再从内存中取,并更新缓存,同时,根据程序的局部性原理,使得大部分情况下缓存都会命中。
目前,Web应用的核心数据通常存放在数据库中,比如说用户信息、订单信息、交易信息等,同时,数据库和编程语言是无关的,通过SQL交互,Java、Php等语言写的程序需要访问数据库,执行业务逻辑,展示结果给用户。但是数据库有一定的局限性,譬如:1.数据库连接是非常 "昂贵 "的资源,为了复用这些资源,目前采用连接池技术,2. 连接池的连接数是有限的,如果用户过多,势必要等待,3. 读写数据时需要加锁。
通过上述介绍,我们知道一个大型系统中数据库往往会成为瓶颈,我们不能每次访问都访问数据库,尤其是在多用户,大并发的情况下。面对这种情况,我们通常采用何种方法呢?在计算机行业中的所有问题,都可以通过增加一个抽象层来解决。那么,针对数据库这个瓶颈,我们可以在应用层和数据库层增加一层,即缓存层。
如何实现缓存
如果你是某某大型公司的首席架构师,现在公司需要自研一套缓存系统,你应该怎么设计呢?我想在设计之前应该想好以下几个问题:
- 缓存里放什么格式的数据?
- 应用程序(客户端)如何访问缓存?
- 缓存空间被应用程序用完了怎么办?
- 要不要支持分布式存储(数据的分片),怎么实现?
1. 缓存里放什么格式的数据?
目前常见的数据格式有序列化对象、XML、JSON、字符串(key,value)和基本的数据结构,其中针对Java语言的序列化对象有序列化和反序列化,而Google研发的protobuf是和语言无关的,比如说Python将某对象序列化,Java能将这个对象进行反序列化。
2. 应用程序如何访问缓存
考虑到公司有很多后端小组,并且使用不同的编程语言,这就要求我们自研的缓存系统应该和编程语言无关,基于此,我们需要制定一套协议来支持各种语言。客户端如何使用这套协议?最常见的就是客户端/服务器模型。首先,服务器监听请求;接着,客户端发送请求,获得响应,其中客户端发送的请求就是协议;最后,基于Socket通信。比如说:set 'name' 'mukedada'
、get name
。
3. 缓存空间被用完了怎么办?
缓存服务器端在启动的时候,应该设置缓存大小,当缓存被沾满时,采用LRU算法。
4. 实现分布式存储
对于大型应用服务器,单机的缓存服务器是支撑不了的。那么,就需要对缓存服务器进行水平扩展(即增删服务器,当活动结束后,就需要考虑删减服务器),那么用什么算法让数据相对平均的分配到每台服务器?同时,这个算法应该放在客户端还是服务端呢?
- 客户端实现
注意这里的客户端指的是Web应用服务,服务器列表信息通过配置文件获得。当节点数发生变化时,需要让客户端知晓。
它的典型应用是Memcached,Memcached使用的是一致性Hash算法,在介绍它之前,我们先来看一下余数算法。对于用户要存储的(key,value),计算key的整数哈希值,然后对服务器的数目求余,这样来确定存储服务器。这个方法存在一个致命的问题:当服务器个数发送变化时,余数会发生变化,这样一来需要重新到数据库获取数据。
为了加深大家的理解,举个具体的实例:假设有3台服务器0、1、2,key1、key2的hash值分别是100,99,对应的余数分别是1和0,也就是说它们分别存放于编号为1和0的服务器中,现在如果增加一台服务器3,那么它们的余数也会随之发生变化,100%4 = 0,99%4 = 3,但是它们在0、3号服务器却找不到对应的数据。
为了解决余数算法存在的问题,我们的先辈们提出了分布式一致性hash算法。它思路就是当服务器个数发生变化时,尽可能的减少影响。譬如:当我们新增node5,只影响局部范围内的key,而余数算法则影响全局。
但是它也存在分布不均匀的问题,导致有的服务器上缓存的数据多,有的少。一种方法就是虚拟节点,也就是说让一个服务器化身为多个虚拟节点,分布到环上。Memcache采用的就是这种方法。
另一种方法就是Hash槽。Redis采用的就是这种方法,在介绍路由实现时会详细介绍该方法。
代理实现
代理程序放在服务器端,它的典型案例有Twemproxy和Codis。它的基本思想:应用程序向Proxy发送请求,Proxy通过一定算法计算得到数据应该从哪个节点获取,并且返回响应给客户端。为了防止Proxy出现单点故障,可以通过集群等方式实现Proxy高可用。
路由实现
它的典型案例就是Redis。它的基本思想是应用程序可以将请求发送到任意一个节点,当节点包含该请求数据,则直接返回响应给应用程序,当节点不包含该请求数据时,则告诉它跳转到其他节点中取数据,其中,客户端程序库需要解析相应的指令。例如:当node1中没有数据,会让客户端程序访问node3,这类似于web中的重定向,缺点: node1需要知道其他节点的数据,即node1和其他节点是相互通信的。
首先它有16284个槽,每个node节点管理一段Hash槽,每当新来一个请求,都对它的key值进行CRC16(key)%16384
求余,最终会落到0~16383这个区间的槽中。
但是,每当新增一个节点时,需要从原先的每个节点中获取hash槽,这时需要涉及数据迁移的过程。如果在数据迁移的过程中有一个用户请求,这个时候该怎么办?目前一种解决方法是让node1和node4的持有相同的槽,但是设置不同的状态,例如node1的槽的状态设置为正在迁移,而node4的状态是正在导入,首先将请求交给node1,如果node1中有数据则直接返回,如果没有则交给ndoe4。如下图所示。
同时,我们注意到node1、node2等存在单点故障,为增加可用性,我们对每个node使用主从模式。数据首先写入到master节点,之后有两种方式,方式一,直接将结果返回给客户端,然后将master节点数据同步到slave从节点中,这样做的好处就是响应周期短,缺点是可能存在数据不一致的情况,即master节点将结果返回给客户端之后,还没来得及将数据同步到slave节点中就发生故障,那么这部分数据就会丢失。方式二,数据写入到master节点之后,需要将数据同步到slave节点成功之后,再将结果返回给客户端,这种方式保证了数据强一致性,但是用户需要更长的时间来等待。
缓存击穿问题
用户每次访问缓存都没有命中,导致每次请求都要访问数据库,这就是缓存击穿问题,出现这种情况导致缓存没起效果,反而增加了系统消耗。针对这个问题,一般诸如双十一等活动都会在活动开始之前将用户信息预先存放到缓存中。
欢迎关注微信公众号:木可大大,所有文章都将同步在公众号上。
漫谈Web缓存架构的更多相关文章
- 千万pv大型web系统架构,学习从点滴开始
架构,刚开始的解释是我从知乎上看到的.什么是架构?有人讲, 说架构并不是一 个很 悬 乎的 东西 , 实际 上就是一个架子 , 放一些 业务 和算法,跟我们的生活中的晾衣架很像.更抽象一点,说架构其 ...
- 浅谈大型web系统架构
动态应用,是相对于网站静态内容而言,是指以c/c++.php.Java.perl..net等服务器端语言开发的网络应用软件,比如论坛.网络相册.交友.BLOG等常见应用.动态应用系统通常与数据库系统. ...
- web缓存
web缓存HTTP协议的一个核心特性,它能最小化网络流量,并且提升用户所感知的整个系统响应速度. 什么能被缓存? *Logo和商标图像 *普通的不变化的图像(例如,导航图标) *CSS样式表 *普通的 ...
- 【架构】浅谈web网站架构演变过程
浅谈web网站架构演变过程 前言 我们以javaweb为例,来搭建一个简单的电商系统,看看这个系统可以如何一步步演变. 该系统具备的功能: 用户模块:用户注册和管理 商品模块:商品展示和管 ...
- (转)web网站架构演变
浅谈web网站架构演变过程 前言 我们以javaweb为例,来搭建一个简单的电商系统,看看这个系统可以如何一步步演变. 该系统具备的功能: 用户模块:用户注册和管理 商品模块:商品展示和管 ...
- Web缓存的作用与类型
前言 Web缓存是指一个Web资源(如html页面,图片,js,数据等)存在于Web服务器和客户端(浏览器)之间的副本.缓存会根据进来的请求保存输出内容的副本:当下一个请求来到的时候,如果是相同的UR ...
- 转:浅谈大型web系统架构
浅谈大型web系统架构 动态应用,是相对于网站静态内容而言,是指以c/c++.php.Java.perl..net等服务器端语言开发的网络应用软件,比如论坛.网络相册.交友.BLOG等常见应用.动态应 ...
- Web缓存基础:术语、HTTP报头和缓存策略
简介 对于您的站点的访问者来说,智能化的内容缓存是提高用户体验最有效的方式之一.缓存,或者对之前的请求的临时存储,是HTTP协议实现中最核心的内容分发策略之一.分发路径中的组件均可以缓存内容来加速后续 ...
- 基于Spring的Web缓存
缓存的基本思想其实是以空间换时间.我们知道,IO的读写速度相对内存来说是非常比较慢的,通常一个web应用的瓶颈就出现在磁盘IO的读写上.那么,如果我们在内存中建立一个存储区,将数据缓存起来,当浏览器端 ...
随机推荐
- KNN算法简单应用
这里是写给小白看的,大牛路过勿喷. 1 KNN算法简介 KNN(K-Nearest Neighbor)工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集 ...
- Python之编码
一.Python2与Python3的区别 1.从宏观上考虑,Python2重复代码太多,错误率高,不够规范.Python崇尚的是语言简洁.优美.清晰.Python3更加规范,重复代码少: 2.Pyth ...
- Java中如何实现j并发更新数据库同一条数据
分情况来说:普通单应用并发.多应用或多台服务器并发 情况一:普通单应用并发 使用关键字synchronized就可实现. 情况二:多应用或多台服务器并发 因多个应用之间并非同一个jvm(应用)内,因此 ...
- Hibernate(二):MySQL server version for the right syntax to use near 'type=InnoDB' at line x
目前使用的hibernate5.2.9版本,配置的mysql方言为: <property name="hibernate.dialect">org.hibernate. ...
- JavaScript正则表达式学习笔记之一 - 理论基础
自从年前得空写了两篇文章之后就开始忙了,这一忙就是2个月
- logging的使用方法
logging的使用方法 1,简单使用方法 >>> import logging >>> logging.warning('this is a warning') ...
- 参加Java培训到底靠不靠谱?
导读 科技越发展,社会越进步,人们越便利,便衍生出更多的人从事程序员这个高大上的职业,可哈尔滨Java培训学校这么多,到底靠不靠谱,会不会处处是陷阱,爱尚实训帮你擦亮眼 随着时代的发展,越来越多的人对 ...
- IT智力面试题
◆ 有一个长方形蛋糕,切掉了长方形的一块(大小和位置随意),你怎样才能直直的一刀下去,将剩下的蛋糕切成大小相等的两块? 答案:将完整的蛋糕的中心与被切掉的那块蛋糕的中心连成一条线.这个方法也适用于立方 ...
- 【PYTHON】递加计数器
计数本:number.txt 1 2 3 4 主程序:计数器 # Author: Stephen Yuan # 递加计算器 import os # 递加计算器 def calc(): file_siz ...
- .NET MD5 加密
using System; using System.Security.Cryptography; using System.Text; namespace Md5Demo { /// <sum ...