diff --git a/notes/HTTP.md b/notes/HTTP.md index 6fec1df9..e6a823cc 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -66,7 +66,7 @@ ## URL -- URI(Uniform Resource Identifier,统一资源标识符) +- URI(Uniform Resource Indentifier,统一资源标识符) - URL(Uniform Resource Locator,统一资源定位符) - URN(Uniform Resource Name,统一资源名称),例如 urn:isbn:0-486-27557-4。 @@ -232,7 +232,7 @@ CONNECT www.example.com:443 HTTP/1.1 - **500 Internal Server Error** :服务器正在执行请求时发生错误。 -- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 +- **503 Service Unavilable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 # 四、HTTP 首部 @@ -313,7 +313,9 @@ CONNECT www.example.com:443 HTTP/1.1 HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。 -Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。它用于告知服务端两个请求是否来自同一浏览器,并保持用户的登录状态。 +Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。 + +Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。 ### 1. 用途 @@ -321,8 +323,6 @@ Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据 - 个性化设置(如用户自定义设置、主题等) - 浏览器行为跟踪(如跟踪分析用户行为等) -Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。由于服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。 - ### 2. 创建过程 服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。 @@ -367,7 +367,7 @@ console.log(document.cookie); 标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。 -标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。因为跨域脚本 (XSS) 攻击常常使用 JavaScript 的 `Document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。 +标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。因为跨站脚本攻击 (XSS) 常常使用 JavaScript 的 `Document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。 ```html Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly @@ -387,7 +387,7 @@ Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径 除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。 -Session 可以存储在服务器上的文件、数据库或者内存中,现在最常见的是将 Session 存储在内存型数据库中,比如 Redis。 +Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在内存型数据库中,比如 Redis。 使用 Session 维护用户登录的过程如下: @@ -485,7 +485,7 @@ ETag: "82e22293907ce725faf67773957acd12" If-None-Match: "82e22293907ce725faf67773957acd12" ``` -Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应, +Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应。 ```html Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT @@ -507,7 +507,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT ### 2. 流水线 -默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到应答过后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。 +默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到相应之后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。 流水线是在同一条长连接上发出连续的请求,而不用等待响应返回,这样可以避免连接延迟。 @@ -627,7 +627,7 @@ HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名, - 网络访问控制 - 访问日志记录 -代理服务器分为正向代理和反向代理两种,用户察觉得到正向代理的存在,而反向代理一般位于内部网络中,用户察觉不到。 +代理服务器分为正向代理和反向代理两种,用户察觉得到正向代理的存在;而反向代理一般位于内部网络中,用户察觉不到。

