Merge pull request #8 from CyC2018/master

merge
This commit is contained in:
林锴 2018-08-22 14:25:53 +08:00 committed by GitHub
commit a212611c84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 95 additions and 128 deletions

View File

@ -1,6 +1,6 @@
| | Ⅱ | Ⅲ | Ⅳ | | Ⅵ | Ⅶ | Ⅷ | Ⅸ | | | | Ⅱ | Ⅲ | Ⅳ | | Ⅵ | Ⅶ | Ⅷ | Ⅸ | |
| :--------: | :---------: | :---------: | :---------: | :---------: | :---------:| :---------: | :-------: | :-------:| :------:| | :--------: | :---------: | :---------: | :---------: | :---------: | :---------:| :---------: | :-------: | :-------:| :------:|
| 算法[:pencil2:](#算法-pencil2) | 操作系统[:computer:](#操作系统-computer)|网络[:cloud:](#网络-cloud) | 面向对象[:couple:](#面向对象-couple) |数据库[:floppy_disk:](#数据库-floppy_disk)| Java [:coffee:](#java-coffee)| 系统设计[:bulb:](#系统设计-bulb)| 工具[:hammer:](#工具-hammer)| 编码实践[:speak_no_evil:](#编码实践-speak_no_evil)| 后记[:memo:](#后记-memo) | | 算法[:pencil2:](#pencil2-算法) | 操作系统[:computer:](#computer-操作系统)|网络[:cloud:](#cloud-网络) | 面向对象[:couple:](#couple-面向对象) |数据库[:floppy_disk:](#floppy_disk-数据库)| Java [:coffee:](#coffee-java)| 系统设计[:bulb:](#bulb-系统设计)| 工具[:hammer:](#hammer-工具)| 编码实践[:speak_no_evil:](#speak_no_evil-编码实践)| 后记[:memo:](#memo-后记) |
<br> <br>
<div align="center"> <div align="center">
@ -11,7 +11,7 @@
<!-- [![](https://img.shields.io/badge/>-gitter-blue.svg)](https://gitter.im/CyC2018-Interview-Notebook/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) [![](https://img.shields.io/badge/_-gitbook-4ab8a1.svg)](https://legacy.gitbook.com/book/cyc2018/interview-notebook/details) --> <!-- [![](https://img.shields.io/badge/>-gitter-blue.svg)](https://gitter.im/CyC2018-Interview-Notebook/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) [![](https://img.shields.io/badge/_-gitbook-4ab8a1.svg)](https://legacy.gitbook.com/book/cyc2018/interview-notebook/details) -->
## 算法 :pencil2: ## :pencil2: 算法
- [剑指 Offer 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/剑指%20offer%20题解.md) - [剑指 Offer 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/剑指%20offer%20题解.md)
@ -25,7 +25,7 @@
排序、并查集、栈和队列、红黑树、散列表。 排序、并查集、栈和队列、红黑树、散列表。
## 操作系统 :computer: ## :computer: 操作系统
- [计算机操作系统](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机操作系统.md) - [计算机操作系统](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机操作系统.md)
@ -35,7 +35,7 @@
基本实现原理以及基本操作。 基本实现原理以及基本操作。
## 网络 :cloud: ## :cloud: 网络
- [计算机网络](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机网络.md) - [计算机网络](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机网络.md)
@ -49,7 +49,7 @@
I/O 模型、I/O 多路复用。 I/O 模型、I/O 多路复用。
## 面向对象 :couple: ## :couple: 面向对象
- [设计模式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/设计模式.md) - [设计模式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/设计模式.md)
@ -59,7 +59,7 @@
三大原则(继承、封装、多态)、类图、设计原则。 三大原则(继承、封装、多态)、类图、设计原则。
## 数据库 :floppy_disk: ## :floppy_disk: 数据库
- [数据库系统原理](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/数据库系统原理.md) - [数据库系统原理](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/数据库系统原理.md)
@ -81,7 +81,7 @@
五种数据类型、字典和跳跃表数据结构、使用场景、和 Memcache 的比较、淘汰策略、持久化、文件事件的 Reactor 模式、复制。 五种数据类型、字典和跳跃表数据结构、使用场景、和 Memcache 的比较、淘汰策略、持久化、文件事件的 Reactor 模式、复制。
## Java :coffee: ## :coffee: Java
- [Java 基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20基础.md) - [Java 基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20基础.md)
@ -103,7 +103,7 @@
NIO 的原理以及实例。 NIO 的原理以及实例。
## 系统设计 :bulb: ## :bulb: 系统设计
- [系统设计基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/系统设计基础.md) - [系统设计基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/系统设计基础.md)
@ -129,7 +129,7 @@
消息处理模型、使用场景、可靠性 消息处理模型、使用场景、可靠性
## 工具 :hammer: ## :hammer: 工具
- [Git](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Git.md) - [Git](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Git.md)
@ -147,7 +147,7 @@
构建工具的基本概念、主流构建工具介绍。 构建工具的基本概念、主流构建工具介绍。
## 编码实践 :speak_no_evil: ## :speak_no_evil: 编码实践
- [重构](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/重构.md) - [重构](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/重构.md)
@ -161,7 +161,7 @@
Google 开源项目的代码风格规范。 Google 开源项目的代码风格规范。
## 后记 :memo: ## :memo: 后记
### About ### About
@ -195,12 +195,6 @@
笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github或者制作项目 README 文档时生成目录时使用:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。 笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github或者制作项目 README 文档时生成目录时使用:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。
### License
在对本作品进行演绎时,请署名并以相同方式共享。
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a>
### Statement ### Statement
本仓库不参与商业行为,不向读者收取任何费用。(This repository is not engaging in business activities, and does not charge readers any fee.) 本仓库不参与商业行为,不向读者收取任何费用。(This repository is not engaging in business activities, and does not charge readers any fee.)
@ -235,5 +229,9 @@ Power by [logomakr](https://logomakr.com/).
<img src="other/10072416.jpg" width="50px"> <img src="other/10072416.jpg" width="50px">
</a> </a>
### License
在对本作品进行演绎时,请署名并以相同方式共享。
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a>

View File

@ -30,15 +30,15 @@ Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程
## 启动速度 ## 启动速度
启动虚拟机需要启动虚拟机的操作系统,再启动相应的应用,这个过程非常慢; 启动虚拟机需要启动虚拟机的操作系统,再启动应用,这个过程非常慢;
而启动 Docker 相当于启动宿主操作系统上的一个进程。 而启动 Docker 相当于启动宿主操作系统上的一个进程。
## 占用资源 ## 占用资源
虚拟机是一个完整的操作系统,需要占用大量的磁盘空间、内存和 CPU一台机器只能开启几十个的虚拟机。 虚拟机是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU一台机器只能开启几十个的虚拟机。
而 Docker 只是一个进程,只需要将应用以及相的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。 而 Docker 只是一个进程,只需要将应用以及相的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。
参考资料: 参考资料:
@ -58,7 +58,7 @@ Docker 使用分层技术和镜像,使得应用可以更容易复用重复部
## 更容易扩展 ## 更容易扩展
可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像得到我们想要的镜像非常容易 可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像可以非常容易得到我们想要的镜像。
参考资料: 参考资料:
@ -91,7 +91,7 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服
镜像包含着容器运行时所需要的代码以及其它组件它是一种分层结构每一层都是只读的read-only layers。构建镜像时会一层一层构建前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。 镜像包含着容器运行时所需要的代码以及其它组件它是一种分层结构每一层都是只读的read-only layers。构建镜像时会一层一层构建前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。
构建容器时通过在镜像的基础上添加一个可写层writable layer用来保存着容器运行过程中的修改。 构建容器时通过在镜像的基础上添加一个可写层writable layer用来保存着容器运行过程中的修改。
<div align="center"> <img src="../pics//docker-filesystems-busyboxrw.png"/> </div><br> <div align="center"> <img src="../pics//docker-filesystems-busyboxrw.png"/> </div><br>
@ -100,3 +100,4 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服
- [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/) - [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/)
- [理解 Docker2Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html) - [理解 Docker2Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html)

View File

@ -707,7 +707,7 @@ public class EqualExample {
## hashCode() ## hashCode()
hasCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。 hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。 在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。

View File

@ -184,10 +184,7 @@ public static void main(String[] args) {
mt.start(); mt.start();
} }
``` ```
## 三种实现方式的比较
- 实现Runnable接又可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;适合多个相同程序代码的线程区处理同一资源的情况。
- 继承Thread类和实现Runnable方法启动线 程都是使用start方法然后JVM虚拟机将此线程放到就绪队列中如果有处理机可用 则执行run方法。
- 实现Callable接又要实现call方法并且线 程执行完毕后会有返回值。其他的两种都是 重写run方法没有返回值。
## 实现接口 VS 继承 Thread ## 实现接口 VS 继承 Thread
实现接口会更好一些,因为: 实现接口会更好一些,因为:
@ -1023,7 +1020,7 @@ ForkJoinPool 实现了工作窃取算法来提高 CPU 的利用率。每个线
如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。 如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。
以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值为 997 而不是 1000。 以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值有可能小于 1000。
```java ```java
public class ThreadUnsafeExample { public class ThreadUnsafeExample {

View File

@ -3602,7 +3602,7 @@ public int countPrimes(int n) {
```java ```java
int gcd(int a, int b) { int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a% b); return b == 0 ? a : gcd(b, a % b);
} }
``` ```
@ -3673,7 +3673,7 @@ public String convertToBase7(int num) {
} }
``` ```
Java 中 static String toString(int num, int radix) 可以将一个整数转换为 redix 进制表示的字符串。 Java 中 static String toString(int num, int radix) 可以将一个整数转换为 radix 进制表示的字符串。
```java ```java
public String convertToBase7(int num) { public String convertToBase7(int num) {
@ -6403,7 +6403,6 @@ public int maxChunksToSorted(int[] arr) {
} }
``` ```
## 图 ## 图
### 二分图 ### 二分图
@ -6620,6 +6619,7 @@ public int[] findRedundantConnection(int[][] edges) {
} }
private class UF { private class UF {
private int[] id; private int[] id;
UF(int N) { UF(int N) {
@ -6665,7 +6665,7 @@ x ^ x = 0 x & x = x x | x = x
``` ```
- 利用 x ^ 1s = \~x 的特点,可以将位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数。 - 利用 x ^ 1s = \~x 的特点,可以将位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数。
- 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask 00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。 - 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。
- 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。 - 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。
位与运算技巧: 位与运算技巧:
@ -6910,7 +6910,6 @@ public boolean isPowerOfFour(int num) {
} }
``` ```
**判断一个数的位级表示是否不会出现连续的 0 和 1** **判断一个数的位级表示是否不会出现连续的 0 和 1**
[693. Binary Number with Alternating Bits (Easy)](https://leetcode.com/problems/binary-number-with-alternating-bits/description/) [693. Binary Number with Alternating Bits (Easy)](https://leetcode.com/problems/binary-number-with-alternating-bits/description/)

View File

@ -19,10 +19,11 @@
* [五、Paxos](#五paxos) * [五、Paxos](#五paxos)
* [执行过程](#执行过程) * [执行过程](#执行过程)
* [约束条件](#约束条件) * [约束条件](#约束条件)
* [五、Raft](#五raft) * [六、Raft](#六raft)
* [单个 Candidate 的竞选](#单个-candidate-的竞选) * [单个 Candidate 的竞选](#单个-candidate-的竞选)
* [多个 Candidate 竞选](#多个-candidate-竞选) * [多个 Candidate 竞选](#多个-candidate-竞选)
* [日志复制](#日志复制) * [数据同步](#数据同步)
* [参考](#参考)
<!-- GFM-TOC --> <!-- GFM-TOC -->
@ -35,7 +36,7 @@
- 互斥量为 1 表示有其它进程在使用锁,此时处于锁定状态; - 互斥量为 1 表示有其它进程在使用锁,此时处于锁定状态;
- 互斥量为 0 表示未锁定状态。 - 互斥量为 0 表示未锁定状态。
1 和 0 可以用一个整型值表示,也可以用某个数据存在或者不存在表示,存在表示互斥量为 1。 1 和 0 可以用一个整型值表示,也可以用某个数据是否存在表示,存在表示互斥量为 1。
## 数据库的唯一索引 ## 数据库的唯一索引
@ -43,7 +44,7 @@
存在以下几个问题: 存在以下几个问题:
- 锁没有失效时间,解锁失败的话其它进程无法再获得锁。 - 锁没有失效时间,解锁失败的话其它进程无法再获得锁。
- 只能是非阻塞锁,插入失败直接就报错了,无法重试。 - 只能是非阻塞锁,插入失败直接就报错了,无法重试。
- 不可重入,已经获得锁的进程也必须重新获取锁。 - 不可重入,已经获得锁的进程也必须重新获取锁。
@ -59,7 +60,7 @@ EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了
使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。 使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。
- 尝试从 N 个相互独立 Redis 实例获取锁,如果一个实例不可用,应该尽快尝试下一个 - 尝试从 N 个相互独立 Redis 实例获取锁;
- 计算获取锁消耗的时间只有当这个时间小于锁的过期时间并且从大多数N / 2 + 1实例上获取了锁那么就认为锁获取成功了 - 计算获取锁消耗的时间只有当这个时间小于锁的过期时间并且从大多数N / 2 + 1实例上获取了锁那么就认为锁获取成功了
- 如果锁获取失败,就到每个实例上释放锁。 - 如果锁获取失败,就到每个实例上释放锁。
@ -67,7 +68,7 @@ EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了
### 1. Zookeeper 抽象模型 ### 1. Zookeeper 抽象模型
Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示它的父节点为 /app1。 Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父节点为 /app1。
<div align="center"> <img src="../pics//31d99967-1171-448e-8531-bccf5c14cffe.jpg" width="400"/> </div><br> <div align="center"> <img src="../pics//31d99967-1171-448e-8531-bccf5c14cffe.jpg" width="400"/> </div><br>
@ -96,21 +97,15 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示
一个节点未获得锁,需要监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知(羊群效应),而我们只希望它的后一个子节点收到通知。 一个节点未获得锁,需要监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知(羊群效应),而我们只希望它的后一个子节点收到通知。
参考:
- [Distributed locks with Redis](https://redis.io/topics/distlock)
- [浅谈分布式锁](http://www.linkedkeeper.com/detail/blog.action?bid=1023)
- [基于 Zookeeper 的分布式锁](http://www.dengshenyu.com/java/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/10/23/zookeeper-distributed-lock.html)
# 二、分布式事务 # 二、分布式事务
指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务。 指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。
例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务。
## 本地消息表 ## 本地消息表
### 1. 原理 本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。
本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性。
1. 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。 1. 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。
2. 之后将本地消息表中的消息转发到 Kafka 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。 2. 之后将本地消息表中的消息转发到 Kafka 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。
@ -118,23 +113,19 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示
<div align="center"> <img src="../pics//e3bf5de4-ab1e-4a9b-896d-4b0ad7e9220a.jpg"/> </div><br> <div align="center"> <img src="../pics//e3bf5de4-ab1e-4a9b-896d-4b0ad7e9220a.jpg"/> </div><br>
### 2. 分析
本地消息表利用了本地事务来实现分布式事务,并且使用了消息队列来保证最终一致性。
## 2PC ## 2PC
两阶段提交Two-phase Commit2PC通过引入协调者Coordinator来协调参与者的行为并最终决定这些参与者是否要真正执行事务。 两阶段提交Two-phase Commit2PC通过引入协调者Coordinator来协调参与者的行为并最终决定这些参与者是否要真正执行事务。
### 1. 运行过程 ### 1. 运行过程
(一)准备阶段 #### 1.1 准备阶段
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。 协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
<div align="center"> <img src="../pics//04f41228-375d-4b7d-bfef-738c5a7c8f07.jpg"/> </div><br> <div align="center"> <img src="../pics//04f41228-375d-4b7d-bfef-738c5a7c8f07.jpg"/> </div><br>
(二)提交阶段 #### 1.2 提交阶段
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。 如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
@ -144,28 +135,22 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示
### 2. 存在的问题 ### 2. 存在的问题
(一)同步阻塞 #### 2.1 同步阻塞
所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
(二)单点问题 #### 2.2 单点问题
协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
(三)数据不一致 #### 2.3 数据不一致
在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
(四)太过保守 #### 2.4 太过保守
任意一个节点失败就会导致整个事务失败,没有完善的容错机制。 任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
参考:
- [聊聊分布式事务,再说说解决方案](https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html)
- [分布式系统的事务处理](https://coolshell.cn/articles/10910.html)
- [深入理解分布式事务](https://juejin.im/entry/577c6f220a2b5800573492be)
# 三、CAP # 三、CAP
分布式系统不可能同时满足一致性CConsistency、可用性AAvailability和分区容忍性PPartition Tolerance最多只能同时满足其中两项。 分布式系统不可能同时满足一致性CConsistency、可用性AAvailability和分区容忍性PPartition Tolerance最多只能同时满足其中两项。
@ -184,7 +169,7 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示
可用性指分布式系统在面对各种异常时可以提供正常服务的能力可以用系统可用时间占总时间的比值来衡量4 个 9 的可用性表示系统 99.99% 的时间是可用的。 可用性指分布式系统在面对各种异常时可以提供正常服务的能力可以用系统可用时间占总时间的比值来衡量4 个 9 的可用性表示系统 99.99% 的时间是可用的。
在可用性条件下,系统提供的服务一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。 在可用性条件下,要求系统提供的服务一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。
## 分区容忍性 ## 分区容忍性
@ -198,16 +183,11 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示
可用性和一致性往往是冲突的,很难都使它们同时满足。在多个节点之间进行数据同步时, 可用性和一致性往往是冲突的,很难都使它们同时满足。在多个节点之间进行数据同步时,
* 为了保证一致性CP就需要让所有节点下线成为不可用的状态等待同步完成 - 为了保证一致性CP就需要让所有节点下线成为不可用的状态等待同步完成
* 为了保证可用性AP在同步过程中允许读取所有节点的数据但是数据可能不一致。 - 为了保证可用性AP在同步过程中允许读取所有节点的数据但是数据可能不一致。
<div align="center"> <img src="../pics//0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg"/> </div><br> <div align="center"> <img src="../pics//0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg"/> </div><br>
参考:
- 倪超. 从 Paxos 到 ZooKeeper : 分布式一致性原理与实践 [M]. 电子工业出版社, 2015.
- [What is CAP theorem in distributed database system?](http://www.colooshiki.com/index.php/2017/04/20/what-is-cap-theorem-in-distributed-database-system/)
# 四、BASE # 四、BASE
BASE 是基本可用Basically Available、软状态Soft State和最终一致性Eventually Consistent三个短语的缩写。 BASE 是基本可用Basically Available、软状态Soft State和最终一致性Eventually Consistent三个短语的缩写。
@ -292,66 +272,69 @@ Acceptor 接收到接受请求时,如果序号大于等于该 Acceptor 承诺
Paxos 协议能够让 Proposer 发送的提议朝着能被大多数 Acceptor 接受的那个提议靠拢,因此能够保证可终止性。 Paxos 协议能够让 Proposer 发送的提议朝着能被大多数 Acceptor 接受的那个提议靠拢,因此能够保证可终止性。
参考: # 六、Raft
- [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/) Raft 也是分布式一致性协议,主要是用来竞选主节点。
- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/)
# 五、Raft
Raft 和 Paxos 类似,但是更容易理解,也更容易实现。
Raft 主要是用来竞选主节点。
## 单个 Candidate 的竞选 ## 单个 Candidate 的竞选
有三种节点Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一般为 150ms\~300ms如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate进入竞选阶段。 有三种节点Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一般为 150ms\~300ms如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate进入竞选阶段。
* 下图表示一个分布式系统的最初阶段,此时只有 Follower没有 Leader。Follower A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。 - 下图展示一个分布式系统的最初阶段,此时只有 Follower 没有 Leader。Node A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。
<div align="center"> <img src="../pics//111521118015898.gif"/> </div><br> <div align="center"> <img src="../pics//111521118015898.gif"/> </div><br>
* 此时 A 发送投票请求给其它所有节点。 - 此时 Node A 发送投票请求给其它所有节点。
<div align="center"> <img src="../pics//111521118445538.gif"/> </div><br> <div align="center"> <img src="../pics//111521118445538.gif"/> </div><br>
* 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。 - 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。
<div align="center"> <img src="../pics//111521118483039.gif"/> </div><br> <div align="center"> <img src="../pics//111521118483039.gif"/> </div><br>
* 之后 Leader 会周期性地发送心跳包给 FollowerFollower 接收到心跳包,会重新开始计时。 - 之后 Leader 会周期性地发送心跳包给 FollowerFollower 接收到心跳包,会重新开始计时。
<div align="center"> <img src="../pics//111521118640738.gif"/> </div><br> <div align="center"> <img src="../pics//111521118640738.gif"/> </div><br>
## 多个 Candidate 竞选 ## 多个 Candidate 竞选
* 如果有多个 Follower 成为 Candidate并且所获得票数相同那么就需要重新开始投票,例如下图中 Candidate B 和 Candidate D 都获得两票,因此需要重新开始投票。 - 如果有多个 Follower 成为 Candidate并且所获得票数相同那么就需要重新开始投票。例如下图中 Node B 和 Node D 都获得两票,需要重新开始投票。
<div align="center"> <img src="../pics//111521119203347.gif"/> </div><br> <div align="center"> <img src="../pics//111521119203347.gif"/> </div><br>
* 当重新开始投票时,由于每个节点设置的随机竞选超时时间不同,因此下一次再次出现多个 Candidate 并获得同样票数的概率很低。 - 由于每个节点设置的随机竞选超时时间不同,因此下一次再次出现多个 Candidate 并获得同样票数的概率很低。
<div align="center"> <img src="../pics//111521119368714.gif"/> </div><br> <div align="center"> <img src="../pics//111521119368714.gif"/> </div><br>
## 日志复制 ## 数据同步
* 来自客户端的修改都会被传入 Leader。注意该修改还未被提交只是写入日志中。 - 来自客户端的修改都会被传入 Leader。注意该修改还未被提交只是写入日志中。
<div align="center"> <img src="../pics//7.gif"/> </div><br> <div align="center"> <img src="../pics//7.gif"/> </div><br>
* Leader 会把修改复制到所有 Follower。 - Leader 会把修改复制到所有 Follower。
<div align="center"> <img src="../pics//9.gif"/> </div><br> <div align="center"> <img src="../pics//9.gif"/> </div><br>
* Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。 - Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。
<div align="center"> <img src="../pics//10.gif"/> </div><br> <div align="center"> <img src="../pics//10.gif"/> </div><br>
* 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。 - 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。
<div align="center"> <img src="../pics//11.gif"/> </div><br> <div align="center"> <img src="../pics//11.gif"/> </div><br>
参考 # 参考
- 倪超. 从 Paxos 到 ZooKeeper : 分布式一致性原理与实践 [M]. 电子工业出版社, 2015.
- [Distributed locks with Redis](https://redis.io/topics/distlock)
- [浅谈分布式锁](http://www.linkedkeeper.com/detail/blog.action?bid=1023)
- [基于 Zookeeper 的分布式锁](http://www.dengshenyu.com/java/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/10/23/zookeeper-distributed-lock.html)
- [Raft: Understandable Distributed Consensus](http://thesecretlivesofdata.com/raft) - [Raft: Understandable Distributed Consensus](http://thesecretlivesofdata.com/raft)
- [聊聊分布式事务,再说说解决方案](https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html)
- [分布式系统的事务处理](https://coolshell.cn/articles/10910.html)
- [深入理解分布式事务](https://juejin.im/entry/577c6f220a2b5800573492be)
- [What is CAP theorem in distributed database system?](http://www.colooshiki.com/index.php/2017/04/20/what-is-cap-theorem-in-distributed-database-system/)
- [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/)
- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/)

View File

@ -207,7 +207,7 @@ ResultSet rs = stmt.executeQuery();
拒绝服务攻击denial-of-service attackDoS亦称洪水攻击其目的在于使目标电脑的网络或系统资源耗尽使服务暂时中断或停止导致其正常用户无法访问。 拒绝服务攻击denial-of-service attackDoS亦称洪水攻击其目的在于使目标电脑的网络或系统资源耗尽使服务暂时中断或停止导致其正常用户无法访问。
分布式拒绝服务攻击distributed denial-of-service attackDDoS指攻击者使用网络上两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。 分布式拒绝服务攻击distributed denial-of-service attackDDoS指攻击者使用两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。
# 参考资料 # 参考资料

View File

@ -9,6 +9,7 @@
* [三、可靠性](#三可靠性) * [三、可靠性](#三可靠性)
* [发送端的可靠性](#发送端的可靠性) * [发送端的可靠性](#发送端的可靠性)
* [接收端的可靠性](#接收端的可靠性) * [接收端的可靠性](#接收端的可靠性)
* [参考资料](#参考资料)
<!-- GFM-TOC --> <!-- GFM-TOC -->
@ -29,15 +30,10 @@
发布与订阅模式和观察者模式有以下不同: 发布与订阅模式和观察者模式有以下不同:
- 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,发布者与订阅者不知道对方的存在,它们之间通过频道进行通信。 - 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,发布者与订阅者不知道对方的存在,它们之间通过频道进行通信。
- 观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,发布者向频道发送一个消息之后,就不需要关心订阅者何时去订阅这个消息。 - 观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,发布者向频道发送一个消息之后,就不需要关心订阅者何时去订阅这个消息,可以立即返回
<div align="center"> <img src="../pics//bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg"/> </div><br> <div align="center"> <img src="../pics//bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg"/> </div><br>
参考:
- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/)
- [消息队列中点对点与发布订阅区别](https://blog.csdn.net/lizhitao/article/details/47723105)
# 二、使用场景 # 二、使用场景
## 异步处理 ## 异步处理
@ -78,3 +74,8 @@
- 保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。 - 保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。
- 保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号。 - 保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号。
# 参考资料
- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/)
- [消息队列中点对点与发布订阅区别](https://blog.csdn.net/lizhitao/article/details/47723105)

View File

@ -507,7 +507,7 @@ public class QuickSort<T extends Comparable<T>> extends Sort<T> {
取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。 取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。
<div align="center"> <img src="../pics//864bfa7d-1149-420c-a752-f9b3d4e782ec.png" width="400"/> </div><br> <div align="center"> <img src="../pics//766aedd0-1b00-4065-aa2b-7d31138df84f.png" width="400"/> </div><br>
```java ```java
private int partition(T[] nums, int l, int h) { private int partition(T[] nums, int l, int h) {
@ -770,7 +770,7 @@ public class HeapSort<T extends Comparable<T>> extends Sort<T> {
| 算法 | 稳定 | 时间复杂度 | 空间复杂度 | 备注 | | 算法 | 稳定 | 时间复杂度 | 空间复杂度 | 备注 |
| :---: | :---: |:---: | :---: | :---: | | :---: | :---: |:---: | :---: | :---: |
| 选择排序 | | N<sup>2</sup> | 1 | | | 选择排序 | × | N<sup>2</sup> | 1 | |
| 冒泡排序 | √ | N<sup>2</sup> | 1 | | | 冒泡排序 | √ | N<sup>2</sup> | 1 | |
| 插入排序 | √ | N \~ N<sup>2</sup> | 1 | 时间复杂度和初始顺序有关 | | 插入排序 | √ | N \~ N<sup>2</sup> | 1 | 时间复杂度和初始顺序有关 |
| 希尔排序 | × | N 的若干倍乘于递增序列的长度 | 1 | | | 希尔排序 | × | N 的若干倍乘于递增序列的长度 | 1 | |

View File

@ -68,7 +68,7 @@
应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。 应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。
关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器存储空间限制。 关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器存储空间限制。
对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。 对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。
@ -78,7 +78,7 @@
实现可扩展主要有两种方式: 实现可扩展主要有两种方式:
- 使用消息队列进行解耦,应用之间通过消息传递的方式进行通信; - 使用消息队列进行解耦,应用之间通过消息传递进行通信;
- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以通过调用可复用的服务来实现业务逻辑,对其它产品没有影响。 - 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以通过调用可复用的服务来实现业务逻辑,对其它产品没有影响。
# 四、可用性 # 四、可用性
@ -87,7 +87,7 @@
保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。 保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。
应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上不会对用户有任何影响。 应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上不会对用户有任何影响。
存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。 存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。
@ -97,7 +97,7 @@
## 服务降级 ## 服务降级
服务降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。 服务降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。
# 五、安全性 # 五、安全性

View File

@ -6,6 +6,7 @@
* [五、缓存问题](#五缓存问题) * [五、缓存问题](#五缓存问题)
* [六、数据分布](#六数据分布) * [六、数据分布](#六数据分布)
* [七、一致性哈希](#七一致性哈希) * [七、一致性哈希](#七一致性哈希)
* [参考资料](#参考资料)
<!-- GFM-TOC --> <!-- GFM-TOC -->
@ -29,10 +30,6 @@
- LRULeast Recently Used最近最久未使用策略优先淘汰最久未使用的数据也就是上次被访问时间距离现在最远的数据。该策略可以保证内存中的数据都是热点数据也就是经常被访问的数据从而保证缓存命中率。 - LRULeast Recently Used最近最久未使用策略优先淘汰最久未使用的数据也就是上次被访问时间距离现在最远的数据。该策略可以保证内存中的数据都是热点数据也就是经常被访问的数据从而保证缓存命中率。
参考资料:
- [缓存那些事](https://tech.meituan.com/cache_about.html)
# 二、LRU # 二、LRU
以下是一个基于 双向链表 + HashMap 的 LRU 算法实现,对算法的解释如下: 以下是一个基于 双向链表 + HashMap 的 LRU 算法实现,对算法的解释如下:
@ -143,10 +140,6 @@ public class LRU<K, V> implements Iterable<K> {
} }
``` ```
源代码:
- [CyC2018/Algorithm](https://github.com/CyC2018/Algorithm/tree/master/Caching)
# 三、缓存位置 # 三、缓存位置
## 浏览器 ## 浏览器
@ -169,7 +162,7 @@ public class LRU<K, V> implements Iterable<K> {
使用 Redis、Memcache 等分布式缓存将数据缓存在分布式缓存系统中。 使用 Redis、Memcache 等分布式缓存将数据缓存在分布式缓存系统中。
相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存而本地缓存需要在服务器集群之间进行同步,实现和性能开销上都非常大。 相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存而本地缓存需要在服务器集群之间进行同步,实现和性能开销上都非常大。
## 数据库缓存 ## 数据库缓存
@ -177,7 +170,7 @@ MySQL 等数据库管理系统具有自己的查询缓存机制来提高 SQL 查
# 四、CDN # 四、CDN
内容分发网络Content distribution networkCDN是一种通过互连的网络系统,利用更靠近用户的服务器更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。 内容分发网络Content distribution networkCDN是一种互连的网络系统利用更靠近用户的服务器从而更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。
CDN 主要有以下优点: CDN 主要有以下优点:
@ -187,11 +180,6 @@ CDN 主要有以下优点:
<div align="center"> <img src="../pics//15313ed8-a520-4799-a300-2b6b36be314f.jpg"/> </div><br> <div align="center"> <img src="../pics//15313ed8-a520-4799-a300-2b6b36be314f.jpg"/> </div><br>
参考资料:
- [内容分发网络](https://zh.wikipedia.org/wiki/%E5%85%A7%E5%AE%B9%E5%82%B3%E9%81%9E%E7%B6%B2%E8%B7%AF)
- [How Aspiration CDN helps to improve your website loading speed?](https://www.aspirationhosting.com/aspiration-cdn/)
# 五、缓存问题 # 五、缓存问题
## 缓存穿透 ## 缓存穿透
@ -213,7 +201,7 @@ CDN 主要有以下优点:
- 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现; - 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现;
- 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。 - 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。
- 也可以进行缓存预热,避免在系统刚启动不久由于还未将大量数据进行缓存而导致缓存雪崩。 - 也可以进行缓存预热,避免在系统刚启动不久由于还未将大量数据进行缓存而导致缓存雪崩。
## 缓存一致性 ## 缓存一致性
@ -243,10 +231,6 @@ CDN 主要有以下优点:
- 能保持数据原有的顺序; - 能保持数据原有的顺序;
- 并且能够准确控制每台服务器存储的数据量,从而使得存储空间的利用率最大。 - 并且能够准确控制每台服务器存储的数据量,从而使得存储空间的利用率最大。
参考资料:
- 大规模分布式存储系统
# 七、一致性哈希 # 七、一致性哈希
Distributed Hash TableDHT 是一种哈希分布方式,其目的是为了克服传统哈希分布在服务器节点数量变化时大量数据失效的问题。 Distributed Hash TableDHT 是一种哈希分布方式,其目的是为了克服传统哈希分布在服务器节点数量变化时大量数据失效的问题。
@ -267,6 +251,10 @@ Distributed Hash TableDHT 是一种哈希分布方式,其目的是为了
数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得大,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。 数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得大,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。
参考资料 # 参考资料
- 大规模分布式存储系统
- [缓存那些事](https://tech.meituan.com/cache_about.html)
- [一致性哈希算法](https://my.oschina.net/jayhu/blog/732849) - [一致性哈希算法](https://my.oschina.net/jayhu/blog/732849)
- [内容分发网络](https://zh.wikipedia.org/wiki/%E5%85%A7%E5%AE%B9%E5%82%B3%E9%81%9E%E7%B6%B2%E8%B7%AF)
- [How Aspiration CDN helps to improve your website loading speed?](https://www.aspirationhosting.com/aspiration-cdn/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB