Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qq_21481385
JavaGuide
提交
ce53c55e
J
JavaGuide
项目概览
qq_21481385
/
JavaGuide
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
JavaGuide
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
ce53c55e
编写于
8月 03, 2020
作者:
G
guide
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[refractor]缓存常见问题更新完善
上级
b4552a64
变更
3
展开全部
隐藏空白更改
内联
并排
Showing
3 changed file
with
202 addition
and
147 deletion
+202
-147
README.md
README.md
+6
-3
docs/database/Redis/redis-all.md
docs/database/Redis/redis-all.md
+71
-144
docs/database/Redis/some-concepts-of-caching.md
docs/database/Redis/some-concepts-of-caching.md
+125
-0
未找到文件。
README.md
浏览文件 @
ce53c55e
...
...
@@ -77,7 +77,7 @@
-
[
MyBatis
](
#mybatis
)
-
[
认证授权(JWT、SSO)
](
#认证授权
)
-
[
分布式
](
#分布式
)
-
[
Elasticsearch(分布式搜索引擎)
](
#elasticsearch
分布式搜索引擎
)
-
[
分布式搜索引擎
](
#
分布式搜索引擎
)
-
[
RPC
](
#rpc
)
-
[
消息队列
](
#消息队列
)
-
[
API 网关
](
#api-网关
)
...
...
@@ -220,6 +220,7 @@
### Redis
*
[
Redis前置菜-关于缓存的一些概念
](
docs/database/Redis/some-concepts-of-caching.md
)
*
[
Redis 常见问题总结
](
docs/database/Redis/redis-all.md
)
*
**Redis 系列文章合集:**
1.
数据结构和算法 :
[
5种基本数据结构
](
docs/database/Redis/redis-collection/Redis(1
)
——5种基本数据结构.md)、
[
跳跃表
](
docs/database/Redis/redis-collection/Redis(2
)
——跳跃表.md)、
[
神奇的HyperLoglog解决统计问题
](
docs/database/Redis/redis-collection/Reids(4
)
——神奇的HyperLoglog解决统计问题.md)、
[
亿级数据过滤和布隆过滤器
](
docs/database/Redis/redis-collection/Redis(5
)
——亿级数据过滤和布隆过滤器.md)、
[
GeoHash查找附近的人
](
docs/database/Redis/redis-collection/Redis(6
)
——GeoHash查找附近的人.md)
...
...
@@ -270,11 +271,13 @@ SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中
[
分布式相关概念入门
](
docs/system-design/website-architecture/分布式.md
)
####
Elasticsearch(分布式搜索引擎)
####
分布式搜索引擎
提高搜索效率。常见于电商购物网站的商品搜索于分类。
代办......
比较常用的是 Elasticsearch 和 Solr。
代办。
#### RPC
...
...
docs/database/Redis/redis-all.md
浏览文件 @
ce53c55e
此差异已折叠。
点击以展开。
docs/database/Redis/some-concepts-of-caching.md
0 → 100644
浏览文件 @
ce53c55e
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
<!-- code_chunk_output -->
-
[
1. 缓存的基本思想
](
#1-缓存的基本思想
)
-
[
2. 使用缓存为系统带来了什么问题
](
#2-使用缓存为系统带来了什么问题
)
-
[
3. 本地缓存解决方案
](
#3-本地缓存解决方案
)
-
[
4. 为什么要有分布式缓存?/为什么不直接用本地缓存?
](
#4-为什么要有分布式缓存为什么不直接用本地缓存
)
-
[
5. 缓存读写模式/更新策略
](
#5-缓存读写模式更新策略
)
-
[
5.1. Cache Aside Pattern(旁路缓存模式)
](
#51-cache-aside-pattern旁路缓存模式
)
-
[
5.2. Read/Write Through Pattern(读写穿透)
](
#52-readwrite-through-pattern读写穿透
)
-
[
5.3. Write Behind Pattern(异步缓存写入)
](
#53-write-behind-pattern异步缓存写入
)
<!-- /code_chunk_output -->
### 1. 缓存的基本思想
很多朋友,只知道缓存可以提高系统性能以及减少请求相应时间,但是,不太清楚缓存的本质思想是什么。
缓存的基本思想其实很简单,就是我们非常熟悉的空间换时间。不要把缓存想的太高大上,虽然,它的确对系统的性能提升的性价比非常高。
其实,我们在学习使用缓存的时候,你会发现缓存的思想实际在操作系统或者其他地方都被大量用到。 比如
**CPU Cache 缓存的是内存数据用于解决 CPU 处理速度和内存不匹配的问题,内存缓存的是硬盘数据用于解决硬盘访问速度过慢的问题。**
**再比如操作系统在 页表方案 基础之上引入了 快表 来加速虚拟地址到物理地址的转换。我们可以把块表理解为一种特殊的高速缓冲存储器(Cache)。**
回归到业务系统来说:
**我们为了避免用户在请求数据的时候获取速度过于缓慢,所以我们在数据库之上增加了缓存这一层来弥补。**
当别人再问你,缓存的基本思想的时候,就把上面 👆 这段话告诉他,我觉得会让别人对你刮目相看。
### 2. 使用缓存为系统带来了什么问题
**软件系统设计中没有银弹,往往任何技术的引入都像是把双刃剑。**
但是,你使用好了之后,这把剑就是好剑。
简单来说,为系统引入缓存之后往往会带来下面这些问题:
_ps:其实我觉得引入本地缓存来做一些简单业务场景的话,实际带来的代价几乎可以忽略,下面 👇 主要是针对分布式缓存来说的。_
1.
**系统复杂性增加**
:引入缓存之后,你要维护缓存和数据库的数据一致性、维护热点缓存等等。
2.
**系统开发成本往往会增加**
:引入缓存意味着系统需要一个单独的缓存服务,这是需要花费相应的成本的,并且这个成本还是很贵的,毕竟耗费的是宝贵的内存。但是,如果你只是简单的使用一下本地缓存存储一下简单的数据,并且数据量不大的话,那么就不需要单独去弄一个缓存服务。
### 3. 本地缓存解决方案
_先来聊聊本地缓存,这个实际在很多项目中用的蛮多,特别是单体架构的时候。数据量不大,并且没有分布式要求的话,使用本地缓存还是可以的。_
常见的单体架构图如下,我们使用
**Nginx**
来做
**负载均衡**
,部署两个相同的服务到服务器,两个服务使用同一个数据库,并且使用的是本地缓存。
![
单体架构
](
./images/redis-all/单体架构.png
)
_那本地缓存的方案有哪些呢?且听 Guide 给你来说一说。_
**一:JDK 自带的 `HashMap` 和 `ConcurrentHashMap` 了。**
`ConcurrentHashMap`
可以看作是线程安全版本的
`HashMap`
,两者都是存放 key/value 形式的键值对。但是,大部分场景来说不会使用这两者当做缓存,因为只提供了缓存的功能,并没有提供其他诸如过期时间之类的功能。一个稍微完善一点的缓存框架至少要提供:过期时间、淘汰机制、命中率统计这三点。
**二: `Ehcache` 、 `Guava Cache` 、 `Spring Cache` 这三者是使用的比较多的本地缓存框架。**
`Ehcache`
的话相比于其他两者更加重量。不过,相比于
`Guava Cache`
、
`Spring Cache`
来说,
`Ehcache`
支持可以嵌入到 hibernate 和 mybatis 作为多级缓存,并且可以将缓存的数据持久化到本地磁盘中、同时也提供了集群方案(比较鸡肋,可忽略)。
`Guava Cache`
和
`Spring Cache`
两者的话比较像。
`Guava`
相比于
`Spring Cache`
的话使用的更多一点,它提供了 API 非常方便我们使用,同时也提供了设置缓存有效时间等功能。它的内部实现也比较干净,很多地方都和
`ConcurrentHashMap`
的思想有异曲同工之妙。
使用
`Spring Cache`
的注解实现缓存的话,代码会看着很干净和优雅,但是很容易出现问题比如缓存穿透、内存溢出。
**三: 后起之秀 Caffeine。**
相比于
`Guava`
来说
`Caffeine`
在各个方面比如性能要更加优秀,一般建议使用其来替代
`Guava`
。并且,
`Guava`
和
`Caffeine`
的使用方式很像!
本地缓存固然好,但是缺陷也很明显,比如多个相同服务之间的本地缓存的数据无法共享。
_下面我们从为什么要有分布式缓存为接入点来正式进入 Redis 的相关问题总结。_
### 4. 为什么要有分布式缓存?/为什么不直接用本地缓存?
_我们可以把分布式缓存(Distributed Cache) 看作是一种内存数据库的服务,它的最终作用就是提供缓存数据的服务。_
如下图所示,就是一个简单的使用分布式缓存的架构图。我们使用 Nginx 来做负载均衡,部署两个相同的服务到服务器,两个服务使用同一个数据库和缓存。
![
集中式缓存架构
](
./images/redis-all/集中式缓存架构.png
)
本地的缓存的优势是低依赖,比较轻量并且通常相比于使用分布式缓存要更加简单。
再来分析一下本地缓存的局限性:
1.
**本地缓存对分布式架构支持不友好**
,比如同一个相同的服务部署在多台机器上的时候,各个服务之间的缓存是无法共享的,因为本地缓存只在当前机器上有。
2.
**本地缓存容量受服务部署所在的机器限制明显。**
如果当前系统服务所耗费的内存多,那么本地缓存可用的容量就很少。
使用分布式缓存之后,缓存部署在一台单独的服务器上,即使同一个相同的服务部署在再多机器上,也是使用的同一份缓存。 并且,单独的分布式缓存服务的性能、容量和提供的功能都要更加强大。
使用分布式缓存的缺点呢,也很显而易见,那就是你需要为分布式缓存引入额外的服务比如 Redis 或 Memcached,你需要单独保证 Redis 或 Memcached 服务的高可用。
### 5. 缓存读写模式/更新策略
**下面介绍到的三种模式各有优劣,不存在最佳模式,根据具体的业务场景选择适合自己的缓存读写模式。**
#### 5.1. Cache Aside Pattern(旁路缓存模式)
1.
写:更新 DB,然后直接删除 cache 。
2.
读:从 cache 中读取数据,读取到就直接返回,读取不到的话,就从 DB 中取数据返回,然后再把数据放到 cache 中。
Cache Aside Pattern 中服务端需要同时维系 DB 和 cache,并且是以 DB 的结果为准。另外,Cache Aside Pattern 有首次请求数据一定不在 cache 的问题,对于热点数据可以提前放入缓存中。
**Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。**
#### 5.2. Read/Write Through Pattern(读写穿透)
Read/Write Through 套路是:服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 DB,从而减轻了应用程序的职责。
1.
写(Write Through):先查 cache,cache 中不存在,直接更新 DB。 cache 中存在,则先更新 cache,然后 cache 服务自己更新 DB(
**同步更新 cache 和 DB**
)。
2.
读(Read Through): 从 cache 中读取数据,读取到就直接返回 。读取不到的话,先从 DB 加载,写入到 cache 后返回响应。
Read-Through Pattern 实际只是在 Cache-Aside Pattern 之上进行了封装。在 Cache-Aside Pattern 下,发生读请求的时候,如果 cache 中不存在对应的数据,是由客户端自己负责把数据写入 cache,而 Read Through Pattern 则是 cache 服务自己来写入缓存的,这对客户端是透明的。
和 Cache Aside Pattern 一样, Read-Through Pattern 也有首次请求数据一定不再 cache 的问题,对于热点数据可以提前放入缓存中。
#### 5.3. Write Behind Pattern(异步缓存写入)
Write Behind Pattern 和 Read/Write Through Pattern 很相似,两者都是由 cache 服务来负责 cache 和 DB 的读写。
但是,两个又有很大的不同:
**Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。**
**Write Behind Pattern 下 DB 的写性能非常高,尤其适合一些数据经常变化的业务场景比如说一篇文章的点赞数量、阅读数量。**
往常一篇文章被点赞 500 次的话,需要重复修改 500 次 DB,但是在 Write Behind Pattern 下可能只需要修改一次 DB 就可以了。
但是,这种模式同样也给 DB 和 Cache 一致性带来了新的考验,很多时候如果数据还没异步更新到 DB 的话,Cache 服务宕机就 gg 了。
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录