diff --git a/notes/Java 基础.md b/notes/Java 基础.md index bdd13aff..af1c6530 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -763,10 +763,10 @@ try { ``` ```html -java.lang.CloneNotSupportedException: CloneExample +java.lang.CloneNotSupportedException: CloneTest ``` -以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。 +以上抛出了 CloneNotSupportedException,这是因为 CloneTest 没有实现 Cloneable 接口。 ```java public class CloneExample implements Cloneable { diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 3d8eecac..800796db 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -23,7 +23,8 @@ * [五、互斥同步](#五互斥同步) * [synchronized](#synchronized) * [ReentrantLock](#reentrantlock) - * [synchronized 和 ReentrantLock 比较](#synchronized-和-reentrantlock-比较) + * [比较](#比较) + * [使用选择](#使用选择) * [六、线程之间的协作](#六线程之间的协作) * [join()](#join) * [wait() notify() notifyAll()](#wait-notify-notifyall) @@ -498,6 +499,8 @@ public synchronized static void fun() { ## ReentrantLock +ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁. + ```java public class LockExample { @@ -529,23 +532,8 @@ public static void main(String[] args) { 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 ``` -ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁,相比于 synchronized,它多了以下高级功能: -**1. 等待可中断** - -当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。 - -**2. 可实现公平锁** - -公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。 - -synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。 - -**3. 锁绑定多个条件** - -一个 ReentrantLock 对象可以同时绑定多个 Condition 对象。 - -## synchronized 和 ReentrantLock 比较 +## 比较 **1. 锁的实现** @@ -553,13 +541,25 @@ synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。 **2. 性能** -新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等。目前来看它和 ReentrantLock 的性能基本持平了,因此性能因素不再是选择 ReentrantLock 的理由。synchronized 有更大的性能优化空间,应该优先考虑 synchronized。 +新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。 -**3. 功能** +**3. 等待可中断** -ReentrantLock 多了一些高级功能。 +当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。 -**4. 使用选择** +ReentrantLock 可中断,而 synchronized 不行。 + +**4. 公平锁** + +公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。 + +synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。 + +**5. 锁绑定多个条件** + +一个 ReentrantLock 可以同时绑定多个 Condition 对象。 + +## 使用选择 除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。 @@ -1232,7 +1232,7 @@ Thread 对象的 start() 方法调用先行发生于此线程的每一个动作 > Thread Join Rule -Thread 对象的结束先行发生于join() 方法返回 。 +Thread 对象的结束先行发生于 join() 方法返回。

@@ -1240,7 +1240,7 @@ Thread 对象的结束先行发生于join() 方法返回 。 > Thread Interruption Rule -对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 Thread.interrupted() 方法检测到是否有中断发生。 +对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。 ### 7. 对象终结规则 @@ -1385,22 +1385,21 @@ synchronized 和 ReentrantLock。 ### 2. 非阻塞同步 -互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步(Blocking Synchronization)。 +互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步。 -从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。 +互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题。论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。 -随着硬件指令集的发展,我们有了另外一个选择:基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采取其他的补偿措施(最常见的补偿措施就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步(Non-Blocking Synchronization)。 +随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略:先进行操作,如果没有其它线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步。 -乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)。 +乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。 -CAS 指令需要有 3 个操作数,分别是内存位置(在 Java 中可以简单理解为变量的内存地址,用 V 表示)、旧的预期值(用 A 表示)和新值(用 B 表示)。CAS 指令执行时,当且仅当 V 符合旧预期值 A 时,处理器用新值 B 更新 V 的值,否则它就不执行更新。但是无论是否更新了 V 的值,都会返回 V 的旧值,上述的处理过程是一个原子操作。 +硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。 J.U.C 包里面的整数原子类 AtomicInteger,其中的 compareAndSet() 和 getAndIncrement() 等方法都使用了 Unsafe 类的 CAS 操作。 -在下面的代码 1 中,使用了 AtomicInteger 执行了自增的操作。代码 2 是 incrementAndGet() 的源码,它调用了 unsafe 的 getAndAddInt() 。代码 3 是 getAndAddInt() 源码,var1 指示内存位置,var2 指示新值,var4 指示操作需要加的数值,这里为 1。在代码 3 的实现中,通过 getIntVolatile(var1, var2) 得到旧的预期值。通过调用 compareAndSwapInt() 来进行 CAS 比较,如果 var2=var5,那么就更新内存地址为 var1 的变量为 var5+var4。可以看到代码 3 是在一个循环中进行,发生冲突的做法是不断的进行重试。 +以下代码使用了 AtomicInteger 执行了自增的操作。 ```java -// 代码 1 private AtomicInteger cnt = new AtomicInteger(); public void add() { @@ -1408,15 +1407,17 @@ public void add() { } ``` +以下代码是 incrementAndGet() 的源码,它调用了 unsafe 的 getAndAddInt() 。 + ```java -// 代码 2 public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } ``` +以下代码是 getAndAddInt() 源码,var1 指示内存地址,var2 指示旧值,var4 指示操作需要加的数值,这里为 1。通过 getIntVolatile(var1, var2) 得到旧的预期值,通过调用 compareAndSwapInt() 来进行 CAS 比较,如果 var2==var5,那么就更新内存地址为 var1 的变量为 var5+var4。可以看到 getAndAddInt() 在一个循环中进行,发生冲突的做法是不断的进行重试。 + ```java -// 代码 3 public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { @@ -1427,7 +1428,9 @@ public final int getAndAddInt(Object var1, long var2, int var4) { } ``` -ABA :如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。J.U.C 包提供了一个带有标记的原子引用类“AtomicStampedReference”来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。 +ABA :如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。 + +J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。 ### 3. 无同步方案 @@ -1435,9 +1438,9 @@ ABA :如果一个变量初次读取的时候是 A 值,它的值被改成了 **(一)可重入代码(Reentrant Code)** -这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。相对线程安全来说,可重入性是更基本的特性,它可以保证线程安全,即所有的可重入的代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。 +这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。 -可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。我们可以通过一个简单的原则来判断代码是否具备可重入性:如果一个方法,它的返回结果是可以预测的,只要输入了相同的数据,就都能返回相同的结果,那它就满足可重入性的要求,当然也就是线程安全的。 +可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。 **(二)栈封闭** diff --git a/notes/Java 虚拟机.md b/notes/Java 虚拟机.md index 1d2b4f14..552a5e94 100644 --- a/notes/Java 虚拟机.md +++ b/notes/Java 虚拟机.md @@ -174,7 +174,7 @@ obj = null; // 使对象只被软引用关联 **(三)弱引用** -被弱引用关联的对象一定会被垃圾收集器回收,也就是说它只能存活到下一次垃圾收集发生之前。 +被弱引用关联的对象一定会被垃圾收集器回收,也就是说它只能存活到下一次垃圾收集。 使用 WeakReference 类来实现弱引用。 @@ -388,11 +388,11 @@ CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。 G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。 -Java 堆被分为新生代、老年代和永久代,其它收集器进行收集的范围都是整个新生代或者老生代,而 G1 可以直接对新生代和永久代一起回收。 +Java 堆被分为新生代、老年代和永久代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。

-G1 把堆划分成多个大小相等的独立区域(Region),新生代和永久代不再物理隔离。 +G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。

@@ -457,15 +457,17 @@ G1 把堆划分成多个大小相等的独立区域(Region),新生代和 (四)动态对象年龄判定 -虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 区中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。 +虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。 (五)空间分配担保 -在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的;如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC,尽管这次 Minor GC 是有风险的;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那这时也要改为进行一次 Full GC。 +在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。 + +如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那么就要进行一次 Full GC。 ### 3. Full GC 的触发条件 -对于 Minor GC,其触发条件非常简单,当 Eden 区空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件: +对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件: (一)调用 System.gc() @@ -483,11 +485,15 @@ G1 把堆划分成多个大小相等的独立区域(Region),新生代和 (四)JDK 1.7 及以前的永久代空间不足 -在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。 +在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。 + +当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。 + +为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。 (五)Concurrent Mode Failure -执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(有时候“空间不足”是指 CMS GC 当前的浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。 +执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。 # 三、类加载机制 diff --git a/notes/MySQL.md b/notes/MySQL.md index 5348018d..d49fc952 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -379,7 +379,7 @@ SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); 垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用的列和不经常被使用的列切分到不同的表中。 -在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库 payDB、用户数据库 userBD 等。 +在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库 payDB、用户数据库 userDB 等。 ## Sharding 策略 diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md index 1d493176..87d210a9 100644 --- a/notes/数据库系统原理.md +++ b/notes/数据库系统原理.md @@ -346,7 +346,7 @@ InnoDB 的 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回 ### 4. UPDATE -将当前系统版本号作为更新后的数据行快照的创建版本号,同时将当前系统版本号作为更新前的数据行快照的删除版本号。可以理解为先执行 DELETE 后执行 INSERT。 +将当前系统版本号作为更新前的数据行快照的删除版本号,并将当前系统版本号作为更新后的数据行快照的创建版本号。可以理解为先执行 DELETE 后执行 INSERT。 ## 快照读与当前读 @@ -491,7 +491,7 @@ Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门 有以下函数依赖: -- Sno -> Sname, Sdept, Mname +- Sno -> Sname, Sdept - Sdept -> Mname 关系-2 diff --git a/notes/计算机网络.md b/notes/计算机网络.md index 1c38cc0a..8603393c 100644 --- a/notes/计算机网络.md +++ b/notes/计算机网络.md @@ -664,7 +664,7 @@ BGP 只能寻找一条比较好的路由,而不是最佳路由。 发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。 -接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {32, 33} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 +接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 ## TCP 可靠传输