auto commit
This commit is contained in:
parent
e4c5f6d1f5
commit
ae92ccfc32
|
@ -58,7 +58,7 @@
|
||||||
|
|
||||||
消息中间件也可称作消息系统 (MQ),它本质上是一个暂存转发消息的一个中间件。在分布式应用当中,我们可以把一个业务操作转换成一个消息,比如支付宝的余额转入余额宝操作,支付宝系统执行减少余额操作之后向消息系统发送一个消息,余额宝系统订阅这条消息然后进行增加余额宝操作。
|
消息中间件也可称作消息系统 (MQ),它本质上是一个暂存转发消息的一个中间件。在分布式应用当中,我们可以把一个业务操作转换成一个消息,比如支付宝的余额转入余额宝操作,支付宝系统执行减少余额操作之后向消息系统发送一个消息,余额宝系统订阅这条消息然后进行增加余额宝操作。
|
||||||
|
|
||||||
#### 2.1 消息处理模型
|
**(一)消息处理模型**
|
||||||
|
|
||||||
<font size=3> **点对点** </font></br>
|
<font size=3> **点对点** </font></br>
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//654acfed-a6a5-4fc7-8f40-3fdcae57bae8.jpg" width="700"/> </div><br>
|
<div align="center"> <img src="../pics//654acfed-a6a5-4fc7-8f40-3fdcae57bae8.jpg" width="700"/> </div><br>
|
||||||
|
|
||||||
#### 2.2 消息的可靠性
|
**(二)消息的可靠性**
|
||||||
|
|
||||||
消息的发送端的可靠性:发送端完成操作后一定能将消息成功发送到消息系统。
|
消息的发送端的可靠性:发送端完成操作后一定能将消息成功发送到消息系统。
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ Java 提供了两种内置的锁的实现,一种是由 JVM 实现的 synchroni
|
||||||
|
|
||||||
### 1. 数据库分布式锁
|
### 1. 数据库分布式锁
|
||||||
|
|
||||||
#### 1.1 基于 MySQL 锁表
|
**(一)基于 MySQL 锁表**
|
||||||
|
|
||||||
该实现方式完全依靠数据库唯一索引来实现。当想要获得锁时,就向数据库中插入一条记录,释放锁时就删除这条记录。如果记录具有唯一索引,就不会同时插入同一条记录。这种方式存在以下几个问题:
|
该实现方式完全依靠数据库唯一索引来实现。当想要获得锁时,就向数据库中插入一条记录,释放锁时就删除这条记录。如果记录具有唯一索引,就不会同时插入同一条记录。这种方式存在以下几个问题:
|
||||||
|
|
||||||
|
@ -180,19 +180,19 @@ Java 提供了两种内置的锁的实现,一种是由 JVM 实现的 synchroni
|
||||||
2. 只能是非阻塞锁,插入失败直接就报错了,无法重试。
|
2. 只能是非阻塞锁,插入失败直接就报错了,无法重试。
|
||||||
3. 不可重入,同一线程在没有释放锁之前无法再获得锁。
|
3. 不可重入,同一线程在没有释放锁之前无法再获得锁。
|
||||||
|
|
||||||
#### 1.2 采用乐观锁增加版本号
|
**(二)采用乐观锁增加版本号**
|
||||||
|
|
||||||
根据版本号来判断更新之前有没有其他线程更新过,如果被更新过,则获取锁失败。
|
根据版本号来判断更新之前有没有其他线程更新过,如果被更新过,则获取锁失败。
|
||||||
|
|
||||||
### 2. Redis 分布式锁
|
### 2. Redis 分布式锁
|
||||||
|
|
||||||
#### 2.1 基于 SETNX、EXPIRE
|
**(一)基于 SETNX、EXPIRE**
|
||||||
|
|
||||||
使用 SETNX(set if not exist)命令插入一个键值对时,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。因此客户端在尝试获得锁时,先使用 SETNX 向 Redis 中插入一个记录,如果返回 True 表示获得锁,返回 False 表示已经有客户端占用锁。
|
使用 SETNX(set if not exist)命令插入一个键值对时,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。因此客户端在尝试获得锁时,先使用 SETNX 向 Redis 中插入一个记录,如果返回 True 表示获得锁,返回 False 表示已经有客户端占用锁。
|
||||||
|
|
||||||
EXPIRE 可以为一个键值对设置一个过期时间,从而避免了死锁的发生。
|
EXPIRE 可以为一个键值对设置一个过期时间,从而避免了死锁的发生。
|
||||||
|
|
||||||
#### 2.2 RedLock 算法
|
**(二)RedLock 算法**
|
||||||
|
|
||||||
ReadLock 算法使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时还可用。
|
ReadLock 算法使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时还可用。
|
||||||
|
|
||||||
|
@ -204,34 +204,34 @@ ReadLock 算法使用了多个 Redis 实例来实现分布式锁,这是为了
|
||||||
|
|
||||||
Zookeeper 是一个为分布式应用提供一致性服务的软件,例如配置管理、分布式协同以及命名的中心化等,这些都是分布式系统中非常底层而且是必不可少的基本功能,但是如果自己实现这些功能而且要达到高吞吐、低延迟同时还要保持一致性和可用性,实际上非常困难。
|
Zookeeper 是一个为分布式应用提供一致性服务的软件,例如配置管理、分布式协同以及命名的中心化等,这些都是分布式系统中非常底层而且是必不可少的基本功能,但是如果自己实现这些功能而且要达到高吞吐、低延迟同时还要保持一致性和可用性,实际上非常困难。
|
||||||
|
|
||||||
#### 3.1 抽象模型
|
**(一)抽象模型**
|
||||||
|
|
||||||
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>
|
||||||
|
|
||||||
#### 3.2 节点类型
|
**(二)节点类型**
|
||||||
|
|
||||||
- 永久节点:不会因为会话结束或者超时而消失;
|
- 永久节点:不会因为会话结束或者超时而消失;
|
||||||
- 临时节点:如果会话结束或者超时就会消失;
|
- 临时节点:如果会话结束或者超时就会消失;
|
||||||
- 有序节点:会在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,依次类推。
|
- 有序节点:会在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,依次类推。
|
||||||
|
|
||||||
#### 3.3 监听器
|
**(三)监听器**
|
||||||
|
|
||||||
为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。
|
为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。
|
||||||
|
|
||||||
#### 3.4 分布式锁实现
|
**(四)分布式锁实现**
|
||||||
|
|
||||||
1. 创建一个锁目录 /lock。
|
1. 创建一个锁目录 /lock。
|
||||||
1. 在 /lock 下创建临时的且有序的子节点,第一个客户端对应的子节点为 /lock/lock-0000000000,第二个为 /lock/lock-0000000001,以此类推。
|
1. 在 /lock 下创建临时的且有序的子节点,第一个客户端对应的子节点为 /lock/lock-0000000000,第二个为 /lock/lock-0000000001,以此类推。
|
||||||
2. 客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁;
|
2. 客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁;
|
||||||
3. 执行业务代码,完成后,删除对应的子节点。
|
3. 执行业务代码,完成后,删除对应的子节点。
|
||||||
|
|
||||||
#### 3.5 会话超时
|
**(五)会话超时**
|
||||||
|
|
||||||
如果一个已经获得锁的会话超时了,因为创建的是临时节点,因此该会话对应的临时节点会被删除,其它会话就可以获得锁了。可以看到,Zookeeper 分布式锁不会出现数据库分布式锁的死锁问题。
|
如果一个已经获得锁的会话超时了,因为创建的是临时节点,因此该会话对应的临时节点会被删除,其它会话就可以获得锁了。可以看到,Zookeeper 分布式锁不会出现数据库分布式锁的死锁问题。
|
||||||
|
|
||||||
#### 3.6 羊群效应
|
**(六)羊群效应**
|
||||||
|
|
||||||
在步骤二,一个节点未获得锁,需要监听监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知,而我们只希望它的下一个子节点收到通知。
|
在步骤二,一个节点未获得锁,需要监听监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知,而我们只希望它的下一个子节点收到通知。
|
||||||
|
|
||||||
|
|
37
notes/算法.md
37
notes/算法.md
|
@ -48,23 +48,23 @@
|
||||||
|
|
||||||
## 数学模型
|
## 数学模型
|
||||||
|
|
||||||
<font size=4> **1. 近似** </font></br>
|
### 1. 近似
|
||||||
|
|
||||||
使用 \~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数 , 例如 N<sup>3</sup>/6-N<sup>2</sup>/2+N/3 \~ N<sup>3</sup>/6。
|
使用 \~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数 , 例如 N<sup>3</sup>/6-N<sup>2</sup>/2+N/3 \~ N<sup>3</sup>/6。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//8f1e2db5-a59b-4633-8b61-6b8b9505b8ea.png" width="600"/> </div><br>
|
<div align="center"> <img src="../pics//8f1e2db5-a59b-4633-8b61-6b8b9505b8ea.png" width="600"/> </div><br>
|
||||||
|
|
||||||
<font size=4> **2. 增长数量级** </font></br>
|
### 2. 增长数量级
|
||||||
|
|
||||||
增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 N<sup>3</sup> 与它是否用 Java 实现,是否运行于特定计算机上无关。
|
增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 N<sup>3</sup> 与它是否用 Java 实现,是否运行于特定计算机上无关。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//521969c9-71f6-44a5-9c78-118530e5c135.png" width="700"/> </div><br>
|
<div align="center"> <img src="../pics//521969c9-71f6-44a5-9c78-118530e5c135.png" width="700"/> </div><br>
|
||||||
|
|
||||||
<font size=4> **3. 内循环** </font></br>
|
### 3. 内循环
|
||||||
|
|
||||||
执行最频繁的指令决定了程序执行的总时间,把这些指令称为程序的内循环。
|
执行最频繁的指令决定了程序执行的总时间,把这些指令称为程序的内循环。
|
||||||
|
|
||||||
<font size=4> **4. 成本模型** </font></br>
|
### 4. 成本模型
|
||||||
|
|
||||||
使用成本模型来评估算法,例如数组的访问次数就是一种成本模型。
|
使用成本模型来评估算法,例如数组的访问次数就是一种成本模型。
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ public class ThreeSum {
|
||||||
|
|
||||||
该算法的内循环为`if (a[i] + a[j] + a[k] == 0)`语句,总共执行的次数为 N(N-1)(N-2) = N<sup>3</sup>/6 - N<sup>2</sup>/2 + N/3,因此它的近似执行次数为 \~N<sup>3</sup>/6,增长数量级为 N<sup>3</sup>。
|
该算法的内循环为`if (a[i] + a[j] + a[k] == 0)`语句,总共执行的次数为 N(N-1)(N-2) = N<sup>3</sup>/6 - N<sup>2</sup>/2 + N/3,因此它的近似执行次数为 \~N<sup>3</sup>/6,增长数量级为 N<sup>3</sup>。
|
||||||
|
|
||||||
**改进**
|
<font size=4> **改进** </font></br>
|
||||||
|
|
||||||
通过将数组先排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在三元组的和为 0。
|
通过将数组先排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在三元组的和为 0。
|
||||||
|
|
||||||
|
@ -131,31 +131,23 @@ public class ThreeSumFast {
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
<font size=4> **1. 大常数** </font></br>
|
### 1. 大常数
|
||||||
|
|
||||||
|
|
||||||
在求近似时,如果低级项的常数系数很大,那么近似的结果就是错误的。
|
在求近似时,如果低级项的常数系数很大,那么近似的结果就是错误的。
|
||||||
|
|
||||||
<font size=4> **2. 缓存** </font></br>
|
### 2. 缓存
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
计算机系统会使用缓存技术来组织内存,访问数组相邻的元素会比访问不相邻的元素快很多。
|
计算机系统会使用缓存技术来组织内存,访问数组相邻的元素会比访问不相邻的元素快很多。
|
||||||
|
|
||||||
<font size=4> **3. 对最坏情况下的性能的保证** </font></br>
|
### 3. 对最坏情况下的性能的保证
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
在核反应堆、心脏起搏器或者刹车控制器中的软件,最坏情况下的性能是十分重要的。
|
在核反应堆、心脏起搏器或者刹车控制器中的软件,最坏情况下的性能是十分重要的。
|
||||||
|
|
||||||
<font size=4> **4. 随机化算法** </font></br>
|
### 4. 随机化算法
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
通过打乱输入,去除算法对输入的依赖。
|
通过打乱输入,去除算法对输入的依赖。
|
||||||
|
|
||||||
<font size=4> **5. 均摊分析** </font></br>
|
### 5. 均摊分析
|
||||||
|
|
||||||
|
|
||||||
将所有操作的总成本除于操作总数来将成本均摊。例如对一个空栈进行 N 次连续的 push() 调用需要访问数组的元素为 N+4+8+16+...+2N=5N-4(N 是向数组写入元素,其余的都是调整数组大小时进行复制需要的访问数组操作),均摊后每次操作访问数组的平均次数为常数。
|
将所有操作的总成本除于操作总数来将成本均摊。例如对一个空栈进行 N 次连续的 push() 调用需要访问数组的元素为 N+4+8+16+...+2N=5N-4(N 是向数组写入元素,其余的都是调整数组大小时进行复制需要的访问数组操作),均摊后每次操作访问数组的平均次数为常数。
|
||||||
|
|
||||||
|
@ -169,7 +161,6 @@ first-in-last-out(FILO)
|
||||||
|
|
||||||
<font size=4> **1. 数组实现** </font></br>
|
<font size=4> **1. 数组实现** </font></br>
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class ResizeArrayStack<Item> implements Iterable<Item> {
|
public class ResizeArrayStack<Item> implements Iterable<Item> {
|
||||||
private Item[] a = (Item[]) new Object[1];
|
private Item[] a = (Item[]) new Object[1];
|
||||||
|
@ -280,7 +271,6 @@ first-in-first-out(FIFO)
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//c64f91e2-f5a8-436b-8663-b8f3fba3e098.png" width="300"/> </div><br>
|
<div align="center"> <img src="../pics//c64f91e2-f5a8-436b-8663-b8f3fba3e098.png" width="300"/> </div><br>
|
||||||
|
|
||||||
|
|
||||||
下面是队列的链表实现,需要维护 first 和 last 节点指针,分别指向队首和队尾。
|
下面是队列的链表实现,需要维护 first 和 last 节点指针,分别指向队首和队尾。
|
||||||
|
|
||||||
这里需要考虑让哪个指针指针链表头部节点,哪个指针指向链表尾部节点。因为出队列操作需要让队首元素的下一个元素成为队首,就需要容易获取下一个元素,而链表的头部节点的 next 指针指向下一个元素,因此让队首指针 first 指针链表的开头。
|
这里需要考虑让哪个指针指针链表头部节点,哪个指针指向链表尾部节点。因为出队列操作需要让队首元素的下一个元素成为队首,就需要容易获取下一个元素,而链表的头部节点的 next 指针指向下一个元素,因此让队首指针 first 指针链表的开头。
|
||||||
|
@ -482,7 +472,6 @@ private void exch(Comparable[] a, int i, int j){
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## 选择排序
|
## 选择排序
|
||||||
|
|
||||||
找到数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
|
找到数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
|
||||||
|
@ -529,7 +518,7 @@ public class Insertion {
|
||||||
|
|
||||||
插入排序对于部分有序数组和小规模数组特别高效。
|
插入排序对于部分有序数组和小规模数组特别高效。
|
||||||
|
|
||||||
**选择排序和插入排序的比较**
|
<font size=3> **选择排序和插入排序的比较** </font> </br>
|
||||||
|
|
||||||
对于随机排序的无重复主键的数组,插入排序和选择排序的运行时间是平方级别的,两者之比是一个较小的常数。
|
对于随机排序的无重复主键的数组,插入排序和选择排序的运行时间是平方级别的,两者之比是一个较小的常数。
|
||||||
|
|
||||||
|
@ -897,10 +886,8 @@ public static Comparable select(Comparable[] a, int k) {
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//b69d7184-ab62-4957-ba29-fb4fa25f9b65.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//b69d7184-ab62-4957-ba29-fb4fa25f9b65.jpg"/> </div><br>
|
||||||
|
|
||||||
|
|
||||||
<font size=4> **2. 有序符号表** </font></br>
|
<font size=4> **2. 有序符号表** </font></br>
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//ba6ae411-82da-4d86-a434-6776d1731e8e.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//ba6ae411-82da-4d86-a434-6776d1731e8e.jpg"/> </div><br>
|
||||||
|
|
||||||
有序符号表的键需要实现 Comparable 接口。
|
有序符号表的键需要实现 Comparable 接口。
|
||||||
|
@ -909,8 +896,6 @@ public static Comparable select(Comparable[] a, int k) {
|
||||||
|
|
||||||
<font size=4> **3. 二分查找实现有序符号表** </font></br>
|
<font size=4> **3. 二分查找实现有序符号表** </font></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
使用一对平行数组,一个存储键一个存储值。
|
使用一对平行数组,一个存储键一个存储值。
|
||||||
|
|
||||||
需要创建一个 Key 类型的 Comparable 对象数组和一个 Value 类型的 Object 对象数组。
|
需要创建一个 Key 类型的 Comparable 对象数组和一个 Value 类型的 Object 对象数组。
|
||||||
|
|
|
@ -517,7 +517,7 @@ void test(i) { // 尝试拿起两把筷子
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//037c3a0b-332d-434d-a374-f343ef72c8e1.jpg" width="400"/> </div><br>
|
<div align="center"> <img src="../pics//037c3a0b-332d-434d-a374-f343ef72c8e1.jpg" width="400"/> </div><br>
|
||||||
|
|
||||||
<font size=3> **(1)管道** </font></br>
|
<font size=3> **(一)管道** </font></br>
|
||||||
|
|
||||||
写进程在管道的尾端写入数据,读进程在管道的首端读出数据。管道提供了简单的流控制机制,进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样地,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。
|
写进程在管道的尾端写入数据,读进程在管道的首端读出数据。管道提供了简单的流控制机制,进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样地,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。
|
||||||
|
|
||||||
|
@ -531,13 +531,13 @@ Linux 中管道通过空文件实现。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//7f642a65-b167-4c8f-b382-8322c6322b2c.jpg" width="400"/> </div><br>
|
<div align="center"> <img src="../pics//7f642a65-b167-4c8f-b382-8322c6322b2c.jpg" width="400"/> </div><br>
|
||||||
|
|
||||||
<font size=3> **(2)消息队列** </font></br>
|
<font size=3> **(二)消息队列** </font></br>
|
||||||
|
|
||||||
消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
|
消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//d49466db-fdd3-4d36-8a86-47dc45c07a1e.jpg" width="400"/> </div><br>
|
<div align="center"> <img src="../pics//d49466db-fdd3-4d36-8a86-47dc45c07a1e.jpg" width="400"/> </div><br>
|
||||||
|
|
||||||
<font size=3> **(3)套接字** </font></br>
|
<font size=3> **(三)套接字** </font></br>
|
||||||
|
|
||||||
套接字也是一种进程间通信机制,与其它通信机制不同的是,它可用于不同机器间的进程通信。
|
套接字也是一种进程间通信机制,与其它通信机制不同的是,它可用于不同机器间的进程通信。
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user