以Redis为例,详谈分布式系统缓存的细枝末节
以Redis为例,详谈分布式系统缓存的细枝末节 0浏览 收藏 本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《以Redis为例,详谈分布式系统缓存的细枝末节》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~ 在分布式Web程序设计中,解决高并发以及内部解耦的关键技术离不开缓存和队列,而缓存角色类似计算机硬件中CPU的各级缓存。如今的业务规模稍大的互联网项目,即使在最初beta版的开发上,都会进行预留设计。但是在诸多应用场景里,也带来了某些高成本的技术问题,需要细致权衡。 本系列主要围绕分布式系统中服务端缓存相关技术,也会结合朋友间的探讨提及自己的思考细节。文中若有不妥之处,恳请指正。本文是本系列的第一篇,打算尽可能详细地谈谈缓存自身的基础设计应用,以及相关的操作细节等(具体应用主要以Redis举例)。 一、服务端数据缓存 1一种区分 缓存基于不同的条件有很多种划分方式,本地缓存(Local cache)和分布式缓存(Distributed cache)是一种常见分类,两者自身又包含很多细类。 本地并不是指程序所在本地服务器(从严格概念来说),而是更细粒度的指位于程序自身的内部存储空间,而分布式更多强调的是存储在进程之外的一个或者多个服务器上,彼此交互通信,在具体软件项目的设计和应用中,多数时候是混合一体。当然,个人认为对缓存本质的理解才是最重要的,至于概念上的分类只是一个不同理解下的划分而已。 2一些技术成本 在具体项目架构设计时,单纯使用前者(本地缓存)的开发成本毋庸置疑是极低的,主要考虑的是本机的内存负载或者极少量的磁盘I/O影响。而后者的设计初心是为了利于分布式程序之间缓存数据的高效共享和管理,除了考虑缓存所在服务器自身的内存负载,设计时更需要充分考虑网络I/O、CPU的负载,以及某些场景下的磁盘I/O的代价,同时还在具体设计时尽可能规避和权衡整体稳定性和效率,这些不仅仅只是作为缓存服务器的硬件成本和技术维护。需要谨慎考虑的底层问题包括缓存间通信、网络负载和延迟等各种需要权衡的细节。 其实如果理解了缓存本质就该知道,任何存储介质在适当的场景下都可以充当一个高效的缓存角色并进行项目集成和缓存间集群。常见主流的Memcached和Redis等均是属于后者范畴,甚至可以包括如基于NoSQL设计的MongoDB这类文档数据库(但这是从角色角度讲,而狭义划分上这是基于磁盘的存储库,需要注意,各有专攻)。 这些第三方缓存在进行项目集成和缓存间集群,也需要解决一些问题。甚至项目迭代到了后期阶段,往往还需要具备较高专业知识的运维同时参与,并且在开发中的逻辑设计和代码实现也会增加一定的工作量。所以有时候在具体项目的设计上,一方面要尽可能预留,一方面还得根据实际情况尽可能精简。 额外说下其他体会:在个人有限的技术学习和实践里,关于节点数据交互,尤其是服务间通信,是不存在完美的闭环的,理论上也都是在“当前阶段”面向“高一致”的权衡罢了(大概跟生活是一样的吧)。 二、缓存数据库结构的设计细节 由于目前个人工作中大多数情况应用的是Redis 3.x,以下若有特性关联,均是以此作为参照说明。 1实例(Instance) 根据业务场景,公共数据和业务耦合数据,一定分别使用不同的实例。如果是单实例,才可以考虑以DB划分。当你使用的是Redis,那么DB在Redis里是有数据隔离,但没有严格权限限制,所以划库只是一种选择。在Cluster集群里则是保持默认单个库,不过实际中我会尝试根据项目大小来调整,至于在哪个开发阶段则是作为预留设计。 额外需要注意的是,作为重度依赖服务器内存的缓存产品,如果开启了持久化(后面会提到),并且在为并发量极大的服务提供支持时,服务器硬件资源会出现大量抢占,请结合持久策略配置,考虑实例是否进行分盘存储。 持久化本质是将内存数据同步写入硬盘(刷盘),而磁盘I/O实在有限,被迫的写入阻塞除了造成线程阻塞和服务超时,还会导致额外异常甚至波及其他底层依赖服务。当然,我的建议是,如果条件允许,最好是在项目初期设计时就进行规划并确定。 2缓存“表”(Table) 一般缓存中并没有传统RDBMS中直观的表概念(往往以键值对“KV”形式存在),但从结构上来讲,键值对本身就可以组装为各种表结构。一般我会先生成数据库表关系图,然后分析什么时候存储字符串,什么时候存储对象,然后使用缓存键(KEY)进行表和字段(列)分割。 假定需要存储一个登录服务器表数据,包含字段(列):name、sign、addr,那么可以考虑将数据结构拆分为以下形式: { key : "server:name" , value : "xxxx" } …