From 56a271b4337ec2f25f46bc3d79267acb7d62946d Mon Sep 17 00:00:00 2001 From: wangxujian-laixuzhui <33362969+wangxujian-laixuzhui@users.noreply.github.com> Date: Thu, 16 Aug 2018 08:49:34 +0800 Subject: [PATCH 01/19] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E4=B8=89=E7=A7=8D=E5=AE=9E=E7=8E=B0=E6=96=B9=E5=BC=8F=E7=9A=84?= =?UTF-8?q?=E6=AF=94=E8=BE=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/Java 并发.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 9cd5955b..1a1bacc0 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -182,7 +182,10 @@ public static void main(String[] args) { mt.start(); } ``` - +## 三种实现方式的比较 +- 实现Runnable接又可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;适合多个相同程序代码的线程区处理同一资源的情况。 +- 继承Thread类和实现Runnable方法启动线 程都是使用start方法,然后JVM虚拟机将此线程放到就绪队列中,如果有处理机可用, 则执行run方法。 +- 实现Callable接又要实现call方法,并且线 程执行完毕后会有返回值。其他的两种都是 重写run方法,没有返回值。 ## 实现接口 VS 继承 Thread 实现接口会更好一些,因为: From c6b517553a562ecec4505f453bff14fd818e3b59 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Fri, 17 Aug 2018 21:19:16 +0800 Subject: [PATCH 02/19] auto commit --- notes/Java 容器.md | 2 +- notes/Java 并发.md | 2 + notes/Leetcode 题解.md | 180 +++++++++++------------ notes/Linux.md | 20 +-- notes/MySQL.md | 314 ++++++++++++++++++++-------------------- notes/Redis.md | 6 +- notes/数据库系统原理.md | 86 +++++------ notes/设计模式.md | 76 +++++----- notes/面向对象思想.md | 15 +- 9 files changed, 349 insertions(+), 352 deletions(-) diff --git a/notes/Java 容器.md b/notes/Java 容器.md index 9959e2ca..001f864c 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -742,7 +742,7 @@ new capacity : 00100000 对于一个 Key, -- 它的哈希值如果在第 6 位上为 0,那么取模得到的结果和之前一样; +- 它的哈希值如果在第 5 位上为 0,那么取模得到的结果和之前一样; - 如果为 1,那么得到的结果为原来的结果 +16。 ### 7. 扩容-计算数组容量 diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 9cd5955b..d3682d33 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -168,6 +168,8 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc 同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。 +当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。 + ```java public class MyThread extends Thread { public void run() { diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md index 44f3d9dc..68af3cb7 100644 --- a/notes/Leetcode 题解.md +++ b/notes/Leetcode 题解.md @@ -2389,8 +2389,6 @@ private void backtracking(int row) {

-dp[N] 即为所求。 - 考虑到 dp[i] 只与 dp[i - 1] 和 dp[i - 2] 有关,因此可以只用两个变量来存储 dp[i - 1] 和 dp[i - 2],使得原来的 O(N) 空间复杂度优化为 O(1) 复杂度。 ```java @@ -2468,16 +2466,6 @@ private int rob(int[] nums, int first, int last) { } ``` -**母牛生产** - -[程序员代码面试指南-P181](#) - -题目描述:假设农场中成熟的母牛每年都会生 1 头小母牛,并且永远不会死。第一年有 1 只小母牛,从第二年开始,母牛开始生小母牛。每只小母牛 3 年之后成熟又可以生小母牛。给定整数 N,求 N 年后牛的数量。 - -第 i 年成熟的牛的数量为: - -

- **信件错排** 题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。 @@ -2491,7 +2479,15 @@ private int rob(int[] nums, int first, int last) {

-dp[N] 即为所求。 +**母牛生产** + +[程序员代码面试指南-P181](#) + +题目描述:假设农场中成熟的母牛每年都会生 1 头小母牛,并且永远不会死。第一年有 1 只小母牛,从第二年开始,母牛开始生小母牛。每只小母牛 3 年之后成熟又可以生小母牛。给定整数 N,求 N 年后牛的数量。 + +第 i 年成熟的牛的数量为: + +

### 矩阵路径 @@ -2517,10 +2513,8 @@ public int minPathSum(int[][] grid) { int[] dp = new int[n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { - if (j == 0) { - dp[j] = dp[j]; // 只能从上侧走到该位置 - } else if (i == 0) { - dp[j] = dp[j - 1]; // 只能从左侧走到该位置 + if (i == 0) { + dp[j] = dp[j - 1]; } else { dp[j] = Math.min(dp[j - 1], dp[j]); } @@ -2584,17 +2578,18 @@ sumRange(0, 5) -> -3 ```java class NumArray { + private int[] sums; public NumArray(int[] nums) { - sums = new int[nums.length]; - for (int i = 0; i < nums.length; i++) { - sums[i] = i == 0 ? nums[0] : sums[i - 1] + nums[i]; + sums = new int[nums.length + 1]; + for (int i = 1; i <= nums.length; i++) { + sums[i] = sums[i - 1] + nums[i - 1]; } } public int sumRange(int i, int j) { - return i == 0 ? sums[j] : sums[j] - sums[i - 1]; + return sums[j + 1] - sums[i]; } } ``` @@ -2634,7 +2629,7 @@ return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] i dp[i] 表示以 A[i] 为结尾的等差递增子区间的个数。 -如果 A[i] - A[i - 1] == A[i - 1] - A[i - 2],表示 [A[i - 2], A[i - 1], A[i]] 是一个等差递增子区间。如果 [A[i - 3], A[i - 2], A[i - 1]] 是一个等差递增子区间,那么 [A[i - 3], A[i - 2], A[i - 1], A[i]] 也是。因此在这个条件下,dp[i] = dp[i-1] + 1。 +在 A[i] - A[i - 1] == A[i - 1] - A[i - 2] 的条件下,{A[i - 2], A[i - 1], A[i]} 是一个等差递增子区间。如果 {A[i - 3], A[i - 2], A[i - 1]} 是一个等差递增子区间,那么 {A[i - 3], A[i - 2], A[i - 1], A[i]} 也是等差递增子区间,dp[i] = dp[i-1] + 1。 ```java public int numberOfArithmeticSlices(int[] A) { @@ -2747,17 +2742,17 @@ public int numDecodings(String s) { ### 最长递增子序列 -已知一个序列 {S1, S2,...,Sn} ,取出若干数组成新的序列 {Si1, Si2,..., Sim},其中 i1、i2 ... im 保持递增,即新序列中各个数仍然保持原数列中的先后顺序,称新序列为原序列的一个 **子序列** 。 +已知一个序列 {S1, S2,...,Sn},取出若干数组成新的序列 {Si1, Si2,..., Sim},其中 i1、i2 ... im 保持递增,即新序列中各个数仍然保持原数列中的先后顺序,称新序列为原序列的一个 **子序列** 。 如果在子序列中,当下标 ix > iy 时,Six > Siy,称子序列为原序列的一个 **递增子序列** 。 -定义一个数组 dp 存储最长递增子序列的长度,dp[n] 表示以 Sn 结尾的序列的最长递增子序列长度。对于一个递增子序列 {Si1, Si2,...,Sim},如果 im < n 并且 Sim < Sn ,此时 {Si1, Si2,..., Sim, Sn} 为一个递增子序列,递增子序列的长度增加 1。满足上述条件的递增子序列中,长度最长的那个递增子序列就是要找的,在长度最长的递增子序列上加上 Sn 就构成了以 Sn 为结尾的最长递增子序列。因此 dp[n] = max{ dp[i]+1 | Si < Sn && i < n} 。 +定义一个数组 dp 存储最长递增子序列的长度,dp[n] 表示以 Sn 结尾的序列的最长递增子序列长度。对于一个递增子序列 {Si1, Si2,...,Sim},如果 im < n 并且 Sim < Sn,此时 {Si1, Si2,..., Sim, Sn} 为一个递增子序列,递增子序列的长度增加 1。满足上述条件的递增子序列中,长度最长的那个递增子序列就是要找的,在长度最长的递增子序列上加上 Sn 就构成了以 Sn 为结尾的最长递增子序列。因此 dp[n] = max{ dp[i]+1 | Si < Sn && i < n} 。 因为在求 dp[n] 时可能无法找到一个满足条件的递增子序列,此时 {Sn} 就构成了递增子序列,需要对前面的求解方程做修改,令 dp[n] 最小为 1,即:

-对于一个长度为 N 的序列,最长递增子序列并不一定会以 SN 为结尾,因此 dp[N] 不是序列的最长递增子序列的长度,需要遍历 dp 数组找出最大值才是所要的结果,即 max{ dp[i] | 1 <= i <= N} 即为所求。 +对于一个长度为 N 的序列,最长递增子序列并不一定会以 SN 为结尾,因此 dp[N] 不是序列的最长递增子序列的长度,需要遍历 dp 数组找出最大值才是所要的结果,max{ dp[i] | 1 <= i <= N} 即为所求。 **最长递增子序列** @@ -2790,7 +2785,7 @@ for (int i = 0; i < n; i++) { return ret; ``` -以上解法的时间复杂度为 O(N2) ,可以使用二分查找将时间复杂度降低为 O(NlogN)。 +以上解法的时间复杂度为 O(N2),可以使用二分查找将时间复杂度降低为 O(NlogN)。 定义一个 tails 数组,其中 tails[i] 存储长度为 i + 1 的最长递增子序列的最后一个元素。对于一个元素 x, @@ -2915,19 +2910,19 @@ public int wiggleMaxLength(int[] nums) { 定义一个二维数组 dp 用来存储最长公共子序列的长度,其中 dp[i][j] 表示 S1 的前 i 个字符与 S2 的前 j 个字符最长公共子序列的长度。考虑 S1i 与 S2j 值是否相等,分为两种情况: -- 当 S1i==S2j 时,那么就能在 S1 的前 i-1 个字符与 S2 的前 j-1 个字符最长公共子序列的基础上再加上 S1i 这个值,最长公共子序列长度加 1 ,即 dp[i][j] = dp[i-1][j-1] + 1。 -- 当 S1i != S2j 时,此时最长公共子序列为 S1 的前 i-1 个字符和 S2 的前 j 个字符最长公共子序列,与 S1 的前 i 个字符和 S2 的前 j-1 个字符最长公共子序列,它们的最大者,即 dp[i][j] = max{ dp[i-1][j], dp[i][j-1] }。 +- 当 S1i==S2j 时,那么就能在 S1 的前 i-1 个字符与 S2 的前 j-1 个字符最长公共子序列的基础上再加上 S1i 这个值,最长公共子序列长度加 1,即 dp[i][j] = dp[i-1][j-1] + 1。 +- 当 S1i != S2j 时,此时最长公共子序列为 S1 的前 i-1 个字符和 S2 的前 j 个字符最长公共子序列,或者 S1 的前 i 个字符和 S2 的前 j-1 个字符最长公共子序列,取它们的最大者,即 dp[i][j] = max{ dp[i-1][j], dp[i][j-1] }。 综上,最长公共子序列的状态转移方程为:

-对于长度为 N 的序列 S1 和 长度为 M 的序列 S2,dp[N][M] 就是序列 S1 和序列 S2 的最长公共子序列长度。 +对于长度为 N 的序列 S1 和长度为 M 的序列 S2,dp[N][M] 就是序列 S1 和序列 S2 的最长公共子序列长度。 与最长递增子序列相比,最长公共子序列有以下不同点: - 针对的是两个序列,求它们的最长公共子序列。 -- 在最长递增子序列中,dp[i] 表示以 Si 为结尾的最长递增子序列长度,子序列必须包含 Si ;在最长公共子序列中,dp[i][j] 表示 S1 中前 i 个字符与 S2 中前 j 个字符的最长公共子序列长度,不一定包含 S1i 和 S2j 。 +- 在最长递增子序列中,dp[i] 表示以 Si 为结尾的最长递增子序列长度,子序列必须包含 Si ;在最长公共子序列中,dp[i][j] 表示 S1 中前 i 个字符与 S2 中前 j 个字符的最长公共子序列长度,不一定包含 S1i 和 S2j。 - 在求最终解时,最长公共子序列中 dp[N][M] 就是最终解,而最长递增子序列中 dp[N] 不是最终解,因为以 SN 为结尾的最长递增子序列不一定是整个序列最长递增子序列,需要遍历一遍 dp 数组找到最大者。 ```java @@ -2956,9 +2951,7 @@ public int lengthOfLCS(int[] nums1, int[] nums2) { - 第 i 件物品没添加到背包,总体积不超过 j 的前 i 件物品的最大价值就是总体积不超过 j 的前 i-1 件物品的最大价值,dp[i][j] = dp[i-1][j]。 - 第 i 件物品添加到背包中,dp[i][j] = dp[i-1][j-w] + v。 -第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。 - -综上,0-1 背包的状态转移方程为: +第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0-1 背包的状态转移方程为:

@@ -2981,11 +2974,11 @@ public int knapsack(int W, int N, int[] weights, int[] values) { **空间优化** -在程序实现时可以对 0-1 背包做优化。观察状态转移方程可以知道,前 i 件物品的状态仅由前 i-1 件物品的状态有关,因此可以将 dp 定义为一维数组,其中 dp[j] 既可以表示 dp[i-1][j] 也可以表示 dp[i][j]。此时, +在程序实现时可以对 0-1 背包做优化。观察状态转移方程可以知道,前 i 件物品的状态仅与前 i-1 件物品的状态有关,因此可以将 dp 定义为一维数组,其中 dp[j] 既可以表示 dp[i-1][j] 也可以表示 dp[i][j]。此时,

-因为 dp[j-w] 表示 dp[i-1][j-w],因此不能先求 dp[i][j-w],以防止将 dp[i-1][j-w] 覆盖。也就是说要先计算 dp[i][j] 再计算 dp[i][j-w],在程序实现时需要按倒序来循环求解。 +因为 dp[j-w] 表示 dp[i-1][j-w],因此不能先求 dp[i][j-w],以防将 dp[i-1][j-w] 覆盖。也就是说要先计算 dp[i][j] 再计算 dp[i][j-w],在程序实现时需要按倒序来循环求解。 ```java public int knapsack(int W, int N, int[] weights, int[] values) { @@ -3205,7 +3198,7 @@ public int findMaxForm(String[] strs, int m, int n) { } ``` -**找零钱的方法数** +**找零钱的最少硬币数** [322. Coin Change (Medium)](https://leetcode.com/problems/coin-change/description/) @@ -3286,60 +3279,6 @@ public int combinationSum4(int[] nums, int target) { } ``` -**只能进行 k 次的股票交易** - -[188. Best Time to Buy and Sell Stock IV (Hard)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/description/) - -```java -public int maxProfit(int k, int[] prices) { - int n = prices.length; - if (k >= n / 2) { // 这种情况下该问题退化为普通的股票交易问题 - int maxProfit = 0; - for (int i = 1; i < n; i++) { - if (prices[i] > prices[i - 1]) { - maxProfit += prices[i] - prices[i - 1]; - } - } - return maxProfit; - } - int[][] maxProfit = new int[k + 1][n]; - for (int i = 1; i <= k; i++) { - int localMax = maxProfit[i - 1][0] - prices[0]; - for (int j = 1; j < n; j++) { - maxProfit[i][j] = Math.max(maxProfit[i][j - 1], prices[j] + localMax); - localMax = Math.max(localMax, maxProfit[i - 1][j] - prices[j]); - } - } - return maxProfit[k][n - 1]; -} -``` - -**只能进行两次的股票交易** - -[123. Best Time to Buy and Sell Stock III (Hard)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/) - -```java -public int maxProfit(int[] prices) { - int firstBuy = Integer.MIN_VALUE, firstSell = 0; - int secondBuy = Integer.MIN_VALUE, secondSell = 0; - for (int curPrice : prices) { - if (firstBuy < -curPrice) { - firstBuy = -curPrice; - } - if (firstSell < firstBuy + curPrice) { - firstSell = firstBuy + curPrice; - } - if (secondBuy < firstSell - curPrice) { - secondBuy = firstSell - curPrice; - } - if (secondSell < secondBuy + curPrice) { - secondSell = secondBuy + curPrice; - } - } - return secondSell; -} -``` - ### 股票交易 **需要冷却期的股票交易** @@ -3432,6 +3371,60 @@ public int maxProfit(int[] prices) { } ``` +**只能进行两次的股票交易** + +[123. Best Time to Buy and Sell Stock III (Hard)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/) + +```java +public int maxProfit(int[] prices) { + int firstBuy = Integer.MIN_VALUE, firstSell = 0; + int secondBuy = Integer.MIN_VALUE, secondSell = 0; + for (int curPrice : prices) { + if (firstBuy < -curPrice) { + firstBuy = -curPrice; + } + if (firstSell < firstBuy + curPrice) { + firstSell = firstBuy + curPrice; + } + if (secondBuy < firstSell - curPrice) { + secondBuy = firstSell - curPrice; + } + if (secondSell < secondBuy + curPrice) { + secondSell = secondBuy + curPrice; + } + } + return secondSell; +} +``` + +**只能进行 k 次的股票交易** + +[188. Best Time to Buy and Sell Stock IV (Hard)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/description/) + +```java +public int maxProfit(int k, int[] prices) { + int n = prices.length; + if (k >= n / 2) { // 这种情况下该问题退化为普通的股票交易问题 + int maxProfit = 0; + for (int i = 1; i < n; i++) { + if (prices[i] > prices[i - 1]) { + maxProfit += prices[i] - prices[i - 1]; + } + } + return maxProfit; + } + int[][] maxProfit = new int[k + 1][n]; + for (int i = 1; i <= k; i++) { + int localMax = maxProfit[i - 1][0] - prices[0]; + for (int j = 1; j < n; j++) { + maxProfit[i][j] = Math.max(maxProfit[i][j - 1], prices[j] + localMax); + localMax = Math.max(localMax, maxProfit[i - 1][j] - prices[j]); + } + } + return maxProfit[k][n - 1]; +} +``` + ### 字符串编辑 **删除两个字符串的字符使它们相等** @@ -3450,11 +3443,8 @@ Explanation: You need one step to make "sea" to "ea" and another step to make "e public int minDistance(String word1, String word2) { int m = word1.length(), n = word2.length(); int[][] dp = new int[m + 1][n + 1]; - for (int i = 0; i <= m; i++) { - for (int j = 0; j <= n; j++) { - if (i == 0 || j == 0) { - continue; - } + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { if (word1.charAt(i - 1) == word2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { diff --git a/notes/Linux.md b/notes/Linux.md index 14d3b317..8ff3e2c6 100644 --- a/notes/Linux.md +++ b/notes/Linux.md @@ -129,15 +129,16 @@ info 与 man 类似,但是 info 将文档分成一个个页面,每个页面 /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin ``` -env 命令可以获取当前终端的环境变量。 - ## sudo sudo 允许一般用户使用 root 可执行的命令,不过只有在 /etc/sudoers 配置文件中添加的用户才能使用该指令。 ## 包管理工具 -RPM 和 DPKG 为最常见的两类软件包管理工具。RPM 全称为 Redhat Package Manager,最早由 Red Hat 公司制定实施,随后被 GNU 开源操作系统接受并成为很多 Linux 系统 (RHEL) 的既定软件标准。与 RPM 进行竞争的是基于 Debian 操作系统 (UBUNTU) 的 DEB 软件包管理工具 DPKG,全称为 Debian Package,功能方面与 RPM 相似。 +RPM 和 DPKG 为最常见的两类软件包管理工具: + +- RPM 全称为 Redhat Package Manager,最早由 Red Hat 公司制定实施,随后被 GNU 开源操作系统接受并成为很多 Linux 系统 (RHEL) 的既定软件标准。 +- 与 RPM 进行竞争的是基于 Debian 操作系统 (Ubuntu) 的 DEB 软件包管理工具 DPKG,全称为 Debian Package,功能方面与 RPM 相似。 YUM 基于 RPM,具有依赖管理功能,并具有软件升级的功能。 @@ -194,13 +195,13 @@ IDE(ATA)全称 Advanced Technology Attachment,接口速度最大为 133MB/ ### 2. SATA -SATA 全称 Serial ATA,也就是使用串口的 ATA 接口,因抗干扰性强,且对数据线的长度要求比 ATA 低很多,支持热插拔等功能,SATA-II 的接口速度为 300MiB/s,而新的 SATA-III 标准可达到 600MiB/s 的传输速度。SATA 的数据线也比 ATA 的细得多,有利于机箱内的空气流通,整理线材也比较方便。 +SATA 全称 Serial ATA,也就是使用串口的 ATA 接口,抗干扰性强,且对数据线的长度要求比 ATA 低很多,支持热插拔等功能。SATA-II 的接口速度为 300MiB/s,而新的 SATA-III 标准可达到 600MiB/s 的传输速度。SATA 的数据线也比 ATA 的细得多,有利于机箱内的空气流通,整理线材也比较方便。

### 3. SCSI -SCSI 全称是 Small Computer System Interface(小型机系统接口),经历多代的发展,从早期的 SCSI-II,到目前的 Ultra320 SCSI 以及 Fiber-Channel(光纤通道),接口型式也多种多样。SCSI 硬盘广为工作站级个人电脑以及服务器所使用,因此会使用较为先进的技术,如碟片转速 15000rpm 的高转速,且资料传输时 CPU 占用率较低,但是单价也比相同容量的 ATA 及 SATA 硬盘更加昂贵。 +SCSI 全称是 Small Computer System Interface(小型机系统接口),经历多代的发展,从早期的 SCSI-II 到目前的 Ultra320 SCSI 以及 Fiber-Channel(光纤通道),接口型式也多种多样。SCSI 硬盘广为工作站级个人电脑以及服务器所使用,因此会使用较为先进的技术,如碟片转速 15000rpm 的高转速,且传输时 CPU 占用率较低,但是单价也比相同容量的 ATA 及 SATA 硬盘更加昂贵。

@@ -229,7 +230,7 @@ Linux 中每个硬件都被当做一个文件,包括磁盘。磁盘以磁盘 MBR 中,第一个扇区最重要,里面有主要开机记录(Master boot record, MBR)及分区表(partition table),其中主要开机记录占 446 bytes,分区表占 64 bytes。 -分区表只有 64 bytes,最多只能存储 4 个分区,这 4 个分区为主分区(Primary)和扩展分区(Extended)。其中扩展分区只有一个,它将其它扇区用来记录分区表,因此通过扩展分区可以分出更多分区,这些分区称为逻辑分区。 +分区表只有 64 bytes,最多只能存储 4 个分区,这 4 个分区为主分区(Primary)和扩展分区(Extended)。其中扩展分区只有一个,它使用其它扇区用记录额外的分区表,因此通过扩展分区可以分出更多分区,这些分区称为逻辑分区。 Linux 也把分区当成文件,分区文件的命名方式为:磁盘文件名 + 编号,例如 /dev/sda1。注意,逻辑分区的编号从 5 开始。 @@ -251,10 +252,10 @@ MBR 不支持 2.2 TB 以上的硬盘,GPT 则最多支持到 233 TB BIOS(Basic Input/Output System,基本输入输出系统),它是一个固件(嵌入在硬件中的软件),BIOS 程序存放在断电后内容不会丢失的只读内存中。 -BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可以开机的磁盘,并读取磁盘第一个扇区的主要开机记录(MBR),由主要开机记录(MBR)执行其中的开机管理程序,这个开机管理程序会加载操作系统的核心文件。 -

+BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可以开机的磁盘,并读取磁盘第一个扇区的主要开机记录(MBR),由主要开机记录(MBR)执行其中的开机管理程序,这个开机管理程序会加载操作系统的核心文件。 + 主要开机记录(MBR)中的开机管理程序提供以下功能:选单、载入核心文件以及转交其它开机管理程序。转交这个功能可以用来实现了多重引导,只需要将另一个操作系统的开机管理程序安装在其它分区的启动扇区上,在启动开机管理程序时,就可以通过选单选择启动当前的操作系统或者转交给其它开机管理程序从而启动另一个操作系统。 下图中,第一扇区的主要开机记录(MBR)中的开机管理程序提供了两个选单:M1、M2,M1 指向了 Windows 操作系统,而 M2 指向其它分区的启动扇区,里面包含了另外一个开机管理程序,提供了一个指向 Linux 的选单。 @@ -283,11 +284,10 @@ BIOS 不可以读取 GPT 分区表,而 UEFI 可以。 除此之外还包括: - superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等; -- block bitmap:记录 block 是否被使用的位域; +- block bitmap:记录 block 是否被使用的位域。

- ## 文件读取 对于 Ext2 文件系统,当要读取一个文件的内容时,先在 inode 中去查找文件内容所在的所有 block,然后把所有 block 的内容读出来。 diff --git a/notes/MySQL.md b/notes/MySQL.md index 7ac9685f..54067977 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -1,22 +1,23 @@ -* [一、存储引擎](#一存储引擎) +* [一、索引](#一索引) + * [B+ Tree 原理](#b-tree-原理) + * [MySQL 索引](#mysql-索引) + * [索引优化](#索引优化) + * [索引的优点](#索引的优点) + * [索引的使用场景](#索引的使用场景) +* [二、查询性能优化](#二查询性能优化) + * [使用 Explain 进行分析](#使用-explain-进行分析) + * [优化数据访问](#优化数据访问) + * [重构查询方式](#重构查询方式) +* [三、存储引擎](#三存储引擎) * [InnoDB](#innodb) * [MyISAM](#myisam) * [比较](#比较) -* [二、数据类型](#二数据类型) +* [四、数据类型](#四数据类型) * [整型](#整型) * [浮点数](#浮点数) * [字符串](#字符串) * [时间和日期](#时间和日期) -* [三、索引](#三索引) - * [B+ Tree 原理](#b-tree-原理) - * [索引分类](#索引分类) - * [索引的优点](#索引的优点) - * [索引优化](#索引优化) -* [四、查询性能优化](#四查询性能优化) - * [使用 Explain 进行分析](#使用-explain-进行分析) - * [优化数据访问](#优化数据访问) - * [重构查询方式](#重构查询方式) * [五、切分](#五切分) * [水平切分](#水平切分) * [垂直切分](#垂直切分) @@ -29,101 +30,7 @@ -# 一、存储引擎 - -## InnoDB - -InnoDB 是 MySQL 默认的事务型存储引擎,只有在需要 InnoDB 不支持的特性时,才考虑使用其它存储引擎。 - -实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ 间隙锁(next-key locking)防止幻影读。 - -主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。 - -内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。 - -支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。 - -## MyISAM - -MyISAM 设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用 MyISAM。 - -MyISAM 提供了大量的特性,包括压缩表、空间数据索引等。 - -不支持事务。 - -不支持行级锁,只能对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。 - -可以手工或者自动执行检查和修复操作,但是和事务恢复以及崩溃恢复不同,可能导致一些数据丢失,而且修复操作是非常慢的。 - -如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。 - -## 比较 - -- 事务:InnoDB 是事务型的,可以使用 Commit 和 Rollback 语句。 - -- 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。 - -- 外键:InnoDB 支持外键。 - -- 备份:InnoDB 支持在线热备份。 - -- 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 - -- 其它特性:MyISAM 支持压缩表和空间数据索引。 - -# 二、数据类型 - -## 整型 - -TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,一般情况下越小的列越好。 - -INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。 - -## 浮点数 - -FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。 - -FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。 - -## 字符串 - -主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。 - -VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内容。但是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作。MyISAM 会将行拆成不同的片段存储,而 InnoDB 则需要分裂页来使行放进页内。 - -VARCHAR 会保留字符串末尾的空格,而 CHAR 会删除。 - -## 时间和日期 - -MySQL 提供了两种相似的日期时间类型:DATETIME 和 TIMESTAMP。 - -### 1. DATETIME - -能够保存从 1001 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。 - -它与时区无关。 - -默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATETIME 值,例如“2008-01-16 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。 - -### 2. TIMESTAMP - -和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年 到 2038 年。 - -它和时区有关,也就是说一个时间戳在不同的时区所代表的具体时间是不同的。 - -MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。 - -默认情况下,如果插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。 - -应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。 - -# 三、索引 - -索引能够轻易将查询性能提升几个数量级。 - -对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效。对于中到大型的表,索引就非常有效。但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。 - -索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。 +# 一、索引 ## B+ Tree 原理 @@ -141,37 +48,37 @@ B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具 进行查找操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。 -插入删除操作记录会破坏平衡树的平衡性,因此在插入删除时,需要对树进行一个分裂、合并、旋转等操作。 +插入删除操作记录会破坏平衡树的平衡性,因此在插入删除操作之后,需要对树进行一个分裂、合并、旋转等操作来维护平衡性。 ### 3. 与红黑树的比较 红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+ Tree 作为索引结构,主要有以下两个原因: -(一)更少的检索次数 +(一)更少的查找次数 -平衡树检索数据的时间复杂度等于树高 h,而树高大致为 O(h)=O(logdN),其中 d 为每个节点的出度。 +平衡树查找操作的时间复杂度等于树高 h,而树高大致为 O(h)=O(logdN),其中 d 为每个节点的出度。 -红黑树的出度为 2,而 B+ Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B+ Tree 大非常多,因此检索的次数也就更多。 +红黑树的出度为 2,而 B+ Tree 的出度一般都非常大,所以红黑树的树高 h 很明显比 B+ Tree 大非常多,检索的次数也就更多。 (二)利用计算机预读特性 -为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,因此速度会非常快。 +为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,因此速度会非常快。 操作系统一般将内存和磁盘分割成固态大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点,并且可以利用预读特性,相邻的节点也能够被预先载入。 -## 索引分类 +## MySQL 索引 + +索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。 ### 1. B+Tree 索引 -B+Tree 索引是大多数 MySQL 存储引擎的默认索引类型。 +是大多数 MySQL 存储引擎的默认索引类型。 因为不再需要进行全表扫描,只需要对树进行搜索即可,因此查找速度快很多。除了用于查找,还可以用于排序和分组。 可以指定多个列作为索引列,多个索引列共同组成键。 -B+Tree 索引适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。 - -如果不是按照索引列的顺序进行查找,则无法使用索引。 +适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。如果不是按照索引列的顺序进行查找,则无法使用索引。 InnoDB 的 B+Tree 索引分为主索引和辅助索引。 @@ -185,12 +92,12 @@ InnoDB 的 B+Tree 索引分为主索引和辅助索引。 ### 2. 哈希索引 -InnoDB 引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。 - 哈希索引能以 O(1) 时间进行查找,但是失去了有序性,它具有以下限制: - 无法用于排序与分组; -- 只支持精确查找,无法用于部分查找和范围查找; +- 只支持精确查找,无法用于部分查找和范围查找。 + +InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。 ### 3. 全文索引 @@ -200,20 +107,12 @@ MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而 InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。 -### 4. 空间数据索引(R-Tree) +### 4. 空间数据索引 -MyISAM 存储引擎支持空间数据索引,可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。 +MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。 必须使用 GIS 相关的函数来维护数据。 -## 索引的优点 - -- 大大减少了服务器需要扫描的数据行数。 - -- 帮助服务器避免进行排序和创建临时表(B+Tree 索引是有序的,可以用来做 ORDER BY 和 GROUP BY 操作); - -- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,也就将相邻的数据都存储在一起)。 - ## 索引优化 ### 1. 独立的列 @@ -266,11 +165,25 @@ customer_id_selectivity: 0.0373 具有以下优点: -- 因为索引条目通常远小于数据行的大小,所以若只读取索引,能大大减少数据访问量。 +- 索引通常远小于数据行的大小,只读取索引能大大减少数据访问量。 - 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。 - 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。 -# 四、查询性能优化 +## 索引的优点 + +- 大大减少了服务器需要扫描的数据行数。 + +- 帮助服务器避免进行排序和分组,也就不需要创建临时表(B+Tree 索引是有序的,可以用于 ORDER BY 和 GROUP BY 操作。临时表主要是在排序和分组过程中创建,因为不需要排序和分组,也就不需要创建临时表)。 + +- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,也就将相邻的数据都存储在一起)。 + +## 索引的使用场景 + +- 对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效。 +- 对于中到大型的表,索引就非常有效。 +- 但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。 + +# 二、查询性能优化 ## 使用 Explain 进行分析 @@ -282,23 +195,13 @@ Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explai - key : 使用的索引 - rows : 扫描的行数 -更多内容请参考:[MySQL 性能优化神器 Explain 使用分析](https://segmentfault.com/a/1190000008131735) - ## 优化数据访问 ### 1. 减少请求的数据量 -(一)只返回必要的列 - -最好不要使用 SELECT * 语句。 - -(二)只返回必要的行 - -使用 WHERE 语句进行查询过滤,有时候也需要使用 LIMIT 语句来限制返回的数据。 - -(三)缓存重复查询的数据 - -使用缓存可以避免在数据库中进行查询,特别要查询的数据经常被重复查询,缓存可以带来的查询性能提升将会是非常明显的。 +- 只返回必要的列:最好不要使用 SELECT * 语句。 +- 只返回必要的行:使用 LIMIT 语句来限制返回的数据。 +- 缓存重复查询的数据:使用缓存可以避免在数据库中进行查询,特别在要查询的数据经常被重复查询时,缓存带来的查询性能提升将会是非常明显的。 ### 2. 减少服务器端扫描的行数 @@ -324,12 +227,12 @@ do { ### 2. 分解大连接查询 -将一个大连接查询(JOIN)分解成对每一个表进行一次单表查询,然后将结果在应用程序中进行关联,这样做的好处有: +将一个大连接查询分解成对每一个表进行一次单表查询,然后将结果在应用程序中进行关联,这样做的好处有: - 让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。 - 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。 - 减少锁竞争; -- 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可扩展。 +- 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可伸缩。 - 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。 ```sql @@ -345,23 +248,111 @@ SELECT * FROM tag_post WHERE tag_id=1234; SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); ``` +# 三、存储引擎 + +## InnoDB + +是 MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎。 + +实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ 间隙锁(Next-Key Locking)防止幻影读。 + +主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。 + +内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。 + +支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。 + +## MyISAM + +设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用它。 + +提供了大量的特性,包括压缩表、空间数据索引等。 + +不支持事务。 + +不支持行级锁,只能对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。 + +可以手工或者自动执行检查和修复操作,但是和事务恢复以及崩溃恢复不同,可能导致一些数据丢失,而且修复操作是非常慢的。 + +如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。 + +## 比较 + +- 事务:InnoDB 是事务型的,可以使用 Commit 和 Rollback 语句。 + +- 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。 + +- 外键:InnoDB 支持外键。 + +- 备份:InnoDB 支持在线热备份。 + +- 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 + +- 其它特性:MyISAM 支持压缩表和空间数据索引。 + +# 四、数据类型 + +## 整型 + +TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,一般情况下越小的列越好。 + +INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。 + +## 浮点数 + +FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。 + +FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。 + +## 字符串 + +主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。 + +VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内容。但是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作。MyISAM 会将行拆成不同的片段存储,而 InnoDB 则需要分裂页来使行放进页内。 + +VARCHAR 会保留字符串末尾的空格,而 CHAR 会删除。 + +## 时间和日期 + +MySQL 提供了两种相似的日期时间类型:DATETIME 和 TIMESTAMP。 + +### 1. DATETIME + +能够保存从 1001 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。 + +它与时区无关。 + +默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATETIME 值,例如“2008-01-16 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。 + +### 2. TIMESTAMP + +和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年 到 2038 年。 + +它和时区有关,也就是说一个时间戳在不同的时区所代表的具体时间是不同的。 + +MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。 + +默认情况下,如果插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。 + +应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。 + # 五、切分 ## 水平切分 -

- 水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。 当一个表的数据不断增多时,Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓存单个数据库的压力。 +

+ ## 垂直切分

垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用的列和不经常被使用的列切分到不同的表中。 -在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库 payDB、用户数据库 userDB 等。 +在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库、用户数据库等。 ## Sharding 策略 @@ -375,15 +366,15 @@ SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); 使用分布式事务来解决,比如 XA 接口。 -### 2. JOIN +### 2. 链接 -可以将原来的 JOIN 查询分解成多个单表查询,然后在用户程序中进行 JOIN。 +可以将原来的 JOIN 分解成多个单表查询,然后在用户程序中进行 JOIN。 ### 3. ID 唯一性 -- 使用全局唯一 ID:GUID。 -- 为每个分片指定一个 ID 范围。 -- 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)。 +- 使用全局唯一 ID:GUID +- 为每个分片指定一个 ID 范围 +- 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法) 更多内容请参考: @@ -396,24 +387,24 @@ SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); 主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。 -- **binlog 线程** :负责将主服务器上的数据更改写入二进制文件(binlog)中。 -- **I/O 线程** :负责从主服务器上读取二进制日志文件,并写入从服务器的中继日志中。 +- **binlog 线程** :负责将主服务器上的数据更改写入二进制日志中。 +- **I/O 线程** :负责从主服务器上读取二进制日志,并写入从服务器的中继日志中。 - **SQL 线程** :负责读取中继日志并重放其中的 SQL 语句。

## 读写分离 -主服务器用来处理写操作以及实时性要求比较高的读操作,而从服务器用来处理读操作。 +主服务器处理写操作以及实时性要求比较高的读操作,而从服务器处理读操作。 -读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。 - -MySQL 读写分离能提高性能的原因在于: +读写分离能提高性能的原因在于: - 主从服务器负责各自的读和写,极大程度缓解了锁的争用; -- 从服务器可以配置 MyISAM 引擎,提升查询性能以及节约系统开销; +- 从服务器可以使用 MyISAM,提升查询性能以及节约系统开销; - 增加冗余,提高可用性。 +读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。 +

# 参考资料 @@ -425,3 +416,4 @@ MySQL 读写分离能提高性能的原因在于: - [How to create unique row ID in sharded databases?](https://stackoverflow.com/questions/788829/how-to-create-unique-row-id-in-sharded-databases) - [SQL Azure Federation – Introduction](http://geekswithblogs.net/shaunxu/archive/2012/01/07/sql-azure-federation-ndash-introduction.aspx "Title of this entry.") - [MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) +- [MySQL 性能优化神器 Explain 使用分析](https://segmentfault.com/a/1190000008131735) diff --git a/notes/Redis.md b/notes/Redis.md index 0981abf0..8f231382 100644 --- a/notes/Redis.md +++ b/notes/Redis.md @@ -49,7 +49,7 @@ Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。 -键的类型只能为字符串,值支持的五种类型数据类型为:字符串、列表、集合、有序集合、散列表。 +键的类型只能为字符串,值支持的五种类型数据类型为:字符串、列表、集合、散列表、有序集合。 Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能,使用分片来扩展写性能。 @@ -58,7 +58,7 @@ Redis 支持很多特性,例如将内存中的数据持久化到硬盘中, | 数据类型 | 可以存储的值 | 操作 | | :--: | :--: | :--: | | STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作
对整数和浮点数执行自增或者自减操作 | -| LIST | 列表 | 从两端压入或者弹出元素
读取单个或者多个元素
进行修剪,只保留一个范围内的元素 | +| LIST | 列表 | 从两端压入或者弹出元素
对单个或者多个元素
进行修剪,只保留一个范围内的元素 | | SET | 无序集合 | 添加、获取、移除单个元素
检查一个元素是否存在于集合中
计算交集、并集、差集
从集合里面随机获取元素 | | HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对
获取所有键值对
检查某个键是否存在| | ZSET | 有序集合 | 添加、获取、删除元素
根据分值范围或者成员来获取元素
计算一个键的排名 | @@ -555,7 +555,7 @@ Sentinel(哨兵)可以监听主服务器,并在主服务器进入下线状 分片是将数据划分为多个部分的方法,可以将数据存储到多台机器里面,这种方法在解决某些问题时可以获得线性级别的性能提升。 -假设有 4 个 Reids 实例 R0,R1,R2,R3,还有很多表示用户的键 user:1,user:2,... 等等,有不同的方式来选择一个指定的键存储在哪个实例中。 +假设有 4 个 Reids 实例 R0,R1,R2,R3,还有很多表示用户的键 user:1,user:2,... ,有不同的方式来选择一个指定的键存储在哪个实例中。 - 最简单的方式是范围分片,例如用户 id 从 0\~1000 的存储到实例 R0 中,用户 id 从 1001\~2000 的存储到实例 R1 中,等等。但是这样需要维护一张映射范围表,维护操作代价很高。 - 还有一种方式是哈希分片,使用 CRC32 哈希函数将键转换为一个数字,再对实例数量求模就能知道应该存储的实例。 diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md index e7ef804b..1bcd2533 100644 --- a/notes/数据库系统原理.md +++ b/notes/数据库系统原理.md @@ -20,6 +20,7 @@ * [可串行化(SERIALIZABLE)](#可串行化serializable) * [五、多版本并发控制](#五多版本并发控制) * [版本号](#版本号) + * [隐藏的列](#隐藏的列) * [Undo 日志](#undo-日志) * [实现过程](#实现过程) * [快照读与当前读](#快照读与当前读) @@ -58,9 +59,7 @@ ### 2. 一致性(Consistency) -数据库在事务执行前后都保持一致性状态。 - -在一致性状态下,所有事务对一个数据的读取结果都是相同的。 +数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。 ### 3. 隔离性(Isolation) @@ -78,10 +77,10 @@ - 只有满足一致性,事务的执行结果才是正确的。 - 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。 -- 在并发的情况下,多个事务并发执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。 +- 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。 - 事务满足持久化是为了能应对数据库崩溃的情况。 -

+

## AUTOCOMMIT @@ -95,25 +94,25 @@ MySQL 默认采用自动提交模式。也就是说,如果不显式使用`STAR T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。 -

+

## 读脏数据 T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。 -

+

## 不可重复读 T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。 -

+

## 幻影读 T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。 -

+

---- @@ -123,7 +122,6 @@ T1 读取某个范围的数据,T2 在这个范围内插 ## 封锁粒度 - MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 应该尽量只锁定需要修改的那部分数据,而不是所有的资源。锁定的数据量越少,发生锁争用的可能就越小,系统的并发程度就越高。 @@ -150,8 +148,8 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 | - | X | S | | :--: | :--: | :--: | -|X|NO|NO| -|S|NO|YES| +|X|×|×| +|S|×|√| ### 2. 意向锁 @@ -170,10 +168,10 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 | - | X | IX | S | IS | | :--: | :--: | :--: | :--: | :--: | -|X |NO |NO |NO | NO| -|IX |NO |YES |NO | YES| -|S |NO |NO |YES | YES| -|IS |NO |YES |YES | YES| +|X |× |× |× | ×| +|IX |× |√ |× | √| +|S |× |× |√ | √| +|IS |× |√ |√ | √| 解释如下: @@ -298,32 +296,30 @@ SELECT ... FOR UPDATE; | 隔离级别 | 脏读 | 不可重复读 | 幻影读 | | :---: | :---: | :---:| :---: | -| 未提交读 | YES | YES | YES | -| 提交读 | NO | YES | YES | -| 可重复读 | NO | NO | YES | -| 可串行化 | NO | NO | NO | +| 未提交读 | √ | √ | √ | +| 提交读 | × | √ | √ | +| 可重复读 | × | × | √ | +| 可串行化 | × | × | × | # 五、多版本并发控制 -多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。 - -而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC。 - -可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。 +多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。 ## 版本号 - 系统版本号:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。 - 事务版本号:事务开始时的系统版本号。 -InooDB 的 MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版本号: +## 隐藏的列 + +MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版本号: - 创建版本号:指示创建一个数据行的快照时的系统版本号; - 删除版本号:如果该快照的删除版本号大于当前事务版本号表示该快照有效,否则表示该快照已经被删除了。 ## Undo 日志 -InnoDB 的 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行(Record)的所有快照连接起来。 +MVCC 使用到的快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行(Record)的所有快照连接起来。

@@ -331,15 +327,13 @@ InnoDB 的 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回 以下实现过程针对可重复读隔离级别。 -### 1. SELECT - 当开始新一个事务时,该事务的版本号肯定会大于当前所有数据行快照的创建版本号,理解这一点很关键。 +### 1. SELECT + 多个事务必须读取到同一个数据行的快照,并且这个快照是距离现在最近的一个有效快照。但是也有例外,如果有一个事务正在修改该数据行,那么它可以读取事务本身所做的修改,而不用和其它事务的读取结果一致。 -把没有对一个数据行做修改的事务称为 T,T 所要读取的数据行快照的创建版本号必须小于 T 的版本号,因为如果大于或者等于 T 的版本号,那么表示该数据行快照是其它事务的最新修改,因此不能去读取它。 - -除了上面的要求,T 所要读取的数据行快照的删除版本号必须大于 T 的版本号,因为如果小于等于 T 的版本号,那么表示该数据行快照是已经被删除的,不应该去读取它。 +把没有对一个数据行做修改的事务称为 T,T 所要读取的数据行快照的创建版本号必须小于 T 的版本号,因为如果大于或者等于 T 的版本号,那么表示该数据行快照是其它事务的最新修改,因此不能去读取它。除此之外,T 所要读取的数据行快照的删除版本号必须大于 T 的版本号,因为如果小于等于 T 的版本号,那么表示该数据行快照是已经被删除的,不应该去读取它。 ### 2. INSERT @@ -385,7 +379,7 @@ MVCC 不能解决幻读的问题,Next-Key Locks 就是为了解决这个问题 锁定一个记录上的索引,而不是记录本身。 -如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚集索引,因此 Record Locks 依然可以使用。 +如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚簇索引,因此 Record Locks 依然可以使用。 ## Gap Locks @@ -397,7 +391,7 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE; ## Next-Key Locks -它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引,也锁定范围内的索引。例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间: +它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引,也锁定索引之间的间隙。例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间: ```sql (negative infinity, 10] @@ -432,10 +426,10 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE; 不符合范式的关系,会产生很多异常,主要有以下四种异常: -- 冗余数据:例如 学生-2 出现了两次。 +- 冗余数据:例如 `学生-2` 出现了两次。 - 修改异常:修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。 -- 删除异常:删除一个信息,那么也会丢失其它信息。例如如果删除了 课程-1,需要删除第一行和第三行,那么 学生-1 的信息就会丢失。 -- 插入异常,例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。 +- 删除异常:删除一个信息,那么也会丢失其它信息。例如删除了 `课程-1` 需要删除第一行和第三行,那么 `学生-1` 的信息就会丢失。 +- 插入异常:例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。 ## 范式 @@ -506,7 +500,11 @@ Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门 非主属性不传递函数依赖于键码。 -上面的 关系-1 中存在以下传递函数依赖:Sno -> Sdept -> Mname,可以进行以下分解: +上面的 关系-1 中存在以下传递函数依赖: + +- Sno -> Sdept -> Mname + +可以进行以下分解: 关系-11 @@ -533,13 +531,19 @@ Entity-Relationship,有三个组成部分:实体、属性、联系。 包含一对一,一对多,多对多三种。 -如果 A 到 B 是一对多关系,那么画个带箭头的线段指向 B;如果是一对一,画两个带箭头的线段;如果是多对多,画两个不带箭头的线段。下图的 Course 和 Student 是一对多的关系。 +- 如果 A 到 B 是一对多关系,那么画个带箭头的线段指向 B; +- 如果是一对一,画两个带箭头的线段; +- 如果是多对多,画两个不带箭头的线段。 + +下图的 Course 和 Student 是一对多的关系。

## 表示出现多次的关系 -一个实体在联系出现几次,就要用几条线连接。下图表示一个课程的先修关系,先修关系出现两个 Course 实体,第一个是先修课程,后一个是后修课程,因此需要用两条线来表示这种关系。 +一个实体在联系出现几次,就要用几条线连接。 + +下图表示一个课程的先修关系,先修关系出现两个 Course 实体,第一个是先修课程,后一个是后修课程,因此需要用两条线来表示这种关系。

@@ -549,7 +553,7 @@ Entity-Relationship,有三个组成部分:实体、属性、联系。

-一般只使用二元联系,可以把多元关系转换为二元关系。 +一般只使用二元联系,可以把多元联系转换为二元联系。

diff --git a/notes/设计模式.md b/notes/设计模式.md index b8f4dc9b..531595e3 100644 --- a/notes/设计模式.md +++ b/notes/设计模式.md @@ -60,9 +60,9 @@ (一)懒汉式-线程不安全 -以下实现中,私有静态变量 uniqueInstance 被延迟化实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。 +以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。 -这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null,那么多个线程会执行 `uniqueInstance = new Singleton();` 语句,这将导致多次实例化 uniqueInstance。 +这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null,那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致多次实例化 uniqueInstance。 ```java public class Singleton { @@ -81,11 +81,21 @@ public class Singleton { } ``` -(二)懒汉式-线程安全 +(二)饿汉式-线程安全 -只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了对 uniqueInstance 进行多次实例化的问题。 +线程不安全问题主要是由于 uniqueInstance 被多次实例化,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。 -但是这样有一个问题,就是当一个线程进入该方法之后,其它线程试图进入该方法都必须等待,因此性能上有一定的损耗。 +但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。 + +```java +private static Singleton uniqueInstance = new Singleton(); +``` + +(三)懒汉式-线程安全 + +只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了多次实例化 uniqueInstance 的问题。 + +但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,因此性能上有一定的损耗。 ```java public static synchronized Singleton getUniqueInstance() { @@ -96,16 +106,6 @@ public static synchronized Singleton getUniqueInstance() { } ``` -(三)饿汉式-线程安全 - -线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。 - -但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。 - -```java -private static Singleton uniqueInstance = new Singleton(); -``` - (四)双重校验锁-线程安全 uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。 @@ -175,7 +175,7 @@ public class Singleton { } ``` -(五)枚举实现 +(六)枚举实现 这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。 @@ -231,27 +231,9 @@ public class Singleton implements Serializable { 简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。 -

- 这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。因为客户类往往有多个,如果不使用简单工厂,所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。 -如果存在下面这种代码,就需要使用简单工厂将对象实例化的部分放到简单工厂中。 - -```java -public class Client { - public static void main(String[] args) { - int type = 1; - Product product; - if (type == 1) { - product = new ConcreteProduct1(); - } else if (type == 2) { - product = new ConcreteProduct2(); - } else { - product = new ConcreteProduct(); - } - } -} -``` +

### 实现 @@ -275,6 +257,27 @@ public class ConcreteProduct2 implements Product { } ``` +以下的 Client 类中包含了实例化的代码,这是一种错误的实现,如果在客户类中存在实例化代码,就需要将代码放到简单工厂中。 + +```java +public class Client { + public static void main(String[] args) { + int type = 1; + Product product; + if (type == 1) { + product = new ConcreteProduct1(); + } else if (type == 2) { + product = new ConcreteProduct2(); + } else { + product = new ConcreteProduct(); + } + // do something with the product + } +} +``` + +以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。 + ```java public class SimpleFactory { public Product createProduct(int type) { @@ -293,6 +296,7 @@ public class Client { public static void main(String[] args) { SimpleFactory simpleFactory = new SimpleFactory(); Product product = simpleFactory.createProduct(1); + // do something with the product } } ``` @@ -2906,7 +2910,7 @@ Java 利用缓存来加速大量小对象的访问时间。 - 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。 - 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。 - 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。 -- 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数,比如智能智能;当第一次引用一个持久化对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。 +- 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个持久化对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。

diff --git a/notes/面向对象思想.md b/notes/面向对象思想.md index 9b59e43d..203c7ebf 100644 --- a/notes/面向对象思想.md +++ b/notes/面向对象思想.md @@ -37,6 +37,7 @@ ```java public class Person { + private String name; private int gender; private int age; @@ -63,17 +64,20 @@ public class Person { 继承实现了 **IS-A** 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。 +继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。 + Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 **向上转型** 。 ```java Animal animal = new Cat(); ``` -继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。 - ## 多态 -多态分为编译时多态和运行时多态。编译时多态主要指方法的重载,运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。 +多态分为编译时多态和运行时多态: + +- 编译时多态主要指方法的重载 +- 运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定 运行时多态有三个条件: @@ -116,7 +120,7 @@ public class Music { # 二、类图 -以下类图使用 [PlantUML](https://www.planttext.com/) 绘制,更多语法及使用请参考:http://plantuml.com/ +以下类图使用 [PlantUML](https://www.planttext.com/) 绘制,更多语法及使用请参考:http://plantuml.com/ 。 ## 泛化关系 (Generalization) @@ -327,7 +331,7 @@ Vihicle .. N ### 2. 合成复用原则 -尽量使用对象组合,而不是继承来达到复用的目的。 +尽量使用对象组合,而不是通过继承来达到复用的目的。 ### 3. 共同封闭原则 @@ -349,3 +353,4 @@ Vihicle .. N - [看懂 UML 类图和时序图](http://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#generalization) - [UML 系列——时序图(顺序图)sequence diagram](http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html) - [面向对象编程三大特性 ------ 封装、继承、多态](http://blog.csdn.net/jianyuerensheng/article/details/51602015) + From 99a1aeb51d11c22ee2221d201e584cb7955b4138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= Date: Fri, 17 Aug 2018 21:36:50 +0800 Subject: [PATCH 03/19] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f35ee455..942d6096 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ ## 算法 :pencil2: +- [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md) + + 排序、并查集、栈和队列、红黑树、散列表。 + - [剑指 Offer 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/剑指%20offer%20题解.md) 目录根据原书第二版进行编排,代码和原书有所不同,尽量比原书更简洁。 @@ -21,10 +25,6 @@ 对题目做了一个大致分类,并对每种题型的解题思路做了总结。 -- [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md) - - 排序、并查集、栈和队列、红黑树、散列表。 - ## 操作系统 :computer: - [计算机操作系统](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机操作系统.md) From d33f9a31119bf9703f78d144a97b2533f80995e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= Date: Sat, 18 Aug 2018 16:29:59 +0800 Subject: [PATCH 04/19] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 942d6096..3209d82f 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,6 @@ ## 算法 :pencil2: -- [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md) - - 排序、并查集、栈和队列、红黑树、散列表。 - - [剑指 Offer 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/剑指%20offer%20题解.md) 目录根据原书第二版进行编排,代码和原书有所不同,尽量比原书更简洁。 @@ -24,6 +20,10 @@ - [Leetcode 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Leetcode%20题解.md) 对题目做了一个大致分类,并对每种题型的解题思路做了总结。 + + - [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md) + + 排序、并查集、栈和队列、红黑树、散列表。 ## 操作系统 :computer: From f100a61a61dea9f7c09ee9493fd0fc8be89568bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= Date: Sat, 18 Aug 2018 16:30:19 +0800 Subject: [PATCH 05/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3209d82f..f9c604d9 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ 对题目做了一个大致分类,并对每种题型的解题思路做了总结。 - - [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md) + - [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md) 排序、并查集、栈和队列、红黑树、散列表。 From fad52dbef4167ace6a352e7c9dd9bf57f52ceb68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= Date: Sat, 18 Aug 2018 16:30:33 +0800 Subject: [PATCH 06/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9c604d9..ce7257e5 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ - [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md) - 排序、并查集、栈和队列、红黑树、散列表。 + 排序、并查集、栈和队列、红黑树、散列表。 ## 操作系统 :computer: From 7186c28cc5a3c42d228188c579f2d7161b4634ac Mon Sep 17 00:00:00 2001 From: zhang xuelong <35026038+zhangxuelong10@users.noreply.github.com> Date: Sun, 19 Aug 2018 18:33:49 +0800 Subject: [PATCH 07/19] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8B=BC=E5=86=99?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/Java 基础.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/Java 基础.md b/notes/Java 基础.md index b329e0bc..44ca873a 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -707,7 +707,7 @@ public class EqualExample { ## hashCode() -hasCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。 +hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。 在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。 From d10ec622b02fff0a465ad8e93ce21a32df3dfe2e Mon Sep 17 00:00:00 2001 From: RocXing <120695313@qq.com> Date: Mon, 20 Aug 2018 18:46:03 +0800 Subject: [PATCH 08/19] =?UTF-8?q?=E8=A1=A5=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/Java 并发.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 64f9e7d6..ea8b2898 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -185,7 +185,7 @@ public static void main(String[] args) { } ``` ## 三种实现方式的比较 -- 实现Runnable接又可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;适合多个相同程序代码的线程区处理同一资源的情况。 +- 实现Runnable接口又可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;适合多个相同程序代码的线程区处理同一资源的情况。 - 继承Thread类和实现Runnable方法启动线 程都是使用start方法,然后JVM虚拟机将此线程放到就绪队列中,如果有处理机可用, 则执行run方法。 - 实现Callable接又要实现call方法,并且线 程执行完毕后会有返回值。其他的两种都是 重写run方法,没有返回值。 ## 实现接口 VS 继承 Thread From 67322b7a32ddf156827a0977a17493e3a3000509 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Mon, 20 Aug 2018 20:32:42 +0800 Subject: [PATCH 09/19] auto commit --- notes/Docker.md | 11 ++++++----- notes/Java 并发.md | 5 +---- notes/Leetcode 题解.md | 9 ++++----- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/notes/Docker.md b/notes/Docker.md index f230f762..80108896 100644 --- a/notes/Docker.md +++ b/notes/Docker.md @@ -30,15 +30,15 @@ Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程 ## 启动速度 -启动虚拟机需要启动虚拟机的操作系统,再启动相应的应用,这个过程会非常慢; +启动虚拟机需要启动虚拟机的操作系统,再启动应用,这个过程非常慢; 而启动 Docker 相当于启动宿主操作系统上的一个进程。 ## 占用资源 -虚拟机是一个完整的操作系统,需要占用大量的磁盘空间、内存和 CPU,一台机器只能开启几十个的虚拟机。 +虚拟机是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU,一台机器只能开启几十个的虚拟机。 -而 Docker 只是一个进程,只需要将应用以及相应的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。 +而 Docker 只是一个进程,只需要将应用以及相关的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。 参考资料: @@ -58,7 +58,7 @@ Docker 使用分层技术和镜像,使得应用可以更容易复用重复部 ## 更容易扩展 -可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像得到我们想要的镜像非常容易。 +可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像可以非常容易得到我们想要的镜像。 参考资料: @@ -91,7 +91,7 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服 镜像包含着容器运行时所需要的代码以及其它组件,它是一种分层结构,每一层都是只读的(read-only layers)。构建镜像时,会一层一层构建,前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。 -在构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。 +构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。

@@ -100,3 +100,4 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服 - [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/) - [理解 Docker(2):Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html) + diff --git a/notes/Java 并发.md b/notes/Java 并发.md index ea8b2898..d3682d33 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -184,10 +184,7 @@ public static void main(String[] args) { mt.start(); } ``` -## 三种实现方式的比较 -- 实现Runnable接口又可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;适合多个相同程序代码的线程区处理同一资源的情况。 -- 继承Thread类和实现Runnable方法启动线 程都是使用start方法,然后JVM虚拟机将此线程放到就绪队列中,如果有处理机可用, 则执行run方法。 -- 实现Callable接又要实现call方法,并且线 程执行完毕后会有返回值。其他的两种都是 重写run方法,没有返回值。 + ## 实现接口 VS 继承 Thread 实现接口会更好一些,因为: diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md index 68af3cb7..04a751c7 100644 --- a/notes/Leetcode 题解.md +++ b/notes/Leetcode 题解.md @@ -3602,7 +3602,7 @@ public int countPrimes(int n) { ```java 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 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 int[] id; UF(int N) { @@ -6665,7 +6665,7 @@ x ^ x = 0 x & x = x x | x = x ``` - 利用 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 与 mask:00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。 - 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。 位与运算技巧: @@ -6910,7 +6910,6 @@ public boolean isPowerOfFour(int num) { } ``` - **判断一个数的位级表示是否不会出现连续的 0 和 1** [693. Binary Number with Alternating Bits (Easy)](https://leetcode.com/problems/binary-number-with-alternating-bits/description/) From f7f68ba1f86b7af5d938dbad8630f7dbfae124a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= <1029579233@qq.com> Date: Mon, 20 Aug 2018 20:50:41 +0800 Subject: [PATCH 10/19] Update README.md --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ce7257e5..bf186a04 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ -## 算法 :pencil2: +### 算法 :pencil2: - [剑指 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) @@ -35,7 +35,7 @@ 基本实现原理以及基本操作。 -## 网络 :cloud: +### 网络 :cloud: - [计算机网络](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机网络.md) @@ -49,7 +49,7 @@ I/O 模型、I/O 多路复用。 -## 面向对象 :couple: +### 面向对象 :couple: - [设计模式](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) @@ -81,7 +81,7 @@ 五种数据类型、字典和跳跃表数据结构、使用场景、和 Memcache 的比较、淘汰策略、持久化、文件事件的 Reactor 模式、复制。 -## Java :coffee: +### Java :coffee: - [Java 基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20基础.md) @@ -103,7 +103,7 @@ NIO 的原理以及实例。 -## 系统设计 :bulb: +### 系统设计 :bulb: - [系统设计基础](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) @@ -147,7 +147,7 @@ 构建工具的基本概念、主流构建工具介绍。 -## 编码实践 :speak_no_evil: +### 编码实践 :speak_no_evil: - [重构](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/重构.md) @@ -161,9 +161,9 @@ Google 开源项目的代码风格规范。 -## 后记 :memo: +### 后记 :memo: -### About +#### About 这个仓库是笔者的一个学习笔记,主要总结一些比较重要的知识点,希望对大家有所帮助。 @@ -171,7 +171,7 @@ [BOOKLIST](https://github.com/CyC2018/Interview-Notebook/blob/master/BOOKLIST.md),这个书单是笔者至今看的一些比较好的技术书籍,虽然没有全都看完,但每本书多多少少都看了一部分。 -### How To Contribute +#### How To Contribute 笔记内容是笔者一个字一个字打上去的,难免会有一些笔误,如果发现笔误可直接在相应文档进行编辑修改。 @@ -179,7 +179,7 @@ 欢迎在 Issue 中提交对本仓库的改进建议~ -### Typesetting +#### Typesetting 笔记内容按照 [中文文案排版指北](http://mazhuang.org/wiki/chinese-copywriting-guidelines/) 进行排版,以保证内容的可读性。 @@ -187,7 +187,7 @@ 笔者将自己实现的文档排版功能提取出来,放在 Github Page 中,无需下载安装即可免费使用:[Text-Typesetting](https://github.com/CyC2018/Markdown-Typesetting)。 -### Uploading +#### Uploading 笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。 @@ -195,21 +195,21 @@ 笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github,或者制作项目 README 文档时生成目录时使用:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。 -### License +#### License 在对本作品进行演绎时,请署名并以相同方式共享。 知识共享许可协议 -### Statement +#### Statement 本仓库不参与商业行为,不向读者收取任何费用。(This repository is not engaging in business activities, and does not charge readers any fee.) -### Logo +#### Logo Power by [logomakr](https://logomakr.com/). -### Acknowledgements +#### Acknowledgements 感谢以下人员对本仓库做出的贡献,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与笔者联系。 From 13e069030293de666b6c99fd928dfdaeb51f505e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= <1029579233@qq.com> Date: Mon, 20 Aug 2018 20:58:21 +0800 Subject: [PATCH 11/19] Update README.md --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index bf186a04..3e829c1b 100644 --- a/README.md +++ b/README.md @@ -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-后记) |
@@ -11,7 +11,7 @@ -### 算法 :pencil2: +## :pencil2: 算法 - [剑指 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) @@ -35,7 +35,7 @@ 基本实现原理以及基本操作。 -### 网络 :cloud: +## :cloud: 网络 - [计算机网络](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机网络.md) @@ -49,7 +49,7 @@ I/O 模型、I/O 多路复用。 -### 面向对象 :couple: +## :couple: 面向对象 - [设计模式](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) @@ -81,7 +81,7 @@ 五种数据类型、字典和跳跃表数据结构、使用场景、和 Memcache 的比较、淘汰策略、持久化、文件事件的 Reactor 模式、复制。 -### Java :coffee: +## :coffee: Java - [Java 基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20基础.md) @@ -103,7 +103,7 @@ NIO 的原理以及实例。 -### 系统设计 :bulb: +## :bulb: 系统设计 - [系统设计基础](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) @@ -147,7 +147,7 @@ 构建工具的基本概念、主流构建工具介绍。 -### 编码实践 :speak_no_evil: +## :speak_no_evil: 编码实践 - [重构](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/重构.md) @@ -161,9 +161,9 @@ Google 开源项目的代码风格规范。 -### 后记 :memo: +## :memo: 后记 -#### About +### About 这个仓库是笔者的一个学习笔记,主要总结一些比较重要的知识点,希望对大家有所帮助。 @@ -171,7 +171,7 @@ [BOOKLIST](https://github.com/CyC2018/Interview-Notebook/blob/master/BOOKLIST.md),这个书单是笔者至今看的一些比较好的技术书籍,虽然没有全都看完,但每本书多多少少都看了一部分。 -#### How To Contribute +### How To Contribute 笔记内容是笔者一个字一个字打上去的,难免会有一些笔误,如果发现笔误可直接在相应文档进行编辑修改。 @@ -179,7 +179,7 @@ 欢迎在 Issue 中提交对本仓库的改进建议~ -#### Typesetting +### Typesetting 笔记内容按照 [中文文案排版指北](http://mazhuang.org/wiki/chinese-copywriting-guidelines/) 进行排版,以保证内容的可读性。 @@ -187,7 +187,7 @@ 笔者将自己实现的文档排版功能提取出来,放在 Github Page 中,无需下载安装即可免费使用:[Text-Typesetting](https://github.com/CyC2018/Markdown-Typesetting)。 -#### Uploading +### Uploading 笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。 @@ -195,21 +195,21 @@ 笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github,或者制作项目 README 文档时生成目录时使用:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。 -#### License +### License 在对本作品进行演绎时,请署名并以相同方式共享。 知识共享许可协议 -#### Statement +### Statement 本仓库不参与商业行为,不向读者收取任何费用。(This repository is not engaging in business activities, and does not charge readers any fee.) -#### Logo +### Logo Power by [logomakr](https://logomakr.com/). -#### Acknowledgements +### Acknowledgements 感谢以下人员对本仓库做出的贡献,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与笔者联系。 From ded6dee5cd049d22fc558b8e5e755c33c0b71b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= <1029579233@qq.com> Date: Mon, 20 Aug 2018 20:59:44 +0800 Subject: [PATCH 12/19] Update README.md --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3e829c1b..d511fa7c 100644 --- a/README.md +++ b/README.md @@ -195,12 +195,6 @@ 笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github,或者制作项目 README 文档时生成目录时使用:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。 -### License - -在对本作品进行演绎时,请署名并以相同方式共享。 - -知识共享许可协议 - ### Statement 本仓库不参与商业行为,不向读者收取任何费用。(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/). +### License +在对本作品进行演绎时,请署名并以相同方式共享。 + +知识共享许可协议 From db08ad2cda7d49e8d93e34c9b6397380cf27bae0 Mon Sep 17 00:00:00 2001 From: Elong Date: Tue, 21 Aug 2018 15:11:03 +0800 Subject: [PATCH 13/19] =?UTF-8?q?Update=20Java=20=E5=B9=B6=E5=8F=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 文字描述修改的更加严谨一些 --- notes/Java 并发.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/Java 并发.md b/notes/Java 并发.md index d3682d33..16e20a24 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -1020,7 +1020,7 @@ ForkJoinPool 实现了工作窃取算法来提高 CPU 的利用率。每个线 如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。 -以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值为 997 而不是 1000。 +以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值一般情况下小于 1000。 ```java public class ThreadUnsafeExample { From cd354fb46cd472b9b20a206df89ae7b83bb0b890 Mon Sep 17 00:00:00 2001 From: wangxujian-laixuzhui <33362969+wangxujian-laixuzhui@users.noreply.github.com> Date: Tue, 21 Aug 2018 18:29:57 +0800 Subject: [PATCH 14/19] =?UTF-8?q?=E5=BF=AB=E6=8E=92=E4=B8=8D=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=EF=BC=8C=E5=A6=82221=EF=BC=8C=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E4=B8=AA2=E4=BC=9A=E5=92=8C1=E4=BA=A4=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/算法.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/算法.md b/notes/算法.md index 30467b42..8007375b 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -770,7 +770,7 @@ public class HeapSort> extends Sort { | 算法 | 稳定 | 时间复杂度 | 空间复杂度 | 备注 | | :---: | :---: |:---: | :---: | :---: | -| 选择排序 | √| N2 | 1 | | +| 选择排序 | ×| N2 | 1 | | | 冒泡排序 | √ | N2 | 1 | | | 插入排序 | √ | N \~ N2 | 1 | 时间复杂度和初始顺序有关 | | 希尔排序 | × | N 的若干倍乘于递增序列的长度 | 1 | | From d4610276542bc6ebd4940a0f0845cc27ce4b74ea Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Tue, 21 Aug 2018 20:05:55 +0800 Subject: [PATCH 15/19] auto commit --- notes/Java 并发.md | 2 +- notes/攻击技术.md | 2 +- notes/消息队列.md | 13 +++++++------ notes/算法.md | 2 +- notes/缓存.md | 30 +++++++++--------------------- 5 files changed, 19 insertions(+), 30 deletions(-) diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 16e20a24..1403f764 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -1020,7 +1020,7 @@ ForkJoinPool 实现了工作窃取算法来提高 CPU 的利用率。每个线 如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。 -以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值一般情况下小于 1000。 +以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值有可能小于 1000。 ```java public class ThreadUnsafeExample { diff --git a/notes/攻击技术.md b/notes/攻击技术.md index d5a735ba..46dd4616 100644 --- a/notes/攻击技术.md +++ b/notes/攻击技术.md @@ -207,7 +207,7 @@ ResultSet rs = stmt.executeQuery(); 拒绝服务攻击(denial-of-service attack,DoS),亦称洪水攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。 -分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用网络上两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。 +分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。 # 参考资料 diff --git a/notes/消息队列.md b/notes/消息队列.md index c5687e8a..209d962a 100644 --- a/notes/消息队列.md +++ b/notes/消息队列.md @@ -9,6 +9,7 @@ * [三、可靠性](#三可靠性) * [发送端的可靠性](#发送端的可靠性) * [接收端的可靠性](#接收端的可靠性) +* [参考资料](#参考资料) @@ -29,15 +30,10 @@ 发布与订阅模式和观察者模式有以下不同: - 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,发布者与订阅者不知道对方的存在,它们之间通过频道进行通信。 -- 观察者模式是同步的,当事件触发时,主题会去调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,发布者向频道发送一个消息之后,就不需要关心订阅者何时去订阅这个消息。 +- 观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,发布者向频道发送一个消息之后,就不需要关心订阅者何时去订阅这个消息,可以立即返回。

-参考: - -- [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) diff --git a/notes/算法.md b/notes/算法.md index 8007375b..152adfda 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -770,7 +770,7 @@ public class HeapSort> extends Sort { | 算法 | 稳定 | 时间复杂度 | 空间复杂度 | 备注 | | :---: | :---: |:---: | :---: | :---: | -| 选择排序 | ×| N2 | 1 | | +| 选择排序 | × | N2 | 1 | | | 冒泡排序 | √ | N2 | 1 | | | 插入排序 | √ | N \~ N2 | 1 | 时间复杂度和初始顺序有关 | | 希尔排序 | × | N 的若干倍乘于递增序列的长度 | 1 | | diff --git a/notes/缓存.md b/notes/缓存.md index 9a9f2810..ed834253 100644 --- a/notes/缓存.md +++ b/notes/缓存.md @@ -6,6 +6,7 @@ * [五、缓存问题](#五缓存问题) * [六、数据分布](#六数据分布) * [七、一致性哈希](#七一致性哈希) +* [参考资料](#参考资料) @@ -29,10 +30,6 @@ - LRU(Least Recently Used):最近最久未使用策略,优先淘汰最久未使用的数据,也就是上次被访问时间距离现在最远的数据。该策略可以保证内存中的数据都是热点数据,也就是经常被访问的数据,从而保证缓存命中率。 -参考资料: - -- [缓存那些事](https://tech.meituan.com/cache_about.html) - # 二、LRU 以下是一个基于 双向链表 + HashMap 的 LRU 算法实现,对算法的解释如下: @@ -143,10 +140,6 @@ public class LRU implements Iterable { } ``` -源代码: - -- [CyC2018/Algorithm](https://github.com/CyC2018/Algorithm/tree/master/Caching) - # 三、缓存位置 ## 浏览器 @@ -169,7 +162,7 @@ public class LRU implements Iterable { 使用 Redis、Memcache 等分布式缓存将数据缓存在分布式缓存系统中。 -相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存。而本地缓存需要在服务器集群之间进行同步,实现和性能开销上都非常大。 +相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存,而本地缓存需要在服务器集群之间进行同步,实现和性能开销上都非常大。 ## 数据库缓存 @@ -177,7 +170,7 @@ MySQL 等数据库管理系统具有自己的查询缓存机制来提高 SQL 查 # 四、CDN -内容分发网络(Content distribution network,CDN)是一种通过互连的网络系统,利用更靠近用户的服务器更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。 +内容分发网络(Content distribution network,CDN)是一种互连的网络系统,它利用更靠近用户的服务器从而更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。 CDN 主要有以下优点: @@ -187,11 +180,6 @@ CDN 主要有以下优点:

-参考资料: - -- [内容分发网络](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 Table(DHT) 是一种哈希分布方式,其目的是为了克服传统哈希分布在服务器节点数量变化时大量数据失效的问题。 @@ -267,6 +251,10 @@ Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了 数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得大,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。 -参考资料: +# 参考资料 +- 大规模分布式存储系统 +- [缓存那些事](https://tech.meituan.com/cache_about.html) - [一致性哈希算法](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/) From 25cd99a8d03637bbc624e58e3991a41c36810c1a Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Tue, 21 Aug 2018 20:14:05 +0800 Subject: [PATCH 16/19] auto commit --- notes/算法.md | 2 +- pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png | Bin 0 -> 19456 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png diff --git a/notes/算法.md b/notes/算法.md index 152adfda..25d1550e 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -507,7 +507,7 @@ public class QuickSort> extends Sort { 取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。 -

+

```java private int partition(T[] nums, int l, int h) { diff --git a/pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png b/pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png new file mode 100644 index 0000000000000000000000000000000000000000..e8a69bff4827a53c56bc89507c42318a2aa8a0d2 GIT binary patch literal 19456 zcmeI4cT`l{mahvqMg&ZNMKl2l2uhYHA|P2Z5=DXp0ZTwKcvL{if&_^YB_oGaWCIjI zG87`asx>>4fnz4Ll17= zwnd?i+aZ65222?pQ7G9qPjR>wKummZLEQJA+q<}Fu7 z9LtdZsIewGMnhdRRL*`!j07J4`iUPA3dOY_4Ihk;aKT&Go-#gyLIqu^J%vJ@HVV3e zLS4h1HbS8u`-$V=2K3W^$qmI{SLcRJKR!ET+WpSZbZ2`@*?iS?W4g$6r1mA>IzMXU zgz=FNwljTK*)sN%(R44)^!G*!J8?Pl>o&!TSbCAuYo~TSCL(E=Xn&jNKy+H^w9&y> z74*t@>!)Y|%vrcB=*qRF$9`w1%_5u|BCeSJ`1+xkE~WE_(5DX6+P>evt(WePr7nXg zg($u+9i5#$@sq+2zP|tUdikOas+Ohp)I)Iv1qDNE`<6H2qYbaQTZIf42pKt4gk%qr zYog+~AW|5dXGgfabRYW>oi+}-GC~X^cjhTP6vpcy+W1H@3QcmY_Owy$J{ZjF`_U2{ z*Knw#T+lNS*Z#*r|DcPoYp(us20SeDd5T^oRYW0i$@=ln)J%-eYh!4F`e;}1ap?+icfXM2a{Hod|04Mr`U0_mYWMM2|E`&*?z z*^jV})EJRBgQ>`RpCbSIc<3`yuKlT^uoj+7_ZI2=TE4a1t1N>>CY1yp(w%GMP1!wC z7m~Q=A(mYnZ0}y2V3FGMkmgtsjXNip<)8og5OFK3EXr=!HGA*185CuEmupn>=e-{= zuxS>)ha2>8u50Dq_1s#!ajDOFu~k|D#yP8Mz-3ls!`8=nWMaHEu{GB4=w~f^)b0p8 z*j(Otd9EEFmy9_+aC%SVJ&3g;8%m>Pf%{wz+7cci%Hu84q8fh0;n8doTn{RZ7 zG)G^x#nA~pYS>&I4&+;RpKeW*j{1^!kGg)bqLAdV>l42-HwT<2FWG(%)r^Fltjnbk z$#aF|069+-zfHjMJjGIvz0noB@mt)+wMV1$%bj2ASQl3h+^;hE=GeLI_1siemPOfp9?l{0Y8P0r~}+HZK<2Gq8{Gj)kk!i zzV_6Uk#;39-kh>|pW-1Owg!`My|4?zVV87zY)0DX@^Yd@TslPqn7%OM&jbu~ad&uZ z&PnI9phgbC1Lqf-#d`N?U((LM9B$ygCONNiJCH%7;Wv}d2_l;&+nZ}Cwqs4y_;>-m z61ywByu7Ups388JE5G<^$T|=@>c}UkeC0$;$fg5MUHRy`Iakx#xjmobvGw@~gRnqu zcuu8yw$pHokb{)V?*2W>?oCe5wX;#VW2t$InpD1gC?mV1z9EXgnNG}IxTnW}$0jq* zz3i1A443Vw{HQO{Lohveu40Cst$lq;(O|H%-bLzM6P!Eh+m(6yL>1lSY~_x%nB`<= z7AMWau0htW4bf&dl50+2DXC(JFW|j z{L9=K(u@@9lj}r{0_B4jG#ShPrZj?oTydh z#Y}h&&v=VEEsx2f$R$zK2sMm-qEp8?>41m|w~Zd;zt}P*bf%Jut(M%E8g=Gw73i1R zPnuS2E$hT`?ytQHvpl-Fw(w|X)nj|iV~V?mCV_#+EQr2>oXlw=fR9dWst;H6mn# z!|+UB&)%3RZKe};Y*`MIkdSDFQI&n*#%UiX>ec|3x#lSEHA=(=rLLsrgm%oLyjeNP ziYH6cm*iB}IV{QvHlZZfD!{bF#!E-9FHPPvAbKNM8{@aMlwR?swnu7!92kah&^p^aic*)V&}~|7oK@_|GF;%lx(7&8LS;#BmG<&>Lvu zDVw0&IB}f)Q$G=M?Twe#uEc0qiia^qviq6p~ zYU=aY*-8yk&) z+NW}Fsf9m1K*2ntsi=GK{_H^cXm;t$Z}t-{@lS8RyYp+g#t`4NC0k4^-rLV%c0eyR z>UgjpUTVO$S%f-T)2E4-{sjK5>RZJ}rAb?#5A6=HWrVf_eYFYAalB_5Nw$98GrdiB zrXg({h47EZ{@e8Fe&L)3){%Tx7L#wzT%`Vu6^77KOH;xIZrN}3<+tRlIGP=B#`J!` zU9vbhhw|cj)k)$`QWLYy{0bge`mJmR&A!AAf3-Q$(C3zDOGiEa%=Jyo_29YWghD~< zsG0!!Nn;u!^xOBIej#TM>3VD}EkDla$|U78FGYqilAP+>-V*MB;ORvF0}<0!bF;69!p-w%Bbibluy*x){HMnEn*DM(JlV%!MW7d zh#@!DV_jUGjnABe`1|cbM2ge`TTC(+roP;ZE3hw~!_iLD_$5u(lfkf9q9;G9)8qqq{UBW=@h|qSQAun)eBC6V%yk| zYF-ZG%rj<}-n5UouYU8M&i^&RwgLZyrP}j-$9u~TNqgl$If*vyhGge7`6avD_f5%u z_E~-I8`By%!J47g-&#z{?pFJJJX%1N%w(nd;`H!IY%pfxY_=B}ZI6`4OkeS%Q$pr( z51;z&2v>_xWmyM$rwolJbX>UBR`ETlf+lQfvWuFKRD#udo`4O)%-P*E=aIhn8kV%R z?jd=8YR>XSm2bEUO(9C6=F~@-r&Xg2Y@v{@I%9dgJ&p*~Q zaKnGOl|zco_*L`MDbDoPpm?gc)iw3%iL=-5%*{Qw+4M;iREW6zp)MDrPJQV*{wn3l zxUWt0^)L)3#}VQnMd>?1n8UhtTIB|pQeHap%4azps?$19p%f?jf&DA1g1)iqpxX*% zQij)+SUKzV6#r?%uo)diT|V}b(ym)BeI%;G3DU9fF?-ZcE^)2YQCr-%%}#{L3fVQ8 z*q0t)v8ETF2ZY4#bYfIY8zNp}ZLX%->DiGtzUHp!l$7YeG+&|M;;0zaDYCZe%+`y- z>Low%Q~fH7OKv@CF3i%IAW15u8eH9GOfxUF~beqn0%v zjdT@bR9* zT*Pg2WSnaqJ2P)Tiht@Qx>)3u$utOd_{-Cr;-nSuhZDA7Lu3nSUrB4u!RZ;iir;Tg zb5Vaa>Ebil96S5gcPIH@Y3gD;voy9;fLs$;ae-_L6r(^#As~di4F18(OnE(E{@s0S?sf_I= zlA1t9eu$MXBN5}q%50!4(dj^h2f+al1%A(K2siO20vJ(?IPT~_eZkj@wWr=V1Jz-8 zxV`DL&|{fHX}k&KN(!FH009WY9Gvy9Z7R1qUEy5zjCU}Z?kQ-cLIp_&U3n~hl0uA$-pjv%MqE0zfOe*-6}ksv+%L!1Nv>qjOy?1#Z^y8S!-`32hU_hDNP-(O#x z*xLqc=P)cF^Sy2TCHTWrF%AO&sSv~jDDue|#P{Kp-Mu!I^gkJsBCB>yM zX;-%1BhRI=I1g)gh)2yh#je_p`dxiA6|pu~!)Ud*)srY4$d`Aond8-jo7I6`Oz_|) z*4fW|^~bkS=$IezA14ONON%E`qB4UHkkJ^&Iu0G;HmN@e0A(D=6~d{OrS8@So#88Y zUg9e1d+1vX=QQbGQ6uE5Ux8KJo+NXk9gKxoqsU61k;xdF*yfL?XkLp~<~{joS%Gb- z%IDhO8A@!>gip9Fg)#kvzfX+jhN@G*@p(ZUU|UV8+0BqV8shdHxal&6y^b%99J5$q zeQg`4mzff(mKLVS2%z04#Fp>Mai2L#r-F&wucrbhly+IG13IAqB=W@M(YJEp94RR& z%snKi`GYWy0>E!x^V`Hy&LkW6rKz%%`p=HL6da&nxbHHbY~KplrYx5#iRhX;7=rT< z|M0<9Q66PGV>~!aW`OFy3EE>@(J;iv|HXeM88VCzcRDmb8>!sZNesL(T%T+|G}l zq|?o;gtg%ymk^tZwUmjjoH#dt)kI)>pH+M{!l$bv8x6=dVv7FE)c_FEmdQ|0b9z2{ zT`5-hwJ#h~tl%Mxq;VXs!8NB|l4GT~vi>ACY5rDXb!aHF1J^d34Gq9VA0BgalGLBy zS{Y;s^FFmTWvJqmTBqu^D4?a{^t`T}H!%*Sz)Hc(!4P)?$G|di{vhuk^ami4BGDci z^gr=4P#BwmvM*%-0ZqZ~Af{o|i`M#G4rkeqC$|eM)Q3bvZ??(Ys&k#IrZ$D#LJIJJ z6j-aK;FrAURX>|eLxmTQ+o+~C;6Z3;@!DuqWBJN}Wr|XqH95WDE36U(KAd>-{t|m6 zT~#P5i;zXY&Bu#4`LhX$%|d_2lyJb5O1}!b0N!sT;4`M@WP2+5An*|b!_Do!>G-y$ z+SAYBPo5MBxR-gxKyf$R(IOj$skWivF?*m4_(TwVlMt*yv5ep$)|btq8x68p%^ZDC z=Vz=&yaClUi2|^y`M|dU>U1KUtCQ>*p(9l}(U-t&-j-D5R_~v&KE4pvc}8(BQZg>v zgOqsw4k_6{cmm;Zxk{|N?@Pc2tE%>(y#W3ci2Xq{2qxh_3 z`--B^$NchLiaL@5I~1aRNPU;K_>VxA$0~6ioE^OfzY+q(cBzs+!LS@?n`d)&R5HZp zQIft?uMeGIj)3#%JirQF{5{AG7BT=PL}2)(#d*r5bqWUI2&}h<8zCB~cgRFN1q(Ljmtlawh~ZkqnD?@yiIFkwUkPM=rrQ zGSgHpMIV|vEaqhgYd?uC6X(*<((Atj{{Fu32&q@PM~0c<@w|j&d&+cQ=MBsnB;^z! zC1zD@jq+VdyP<2qU830Wv-a^wZpg7$dxPrykf}=P0V5J891+UMRQe$0UYOPH&X|XW5}ZWU3?68q>w{4Jx;ObZ67SKP{je`Onnd6E9u|t za)aAE%Xm1^D~fi~gW&dlEKV^KP@AOGecmAH=JQrUHu0)#9XqVJVGE*gYYu(qiDX}@ zPLZ{d5Nd@GHP-BGG<_77JKoTu|K^A|PJ1(TINsT8K#QMF2FyaOz`i9Wqh!o5&h-6b zq9z6#S=fLvWwlztkz6LS;#JNud{)|*=iAa}Av2czz(|Io2mqtmGI?)m> zS5j@OH>dksLafJ}d+2lD_4AVUJ}x_#bPDskRNEdQ)Wet7Ddrwc^({{Dcrh9l^?qpX z<13d~LMu$|M9d^B2nlBh*s+KM=tVpN>sq$I`l;SBAjR@H&^5m(Fl+rtuE_$F zDp>J}EQ=j_vPo2wTvm86GT#bW3_$AHgjrR4PE&E-w9U0BsxZ#13KY7SuGz-7i(I~o zu9sQ$rtUfX zJ!vv>8&-ub&rh6BwYwgcJG1CZZ8>p!SeE~}4CV}H{};>nFhcwqhsX}Pk{?LYb$W>z z^rR!Wn6`xs+Mb#CTAAKZ05KIEyeh6Fc<`P!9Om4SwXZbCJ4E=5@j-aTbzGil<8P$& zJd5#_yvCQ-3N5>u7+f2f;9zi}22{B_1jcCU75NU!)@V$b1%E&aC96o>n5_OfbCKh( zLDpQ0#k~0$XCy<5wOal|sWY`!BT zG{lKj{xA#iy2<7iTTG+Cs*szcx5TPfk6o4O#4k$!4#Nl?X`a5hJOs*0@R}4g)L4)` zQOngi=1;pKS1maVn0q~Xjq1edxDxb0G(l6J@gz3lC`ll7PzAWC7Oppo4*YczWY#4hyY(fp?DIbEWj{$V-mq z4yE7g0}D(V;F-WMcYx6~2&=>dd)$X+wQV?oHVIl?a36K6{sc`hgtt@;$mHCrlKqBL#ajZKS(Z7!fe!da_ z93h7$c7uw(BQ!j<2*7!;xr*U7{P+v^{qDU$O=)>(TR#;5JTxgy8r7{ zyYV3N!HV)29Xvw5APMMs)hO$v7$^c3$x$O@5N7;e(`UH+)k0uWBg%(d7>XXI4$xPpfY zglSi#b5qs?iFEV8@2~|Gg&8U8@q=p+c?QpbkfkA8ue1R)B97thHA$- z;Bexm<+skyLERET_#dR=2kWsA@B=|%k_+oD8K*?0AAo7fgX}NcyS0Te>7TM61XjRN zK*RP99E?5eCJ*wRZ0~O8nM&@t?r(HS$i0d9n&aL1=Iy`Bmkz;V0Zo7}W%sL#s(afi z+)63Cg=Wi6>|sV=s59R)E5*|7X@hxyHpS>ud)i_}Tr%GefYM_RBLe!D^&WE9jDKDi z=&mMCOe^&0K8y#DNI`^Q#<|YFpzIdgo(tS_FFBpSoqZrG%GvYX<{&1CLh|s zoE2t~Jmz)1J|2-xU1T!;~&-BCJ#I{XmVDV)@dhFoMMk_5~&s1)t_ET$ME zXrd2#n08nqRf1Km8<7+{h_Xb4=(G*2h5&qqW|XMgdIazxc4htgP)&Vv0MsVl`EK6< zd0mE-bV2!gl3*3G#a~~wP3dPq#D75B=09^zQ@iq5^(+Qow?>k&Jm$eyJ+@u*!{f$_ zB6y`UV_j85u8)8F$w~sia^Mleri-bj41YBTDA9Z@%xw(5*E<7^g!u8c65u|Tzea*W zYZFmu<(FVxR%WOhi*Up)+iPRfUB>H8fRY+~DB0v;XdB@>xQpv8uBdD3nGAN39sDWM znTN5DLiAp+!`F#l$lwT90D<*R)lYk>tGaYb?2SaQ!nkDdNy_fn8}tP&qBN8IU00{XMN0-dAmbLj*Ct^G|!vN(dC zbszQ$HYY$0v`-Uh@#bU&lLZEq?uB%yd5}ciX$`+K&c?)a=~7SI(E{gi1hPFa2*Ru{ znr~DyAo=z3+?FnM4N>bT6@E0OwWugyc0pTgpA%$qzeM?f z0PagAI$?h*IdUav>!xlkk@5H4@hFffZ)j zjA|^Mt+4bug1))bL<&1!Z%QT}3YNI)716RW^_S<)Rifn9p3au<*g9Ksq zn1<2~mFX9Y5rLTPWLD9x6sgZ;w%iopBBE=3vGIzwi_DjhZ{07*?fB!44r4N?15Cm4 zgHQ>?L7ijtqOOj2!M5KgXB3S_xW>hIV7v4l#EPtsCz?WjW7+X{2~wQfBpgfk!wXO`1`Z*zZBSP4>U8S0_>ZQmg7?Ic-kc;8a(v|?f3|UEvyQg6 z0O;Ge50KN{?8}M93N}-5>fZ;2slBp@U9^U%+#e(6_YJiSMo`}e9)Kl~?W80f=sdi-** zi}=7GaUr=y2=HY3a{92rbKtw(7m55`i8TJ5jj+TB{@qnLa}7Gws@|G<%6_>0u557(p&sY=g}GdWdOZ71aeNsSPC+yD}gwBYBzJgoA2+u zT;L_>^BE+F7M$|*50)aCriND7K(^!l_16RU7iVvoWO`u$4NaLm~)ZEWU=oyZx08$?l59H*H+WTKnZ3QIF^#W7o9Qtbm+E zaKHp#OI<@IdN^tK0Qp-P((w?lzCxQpYo#{qha>7Z8Rc_mevV-6WS2XDH~&pyRPB_a z%$b#OORu9h@Uv#Nt~sKl`fN|4RR%HLdSt780>g*`f4}kA2lc-ZBQ=6=a=eI*A1Ddv znn+d4hd{2@Lo$$go15F7&{1Xe*`E|0&TIof(rd;`oh2E|qMC*^)3W>YhW3D)slSF9 z#>XqA;FJQFZSk#IKogS-KR!|P#y^rAi^y=kz|q7Fs)d6|C8;&ZXUs2+s+6mZTAcj! zoi}k|5R^@17uas--v{taN1?VEidt5#Ew=LY=>-NI?3{v+m|6@*)imK?j(3d<8I z6fjn$oo{C7oq;e-M47S|79|*dDY7y6ru@2Xf3Bl=q_27@pf)D=J{jfm;KGY6F#B%B zfuc5WE+Qcv>35gqsa%8Y#1%Vo4d?hf$&)JV{Ur&H&P-I!#o-G4U?R;+>?ZDtm1fZT z41uHt5rwhDI(`IT!#GJBhjdxH+MFn~HB_zb+=*=?yQ=~-QIgkn{SsbcLC<-c+ua5# zNjwF;`nWsioB8R?`~Xh?55AvMcD`3c&K` zFc!A#WG`(Imtb7ik?0JzDSb3O^`T3*4UPkcRs<}8Ce4MJP+-<1pkF{M-F`SUw%Hc{ zZak`QXy5gcx|XrdmTa$+>Z2CQKBVZ$)~g~U3^`n3&+X5}FUZ@sp5-802}788^u6l# zUm_Vb^j<-5Ld{^+cGNt%nVZ^PM)p61iOvm+!HD)@4Ols7s5QxaMemi>7w`nz-ONba znd&xcYLlcez+hsl1Bt5R4EVo+;(~oMOMRpIQ&;F=M3lsGOjbHn7jO~iAngy(c_hkV z)KbnIW3WmWsuj@dcxQ&WGgoiqUp7u>cjj)jdh@R1N=GUwG6Yhk)0jBfs!)&@hBHw2 zpB7pD`qUKk@5mtue5vGETW!SWW`m!SssJR|5}HaIAC&1;<61g1ye{j;%f56hoJ@|6 z_kyeaY*En<*mZ#pES83FOeX~yrj&SjRsZot{5psxNdc0wo(bvWm{?i zY1ec+uS6{I|0xkdiCQr-kZXLa6sO2e!RwT+6x);sAr9gnmg#ZdYmW8zQ@#3-1*t7- z`ERX3`ls2zzwCyi!XOLa{v6K94`}JPoOK}z1hL73DD(&|U&GX3r5NjO#nw+zOLhr? z6@&~V?Vwo5bKhDYk9p8aRt+5AD&`eZzyWD~so9@FsxbcaFoZ}ECs-73{LwUbc?l_* zYfF~1<&_r!MK$6vdZiIPB>$uC?}II&<6@pwFJGQ{JM%_*<-&Uqe^^Z>fe$yDQbU2F zhWO19a+Su2n>wZe3G0PxNR3>R7F#t$D<{fHegd$6nUv`-EJ{h=c_@NcxX@%5%pg;KdUbx!zD}WvuDIx)-*HK|4tRvPzs^$J4O#u#o-!ltY2>fC4#?MVRjC z50cXwcYp`{UYUo;rtSfg)VO0*6X5^ny@+f$mw2N_Z2Q=s%&HmO%FOrrF4F~n@+pCe zoo$yqrv=kIv(~d4v^XRV@Yv`blm{<_*l2tq!X(?Z1 z-QNH;RrfzbfvZLGG#dzZ^Tez%J6kJJ5J2`K1!tq6fXZ<5|IFa{m8i6TZn!4#54Z%v z=qSL)KdbD>X!&>oh3B8&u5@?H5BESyYe$pA^|=t0@>%p?5+E;pkhKDO1qdA;obZFZ`tj%9bp`pq9ehffM%dY*~BLrHz2tmz_uRop%LK z>50y(+T;6nMPWhXHD^j3X069m<@Tj;Wl41+TnZ{BA?WlsZ%yzlhRhUas_%B^(WNK* zNE?9loSY^?IRWttas`ZKx;WlC3Om2z+Fgvz!{w+$9c46d}K4*N1``e zh&ceu83{uc0ntVz5IMcmK&3Xyd`eD|sf&Ti>QQQm7H)mx1xxIm=QM8)BeYsOV(Alw zSoavl*W^zcwJ&Qm7Lv&3kPwe|$kf~tu^AB70eKidEJewM)b+a*GHg^@P z`3VN(T=Q*FV?_Ow)Q+kzRioGY2U}c1c2+!g?jZp_qdD=etm!Wm=n;K&S{X{bYOlS` zR(aEetIRwXUY?VFcx($Rp+Yaw2zip@pC#mDvtJA2nUuyNhVJUg0<`i{8LW8a?FtMG z6wssZe7Rly;>8kppatkWW>g=YvTY1b{z9#V44D`vziHZP@8BRA`+fy2siWE^0_SKwI z1UXh?UbCoWpdJvP^J(s(^#FqIKONjvGx7YcW@1B6{-I`?y81F7xS4$;yM9@Bz6G9; z;5;ti4@JHDsxlj)A?v|&Wljr+$D%TZ5eZ68hjl1JNLy-_Tu){6&$0*C7N}#kb{R`$ zm|Ip_uLE|XHRzw7epd5U%4t%EFz?$}c(XMgGM3gr~DZ|`!c0mvSecl&rJimr{E zSU}`UBm?>8s(BB;*OM-T_~)}$oLII5RQfd+ISOd+xPuY4iaA+V;PX6mn$KtR_L_qtD-~ntHg&76=P9hh%W!K3>UxqiWL3?XVLs ziD8w(dY*)Y0e9VO+6Vp5Gjz{guHH~P3(zU*(9&s}k_l!Zifrd+97ZwsB4btb ztjp%4^1!dC+O3omcZ0gD+Xr+GY^_h`6!{7vWq2GkVQnKaIK+`^KM1PbJo~l;mP^lM zdkUk^<8%7VYBkCY-K+sn^+-sxgW4k}^z~=Y>m0_qx{L}FW~q9uS4Yzse4yfztc;2{ zje~$Oeo5LJ}&3q*m&FGwjjV00tau$XuaPZenOu z9oGE#=n%LKtViT(oaXCT8bPLZ;wQK@;s)u~#u^l*EwfW_bq-0>Q{X@01!ECZ0fRP-Q zei7I+wT2g!v_l9BUM_MgZ2TPAuZPTu8!0k4 z>cI;o4dgR_p5L~B%cgn|nxfT~?HtNsqU(BY(qcW4XP1%Dkxn)ruMg5OzaEZcl08~; zpC@`sM9#|ffNT_>bG&sY(J|E0{QN|ahU%oQPqhw>!UA^u8yh@l^~#&og~`_9p`04L z+3mR>_E78Cp1dmatWB9obGNQ+M;~-xqB#sdfN#z1tGVt$aq9AW@~6y*PK%(KmTIUR zM@z#^!gjDIx4+j{}BblBO(>-vi$(*{iVs u*Z&&Q|2a=m^;_HVRd+q?CcCx@dMlKSq~eY2>vx_Yg|PpB|NrOj|NjLb#r*gH literal 0 HcmV?d00001 From 673c9aa04a4ea67badff80df67cdd8aa3d8ad2df Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Tue, 21 Aug 2018 20:44:53 +0800 Subject: [PATCH 17/19] auto commit --- notes/分布式.md | 105 ++++++++++++++++++------------------------ notes/系统设计基础.md | 8 ++-- 2 files changed, 48 insertions(+), 65 deletions(-) diff --git a/notes/分布式.md b/notes/分布式.md index c048b43e..e98f2860 100644 --- a/notes/分布式.md +++ b/notes/分布式.md @@ -19,10 +19,11 @@ * [五、Paxos](#五paxos) * [执行过程](#执行过程) * [约束条件](#约束条件) -* [五、Raft](#五raft) +* [六、Raft](#六raft) * [单个 Candidate 的竞选](#单个-candidate-的竞选) * [多个 Candidate 竞选](#多个-candidate-竞选) - * [日志复制](#日志复制) + * [数据同步](#数据同步) +* [参考](#参考) @@ -35,7 +36,7 @@ - 互斥量为 1 表示有其它进程在使用锁,此时处于锁定状态; - 互斥量为 0 表示未锁定状态。 -1 和 0 可以用一个整型值表示,也可以用某个数据存在或者不存在表示,存在表示互斥量为 1。 +1 和 0 可以用一个整型值表示,也可以用某个数据是否存在表示,存在表示互斥量为 1。 ## 数据库的唯一索引 @@ -43,7 +44,7 @@ 存在以下几个问题: -- 锁没有失效时间,解锁失败的话其它进程无法再获得锁。 +- 锁没有失效时间,解锁失败的话其它进程无法再获得该锁。 - 只能是非阻塞锁,插入失败直接就报错了,无法重试。 - 不可重入,已经获得锁的进程也必须重新获取锁。 @@ -59,7 +60,7 @@ EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了 使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。 -- 尝试从 N 个相互独立 Redis 实例获取锁,如果一个实例不可用,应该尽快尝试下一个; +- 尝试从 N 个相互独立 Redis 实例获取锁; - 计算获取锁消耗的时间,只有当这个时间小于锁的过期时间,并且从大多数(N / 2 + 1)实例上获取了锁,那么就认为锁获取成功了; - 如果锁获取失败,就到每个实例上释放锁。 @@ -67,7 +68,7 @@ EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了 ### 1. Zookeeper 抽象模型 -Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示它的父节点为 /app1。 +Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父节点为 /app1。

@@ -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. 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。 2. 之后将本地消息表中的消息转发到 Kafka 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。 @@ -118,23 +113,19 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示

-### 2. 分析 - -本地消息表利用了本地事务来实现分布式事务,并且使用了消息队列来保证最终一致性。 - ## 2PC 两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。 ### 1. 运行过程 -(一)准备阶段 +#### 1.1 准备阶段 协调者询问参与者事务是否执行成功,参与者发回事务执行结果。

-(二)提交阶段 +#### 1.2 提交阶段 如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。 @@ -144,28 +135,22 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示 ### 2. 存在的问题 -(一)同步阻塞 +#### 2.1 同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。 -(二)单点问题 +#### 2.2 单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。 -(三)数据不一致 +#### 2.3 数据不一致 在阶段二,如果协调者只发送了部分 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 分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance),最多只能同时满足其中两项。 @@ -184,7 +169,7 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示 可用性指分布式系统在面对各种异常时可以提供正常服务的能力,可以用系统可用时间占总时间的比值来衡量,4 个 9 的可用性表示系统 99.99% 的时间是可用的。 -在可用性条件下,系统提供的服务一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。 +在可用性条件下,要求系统提供的服务一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。 ## 分区容忍性 @@ -198,16 +183,11 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示 可用性和一致性往往是冲突的,很难都使它们同时满足。在多个节点之间进行数据同步时, -* 为了保证一致性(CP),就需要让所有节点下线成为不可用的状态,等待同步完成; -* 为了保证可用性(AP),在同步过程中允许读取所有节点的数据,但是数据可能不一致。 +- 为了保证一致性(CP),就需要让所有节点下线成为不可用的状态,等待同步完成; +- 为了保证可用性(AP),在同步过程中允许读取所有节点的数据,但是数据可能不一致。

-参考: - -- 倪超. 从 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 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。 @@ -292,66 +272,69 @@ Acceptor 接收到接受请求时,如果序号大于等于该 Acceptor 承诺 Paxos 协议能够让 Proposer 发送的提议朝着能被大多数 Acceptor 接受的那个提议靠拢,因此能够保证可终止性。 -参考: +# 六、Raft -- [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/) -- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/) - -# 五、Raft - -Raft 和 Paxos 类似,但是更容易理解,也更容易实现。 - -Raft 主要是用来竞选主节点。 +Raft 也是分布式一致性协议,主要是用来竞选主节点。 ## 单个 Candidate 的竞选 有三种节点:Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一般为 150ms\~300ms,如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段。 -* 下图表示一个分布式系统的最初阶段,此时只有 Follower,没有 Leader。Follower A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。 +- 下图展示一个分布式系统的最初阶段,此时只有 Follower 没有 Leader。Node A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。

-* 此时 A 发送投票请求给其它所有节点。 +- 此时 Node A 发送投票请求给其它所有节点。

-* 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。 +- 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。

-* 之后 Leader 会周期性地发送心跳包给 Follower,Follower 接收到心跳包,会重新开始计时。 +- 之后 Leader 会周期性地发送心跳包给 Follower,Follower 接收到心跳包,会重新开始计时。

## 多个 Candidate 竞选 -* 如果有多个 Follower 成为 Candidate,并且所获得票数相同,那么就需要重新开始投票,例如下图中 Candidate B 和 Candidate D 都获得两票,因此需要重新开始投票。 +- 如果有多个 Follower 成为 Candidate,并且所获得票数相同,那么就需要重新开始投票。例如下图中 Node B 和 Node D 都获得两票,需要重新开始投票。

-* 当重新开始投票时,由于每个节点设置的随机竞选超时时间不同,因此能下一次再次出现多个 Candidate 并获得同样票数的概率很低。 +- 由于每个节点设置的随机竞选超时时间不同,因此下一次再次出现多个 Candidate 并获得同样票数的概率很低。

-## 日志复制 +## 数据同步 -* 来自客户端的修改都会被传入 Leader。注意该修改还未被提交,只是写入日志中。 +- 来自客户端的修改都会被传入 Leader。注意该修改还未被提交,只是写入日志中。

-* Leader 会把修改复制到所有 Follower。 +- Leader 会把修改复制到所有 Follower。

-* Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。 +- Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。

-* 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。 +- 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。

-参考: +# 参考 +- 倪超. 从 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) +- [聊聊分布式事务,再说说解决方案](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/) diff --git a/notes/系统设计基础.md b/notes/系统设计基础.md index 805764a9..b6a3ddf5 100644 --- a/notes/系统设计基础.md +++ b/notes/系统设计基础.md @@ -68,7 +68,7 @@ 应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。 -关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器存储空间限制。 +关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器的存储空间限制。 对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。 @@ -78,7 +78,7 @@ 实现可扩展主要有两种方式: -- 使用消息队列进行解耦,应用之间通过消息传递的方式进行通信; +- 使用消息队列进行解耦,应用之间通过消息传递进行通信; - 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以通过调用可复用的服务来实现业务逻辑,对其它产品没有影响。 # 四、可用性 @@ -87,7 +87,7 @@ 保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。 -应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上不会对用户有任何影响。 +应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上,不会对用户有任何影响。 存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。 @@ -97,7 +97,7 @@ ## 服务降级 -服务器降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。 +服务降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。 # 五、安全性 From 762c3c698c936492dbc03dec7bf821da4ad5364b Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 23 Aug 2018 21:37:51 +0800 Subject: [PATCH 18/19] auto commit --- notes/Redis.md | 2 +- notes/剑指 offer 题解.md | 2 +- notes/构建工具.md | 2 +- notes/算法.md | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/notes/Redis.md b/notes/Redis.md index 8f231382..629fcdd8 100644 --- a/notes/Redis.md +++ b/notes/Redis.md @@ -549,7 +549,7 @@ def main(): # 十二、Sentinel -Sentinel(哨兵)可以监听主服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。 +Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。 # 十三、分片 diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md index 1bac6bbb..72c20173 100644 --- a/notes/剑指 offer 题解.md +++ b/notes/剑指 offer 题解.md @@ -1082,7 +1082,7 @@ false ```java public boolean isNumeric(char[] str) { - if (str == null) + if (str == null || str.length == 0) return false; return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?"); } diff --git a/notes/构建工具.md b/notes/构建工具.md index c0e4acd5..ce157934 100644 --- a/notes/构建工具.md +++ b/notes/构建工具.md @@ -15,7 +15,7 @@ ## 运行单元测试 -不再需要在项目代码中添加测试代码,从而污染项目代码。 +不再需要在项目代码中添加测试代码,从而避免了污染项目代码。 ## 将源代码转化为可执行文件 diff --git a/notes/算法.md b/notes/算法.md index 25d1550e..a9f9e689 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -23,7 +23,7 @@ * [五、栈和队列](#五栈和队列) * [栈](#栈) * [队列](#队列) -* [六、查找](#六查找) +* [六、符号表](#六符号表) * [初级实现](#初级实现) * [二叉查找树](#二叉查找树) * [2-3 查找树](#2-3-查找树) @@ -1290,7 +1290,7 @@ public class ListQueue implements MyQueue { -# 六、查找 +# 六、符号表 符号表(Symbol Table)是一种存储键值对的数据结构,可以支持快速查找操作。 @@ -1411,9 +1411,9 @@ public class ListUnorderedST implements UnorderedST { 使用一对平行数组,一个存储键一个存储值。 -rank() 方法至关重要,当键在表中时,它能够知道该键的位置;当键不在表中时,它也能知道在何处插入新键。 +二分查找的 rank() 方法至关重要,当键在表中时,它能够知道该键的位置;当键不在表中时,它也能知道在何处插入新键。 -复杂度:二分查找最多需要 logN+1 次比较,使用二分查找实现的符号表的查找操作所需要的时间最多是对数级别的。但是插入操作需要移动数组元素,是线性级别的。 +二分查找最多需要 logN+1 次比较,使用二分查找实现的符号表的查找操作所需要的时间最多是对数级别的。但是插入操作需要移动数组元素,是线性级别的。 ```java public class BinarySearchOrderedST, Value> implements OrderedST { @@ -2254,7 +2254,7 @@ from H1 to H3 - c : 40 - d : 80 -可以将每种字符转换成二进制编码,例如将 a 转换为 00,b 转换为 01,c 转换为 10,d 转换为 11。这是最简单的一种编码方式,没有考虑各个字符的权值(出现频率)。而哈夫曼编码能让出现频率最高的字符的编码最短,从而保证整体的编码长度最短。 +可以将每种字符转换成二进制编码,例如将 a 转换为 00,b 转换为 01,c 转换为 10,d 转换为 11。这是最简单的一种编码方式,没有考虑各个字符的权值(出现频率)。而哈夫曼编码采用了贪心策略,使出现频率最高的字符的编码最短,从而保证整体的编码长度最短。 首先生成一颗哈夫曼树,每次生成过程中选取频率最少的两个节点,生成一个新节点作为它们的父节点,并且新节点的频率为两个节点的和。选取频率最少的原因是,生成过程使得先选取的节点在树的最底层,那么需要的编码长度更长,频率更少可以使得总编码长度更少。 From cf2e79d8329c53f7036594cf459be689ab45eb94 Mon Sep 17 00:00:00 2001 From: newbieof410 Date: Fri, 24 Aug 2018 10:35:57 +0800 Subject: [PATCH 19/19] Update MySQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改笔误 --- notes/MySQL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notes/MySQL.md b/notes/MySQL.md index 54067977..bb465ff3 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -214,7 +214,7 @@ Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explai 一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。 ```sql -DELEFT FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH); +DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH); ``` ```sql @@ -342,7 +342,7 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。 -当一个表的数据不断增多时,Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓存单个数据库的压力。 +当一个表的数据不断增多时,Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓解单个数据库的压力。