learn.lianglianglee.com/专栏/300分钟吃透分布式缓存-完/03 设计缓存架构时需要考量哪些因素?.md.html
2022-08-14 03:40:33 +08:00

340 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>03 设计缓存架构时需要考量哪些因素?.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/00 开篇寄语:缓存,你真的用对了吗?.md.html">00 开篇寄语:缓存,你真的用对了吗?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/01 业务数据访问性能太低怎么办?.md.html">01 业务数据访问性能太低怎么办?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/02 如何根据业务来选择缓存模式和组件?.md.html">02 如何根据业务来选择缓存模式和组件?</a>
</li>
<li>
<a class="current-tab" href="/专栏/300分钟吃透分布式缓存-完/03 设计缓存架构时需要考量哪些因素?.md.html">03 设计缓存架构时需要考量哪些因素?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/04 缓存失效、穿透和雪崩问题怎么处理?.md.html">04 缓存失效、穿透和雪崩问题怎么处理?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/05 缓存数据不一致和并发竞争怎么处理?.md.html">05 缓存数据不一致和并发竞争怎么处理?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/06 Hot Key和Big Key引发的问题怎么应对.md.html">06 Hot Key和Big Key引发的问题怎么应对</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/07 MC为何是应用最广泛的缓存组件.md.html">07 MC为何是应用最广泛的缓存组件</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/08 MC系统架构是如何布局的.md.html">08 MC系统架构是如何布局的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/09 MC是如何使用多线程和状态机来处理请求命令的.md.html">09 MC是如何使用多线程和状态机来处理请求命令的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/10 MC是怎么定位key的.md.html">10 MC是怎么定位key的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/11 MC如何淘汰冷key和失效key.md.html">11 MC如何淘汰冷key和失效key</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/12 为何MC能长期维持高性能读写.md.html">12 为何MC能长期维持高性能读写</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/13 如何完整学习MC协议及优化client访问.md.html">13 如何完整学习MC协议及优化client访问</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/14 大数据时代MC如何应对新的常见问题.md.html">14 大数据时代MC如何应对新的常见问题</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/15 如何深入理解、应用及扩展 Twemproxy.md.html">15 如何深入理解、应用及扩展 Twemproxy</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/16 常用的缓存组件Redis是如何运行的.md.html">16 常用的缓存组件Redis是如何运行的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/17 如何理解、选择并使用Redis的核心数据类型.md.html">17 如何理解、选择并使用Redis的核心数据类型</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/18 Redis协议的请求和响应有哪些“套路”可循.md.html">18 Redis协议的请求和响应有哪些“套路”可循</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/19 Redis系统架构中各个处理模块是干什么的.md.html">19 Redis系统架构中各个处理模块是干什么的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/20 Redis如何处理文件事件和时间事件.md.html">20 Redis如何处理文件事件和时间事件</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/21 Redis读取请求数据后如何进行协议解析和处理.md.html">21 Redis读取请求数据后如何进行协议解析和处理</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/22 怎么认识和应用Redis内部数据结构.md.html">22 怎么认识和应用Redis内部数据结构</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/23 Redis是如何淘汰key的.md.html">23 Redis是如何淘汰key的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/24 Redis崩溃后如何进行数据恢复的.md.html">24 Redis崩溃后如何进行数据恢复的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/25 Redis是如何处理容易超时的系统调用的.md.html">25 Redis是如何处理容易超时的系统调用的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/26 如何大幅成倍提升Redis处理性能.md.html">26 如何大幅成倍提升Redis处理性能</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/27 Redis是如何进行主从复制的.md.html">27 Redis是如何进行主从复制的</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/28 如何构建一个高性能、易扩展的Redis集群.md.html">28 如何构建一个高性能、易扩展的Redis集群</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/29 从容应对亿级QPS访问Redis还缺少什么.md.html">29 从容应对亿级QPS访问Redis还缺少什么</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/30 面对海量数据,为什么无法设计出完美的分布式缓存体系?.md.html">30 面对海量数据,为什么无法设计出完美的分布式缓存体系?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/31 如何设计足够可靠的分布式缓存体系,以满足大中型移动互联网系统的需要?.md.html">31 如何设计足够可靠的分布式缓存体系,以满足大中型移动互联网系统的需要?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/32 一个典型的分布式缓存系统是什么样的?.md.html">32 一个典型的分布式缓存系统是什么样的?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/33 如何为秒杀系统设计缓存体系?.md.html">33 如何为秒杀系统设计缓存体系?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/34 如何为海量计数场景设计缓存体系?.md.html">34 如何为海量计数场景设计缓存体系?</a>
</li>
<li>
<a href="/专栏/300分钟吃透分布式缓存-完/35 如何为社交feed场景设计缓存体系.md.html">35 如何为社交feed场景设计缓存体系</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>03 设计缓存架构时需要考量哪些因素?</h1>
<p>你好,我是你的缓存老师陈波,欢迎进入第 3 课时“缓存的引入及架构设计”。</p>
<p>至此,缓存原理相关的主要知识点就讲完了,接下来会讲到如何引入缓存并进行设计架构,以及在缓存设计架构中的一些关键考量点。</p>
<h6>缓存的引入及架构设计</h6>
<h6>缓存组件选择</h6>
<p>在设计架构缓存时,你首先要选定缓存组件,比如要用 Local-Cache还是 Redis、Memcached、Pika 等开源缓存组件,如果业务缓存需求比较特殊,你还要考虑是直接定制开发一个新的缓存组件,还是对开源缓存进行二次开发,来满足业务需要。</p>
<h6>缓存数据结构设计</h6>
<p>确定好缓存组件后,你还要根据业务访问的特点,进行缓存数据结构的设计。对于直接简单 KV 读写的业务,你可以将这些业务数据封装为 String、Json、Protocol Buffer 等格式,序列化成字节序列,然后直接写入缓存中。读取时,先从缓存组件获取到数据的字节序列,再进行反序列化操作即可。对于只需要存取部分字段或需要在缓存端进行计算的业务,你可以把数据设计为 Hash、Set、List、Geo 等结构,存储到支持复杂集合数据类型的缓存中,如 Redis、Pika 等。</p>
<h6>缓存分布设计</h6>
<p>确定了缓存组件,设计好了缓存数据结构,接下来就要设计缓存的分布。可以从 3 个维度来进行缓存分布设计。</p>
<ol>
<li>首先,要选择分布式算法,是采用取模还是一致性 Hash 进行分布。取模分布的方案简单,每个 key 只会存在确定的缓存节点,一致性 Hash 分布的方案相对复杂,一个 key 对应的缓存节点不确定。但一致性 Hash 分布,可以在部分缓存节点异常时,将失效节点的数据访问均衡分散到其他正常存活的节点,从而更好地保证了缓存系统的稳定性。</li>
<li>其次,分布读写访问如何进行实施,是由缓存 Client 直接进行 Hash 分布定位读写,还是通过 Proxy 代理来进行读写路由Client 直接读写,读写性能最佳,但需要 Client 感知分布策略。在缓存部署发生在线变化时,也需要及时通知所有缓存 Client避免读写异常另外Client 实现也较复杂。而通过 Proxy 路由Client 只需直接访问 Proxy分布逻辑及部署变更都由 Proxy 来处理,对业务应用开发最友好,但业务访问多一跳,访问性能会有一定的损失。</li>
<li>最后,缓存系统运行过程中,如果待缓存的数据量增长过快,会导致大量缓存数据被剔除,缓存命中率会下降,数据访问性能会随之降低,这样就需要将数据从缓存节点进行动态拆分,把部分数据水平迁移到其他缓存节点。这个迁移过程需要考虑,是由 Proxy 进行迁移还是缓存 Server 自身进行迁移,甚至根本就不支持迁移。对于 Memcached一般不支持迁移对 Redis社区版本是依靠缓存 Server 进行迁移,而对 Codis 则是通过 Admin、Proxy 配合后端缓存组件进行迁移。</li>
</ol>
<h6>缓存架构部署及运维管理</h6>
<p>设计完毕缓存的分布策略后,接下来就要考虑缓存的架构部署及运维管理了。架构部署主要考虑如何对缓存进行分池、分层、分 IDC以及是否需要进行异构处理。</p>
<ol>
<li>核心的、高并发访问的不同数据,需要分别分拆到独立的缓存池中,进行分别访问,避免相互影响;访问量较小、非核心的业务数据,则可以混存。</li>
<li>对海量数据、访问超过 10100万 级的业务数据,要考虑分层访问,并且要分摊访问量,避免缓存过载。</li>
<li>如果业务系统需要多 IDC 部署甚至异地多活,则需要对缓存体系也进行多 IDC 部署,要考虑如何跨 IDC 对缓存数据进行更新,可以采用直接跨 IDC 读写,也可以采用 DataBus 配合队列机进行不同 IDC 的消息同步,然后由消息处理机进行缓存更新,还可以由各个 IDC 的 DB Trigger 进行缓存更新。</li>
<li>某些极端场景下,还需要把多种缓存组件进行组合使用,通过缓存异构达到最佳读写性能。</li>
<li>站在系统层面,要想更好得管理缓存,还要考虑缓存的服务化,考虑缓存体系如何更好得进行集群管理、监控运维等。</li>
</ol>
<h6>缓存设计架构的常见考量点</h6>
<p>在缓存设计架构的过程中,有一些非常重要的考量点,如下图所示,只有分析清楚了这些考量点,才能设计架构出更佳的缓存体系。</p>
<p><img src="assets/CgotOV2kRs6APtjoAADXrLxuEqI510.png" alt="img" /></p>
<h6>读写方式</h6>
<p>首先是 value 的读写方式。是全部整体读写,还是只部分读写及变更?是否需要内部计算?比如,用户粉丝数,很多普通用户的粉丝有几千到几万,而大 V 的粉丝更是高达几千万甚至过亿,因此,获取粉丝列表肯定不能采用整体读写的方式,只能部分获取。另外在判断某用户是否关注了另外一个用户时,也不需要拉取该用户的全部关注列表,直接在关注列表上进行检查判断,然后返回 True/False 或 0/1 的方式更为高效。</p>
<h6>KV size</h6>
<p>然后是不同业务数据缓存 KV 的 size。如果单个业务的 KV size 过大,需要分拆成多个 KV 来缓存。但是,不同缓存数据的 KV size 如果差异过大,也不能缓存在一起,避免缓存效率的低下和相互影响。</p>
<h6>key 的数量</h6>
<p>key 的数量也是一个重要考虑因素。如果 key 数量不大,可以在缓存中存下全量数据,把缓存当 DB 存储来用,如果缓存读取 miss则表明数据不存在根本不需要再去 DB 查询。如果数据量巨大,则在缓存中尽可能只保留频繁访问的热数据,对于冷数据直接访问 DB。</p>
<h6>读写峰值</h6>
<p>另外,对缓存数据的读写峰值,如果小于 10万 级别,简单分拆到独立 Cache 池即可。而一旦数据的读写峰值超过 10万 甚至到达 100万 级的QPS则需要对 Cache 进行分层处理,可以同时使用 Local-Cache 配合远程 cache甚至远程缓存内部继续分层叠加分池进行处理。微博业务中大多数核心业务的 Memcached 访问都采用的这种处理方式。</p>
<h6>命中率</h6>
<p>缓存的命中率对整个服务体系的性能影响甚大。对于核心高并发访问的业务,需要预留足够的容量,确保核心业务缓存维持较高的命中率。比如微博中的 Feed Vector Cache常年的命中率高达 99.5% 以上。为了持续保持缓存的命中率,缓存体系需要持续监控,及时进行故障处理或故障转移。同时在部分缓存节点异常、命中率下降时,故障转移方案,需要考虑是采用一致性 Hash 分布的访问漂移策略,还是采用数据多层备份策略。</p>
<h2>过期策略</h2>
<ul>
<li>可以设置较短的过期时间,让冷 key 自动过期;</li>
<li>也可以让 key 带上时间戳,同时设置较长的过期时间,比如很多业务系统内部有这样一些 keykey_20190801。</li>
</ul>
<h6>平均缓存穿透加载时间</h6>
<p>平均缓存穿透加载时间在某些业务场景下也很重要,对于一些缓存穿透后,加载时间特别长或者需要复杂计算的数据,而且访问量还比较大的业务数据,要配置更多容量,维持更高的命中率,从而减少穿透到 DB 的概率,来确保整个系统的访问性能。</p>
<h6>缓存可运维性</h6>
<p>对于缓存的可运维性考虑,则需要考虑缓存体系的集群管理,如何进行一键扩缩容,如何进行缓存组件的升级和变更,如何快速发现并定位问题,如何持续监控报警,最好有一个完善的运维平台,将各种运维工具进行集成。</p>
<h6>缓存安全性</h6>
<p>对于缓存的安全性考虑,一方面可以限制来源 IP只允许内网访问同时对于一些关键性指令需要增加访问权限避免被攻击或误操作时导致重大后果。</p>
<p>好了第3课时的内容到这里就全部结束了我们一起来做一个简单的回顾。首先我们学习了在系统研发中如何引入缓存如何按照4步走对缓存进行设计架构及管理。最后还熟悉了缓存设计架构中的考量点这样你在缓存设计架构时对号入座即可。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/300分钟吃透分布式缓存-完/02 如何根据业务来选择缓存模式和组件?.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/300分钟吃透分布式缓存-完/04 缓存失效、穿透和雪崩问题怎么处理?.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70996e3669403d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>