Skip to content

Commit 6cb6982

Browse files
committed
📝 Writing docs.
1 parent 930c32d commit 6cb6982

1 file changed

Lines changed: 89 additions & 0 deletions

File tree

docs/distributed/分布式技术实现.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ tags:
3030
- [分布式存储](#分布式存储)
3131
- [数据分布](#数据分布)
3232
- [数据复制](#数据复制)
33+
- [分布式缓存](#分布式缓存)
34+
- [分布式缓存的实现方式](#分布式缓存的实现方式)
35+
- [缓存问题](#缓存问题)
3336
- [分布式计算](#分布式计算)
3437
- [负载均衡](#负载均衡)
3538
- [算法](#算法)
@@ -568,6 +571,90 @@ tomcat2 server.xml
568571

569572
### 数据复制
570573

574+
## 分布式缓存
575+
576+
使用缓存的好处:
577+
578+
- 提升数据读取速度
579+
- 提升系统扩展能力,通过扩展缓存,提升系统承载能力
580+
- 降低存储成本,Cache+DB 的方式可以承担原有需要多台 DB 才能承担的请求量,节省机器成本
581+
582+
根据业务场景,通常缓存有以下几种使用方式
583+
584+
- 懒汉式(读时触发):写入 DB 后, 然后把相关的数据也写入 Cache
585+
- 饥饿式(写时触发):先查询 DB 里的数据, 然后把相关的数据写入 Cache
586+
- 定期刷新:适合周期性的跑数据的任务,或者列表型的数据,而且不要求绝对实时性
587+
588+
缓存分类:
589+
590+
- 应用内缓存:如:EHCache
591+
- 分布式缓存:如:Memached、Redis
592+
593+
### 分布式缓存的实现方式
594+
595+
#### 数据分片
596+
597+
数据分片就是把数据均匀分散到多个实例中。数据分片可以采用以下几种规则:区间分片、hash 分片、 slot 分片。
598+
599+
对于 hash 分片,主要的哈希算法有静态哈希和一致性哈希,静态哈希和一致性哈希对比如下:
600+
601+
- 静态哈希(取模求余)
602+
- 优点:算法简单
603+
- 缺点:加减节点时震荡厉害, 命中率下降厉害
604+
- 一致性哈希
605+
- 优点:加减节点时震荡较小, 保持较高命中率
606+
- 缺点:自动 rehash 场景下会数据不一致的问题(同一份数据的请求在不同节点漂移)
607+
608+
### 缓存问题
609+
610+
#### 1. 缓存雪崩
611+
612+
缓存雪崩是指:在高并发场景下,由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库 CPU 和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
613+
614+
解决方案:
615+
616+
- 用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。
617+
- 还有一个简单的方案,就是将缓存失效时间分散开,不要所有缓存时间长度都设置成 5 分钟或者 10 分钟;比如我们可以在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
618+
619+
缓存失效时产生的雪崩效应,将所有请求全部放在数据库上,这样很容易就达到数据库的瓶颈,导致服务无法正常提供。尽量避免这种场景的发生。
620+
621+
#### 2. 缓存穿透
622+
623+
缓存穿透是指:用户查询的数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
624+
625+
当在流量较大时,出现这样的情况,一直请求 DB,很容易导致服务挂掉。
626+
627+
解决方案:
628+
629+
1. 在封装的缓存 SET 和 GET 部分增加个步骤,如果查询一个 KEY 不存在,就以这个 KEY 为前缀设定一个标识 KEY;以后再查询该 KEY 的时候,先查询标识 KEY,如果标识 KEY 存在,就返回一个协定好的非 false 或者 NULL 值,然后 APP 做相应的处理,这样缓存层就不会被穿透。当然这个验证 KEY 的失效时间不能太长。
630+
2. 如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,一般只有几分钟。
631+
3. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。
632+
633+
#### 3. 缓存预热
634+
635+
缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
636+
637+
解决方案:
638+
639+
1. 直接写个缓存刷新页面,上线时手工操作下;
640+
2. 数据量不大,可以在项目启动的时候自动进行加载;
641+
3. 定时刷新缓存;
642+
643+
#### 4. 缓存更新
644+
645+
除了缓存服务器自带的缓存失效策略之外(Redis 默认的有 6 中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
646+
647+
1. 定时去清理过期的缓存;
648+
2. 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
649+
650+
两者各有优劣,第一种的缺点是维护大量缓存的 key 是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。
651+
652+
#### 5. 缓存降级
653+
654+
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
655+
656+
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
657+
571658
## 分布式计算
572659

573660
## 负载均衡
@@ -701,3 +788,5 @@ PAC 服务器是用来判断一个请求是否要经过代理。
701788
- https://juejin.im/post/5a20cd8bf265da43163cdd9a
702789
- https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8
703790
- https://github.com/L316476844/distributed-session
791+
- [分布式缓存架构基础](https://juejin.im/entry/57e39e320e3dd90058021bff)
792+
- [阿里P8技术专家细究分布式缓存问题](https://www.toutiao.com/i6533812974807679495/?tt_from=weixin&utm_campaign=client_share&from=singlemessage&timestamp=1521281305&app=news_article&utm_source=weixin&iid=28128279343&utm_medium=toutiao_android&weixin_list=1&wxshare_count=2&pbid=6517746516513195523)

0 commit comments

Comments
 (0)