├── .gitattributes ├── Java面试问题整理 └── Java问题整理.md ├── LICENSE ├── MySQL面试问题整理 ├── MySQL问题整理.assets │ ├── image-20221201142516000.png │ ├── image-20221201143241713.png │ ├── image-20221201143245347.png │ ├── image-20221201143303460.png │ ├── image-20221201144758237.png │ ├── image-20221201151724223.png │ ├── image-20221201162155274.png │ ├── image-20221211155428530.png │ ├── image-20221211210331900.png │ ├── image-20221211211323227.png │ ├── image-20221211213852774.png │ ├── image-20221211221636235.png │ ├── image-20221211224409445.png │ ├── image-20221212140751245.png │ ├── image-20221212151348807.png │ ├── image-20221212163152470.png │ ├── image-20221212163442391.png │ ├── image-20221212163547284.png │ ├── image-20221212163826562.png │ ├── image-20221212164913847.png │ ├── image-20221212165049592.png │ ├── image-20221212165055918.png │ ├── image-20221212165146640.png │ ├── image-20221212170058437.png │ ├── image-20221212170145208.png │ ├── image-20221212170149995.png │ ├── image-20221212214134578.png │ ├── image-20221212214154824.png │ ├── image-20221212214630847.png │ ├── image-20221212214817884.png │ ├── image-20221212215049288.png │ ├── image-20221212215237272.png │ ├── image-20221212225723841.png │ ├── image-20221212225829061.png │ ├── image-20221212230946328.png │ ├── image-20221212231421681.png │ ├── image-20221213133745476.png │ ├── image-20221213135823899.png │ ├── image-20221213141320334.png │ ├── image-20221213142016600.png │ ├── image-20221213142612507.png │ ├── image-20221213142925956.png │ ├── image-20221213143006441.png │ ├── image-20221213143203979.png │ ├── image-20221213143505802.png │ ├── image-20221213143523671.png │ ├── image-20221213143554868.png │ ├── image-20221213143826673.png │ ├── image-20221213144331585.png │ ├── image-20221213145005617.png │ ├── image-20221213145238809.png │ ├── image-20221213145701443.png │ └── image-20221213150628725.png └── MySQL问题整理.md ├── README.md ├── Redis面试问题整理 ├── Redis问题整理.assets │ └── image-20230306160134208.png └── Redis问题整理.md ├── 操作系统问题整理 ├── 操作系统八股.assets │ ├── image-20230202234839001.png │ ├── image-20230202234919691.png │ ├── image-20230202234924892.png │ ├── image-20230203000640690.png │ ├── image-20230203001008346.png │ ├── image-20230203001059458.png │ ├── image-20230203142220904.png │ ├── image-20230203154118858.png │ ├── image-20230203154706712.png │ ├── image-20230203154722404.png │ ├── image-20230203154742836.png │ ├── image-20230203154822362.png │ ├── image-20230203154824865.png │ ├── image-20230203155105227.png │ ├── image-20230203162644946.png │ ├── image-20230203163726654.png │ ├── image-20230203163801981.png │ ├── image-20230203165515837.png │ ├── image-20230203170706761.png │ ├── image-20230203213538415.png │ ├── image-20230203213605267.png │ ├── image-20230203220950140.png │ ├── image-20230203221307695.png │ ├── image-20230203221317632.png │ ├── image-20230204142634408.png │ ├── image-20230204142754599.png │ ├── image-20230204143313238.png │ ├── image-20230204143408791.png │ ├── image-20230204143434295.png │ ├── image-20230204143453279.png │ ├── image-20230204143521125.png │ ├── image-20230204143644032.png │ ├── image-20230204143656785.png │ ├── image-20230204143718506.png │ ├── image-20230204143807163.png │ ├── image-20230204143820697.png │ └── image-20230204143830688.png └── 操作系统八股.md └── 计算机网络问题整理 ├── 计网八股.assets ├── image-20230126145202123.png ├── image-20230126145220689.png ├── image-20230126145614195.png ├── image-20230126145639682.png ├── image-20230126145724420.png ├── image-20230126145804277.png ├── image-20230126145919764.png ├── image-20230126150006442.png ├── image-20230126150247839.png ├── image-20230126150736494.png ├── image-20230126150853404.png ├── image-20230126150950542.png ├── image-20230126151036131.png ├── image-20230126151059580.png ├── image-20230126151116758.png ├── image-20230126151132578.png ├── image-20230126151144335.png ├── image-20230126151228887.png ├── image-20230126151307696.png ├── image-20230126151345159.png ├── image-20230126151359357.png ├── image-20230126151831836.png ├── image-20230126162417520.png ├── image-20230126171413301.png ├── image-20230126171450973.png ├── image-20230126190019529.png ├── image-20230126190132484.png ├── image-20230126190420778.png ├── image-20230126190513685.png ├── image-20230126190808303.png ├── image-20230126190941783.png ├── image-20230126192500961.png ├── image-20230126192814076.png ├── image-20230201140944524.png ├── image-20230201141056876.png ├── image-20230201141210092.png ├── image-20230201145201966.png ├── image-20230201163118587.png ├── image-20230201201111914.png ├── image-20230201201153169.png ├── image-20230201201504557.png ├── image-20230201201611677.png ├── image-20230201232550574.png ├── image-20230201233607275.png ├── image-20230201233724355.png ├── image-20230202001455580.png ├── image-20230202141157456.png ├── image-20230202141230025.png ├── image-20230202141330954.png ├── image-20230202141530611.png ├── image-20230202142808507.png ├── image-20230202142907076.png ├── image-20230202142925512.png ├── image-20230202142943183.png ├── image-20230202143914913.png ├── image-20230202144053005.png ├── image-20230202144156005.png ├── image-20230202150851545.png ├── image-20230202151753926.png ├── image-20230202151927972.png ├── image-20230202151940614.png ├── image-20230202152017320.png ├── image-20230202152027864.png ├── image-20230202152123506.png ├── image-20230202152324041.png ├── image-20230202152332562.png ├── image-20230202152354957.png ├── image-20230202152411431.png ├── image-20230202152717672.png ├── image-20230202152818780.png ├── image-20230202153827325.png ├── image-20230202153920169.png ├── image-20230202153945231.png ├── image-20230202154024196.png ├── image-20230202154234836.png ├── image-20230202155810856.png ├── image-20230202155838360.png ├── image-20230202160019143.png ├── image-20230202160146438.png ├── image-20230202210614068.png ├── image-20230202212457439.png ├── image-20230202212640247.png ├── image-20230202212656485.png ├── image-20230202212840841.png ├── image-20230202213116722.png ├── image-20230202213138742.png ├── image-20230202215911544.png ├── image-20230202220026412.png ├── image-20230202220049827.png ├── image-20230202220236535.png ├── image-20230202222440843.png ├── image-20230202222526258.png ├── image-20230202222709619.png ├── image-20230202222820668.png ├── image-20230202222843196.png ├── image-20230202224150400.png ├── image-20230202224211893.png ├── image-20230202224246159.png ├── image-20230202225809040.png ├── image-20230202233949212.png ├── image-20230202234005218.png ├── image-20230202234046383.png └── image-20230202234202774.png └── 计网八股.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Java面试问题整理/Java问题整理.md: -------------------------------------------------------------------------------- 1 | # 线程 2 | 3 | ## Java线程池七大参数详解 4 | 5 | **JDK1.8线程池参数源代码:** 6 | 7 | ```java 8 | public ThreadPoolExecutor(int corePoolSize, 9 | int maximumPoolSize, 10 | long keepAliveTime, 11 | TimeUnit unit, 12 | BlockingQueue workQueue, 13 | RejectedExecutionHandler handler) { 14 | this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 15 | Executors.defaultThreadFactory(), handler); 16 | } 17 | ``` 18 | 19 | **一、corePoolSize** 20 | 21 | 指的是核心线程大小,线程池中维护一个最小的线程数量,即使这些线程处于空闲状态,也一直存在池中,除非设置了**核心线程超时时间**。 22 | 23 | 这也是源码中的注释说明。 24 | 25 | ```java 26 | /** @param corePoolSize the number of threads to keep in the pool, 27 | * even if they are idle, unless {@code allowCoreThreadTimeOut} is set. 28 | */ 29 | ``` 30 | 31 | **二、maximumPoolSize** 32 | 33 | 指的是**线程池中允许的最大线程数量**。当线程池中核心线程都处理执行状态,有**新请求的任务**: 34 | 35 | **1、工作队列未满:新请求的任务加入工作队列** 36 | 37 | **2、工作队列已满:线程池会创建新线程,来执行这个任务**。当然,创建新线程不是无限制的,因为会受到maximumPoolSize最大线程数量的限制。 38 | 39 | **三、keepAliveTime** 40 | 41 | 指的是**空闲线程存活时间**。具体说,当线程数大于核心线程数时,空闲线程在等待新任务到达的最大时间,如果超过这个时间还没有任务请求,该空闲线程就会被销毁。 42 | 43 | 可见官方注释: 44 | 45 | ```mysql 46 | /** @param keepAliveTime when the number of threads is greater than 47 | * the core, this is the maximum time that excess idle threads 48 | * will wait for new tasks before terminating. 49 | */ 50 | ``` 51 | 52 | **四、unit** 53 | 54 | 是指**空闲线程存活时间的单位**。keepAliveTime的计量单位。枚举类型TimeUnit类。 55 | 56 | **五、workQueue** 57 | 58 | **1、ArrayBlockingQueue** 59 | 60 | 基于数组的有界阻塞队列,特点**FIFO**(先进先出)。 61 | 62 | **当线程池中已经存在最大数量的线程时候,再请求新的任务,这时就会将任务加入工作队列的队尾,一旦有空闲线程,就会取出队头执行任务。因为是基于数组的有界阻塞队列,所以可以避免系统资源的耗尽**。 63 | 64 | 那么如果出现有界队列已满,最大数量的所有线程都处于执行状态,这时又有新的任务请求,怎么办呢? 65 | 66 | 这时候会采用**Handler拒绝策略**,对请求的任务进行处理。后面会详细介绍。 67 | 68 | **2、LinkedBlockingQueue** 69 | 70 | 基于链表的无界阻塞队列,默认最大容量Integer.MAX_VALUE( ![2^{32}-1](https://latex.codecogs.com/gif.latex?2%5E%7B32%7D-1)),可认为是**无限队列**,特点FIFO。 71 | 72 | 关于maximumPoolSize参数在工作队列为LinkedBlockingQueue时候,是否起作用这个问题,我们需要视情况而定! 73 | 74 | > **情况①**:如果指定了工作队列大小,比如core=2,max=3,workQueue=2,任务数task=5,这种情况的最大线程数量的限制是有效的。 75 | > 76 | > **情况②**:如果工作队列大小默认![2^{32}-1](https://latex.codecogs.com/gif.latex?2%5E%7B32%7D-1),这时maximumPoolSize不起作用,因为新请求的任务一直可以加到队列中。 77 | 78 | **3、PriorityBlockingQueue** 79 | 80 | 优先级无界阻塞队列,前面两种工作队列特点都是FIFO,而**优先级阻塞队列可以通过参数Comparator实现对任务进行排序,不按照FIFO执行**。 81 | 82 | **4、SynchronousQueue** 83 | 84 | 不缓存任务的阻塞队列,它实际上**不是真正的队列,因为它没有提供存储任务的空间**。生产者一个任务请求到来,会直接执行,也就是说**这种队列在消费者充足的情况下更加适合**。因为这种队列没有存储能力,所以只有当另一个线程(消费者)准备好工作,put(入队)和take(出队)方法才不会是阻塞状态。 85 | 86 | 以上四种工作队列,跟线程池结合就是一种**生产者-消费者 设计模式**。生产者把新任务加入工作队列,消费者从队列取出任务消费,BlockingQueue可以使用任意数量的生产者和消费者,这样实现了解耦,简化了设计。 87 | 88 | **六、threadFactory** 89 | 90 | 线程工厂,创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等。 91 | 92 | > 守护线程(Daemon Thread) 在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。 93 | > 94 | > 所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。 95 | > 96 | > 用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 97 | > 98 | > 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。 99 | > 100 | > 将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点: 101 | > 102 | > (1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。 103 | > 104 | > (2) 在Daemon线程中产生的新线程也是Daemon的。 105 | > 106 | > (3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。 107 | 108 | 官方使用默认的线程工厂源码如下: 109 | 110 | ```java 111 | /** 112 | * The default thread factory 113 | 114 | */ 115 | static class DefaultThreadFactory implements ThreadFactory { 116 | private static final AtomicInteger poolNumber = new AtomicInteger(1); 117 | private final ThreadGroup group; 118 | private final AtomicInteger threadNumber = new AtomicInteger(1); 119 | private final String namePrefix; 120 | DefaultThreadFactory() { 121 | SecurityManager s = System.getSecurityManager(); 122 | group = (s != null) ? s.getThreadGroup() : 123 | Thread.currentThread().getThreadGroup(); 124 | namePrefix = "pool-" + 125 | poolNumber.getAndIncrement() + 126 | "-thread-"; 127 | 128 | } 129 | 130 | public Thread newThread(Runnable r) { 131 | 132 | Thread t = new Thread(group, r, 133 | 134 | namePrefix + threadNumber.getAndIncrement(), 135 | 136 | 0); 137 | if (t.isDaemon()) t.setDaemon(false); 138 | if (t.getPriority() != Thread.NORM_PRIORITY) 139 | 140 | t.setPriority(Thread.NORM_PRIORITY); 141 | return t 142 | 143 | } 144 | } 145 | ``` 146 | 147 | **七、handler** 148 | 149 | Java 并发超出线程数和工作队列时候的任务请求处理策略,使用了**策略设计模式**。 150 | 151 | **策略1:ThreadPoolExecutor.AbortPolicy(默认)** 152 | 153 | 在默认的处理策略。**该处理在拒绝时抛出RejectedExecutionException,拒绝执行。** 154 | 155 | ```java 156 | public static class AbortPolicy implements RejectedExecutionHandler { 157 | /** 158 | * Creates an {@code AbortPolicy}. 159 | */ 160 | public AbortPolicy() { } 161 | /** 162 | * Always throws RejectedExecutionException. 163 | * 164 | * @param r the runnable task requested to be executed 165 | * @param e the executor attempting to execute this task 166 | * @throws RejectedExecutionException always 167 | */ 168 | public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 169 | throw new RejectedExecutionException("Task " + r.toString() + 170 | " rejected from " + 171 | e.toString()); 172 | } 173 | } 174 | ``` 175 | 176 | **策略2:ThreadPoolExecutor.CallerRunsPolicy** 177 | 178 | **调用 execute 方法的线程本身运行任务**。这提供了一个简单的反馈控制机制,可以降低新任务提交的速度。 179 | 180 | ```java 181 | public static class CallerRunsPolicy implements RejectedExecutionHandler { 182 | /** 183 | * Creates a {@code CallerRunsPolicy}. 184 | */ 185 | public CallerRunsPolicy() { } 186 | /** 187 | * Executes task r in the caller's thread, unless the executor 188 | * has been shut down, in which case the task is discarded. 189 | * 190 | * @param r the runnable task requested to be executed 191 | * @param e the executor attempting to execute this task 192 | */ 193 | public void rejectedExecution(Runnable r, ThreadPoolExecutor e) 194 | if (!e.isShutdown()) { 195 | r.run(); 196 | } 197 | } 198 | } 199 | ``` 200 | 201 | **策略3:ThreadPoolExecutor.DiscardOldestPolicy** 202 | 203 | 如果**执行程序未关闭,则删除工作队列头部的任务**,然后重试执行(可能再次失败,导致重复执行)。 204 | 205 | ```java 206 | public static class DiscardOldestPolicy implements RejectedExecutionHandler { 207 | 208 | 209 | 210 | /** 211 | * Creates a {@code DiscardOldestPolicy} for the given executor. 212 | 213 | 214 | 215 | */ 216 | public DiscardOldestPolicy() { } 217 | 218 | 219 | /** 220 | * Obtains and ignores the next task that the executor 221 | * * would otherwise execute, if one is immediately available, 222 | * and then retries execution of task r, unless the executor 223 | * is shut down, in which case task r is instead discarded. * 224 | * @param r the runnable task requested to be executed 225 | * @param e the executor attempting to execute this task 226 | */ 227 | public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 228 | if (!e.isShutdown()) { 229 | 230 | e.getQueue().poll(); 231 | 232 | e.execute(r); 233 | 234 | } 235 | } 236 | } 237 | ``` 238 | 239 | **策略4:ThreadPoolExecutor.DiscardPolicy** 240 | 241 | ```java 242 | public static class DiscardPolicy implements RejectedExecutionHandler { 243 | /** 244 | * Creates a {@code DiscardPolicy}. 245 | */ 246 | 247 | public DiscardPolicy() { } 248 | /** 249 | * Does nothing, which has the effect of discarding task r. 250 | * 251 | * @param r the runnable task requested to be executed 252 | * @param e the executor attempting to execute this task 253 | */ 254 | public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 255 | 256 | } 257 | } 258 | ``` 259 | 260 | **八、ThreadPoolExecutor线程池参数设置技巧** 261 | 262 | **一、ThreadPoolExecutor的重要参数** 263 | 264 | * corePoolSize:核心线程数 265 | * 核心线程会一直存活,及时没有任务需要执行 266 | * 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理 267 | * 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭 268 | * queueCapacity:任务队列容量(阻塞队列) 269 | * 当核心线程数达到最大时,新任务会放在队列中排队等待执行 270 | 271 | * maxPoolSize:最大线程数 272 | * 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务 273 | * 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常 274 | 275 | * keepAliveTime:线程空闲时间 276 | * 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize 277 | * 如果allowCoreThreadTimeout=true,则会直到线程数量=0 278 | * allowCoreThreadTimeout:允许核心线程超时 279 | * rejectedExecutionHandler:任务拒绝处理器 280 | * 两种情况会拒绝处理任务: 281 | * 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务 282 | * 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务 283 | * 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常 284 | * ThreadPoolExecutor类有几个内部实现类来处理这类情况: 285 | * AbortPolicy 丢弃任务,抛运行时异常 286 | * CallerRunsPolicy 执行任务 287 | * DiscardPolicy 忽视,什么都不会发生 288 | * DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务 289 | * 实现RejectedExecutionHandler接口,可自定义处理器 290 | 291 | **二、ThreadPoolExecutor执行顺序:** 292 | 293 | 线程池按以下行为执行任务 294 | 295 | 1. 当线程数小于核心线程数时,创建线程。 296 | 297 | 2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。 298 | 299 | 3. 当线程数大于等于核心线程数,且任务队列已满 300 | 301 | 1) 若线程数小于最大线程数,创建线程 302 | 303 | 2. 若线程数等于最大线程数,抛出异常,拒绝任务 304 | 305 | # 锁 306 | 307 | ## JVM 本地锁 308 | 309 | 一、以下三种情况可能导致 JVM 本地锁失效 310 | 311 | 1. 多例模式 312 | 313 | ```java 314 | @Scope(Value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) 315 | ``` 316 | 317 | 2. 开启事务 318 | 319 | ```java 320 | @Transactional(默认隔离级别是数据库的隔离级别,因为mysql隔离级别是可重复读,所以会产生问题) 321 | ``` 322 | 323 | 3. 集群部署 324 | 325 | ## MySQL 锁 326 | 327 | 更新数量时进行判断 328 | 329 | 解决:1. 锁范围问题 (行级锁,表级锁) 2. 同一个商品有多条库存记录 3. 无法记录库存变化前后的状态 330 | 331 | > 行级锁都是基于索引的。如果一条 SQL 语句用不到索引是不会使用行级锁的,而会使用表级索把整个表锁住。 332 | > 333 | > MySQL悲观锁中使用行级锁:1. 锁的查询或者更新条件必须是索引字段。 334 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221201142516000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221201142516000.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221201143241713.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221201143241713.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221201143245347.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221201143245347.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221201143303460.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221201143303460.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221201144758237.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221201144758237.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221201151724223.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221201151724223.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221201162155274.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221201162155274.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221211155428530.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221211155428530.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221211210331900.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221211210331900.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221211211323227.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221211211323227.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221211213852774.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221211213852774.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221211221636235.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221211221636235.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221211224409445.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221211224409445.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212140751245.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212140751245.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212151348807.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212151348807.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212163152470.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212163152470.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212163442391.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212163442391.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212163547284.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212163547284.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212163826562.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212163826562.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212164913847.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212164913847.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212165049592.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212165049592.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212165055918.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212165055918.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212165146640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212165146640.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212170058437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212170058437.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212170145208.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212170145208.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212170149995.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212170149995.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212214134578.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212214134578.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212214154824.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212214154824.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212214630847.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212214630847.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212214817884.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212214817884.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212215049288.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212215049288.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212215237272.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212215237272.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212225723841.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212225723841.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212225829061.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212225829061.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212230946328.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212230946328.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221212231421681.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221212231421681.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213133745476.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213133745476.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213135823899.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213135823899.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213141320334.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213141320334.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213142016600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213142016600.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213142612507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213142612507.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213142925956.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213142925956.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213143006441.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213143006441.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213143203979.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213143203979.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213143505802.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213143505802.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213143523671.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213143523671.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213143554868.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213143554868.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213143826673.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213143826673.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213144331585.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213144331585.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213145005617.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213145005617.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213145238809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213145238809.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213145701443.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213145701443.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.assets/image-20221213150628725.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/MySQL面试问题整理/MySQL问题整理.assets/image-20221213150628725.png -------------------------------------------------------------------------------- /MySQL面试问题整理/MySQL问题整理.md: -------------------------------------------------------------------------------- 1 | # 一、MySQL索引 2 | 3 | ## MySQL 如何实现索引机制? 4 | 5 | 三类:B+树索引、Hash索引、全文索引 6 | 7 | ## InnoDB索引和MyISAM索引实现的区别? 8 | 9 | MyISAM索引文件和数据文件是分离的,使用B+树实现,主键索引和辅助索引实现一致,索引文件仅保存记录所在页的指针(物理地址),通过这些地址来读取页,进而读取被索引的行。 10 | 11 | ## 一个表如果没有创建索引,那么还会创建B+树吗? 12 | 13 | 会,MySQL会隐式创建基于row_id的索引。 14 | 15 | # 二、说一下B+树索引实现原理 16 | 17 | 假设有一个表index_demo,表中有2个 INT 类型的列,1个 CHAR(1) 类型的列,c1列为主键 18 | 19 | ```mysql 20 | CREATE TABLE index_demo(c1 INT, c2 INT, c3 CHAR(1), PRIMARY KEY(C1)); 21 | ``` 22 | 23 | index_demo表的简化的行格式示意图如下: 24 | 25 | ![image-20221201142516000](MySQL问题整理.assets/image-20221201142516000.png) 26 | 27 | 我们只在示意图里展示记录的这几个部分: 28 | 29 | * `record_type:` 表示记录的类型,0是普通记录、2是最小记录、3是最大记录、1是B+树非叶子节点记录。 30 | * `next_record:` 表示下一条记录的相对位置,我们用箭头来表明下一条记录。 31 | * `各个列的值:` 这里只记录在 index_demo 表中的三个列,分别是c1、c2 和 c3。 32 | * `其他信息:` 除了上述三种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。 33 | 34 | 将`其他信息`项暂时去掉并把它竖起来的效果就是这样: 35 | 36 | image-20221201143245347 37 | 38 | ![image-20221201143303460](MySQL问题整理.assets/image-20221201143303460.png) 39 | 40 | `MySQL InnoDB的默认的页大小就是16KB`,因此数据存储在磁盘中,可能会占用多个数据页。 41 | 42 | ![image-20221201144758237](MySQL问题整理.assets/image-20221201144758237.png) 43 | 44 | ## 聚簇索引和非聚簇索引 B+ 树实现有什么区别? 45 | 46 | **聚簇索引** 47 | 48 | **特点:** 49 | 50 | * `索引和数据保存在同一个B+树中`。 51 | * `页内的记录`是按照`主键`的大小排序排成一个单向链表。 52 | * `页与页之间`也是根据页中记录的`主键`的大小顺序排成一个`双向链表`。 53 | 54 | * 非叶子节点存储的是记录的`主键+页号`。 55 | * 叶子节点存储的是`完整的用户记录`。 56 | 57 | **限制:** 58 | 59 | * 只有InnoDB引擎支持聚簇索引,`MyISAM`不支持聚簇索引。 60 | * 由于数据的物理存储排序方式只能有一种,所以`每个MySQL的表只能有一个聚簇索引`。 61 | * 如果没有为表定义主键,InnoDB会选择`非空的唯一索引列替代`。如果没有这样的列,InnoDB会`隐式的定义一个主键`作为聚簇索引。 62 | * 为了充分利用聚簇索引的聚簇特性,InnoDB中的表的`主键应选择有序的id`,不建议使用无序的id,比如UUID、MD5、HASH、字符串作为主键,无法保证数据的顺序增长。 63 | 64 | **非聚簇索引** 65 | 66 | (二级索引、辅助索引) 67 | 68 | `聚簇索引`,只能在搜索条件时`主键值`时才发挥作用,因为B+树中的数据都是按照主键进行排序的,如果我们想以别的列作为搜索条件,那么需要创建`非聚簇索引`。 69 | 70 | 例如,`以c2列作为搜索条件`,那么需要使用`c2列创建一颗B+树`,如下所示: 71 | 72 | ![image-20221201162155274](MySQL问题整理.assets/image-20221201162155274.png) 73 | 74 | **与聚簇索引有几处不同:** 75 | 76 | * `页内的记录`是按照从`c2列`的大小排序排成一个`单向链表`。 77 | * `页和页之间`也是根据页中的记录的`c2列`的大小排序排成一个`双向链表`。 78 | * 非叶子节点存储的是记录的`c2列+页号`。 79 | * 叶子节点存储的并不是完整的用户记录,而只是`c2列+主键`这两个列的值。 80 | 81 | ## 平衡二叉树、红黑树、B树和B+树的区别是什么?都有哪些应用场景? 82 | 83 | ## 一个B+树中大概能存放多少条索引记录? 84 | 85 | ## 使用B+树存储的索引CRUD执行效率如何? 86 | 87 | ## 什么是自适应哈希索引 88 | 89 | 自适应哈希索引是InnoDB引擎的一个特殊功能,把它注意到某些索引值被使用的非常频繁时,会在内存中基于B-Tree所有之上再创建一个哈希索引,这就让B-Tree索引也具有哈希索引的一些优点,比如快速哈希查找。这是一个完全自动的内部行为,用户无法控制或配置。 90 | 91 | ## 为什么官方建议使用自增长主键作为索引?(说一下自增主键和字符串类型主键的区别和影响) 92 | 93 | * 自增主键能够维持底层数据顺序写入 94 | * 读取可以由B+树的二分查找定位 95 | * 支持范围查找,范围数据自带顺序 96 | 97 | ## 使用int自增主键后,最大id是10,删除id为10和9的字段后,再插入记录,最后添加的id是多少?删除后重启MySQL然后添加记录最后id是多少? 98 | 99 | 删除之后 100 | 101 | * 如果重启,会从最大的id开始递增 102 | * 如果未重启,会延续删除之前最大的id开始递增 103 | 104 | # 三、索引的优缺点 105 | 106 | **优点** 107 | 108 | 聚簇(主键)索引: 109 | 110 | * 顺序读写 111 | * 范围快速查找 112 | * 范围查找自带顺序 113 | 114 | 非聚簇索引: 115 | 116 | * 条件查询避免全表扫描 117 | * 范围,排序,分组查询返回行ID,排序分组后,再回表查询完整数据,有可能利用顺序读写 118 | * 覆盖索引不需要回表操作 119 | 120 | ## 如果是大段文本内容,如何创建(优化)索引 121 | 122 | 第一种方式是分表存储,然后创建索引 123 | 124 | 第二种使用ElasticSearch为大文本创建索引 125 | 126 | # 四、什么是聚簇索引 127 | 128 | ## 一个表中可以有多个(非)聚簇索引吗? 129 | 130 | 聚簇索引只能有一个,非聚簇索引可以有多个 131 | 132 | ## CRUD时聚簇索引与非聚簇索引的区别是什么? 133 | 134 | * 聚簇索引插入新值比采用非聚簇索引的速度要慢很多,因为插入要保证主键不能重复 135 | * 聚簇索引范围,排序查找效率高,因为是有序的 136 | * 非聚簇索引访问需要两次索引查找,第一次找主键值,第二次根据主键值找到行数据。(覆盖索引除外) 137 | 138 | ## 非聚簇索引为什么不存数据地址值而存储主键? 139 | 140 | 因为聚簇索引中有时会引发分页操作、重排操作,数据有可能会移动 141 | 142 | # 五、什么是回表 143 | 144 | ## 非聚簇索引一定回表查询吗? 145 | 146 | 不一定,只要B+树中包含的字段(创建索引的字段),覆盖(包含)想要select的字段,那么久不会回表查询了。 147 | 148 | ## 为什么要回表查询?直接存储数据不可以吗? 149 | 150 | 为了控制非聚簇索引的大小 151 | 152 | ## 如果把一个 InnoDB 表的主键删掉,是不是就没有主键,就没办法进行回表查询了? 153 | 154 | 不是,InnoDB会生成 rowid 辅助回表查询 155 | 156 | # 六、什么是联合索引,组合索引,复合索引 157 | 158 | `为c2和c3列建立联合索引`,如下所示: 159 | 160 | ![image-20221211155428530](MySQL问题整理.assets/image-20221211155428530.png) 161 | 162 | # 七、什么是唯一索引 163 | 164 | ## 什么时候使用唯一索引? 165 | 166 | 业务需求唯一字段的时候,一般不考虑性能问题 167 | 168 | # 八、什么时候适合创建索引,什么时候不适合创建索引? 169 | 170 | 适合创建索引 171 | 172 | * 频繁作为where条件语句查询字段 173 | * 关联字段需要建立索引 174 | * 排序字段可以建立索引 175 | * 分组字段可以建立索引 176 | * 统计字段可以建立索引(如 count(), max()) 177 | 178 | 不适合创建索引 179 | 180 | * 频繁更新的字段不适合创建索引 181 | * where, 分组,排序中用不到的字段不必要创建索引 182 | * 可以确定表数据非常少不需要建立索引 183 | * 参与mysql函数计算的列不适合建立索引 184 | 185 | # 九、什么是索引下推 186 | 187 | 未开启索引下推: 188 | 189 | * 根据筛选条件在索引树中筛选第一个条件 190 | * 获得结果集后回表操作 191 | * 进行其他条件筛选 192 | * 再次回表查询 193 | 194 | 开启索引下推:在条件查询时,当前索引树如果全部满足全部筛选条件,可以在当前树中完成全部筛选过滤, 得到比较小的结果集再进行回表操作。 195 | 196 | # 十、有哪些情况下会导致索引失效? 197 | 198 | * 计算、函数导致索引失效 199 | 200 | ```mysql 201 | EXPLAIN SELECT * FROM emp WHERE emp.name LIKE 'abc%'; 202 | EXPLAIN SELECT * FROM emp WHERE LEFT(emp.name, 3) = 'abc'; # 索引失效 203 | ``` 204 | 205 | * LIKE以%, _ 开头索引失效 206 | 207 | ```mysql 208 | EXPLAIN SELECT * FROM emp WHERE emp.name LIKE '%ab%'; # 索引失效 209 | ``` 210 | 211 | * 不等于(!= 或者 <>)索引失效 212 | 213 | ```mysql 214 | EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.name <> 'abc'; # 索引失效 215 | ``` 216 | 217 | * IS NOT NULL 失效 和 IS NULL(有可能) 218 | 219 | **注意:**当数据库中的数据的索引列的`NULL值达到比较高的比例的时候`,即使在IS NOT NULL的情况下MYSQL的查询优化器会选择使用索引,此时`type的值是range(范围查询)` 220 | 221 | * 类型转换导致索引失效 222 | 223 | * 复合索引失效 224 | 225 | * 查询优化器决定是否使得索引失效 226 | 227 | ## 为什么LIKE以%开头索引会失效? 228 | 229 | user(id, name, age) 230 | 231 | 使用name创建索引 232 | 233 | ```mysql 234 | SELECT * FROM user WHERE name LIKE '%明'; # type = all 235 | SELECT name FROM user WHERE name LIKE '%明'; # type = index 236 | ``` 237 | 238 | 其实并不会完全失效,覆盖索引下会出现 type = index,表示遍历了索引树,再回表查询 239 | 240 | 覆盖索引没有生效的时会直接type=all 241 | 242 | # 十一、一个表有多个索引的时候,能否手动选择使用哪个索引? 243 | 244 | 不可用手动直接干预,只能通过mysql优化器自动选择 245 | 246 | ## 如何查看一个表的索引? 247 | 248 | ```mysql 249 | show index from t_emp; # 显示表上的索引 250 | explain select * from t_emp where deptid=1; # 显示可能会用到的索引及最终使用的索引 251 | ``` 252 | 253 | ## 能否查看到索引选择的逻辑?是否使用过optimize_trace? 254 | 255 | ```mysql 256 | set session optimizer_trace = "enabled=on", end_markers_in_json=on; 257 | select * from information_schema.OPTIMIZER_TRACE; 258 | set session optimizer_trace="enabled=off"; 259 | ``` 260 | 261 | ## 多个索引优先级是如何匹配的? 262 | 263 | 1. 主键(唯一索引)匹配 264 | 2. 全值匹配(单值匹配) 265 | 3. 最左前缀匹配 266 | 4. 范围匹配 267 | 5. 索引扫描 268 | 6. 全表扫描 269 | 270 | 一般性建议 271 | 272 | * 对于单键索引,尽量选择过滤性更好的索引(例如:手机号,邮件,身份证) 273 | * 在选择组合索引的时候,过滤性最好的字段在索引字段顺序中,位置越靠前越好 274 | * 选择组合索引时,尽量包含where中更多字段的索引 275 | * 组合索引出现范围查询时,尽量把这个字段放在索引次序的最后面 276 | * 尽量避免造成索引失效的情况 277 | 278 | # 十二、使用 Order By 时能否通过索引排序? 279 | 280 | 没有过滤条件不走索引 281 | 282 | ## 通过索引排序内部流程是什么? 283 | 284 | 关键配置: 285 | 286 | * sort_buffer 可供排序的内存缓冲区大小 287 | * max_length_for_sort_data 单行所有字段总和限制,超过这个大小启动双路排序 288 | 289 | 1. 通过索引过滤筛选条件所需要排序的字段+其他字段(如果是符合索引) 290 | 291 | 2. 判断所有内容是否覆盖select的字段 292 | 293 | 1)如果覆盖索引,select的字段和排序都在索引上,那么再内存中进行排序,排序后输出结果 294 | 295 | 2)如果索引没有覆盖查询字段,接下来计算select的字段是否超过max_length_for_sort_data限制,如果超过,启动双路排序,否则使用单路 296 | 297 | ## 什么是双路排序和单路排序 298 | 299 | 单路排序:一次取出所有字段进行排序,内存不够用的时候会使用磁盘 300 | 301 | 双路排序:取出排序字段进行排序,排序完成后再次回表查询所需要的其他字段 302 | 303 | ## Group By 分组和 Order By 在索引使用上有什么区别? 304 | 305 | group by 使用索引的原则几乎跟order by一致,唯一区别: 306 | 307 | * group by 先排序再分组,按照索引建的最佳左前缀法则 308 | * group by 没有过滤条件,也可以用上索引。order by 必须有过滤条件才能使用上索引 309 | 310 | ## 如果表中有字段为null,又被经常查询该不该给这个字段创建索引? 311 | 312 | 应该创建索引,使用的时候尽量使用 is null 判断。 313 | 314 | # 十三、MySQL内部技术架构 315 | 316 | ## MySQL内部支持缓存查询吗? 317 | 318 | 当MySQL接收到客户端的查询SQL之后,仅仅只需要对齐进行相应的权限验证之后,就会通过Query Cache来查找结果,甚至都不需要经过Optimizer模块进行执行计划的分析优化,更不需要发生任何存储引擎的交互 319 | 320 | MySQL5.7支持内部缓存,8.0之后就废弃掉了 321 | 322 | ### MySQL8.0之后为什么废除查询缓存? 323 | 324 | 缓存的意义在于快速查询提升系统西能,可以灵活控制缓存的一致性 325 | 326 | mysql缓存的限制 327 | 328 | 1. mysql基本没有手段灵活的管理缓存失效和生效,尤其对于频繁更新的表 329 | 2. SQL必须完全一致才会Cache命中 330 | 3. 为了节省内存空间,太大的result set不会被cache 331 | 4. mysql缓存在分库分表环境下不起作用 332 | 5. 执行SQL里有出触发器,自定义函数时,mysql缓存也是不起作用的 333 | 6. 在表的结构和数据发生改变时,基于该表相关的cache立即全部失效 334 | 335 | ### 替代方案是什么 336 | 337 | 应用层组织缓存,最简单是使用redis, ehcached等。 338 | 339 | ## MySQL内部有哪些核心模块组成,作用是什么? 340 | 341 | image-20221211210331900 342 | 343 | ## 一条sql发送给mysql后,内部是如何执行的?(说一下MySQL执行一条查询语句的内部执行过程?) 344 | 345 | ![image-20221211211323227](MySQL问题整理.assets/image-20221211211323227.png) 346 | 347 | **首先**,`MySQL客户端通过协议与MySQL服务器建立连接,通过SQL接口发送SQL语句,[先查询缓存,如果命中,直接返回结果,否则进行语句解析(对于MySQL5.7, 8.0废除了内部缓存)]`。 348 | 349 | **接下来**,`MySQL解析器通过关键字将SQL语句进行解析,并生成一颗对应的解析树`,解析器使用MySQL语法规则验证和解析SQL语句。例如,它将验证是否使用了错误的关键字,或者使用关键字的顺序是否正确,引号能否前后匹配等;`预处理器则根据MySQL规则进一步检查解析树是否合法`,例如,这里将检查数据表和数据列是否存在,还会解析名字和别名,看是否有歧义等。`然后预处理器会进行查询重写,生成一颗新的解析树。` 350 | 351 | **接下来**,`查询优化器将解析树转化成执行计划`。MySQL优化程序会对我们的语句做一些优化,如子查询转为连接、表达式简化等等。优化的结果就是生成一个执行计划,这个执行计划表明了应该使用哪些索引执行查询,以及表之间的连接顺序是啥样,等等。我们可以使用EXPLAIN语句来查看某个语句的执行计划。 352 | 353 | **最后**,`进入执行器阶段`。完成查询优化后,`查询执行引擎`会按照生成的执行计划调用存储一起提供的接口执行SQL查询并将结果返回给客户端。在MySQL8一下的版本,如果设置了查询缓存,这时会讲查询结果进行缓存,再返回给客户端。 354 | 355 | image-20221211213852774 356 | 357 | ### MySQL提示“不存在此列"是执行到哪个节点报出的? 358 | 359 | Paster:解析器 分析sql语法的时候检查的列。 360 | 361 | ### 如果一张表创建了多个索引,在哪个阶段或模块进行的索引选择? 362 | 363 | 在优化器阶段**Optimizer: 查询优化器** 364 | 365 | ## MySQL8.0存储引擎 366 | 367 | 1. InnoDB存储引擎 368 | 369 | * InnoDB是MySQL的默认事务型引擎。它被设计用来处理大量的短期事务。可以确保事务的完整提交和回滚。 370 | * 除非有非常特别的原因需要使用其他的存储引擎,否则应该优先考虑InnoDB引擎。 371 | * 数据文件结构 372 | * 表名.frm存储表结构(MySQL8.0时,合并在表名.ibd中) 373 | * 表名.ibd存储数据和索引 374 | * InnoDB不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性影响。 375 | 376 | 2. MyISM存储引擎 377 | 378 | * MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但`MyISAM不支持事务和行级锁`,有一个毫无疑问的缺陷就是崩溃后无法安全恢复 379 | * 优势是访问的速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用 380 | * 数据文件结构 381 | * 表名.frm存储表结构 382 | * 表名.MYD存储数据 383 | * 表名.MYI存储索引 384 | * MyISAM只缓存索引,不缓存真实数据 385 | 386 | ## MySQL存储引擎架构了解吗? 387 | 388 | 以下是官网的InnoDB引擎结构图,主要分为内存结构和磁盘结构两大部分。 389 | 390 | image-20221211221636235 391 | 392 | ### 能否单独为一张表设置存储引擎? 393 | 394 | 可以 395 | 396 | `方法一:` 397 | 398 | 设置默认存储引擎: 399 | 400 | ```mysql 401 | SET DEFAULT_STORAGE_ENGINE=MyISAM; 402 | ``` 403 | 404 | `方法二:` 405 | 406 | 或者修改 my.cnf 文件:vim /etc/my.cnf 407 | 408 | 新增一行:default-storage-engine=MyISAM 409 | 410 | 重启MySQL:systemctl restart mysqld 411 | 412 | `方法三:` 413 | 414 | 我们可以为不同的表设置不同的存储引擎 415 | 416 | ```mysql 417 | CREATE TABLE 表明(创建语句;) ENGINE = 存储引擎名称; 418 | ALTER TABLE 表名 ENGINE = 存储引擎名称; 419 | ``` 420 | 421 | ## MyISAM 和 InnoDB 的区别是什么? 422 | 423 | ![image-20221211224409445](MySQL问题整理.assets/image-20221211224409445.png) 424 | 425 | # 十四、MySQL事务 426 | 427 | ## 什么是ACID? 428 | 429 | **1、原子性A** atomicity 430 | 431 | `只做一个步骤` 432 | 433 | 事务是数据库的逻辑工作单元,事务中包含的各操作`要么都做,要么都不做` 434 | 435 | **2、一致性C** consistency 436 | 437 | `保证要吃完`刚张嘴就挂了,失去一致性 438 | 439 | 事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。 440 | 441 | **3、隔离性I** isolation 442 | 443 | `不被干扰` 444 | 445 | 一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能相互干扰。 446 | 447 | **4、持久性 永久性D** durability 448 | 449 | 指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其执行结果有任何影响。 450 | 451 | ## 并发事务会有哪些问题? 452 | 453 | ### 什么是脏读(Dirty read)? 454 | 455 | 一个事务在处理过程中读取另外一个事务未提交的数据 456 | 457 | 当一个事务正在访问数据并且对其进行了修改,但是还没提交事务,这时另外一个事务也访问了这个数据,然后使用了这个数据,因为这个数据的修改还没提交到数据库,所以另外一个事务读取的数据就是“脏数据”,这种行为就是”脏读“,依据“脏数据”所做的操作可能是会出现问题的。 458 | 459 | ### 修改丢失(Lost of modify) 460 | 461 | 指一个事务读取一个数据时,另外一个数据也访问了该数据,那么在第一次事务修改了这个数据之后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就会丢失,这种情况下就被称为修改丢失 462 | 463 | ### 不可重复读(Unrepeatable Read) 464 | 465 | 指在一个事务内多次读取同一数据,在这个事务还没结束时,另外一个事务也访问了这个数据并对这个数据进行了修改,那么就可能造成第一个事务两次读取的数据不一致,这种情况被称为不可重复读。 466 | 467 | ### 幻读(Phantom Read) 468 | 469 | 指同一个事务内多次查询返回的结果集总数不一样(比如增加了或者减少了行记录) 470 | 471 | 幻读与不可重复读类似,幻读是指一个事务读取了几行数据,这个事务还没结束,接着另外一个事务插入了一些数据,在随后的查询中,第一个事务读取到的数据就会比原本读取到的多,就好像发生了幻觉一样,所以称为幻读 472 | 473 | ## MySQL是如何避免事务并发问题的? 474 | 475 | ### 什么是事物隔离级别? 476 | 477 | image-20221212140751245 478 | 479 | ### 默认的级别是什么? 480 | 481 | `MySQL InnoDB` 存储引擎默认的事务隔离级别是 可重复读(REPEATABLE-READ) 482 | 483 | ### 如何选择事务隔离级别? 484 | 485 | 隔离级别越低,事务请求的锁越少相应性能也就越高,如没有特殊要求或有错误发生,使用默认的隔离级别即可,如果系统又高频读写并且对一致性要求高那么久需要比较高的事务隔离级别甚至串行化。 486 | 487 | ### 靠缓存可以提高事务隔离级别的性能吗 488 | 489 | 提升事务级别的目的本质是提供更高的数据一致性,如果前置有缓存,那么缓存只能提供高效读并不能保证数据及时一致性,相反的我们还需要对缓存管理有额外的开销。 490 | 491 | ## MySQL事务隔离是如何实现的? 492 | 493 | 隔离的实现主要是读写锁和MVCC 494 | 495 | ## 什么是一致性非锁定读和锁定读? 496 | 497 | **锁定读** 498 | 499 | 使用到了读写锁 500 | 501 | 读写锁是最简单直接的事务隔离实现方式 502 | 503 | * 每次读操作需要获取一个共享(读)锁,每次写操作需要获取一个写锁。 504 | * 共享锁之间不会产生互斥,共享锁和写锁之间、以及写锁与写锁之间会产生互斥。 505 | * 当产生锁竞争时,需要等待其中一个操作释放锁后,另一个操作才能获取到锁。 506 | 507 | 锁机制,解决的就是多个事务同时更新数据,此时必须要有一个加锁的机制 508 | 509 | * 行锁(记录锁):解决的就是**多个事务同时更新一行数据** 510 | * 间隙锁:解决的就是**多个事务同时更新多行数据** 511 | 512 | ```mysql 513 | select ... lock in share mode 514 | select ... for update 515 | insert、update、delete 516 | ``` 517 | 518 | **非锁定读** 519 | 520 | 使用mvcc多版本控制实现 521 | 522 | ## 说一下MVCC内部细节 523 | 524 | 多版本并发控制,MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据的并发访问 525 | 526 | InnoDB是一个多版本的存储引擎。它保存有关已更改行的旧版本的信息,以支持并发和回滚等事务特性。这些信息存储在一个称为回滚段的数据结构中的系统表空间或undo表空间中。InnoDB使用回滚段中的信息来执行事务回滚所需的撤销操作。它还是用这些信息构建行的早期版本,以实现一致的读取 527 | 528 | MVCC的实现依赖于:隐藏字段、Read View、undo log 529 | 530 | **隐藏字段** 531 | 532 | ![image-20221212151348807](MySQL问题整理.assets/image-20221212151348807.png) 533 | 534 | **Read View** 535 | 536 | 不同的事务隔离界别中,当有事务在执行过程中修改了数据(更新版本号),在并发事务时需要判断一下版本链中的哪个版本是当前事务可见的。为此InnoDB有了ReadView的概念,使用ReadView来记录和隔离不同事务并发时此记录的哪些版本是对当前访问事务可见的。 537 | 538 | **Undo Log** 539 | 540 | 除了用来回滚数据,还可以读取可见版本的数据。以此实现非锁定读 541 | 542 | ## MySQL事务一致性,原子性是如何实现的? 543 | 544 | 首先是通过锁和MVCC实现了执行过程的一致性和原子性 545 | 546 | 其次是在备灾方面通过Redo Log实现,Redo Log会把事务在执行过程中对数据库所做的所有修改都记录下来,在之后系统崩溃重启后可以把事务所做的任何修改都恢复出来。 547 | 548 | ## MySQL事务的持久性是如何实现的? 549 | 550 | 使用Redo Log保证了事务的持久性。当事物提交时,必须先将事务的所有日志写入日志文件进行持久化,就是我们常说的WAL(write ahead log)机制,如果出现断电重启便可以从 Redo Log 中恢复,如果 Redo Log 写入失败那么也就意味着修改失败整个事务也就直接回滚了。 551 | 552 | ## 表级锁和行级锁有什么区别? 553 | 554 | 表级锁:串行化(serializable)时,整表加锁,事务访问表数据时需要申请锁,虽然可分为读锁和写锁,但毕竟是锁住整张表,会导致并发能力下降,一般是做ddl处理时使用 555 | 556 | 行级锁:除了串行化(serializable)时 InnoDB 使用的都是行级锁,只锁一行数据,其他行数据不影响,并发能力强。 557 | 558 | ## 什么是行级锁? 559 | 560 | 行级锁实现比较复杂不是单纯锁住一张数据,是由MVCC完成的。 561 | 562 | ## 什么是共享锁(读锁)? 563 | 564 | 共享锁或S锁,其他事务可以继续加共享锁,但不能加排他锁 565 | 566 | ## 什么是排他锁(写锁/独占锁)? 567 | 568 | 排他锁或X锁,在进行写操作之前要申请并获得,其他事务不能再获得任何锁。 569 | 570 | ## 什么是意向锁? 571 | 572 | 它分为意向共享锁(IS)和意向排他锁(IX) 573 | 574 | 一个事务对一张表的某行添加共享锁前,必须获得对该表一个IS锁或者优先级更高的锁。 575 | 576 | 一个事务对一张表的某行添加排他锁之前,它必须对该表获得一个IX锁。 577 | 578 | 意向锁属于表锁,它不与innodb中的行锁冲突,任意两个意向锁之间也不会产生冲突,但是会与表锁(S锁和X锁)产生冲突。 579 | 580 | ## InnoDB支持哪几种锁? 581 | 582 | 表锁,行锁,间隙锁,Next-Key锁等 583 | 584 | 在Serializable中读加共享锁,写加排他锁,读写互斥 585 | 586 | 两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫二段锁) 587 | 588 | ## 当前读和快照读分别是什么? 589 | 590 | 当前读:在锁定读(使用锁隔离事务)的时候读到的最新版本的数据 591 | 592 | 快照读:可重复读(repeatable_read)下mvcc生效读取的是数据的快照,并不是最新版本的数据(未提交事务的数据) 593 | 594 | # 十五、什么是XA协议? 595 | 596 | image-20221212163152470 597 | 598 | ![image-20221212163442391](MySQL问题整理.assets/image-20221212163442391.png) 599 | 600 | ![image-20221212163547284](MySQL问题整理.assets/image-20221212163547284.png) 601 | 602 | ## 什么是MySQL XA事务 603 | 604 | MySQL的XA事务分为两部分: 605 | 606 | 1. InnoDB内部本地普通事务操作协调数据写入与log写入两阶段提交 607 | 2. 外部分布式事务 608 | 609 | ![image-20221212163826562](MySQL问题整理.assets/image-20221212163826562.png) 610 | 611 | ## XA事务与普通事务区别是什么? 612 | 613 | XA事务可以跨库或跨服务器,属于分布式事务,同时XA事务还支撑了InnoDB内部日志两阶段记录 614 | 615 | 普通事务只能再单库中执行 616 | 617 | # 十六、是否使用过select for update?会产生哪些操作? 618 | 619 | select本身是一个查询语句,查询语句是不会产生冲突的一种行为,一般情况下是没有锁的,用select for update 会让select语句产生一个排他锁(X),这个锁和update的效果一样,会使两个事物无法同时更新一条记录。 620 | 621 | http://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html 622 | 623 | http://dev.mysql.com/doc/refman/8.0/en/select.html 624 | 625 | ![image-20221212164913847](MySQL问题整理.assets/image-20221212164913847.png) 626 | 627 | # 十七、说一下MySQL死锁的原因和处理方法 628 | 629 | * 死锁和锁等待是两个概念 630 | * 如未开启事务,多个客户端执行的insert操作 631 | 632 | * 当多个事务同时持有和同一资源上的锁而产生循环依赖的时候就会产生死锁。 633 | 634 | image-20221212165055918 635 | 636 | ![image-20221212165146640](MySQL问题整理.assets/image-20221212165146640.png) 637 | 638 | # 十八、MySQL日志 639 | 640 | ## MySQL会产生几种日志? 641 | 642 | ![image-20221212170058437](MySQL问题整理.assets/image-20221212170058437.png) 643 | 644 | image-20221212170149995 645 | 646 | ## bin log作用是什么? 647 | 648 | MySQL的bin log日志是用来记录MySQL中增删改查时的记录日志。 649 | 650 | 当你的一条sql操作对数据库中的内容进行了更新,就会增加一条bin log日志。查询操作不会记录到bin log中。 651 | 652 | bin log最大的用处就是进行**主从复制,以及数据库的恢复。** 653 | 654 | ## redo log作用是什么? 655 | 656 | redo log是一种基于磁盘的数据结构,用来在MySQL宕机情况下将不完整的事务执行数据纠正,redo日志记录事务执行后的状态。 657 | 658 | 当事务开始后,redo log就开始产生,并且随着事务的执行不断写入redo log file中。redo log file中记录了xx页做了xx修改的信息,我们都知道数据库的更新操作会在内存中先执行,最后刷入磁盘。 659 | 660 | redo log就是为了回复更新了内存但是由于宕机等原因没有刷入磁盘中的那部分数据。 661 | 662 | ## undo log作用是什么? 663 | 664 | undo log主要用来回滚到某一个版本,是一种逻辑日志。 665 | 666 | undo log记录的是修改之前的数据,比如:当delete一条记录时,undolog中会记录一条对应的insert记录,从而保证能恢复到数据修改之前。在执行事务回滚的时候,就可以通过undo log中的记录内容并以此进行回滚。 667 | 668 | undo log还可以提供多版本并发控制下的读取(MVCC)。 669 | 670 | ## MySQL日志是否实时写入磁盘?bin log刷盘机制是如何实现的?redo log刷盘机制是如何实现的?undo log刷盘机制是如何实现的? 671 | 672 | 磁盘写入固然是比较慢的 673 | 674 | ![image-20221212214134578](MySQL问题整理.assets/image-20221212214134578.png) 675 | 676 | image-20221212214154824 677 | 678 | ![image-20221212214630847](MySQL问题整理.assets/image-20221212214630847.png) 679 | 680 | image-20221212214817884 681 | 682 | ![image-20221212215049288](MySQL问题整理.assets/image-20221212215049288.png) 683 | 684 | ## MySQL的binlog有几种录入格式?分别有什么区别? 685 | 686 | image-20221212215237272 687 | 688 | ## MySQL集群同步时为什么使用binlog?优缺点是什么? 689 | 690 | * binlog是mysql提供的日志,所有存储引擎都可用。 691 | * 支持增量同步 692 | * binlog还可以供其他中间件读取,比如同步到hdfs中 693 | * 如果复制表数据: 694 | * 不支持某个阶段回放 695 | * 直接复制数据过程中一旦中断复制(比如断网),很难确定复制的offset 696 | 697 | # 十九、MySQL开发 698 | 699 | ## 可以使用MySQL直接存储文件吗? 700 | 701 | 可以使用BLOB(binary large object),用来存储二进制大对象的字段类型 702 | 703 | TinyBlob 255 值的长度加上用于记录长度的1个字节(8位) 704 | 705 | Blob 65K 值的长度加上用于记录长度的2个字节(16位) 706 | 707 | MediumBlob 16M 值的长度加上用于记录长度的3个字节(24位) 708 | 709 | LongBlob 4G 值的长度加上用于记录长度的4个字节(32位) 710 | 711 | ## 什么时候存,什么时候不存 712 | 713 | 存:需要高效查询并且文件很小的时候 714 | 715 | 不存:文件比较大,数据量多或变更频繁的问题 716 | 717 | ## 存储的时候有遇到过什么问题吗? 718 | 719 | 1. 上传数据过大sql执行失败,调整max_allowed_packet 720 | 2. 主从同步数据时比较慢 721 | 3. 占用线程阻塞 722 | 4. 占用网络带宽 723 | 5. 高频访问的图片无法使用浏览器缓存 724 | 725 | ## Emoji乱码怎么办 726 | 727 | 使用utf8mb4 728 | 729 | MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在uft8mb4是utf8的超集,除了将编码改为utf8bm4外不需要做其他转换。当然,一般情况下使用uft8就够了。 730 | 731 | ## 如何存储ip地址 732 | 733 | 1. 使用字符串 734 | 2. 使用无符号整型 735 | 736 | * 四字节即解决问题 737 | * 可以支持范围查询 738 | * INET_ATON() 和 INET_NTOA() ipv6 使用 INET6_ATON() 和 INET6_NTOA() 739 | 740 | ## 长文本如何存储? 741 | 742 | 可以使用Text存储 743 | 744 | **TINYTEXT(255长度)** 745 | 746 | **TEXT(65535)** 747 | 748 | **MEDIUMTEXT(int 最大值 16M)** 749 | 750 | **LONGTEXT(long最大值4G)** 751 | 752 | ## 大段文本如何设计表结构 753 | 754 | 1. 将大段文本同时存储到搜索引擎 755 | 2. 分段存储 756 | 3. 分段后多段存储 757 | 758 | ## 大段文本查找时如何建立索引? 759 | 760 | 1. 全文索引,模糊匹配最好存储到索引引擎中 761 | 2. 指定索引长度 762 | 3. 分段存储后创建索引 763 | 764 | ## 有没有在开发中使用过TEXT,BLOB数据类型 765 | 766 | BLOB之前做ERP的时候使用过,互联网项目一般不用BLOB 767 | 768 | TEXT 文献,文章,小说类,新闻,会议内容等 769 | 770 | ## 日期,时间如何存取? 771 | 772 | 使用TIMESTAMP,DATETIME 773 | 774 | ## TIMESTAMP,DATETIME的区别是什么? 775 | 776 | ![image-20221212225723841](MySQL问题整理.assets/image-20221212225723841.png) 777 | 778 | ![image-20221212225829061](MySQL问题整理.assets/image-20221212225829061.png) 779 | 780 | ## 为什么不适用字符串存储日期? 781 | 782 | 字符串无法完成数据库内部的范围筛选 783 | 784 | 在大数据量存储优化索引时,查询必须加上时间范围 785 | 786 | ## 如果需要使用时间戳timestamp和int该如何选择? 787 | 788 | int 存储空间小,存储查询效率高,不受时区影响,精度低 789 | 790 | timestamp 存储空间小,可以使用数据库内部时间函数比如更新,精度高,需要注意时区转换,timestamp更易读 791 | 792 | 一般选择timestamp,这两者性能差异不明显,本质上存储都是使用的Int 793 | 794 | ## char与varchar的区别?如何选择? 795 | 796 | 1. char的优点是存储空间固定(最大255),没有碎片,尤其更新比较频繁的时候,方便数据文件指针的操作,所以存储读取速度快。缺点是空间夯余,对于数据量大的表,非固定长度属性使用char字段,空间浪费。 797 | 2. varchar字段,存储的控件根据存储的内容变化,空间长度为L+size,存储内容长度加描述存储内容长度信息,有点就是空间借阅,缺点就是读取和存储的时候,需要读取信息计算下标,才能获取完整的内容。 798 | 799 | ## 财务计算有没有出现过错乱? 800 | 801 | image-20221212230946328 802 | 803 | ## decimal与float,double的区别是什么 804 | 805 | image-20221212231421681 806 | 807 | ## 浮点类型何如选型?为什么? 808 | 809 | * 需要不丢失精度的计算使用DECIMAL 810 | * 仅用于展示没有计算的小数存储可以使用字符串存储 811 | * 低价值数据允许计算后丢失精度可以使用float double 812 | * 整型记录不会出现小数的不要使用浮点类型 813 | 814 | # 二十、预编译sql是什么? 815 | 816 | * 预编译sql会被mysql缓存下来 817 | * 作用域是每个session,对其他session无效,重新连接也会失效 818 | * 提高安全性防止sql注入 819 | * 编译语句有可能被重复调用,也就是说sql相同参数不同在同一session中重复查询执行效率明显比较高 820 | * mysql 8 支持服务器端的预编译 821 | 822 | # 二十一、子查询和join哪个效率高? 823 | 824 | 子查询虽然很灵活,但是执行效率并不高 825 | 826 | ## 为什么子查询效率低? 827 | 828 | 在执行子查询的时候,MySQL创建了临时表,查询完毕后再删除这些临时表 829 | 830 | 子查询的速度慢的原因是多了一个创建和销毁临时表的过程 831 | 832 | 而join则不需要创建临时表 所以会比子查询快一点 833 | 834 | ## join查询可以无限叠加吗?MySQL对json查询有什么限制吗? 835 | 836 | 建议join不超过3张表关联,mysql对内存敏感,关联过多会占用更多内存空间,使性能下降 837 | 838 | 系统限制最多关联61个表 839 | 840 | ## join查询算法了解吗? 841 | 842 | * Simple Nested-Loop Join: SNLJ,简单嵌套循环连接 843 | * Index Nested-Loop Join: INLJ,索引嵌套循环连接 844 | * Block Nested-Loop Join: BNLJ,缓存块嵌套循环连接 845 | 846 | ## 如何优化多过的join查询关联 847 | 848 | * 适当使用冗余字段减少多表关联查询 849 | 850 | * 驱动表和被驱动表(小表join大表) 851 | * 业务允许的话,尽量使用inner join让系统帮忙自动选择驱动表 852 | * 关联字段一定创建索引 853 | * 调整JOIN BUFFER大小 854 | 855 | # 二十二、是否有过MySQL调优经验? 856 | 857 | 调优: 858 | 859 | 1. sql 调优 860 | 2. 表(结构)设计调优 861 | 3. 索引调优 862 | 4. 慢查询调优 863 | 5. 操作系统调优 864 | 6. 数据库参数调优 865 | 866 | ## 开发中使用过哪些调优工具? 867 | 868 | 官方自带: 869 | 870 | * EXPLAIN 871 | * mysqldumpslow 872 | * show profiles 873 | * optimizer_trace 874 | 875 | 第三方:性能诊断工具,参数扫描提供建议,参数辅助优化 876 | 877 | ## 如何监控线上环境中执行比较慢的sql?如何分析一条慢sql? 878 | 879 | 开启慢查询日志,收集sql 880 | 881 | 默认情况下,MySQL数据库没有开启慢查询日志,需要我们手动来设置这个参数。 882 | 883 | 当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能印象。慢查询日志支持将日志记录写入文件。 884 | 885 | **查看及开启** 886 | 887 | 1. 默认关闭 888 | 889 | `SHOW VARIABLES LIKE '%slow_query_log%';` 890 | 891 | 默认情况下slow_query_log的值为OFF, 表示慢查询日志是禁用的 892 | 893 | 2. 开启:`set global slow_query_log = 1;` 894 | 895 | 只对窗口生效,重启服务失效 896 | 897 | 慢查询日志记录long_query_time时间 898 | 899 | ```mysql 900 | SHOW VARIABLES LIKE '%long_query_log%'; 901 | SHOW GLOBAL VARIABLES LIKE 'long_query_time'; 902 | ``` 903 | 904 | 全局变量设置,对所有客户端有效。但必须是设置后进行登录的客户端 905 | 906 | `SET GLOBAL long_query_time = 0.1;` 907 | 908 | 对当前会话连接立即生效,对其他客户端无效 909 | 910 | `SET SESSION long_query_time = 0.1;` 911 | 912 | 假如运行时间正好等于long_query_time的情况,并不会被记录下来。也就是说,在mysql源码里的判断大于long_query_time,而非大于等于。 913 | 914 | 1. 永久生效 915 | 916 | * 修改配置文件my.cnf(其他系统变量也是如此) 917 | * [mysqld] 下增加或修改参数 918 | * slow_query_log和slow_query_log_file后,然后重启MySQL服务器。也即将如下两行配置进my.cnf文件 919 | 920 | slow_query_log = 1 921 | 922 | slow_query_log_file=/var/lib/mysql/localhost-slow.log 923 | 924 | long_query_time=3 925 | 926 | log_output=FILE 927 | 928 | * 关于慢查询的参数slow_query_log_file,它指定慢查询日志文件的存放路径,如果不设置,系统默认文件:[host-name]-slow.log 929 | 930 | **case** 931 | 932 | 记录慢SQL并后续分析 933 | 934 | SELECT * FROM emp; 935 | 936 | SELECT * FROM emp WHERE deptid > 1; 937 | 938 | 查询当前系统中有多少条慢查询记录或者直接慢查询日志 939 | 940 | /var/lib/mysql/localhost-slow.log 941 | 942 | SHOW GLOBAL STATUS LIKE '%Slow_queries%'; 943 | 944 | **日志分析工具mysqldumpslow** 945 | 946 | 1. 在生产环境中,如果要手工分析日志,查找,分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow 947 | 2. 查看mysqldumpslow的帮助信息 948 | 949 | ![image-20221213133745476](MySQL问题整理.assets/image-20221213133745476.png) 950 | 951 | # 二十三、如何查看当前sql使用了哪个索引? 952 | 953 | 可以使用EXPLAIN,选择索引过程可以使用optimizer_trace 954 | 955 | # 二十四、MySQL数据库cpu飙升的话你会如何分析? 956 | 957 | **1. 使用top观察mysqld的cpu利用率** 958 | 959 | 1. 切换到常用的数据库 960 | 2. 使用show full processlist; 查看会话 961 | 3. 观察是哪些sql消耗了资源,其中重点观察state指标 962 | 4. 定位到具体sql 963 | 964 | **2.pidstat** 965 | 966 | 1. 定位到线程 967 | 2. 在PERFORMANCE_SCHEMA. THREADS中记录了thread_os_id找到线程执行的sql 968 | 3. 根据操作系统id可以到processlist表找到对应的会话 969 | 4. 在会话中即可定位到问题sql 970 | 971 | **3. 使用show profile观察sql各个阶段耗时** 972 | 973 | **4. 服务器上是否运行了其他程序** 974 | 975 | **5. 检查是否有慢查询** 976 | 977 | **6. pref top** 978 | 979 | 使用pref工具分析那个函数引发的cpu过高来追踪定位 980 | 981 | ![image-20221213135823899](MySQL问题整理.assets/image-20221213135823899.png) 982 | 983 | # 二十五、有没有进行过分库分表? 984 | 985 | **垂直分库** 986 | 987 | 一个数据库由很多表构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分不到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图: 988 | 989 | image-20221213141320334 990 | 991 | **水平分库** 992 | 993 | 把一张表里的内容按照不同的规则,写到不同的库里 994 | 995 | 相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图: 996 | 997 | image-20221213142016600 998 | 999 | ## 什么时候进行分库分表?有没有配合ES使用经验? 1000 | 1001 | 1. 能不分就不分 1002 | 2. 单机性能下降明显的时候 1003 | 3. 增加缓存(通常查询量比较大),细分业务 1004 | 4. 首先尝试主备集群,读写分离 1005 | 5. 尝试分库 1006 | 6. 尝试分表 1007 | 1008 | 大数据量下可以配合ES完成高效查询 1009 | 1010 | ## 说一下实现分库分表工具的具体思路 1011 | 1012 | 1. 伪装成mysql服务器,代理用户请求转发到真实服务器 1013 | 2. 基于本地AOP实现,拦截sql,改写,路由和结果归集处理。 1014 | 1015 | ## 用过哪些分库分表工具 1016 | 1017 | ![image-20221213142612507](MySQL问题整理.assets/image-20221213142612507.png) 1018 | 1019 | ## 分库分表可能会有哪些问题? 1020 | 1021 | 经典的问题 1022 | 1023 | 1. 执行效率明显下降 1024 | 2. 表结构很难再次调整 1025 | 3. 引发分布式id问题 1026 | 4. 产生跨库join 1027 | 5. 代理类中间件网络io成为瓶颈 1028 | 1029 | # 二十六、说一下读写分离的常见方案? 1030 | 1031 | image-20221213142925956 1032 | 1033 | image-20221213143006441 1034 | 1035 | # 二十七、为什么使用视图?什么是视图? 1036 | 1037 | image-20221213143203979 1038 | 1039 | # 二十八、什么是存储过程?有没有使用过? 1040 | 1041 | 项目中禁止使用存储过程,存储过程难以调试和扩展,更没有移植性 1042 | 1043 | # 二十九、有没有使用过外键?有什么需要注意的地方 1044 | 1045 | 不得使用外键与级联,一切外键概念必须在应用层解决 1046 | 1047 | # 三十、用过processlist吗? 1048 | 1049 | ![image-20221213143505802](MySQL问题整理.assets/image-20221213143505802.png) 1050 | 1051 | ![image-20221213143523671](MySQL问题整理.assets/image-20221213143523671.png) 1052 | 1053 | ![image-20221213143554868](MySQL问题整理.assets/image-20221213143554868.png) 1054 | 1055 | # 三十一、某个表有数千万数据,查询比较慢,如何优化?说一下思路 1056 | 1057 | image-20221213144331585 1058 | 1059 | # 三十二、count(列名)和count(*)有什么区别? 1060 | 1061 | count(\*)是SQL92定义的 1062 | 1063 | 标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关 1064 | 1065 | 说明:count(\*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行, 1066 | 1067 | # 三十三、如果有超大分页该怎么处理? 1068 | 1069 | ![image-20221213145005617](MySQL问题整理.assets/image-20221213145005617.png) 1070 | 1071 | ![image-20221213145238809](MySQL问题整理.assets/image-20221213145238809.png) 1072 | 1073 | # 三十四、MySQL服务器毫无规律的异常重启如何排查问题? 1074 | 1075 | ![image-20221213145701443](MySQL问题整理.assets/image-20221213145701443.png) 1076 | 1077 | # 三十五、Mysql线上修改表结构有哪些风险? 1078 | 1079 | image-20221213150628725 1080 | 1081 | 建议:建个新表,导入数据后重命名 1082 | 1083 | # 三十六、什么是mysql多实例部署? 1084 | 1085 | 指的是在一台主机上部署多个实例 1086 | 1087 | 主要目的是压榨服务器性能 1088 | 1089 | 缺点是项目影响 1090 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 1. JavaInterview 2 | 个人整理的面试常见问题 3 | 4 | 慢慢更新,一点点学,想看的可以关注 5 | -------------------------------------------------------------------------------- /Redis面试问题整理/Redis问题整理.assets/image-20230306160134208.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/Redis面试问题整理/Redis问题整理.assets/image-20230306160134208.png -------------------------------------------------------------------------------- /Redis面试问题整理/Redis问题整理.md: -------------------------------------------------------------------------------- 1 | ## Redis 怎么保证缓存一致性? 2 | 3 | 当需要更新数据时,无论是先写到Redis里再写到MySQL,还是先写MySQL再写Redis,这两步写操作不能保证原子性,所以会出现Redis和MySQL里的数据不一致。因为写和读是并发的,没法保证顺序,如果对Redis里的数据**设置过期时间**就能够保证最终一致性,对架构做的优化只能降低不一致性发生的概率,不能从根本上避免不一致性。 4 | 5 | ● 先更新数据库,再更新缓存(×):假设有线程A先更新数据库,线程B后更新缓存,然后由于网络原因,线程B先更新了缓存,最后线程A再更新缓存,这时候会出现数据库和缓存不一致的情况。 6 | 7 | ● 先更新缓存,在更新数据库(×):因为以数据库的数据为准,所以先更新数据库。 8 | 9 | ● 先删除缓存,再更新数据库:如果删除了缓存Redis,还没有来得及写进MySQL数据库,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。怎么保证缓存一致性? 10 | 11 | **解决办法** 12 | 13 | * **延迟双删** 14 | 15 | 1)先删除缓存 2)再写数据库3)休眠500毫秒4)再次删除缓存。**那么,这个500毫秒怎么确定的,具体该休眠多久呢?**需要评估自己的项目的读数据业务逻辑的耗时。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。当然这种策略还要考虑redis和数据库主从同步的耗时。最后的的写数据的休眠时间:则在读数据业务逻辑的耗时基础上,加几百ms即可。比如:休眠1秒。 16 | 17 | 2)**设置缓存过期时间:**从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。 18 | 19 | **该方案的弊端:**结合双删策略+缓存超时设置,这样最差的情况就是在超时时间内数据存在不一致,而且又增加了写请求的耗时。 20 | 21 | 上面的方法在高并发情况下,休眠500毫秒太多了,并且吞吐量降低了,所以可以采取异步删除的策略,另起一个线程。**如果第二次删除缓存失败怎么办?**会再次出现缓存和数据库不一致的问题。 22 | 23 | **延迟双删中的删为什么不是更新缓存,而是删除缓存?** 24 | 25 | 举个栗子:一个缓存涉及的表的字段,在 1 分钟内就修改了 20 次,或者是 100 次,那么缓存更新 20 次、100 次;但是这个缓存在 1 分钟内只被读取了 1 次,有大量的冷数据。 26 | 27 | 实际上,如果你只是删除缓存的话,那么在 1 分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低。用到缓存才去算缓存。 28 | 29 | * ##### 异步更新缓存(基于订阅binlog的同步机制,分为7个步骤) 30 | 31 | 1.**读Redis**:热数据基本都在Redis 32 | 33 | 2.**写MySQL**: 增删改都是操作MySQL 34 | 35 | 3.**更新Redis数据**:一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。 36 | 37 | 该方法主要强调的是读和写分开,读就redis专业读,写就mysql写然后同步,又读又写很容易有数据不一致。注意:除了binlog,常用的队列还可以使用**Redis**,Kafka,AMQ,RMQ,阿里的canal**。 38 | 39 | ● 先更新数据库,再删除缓存:如果这时候(1)缓存刚好失效,(2)线程A进行查询得到旧值,(3)线程B更新数据库,(4)删除缓存,(5)线程A写入缓存,则也会出现数据不一致情况。 40 | 41 | 然而,发生这种情况的概率又有多少呢? 42 | 发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。 43 | 44 | 如果有人抬杠,一定要解决怎么办? 45 | 46 | 可以采用前面提到的延迟双删策略。 47 | 48 | 还有其他造成不一致的原因么? 49 | 有的,这也是缓存更新策略(先删除缓存,再更新数据库)和缓存更新策略(先更新数据库,再删除缓存)都存在的一个问题,如果删缓存失败了怎么办,那不是会有不一致的情况出现么。比如一个写数据请求,然后写入数据库了,删缓存失败了,这会就出现不一致的情况了。这也是缓存更新策略(先删除缓存,再更新数据库)里留下的最后一个疑问。如何解决? 50 | 提供一个保障的**重试机制**即可,这里给出两套方案,在这里只说明其中最好的一套,另一套会对业务代码造成大量的侵入,如下: 51 | 52 | image-20230306160134208 53 | 54 | 流程如下图所示: 55 | (1)更新数据库数据 56 | (2)数据库会将操作信息写入binlog日志当中 57 | (3)订阅binlog的程序提取出所需要的数据以及key 58 | (4)另起一段非业务代码,获得该信息 59 | (5)尝试更新、删除缓存操作,发现操作失败 60 | (6)将这些信息发送至消息队列 61 | (7)重新从消息队列中获得该数据,重试操作。 62 | 63 | **备注说明**:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。另外,重试机制,采用的是消息队列的方式。如果对一致性要求不是很高,直接在程序中另起一个线程,每隔一段时间去重试即可,这些大家可以灵活自由发挥,只是提供一个思路。 64 | 65 | ## 什么是Redis缓存穿透、缓存击穿、缓存雪崩? 66 | 67 | **缓存穿透:**一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库**短时间内承受大量请求**而崩掉。如发起为id为“-1”的数据或id为特别大不存在的数据。 68 | 69 | 解决方案:(1)简单暴力的方法:接口层增加校验,如判断id<=0的直接拦截;(2)简单暴力的方法:如果一个查询返回的数据为空(不管是缓存中,还是数据库中不存在该数据),我们仍然把这个空结果进行缓存**,有效时间可以设置短点**,如30秒,最长不超过5分钟(否则有可能在真的有数据的时候影响了业务正常流程)。这样可以防止攻击用户反复用同一个id暴力攻击。要知道正常用户是不会在单秒内发起这么多次请求的,那网关层Nginx本渣我也记得有配置项,可以让运维大大对单个IP每秒访问次数超出阈值的IP都**拉黑**。(3)最常见的则是采用布隆过滤器,将所有可能存在的数据**哈希**到一个足够大的**bitmap**中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对数据库的查询压力。 70 | 71 | ● **缓存击穿:**指大量用户同时访问一条缓存中没有**但数据库中有的数据**(一般是缓存时间到期),而查询数据量巨大,引起数据库压力过大甚至宕机。 72 | 73 | 解决方案:(1)设置热点数据永远不过期;(2)使用互斥锁,在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用Redis的SETNX去set一个mutex key,如果能成功设置,再进行load db的操作并回设缓存;否则的话,表示当前以及有其它线程获取了互斥锁并读取数据库回存缓存了,当前线程重新获取缓存即可。 74 | 75 | **缓存雪崩:**缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至宕机。和缓存击穿不同的是,缓存击穿指并发查同一条数据(例如抢购同一件商品),缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。 76 | 77 | **场景:** 78 | 79 | 目前电商首页以及热点数据都会去做缓存 ,一般缓存都是定时任务去刷新,或者是查不到之后去更新的,定时任务刷新就有一个问题。 80 | 81 | 举个简单的例子:如果所有首页的Key失效时间都是12小时,中午12点刷新的,我零点有个秒杀活动大量用户涌入,假设当时每秒 6000 个请求,本来缓存在可以扛住每秒 5000 个请求,但是缓存当时所有的Key都失效了。此时 1 秒 6000 个请求全部落数据库,数据库必然扛不住,它会报一下警,真实情况可能DBA都没反应过来就直接挂了。此时,如果没用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。**这就是我理解的缓存雪崩**。 82 | 83 | 解决方案: 84 | 85 | (1)缓存数据的过期时间随机设置。 86 | 87 | setRedis(Key,value,time + Math.random() * 10000); 88 | 89 | (2)如果缓存数据库是分布式部署,将热点数据均匀分布在不同的服务器中。 90 | 91 | (3)设置热点数据永远不过期。有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷下缓存就完事了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。 92 | 93 | 总结: 94 | 95 | 一般避免以上情况发生我们从三个时间段去分析下: 96 | 97 | ● 事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。 98 | 99 | ● 事中:本地 ehcache 缓存 + **Hystrix** **限流**+**降级**,避免MySQL 被打死。 100 | 101 | ● 事后:Redis 持久化 RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。 102 | 103 | ## 过期的数据的删除策略了解么? 104 | 105 | 如果假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢? 106 | 107 | 常用的过期数据的删除策略就两个(重要!自己造缓存轮子的时候需要格外考虑的东西): 108 | 109 | 1. **惰性删除** :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。 110 | 2. **定期删除** : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。 111 | 112 | 定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 **定期删除+惰性/懒汉式删除** 。 113 | 114 | 但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。 115 | 116 | 怎么解决这个问题呢?答案就是:**Redis 内存淘汰机制。** 117 | 118 | ## Redis 内存淘汰机制了解么? 119 | 120 | > 相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据? 121 | 122 | Redis 提供 6 种数据淘汰策略: 123 | 124 | 1. **volatile-lru(least recently used)**:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 125 | 2. **volatile-ttl**:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 126 | 3. **volatile-random**:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 127 | 4. **allkeys-lru(least recently used)**:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的) 128 | 5. **allkeys-random**:从数据集(server.db[i].dict)中任意选择数据淘汰 129 | 6. **no-eviction**:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧! 130 | 131 | 4.0 版本后增加以下两种: 132 | 133 | 1. **volatile-lfu(least frequently used)**:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰 134 | 2. **allkeys-lfu(least frequently used)**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230202234839001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230202234839001.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230202234919691.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230202234919691.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230202234924892.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230202234924892.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203000640690.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203000640690.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203001008346.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203001008346.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203001059458.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203001059458.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203142220904.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203142220904.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203154118858.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203154118858.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203154706712.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203154706712.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203154722404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203154722404.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203154742836.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203154742836.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203154822362.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203154822362.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203154824865.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203154824865.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203155105227.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203155105227.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203162644946.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203162644946.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203163726654.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203163726654.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203163801981.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203163801981.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203165515837.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203165515837.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203170706761.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203170706761.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203213538415.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203213538415.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203213605267.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203213605267.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203220950140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203220950140.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203221307695.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203221307695.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230203221317632.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230203221317632.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204142634408.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204142634408.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204142754599.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204142754599.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143313238.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143313238.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143408791.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143408791.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143434295.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143434295.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143453279.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143453279.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143521125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143521125.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143644032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143644032.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143656785.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143656785.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143718506.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143718506.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143807163.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143807163.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143820697.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143820697.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.assets/image-20230204143830688.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/操作系统问题整理/操作系统八股.assets/image-20230204143830688.png -------------------------------------------------------------------------------- /操作系统问题整理/操作系统八股.md: -------------------------------------------------------------------------------- 1 | 内容参考自:https://xiaolincoding.com/os/ 2 | 3 | # 进程、线程基础知识 4 | 5 | ## 进程 6 | 7 | 我们编写的代码只是一个存储在硬盘的静态文件,通过编译后就会生成二进制可执行文件,当我们运行这个可执行文件后,它会被装载到内存中,接着 CPU 会执行程序中的每一条指令,那么这个**运行中的程序,就被称为「进程」(Process)**。 8 | 9 | ### 进程的状态 10 | 11 | 进程有着「运行 - 暂停 - 运行」的活动规律。一般说来,一个进程并不是自始至终连续不停地运行的,它与并发执行中的其他进程的执行是相互制约的。 12 | 13 | 它有时处于运行状态,有时又由于某种原因而暂停运行处于等待状态,当使它暂停的原因消失后,它又进入准备运行状态。 14 | 15 | 所以,**在一个进程的活动期间至少具备三种基本状态,即运行状态、就绪状态、阻塞状态。** 16 | 17 | image-20230202234839001 18 | 19 | 上图中各个状态的意义: 20 | 21 | - 运行状态(*Running*):该时刻进程占用 CPU; 22 | - 就绪状态(*Ready*):可运行,由于其他进程处于运行状态而暂时停止运行; 23 | - 阻塞状态(*Blocked*):该进程正在等待某一事件发生(如等待输入/输出操作的完成)而暂时停止运行,这时,即使给它CPU控制权,它也无法运行; 24 | 25 | 当然,进程还有另外两个基本状态: 26 | 27 | - 创建状态(*new*):进程正在被创建时的状态; 28 | - 结束状态(*Exit*):进程正在从系统中消失时的状态; 29 | 30 | 于是,一个完整的进程状态的变迁如下图: 31 | 32 | image-20230202234924892 33 | 34 | 再来详细说明一下进程的状态变迁: 35 | 36 | - *NULL -> 创建状态*:一个新进程被创建时的第一个状态; 37 | - *创建状态 -> 就绪状态*:当进程被创建完成并初始化后,一切就绪准备运行时,变为就绪状态,这个过程是很快的; 38 | - *就绪态 -> 运行状态*:处于就绪状态的进程被操作系统的进程调度器选中后,就分配给 CPU 正式运行该进程; 39 | - *运行状态 -> 结束状态*:当进程已经运行完成或出错时,会被操作系统作结束状态处理; 40 | - *运行状态 -> 就绪状态*:处于运行状态的进程在运行过程中,由于分配给它的运行时间片用完,操作系统会把该进程变为就绪态,接着从就绪态选中另外一个进程运行; 41 | - *运行状态 -> 阻塞状态*:当进程请求某个事件且必须等待时,例如请求 I/O 事件; 42 | - *阻塞状态 -> 就绪状态*:当进程要等待的事件完成时,它从阻塞状态变到就绪状态; 43 | 44 | 如果有大量处于阻塞状态的进程,进程可能会占用着物理内存空间,显然不是我们所希望的,毕竟物理内存空间是有限的,被阻塞状态的进程占用着物理内存就一种浪费物理内存的行为。 45 | 46 | 所以,在虚拟内存管理的操作系统中,通常会把阻塞状态的进程的物理内存空间换出到硬盘,等需要再次运行的时候,再从硬盘换入到物理内存。 47 | 48 | image-20230203000640690 49 | 50 | 那么,就需要一个新的状态,来**描述进程没有占用实际的物理内存空间的情况,这个状态就是挂起状态**。这跟阻塞状态是不一样,阻塞状态是等待某个事件的返回。 51 | 52 | 另外,挂起状态可以分为两种: 53 | 54 | - 阻塞挂起状态:进程在外存(硬盘)并等待某个事件的出现; 55 | - 就绪挂起状态:进程在外存(硬盘),但只要进入内存,即刻立刻运行; 56 | 57 | 这两种挂起状态加上前面的五种状态,就变成了七种状态变迁(留给我的颜色不多了),见如下图: 58 | 59 | image-20230203001008346 60 | 61 | 导致进程挂起的原因不只是因为进程所使用的内存空间不在物理内存,还包括如下情况: 62 | 63 | - 通过 sleep 让进程间歇性挂起,其工作原理是设置一个定时器,到期后唤醒进程。 64 | - 用户希望挂起一个程序的执行,比如在 Linux 中用 `Ctrl+Z` 挂起进程; 65 | 66 | ### 进程的控制结构 67 | 68 | 在操作系统中,是用**进程控制块**(*process control block,PCB*)数据结构来描述进程的。 69 | 70 | **PCB 是进程存在的唯一标识**,这意味着一个进程的存在,必然会有一个 PCB,如果进程消失了,那么 PCB 也会随之消失。 71 | 72 | > PCB 具体包含什么信息呢? 73 | 74 | **进程描述信息:** 75 | 76 | - 进程标识符:标识各个进程,每个进程都有一个并且唯一的标识符; 77 | - 用户标识符:进程归属的用户,用户标识符主要为共享和保护服务; 78 | 79 | **进程控制和管理信息:** 80 | 81 | - 进程当前状态,如 new、ready、running、waiting 或 blocked 等; 82 | - 进程优先级:进程抢占 CPU 时的优先级; 83 | 84 | **资源分配清单:** 85 | 86 | - 有关内存地址空间或虚拟地址空间的信息,所打开文件的列表和所使用的 I/O 设备信息。 87 | 88 | **CPU 相关信息:** 89 | 90 | - CPU 中各个寄存器的值,当进程被切换时,CPU 的状态信息都会被保存在相应的 PCB 中,以便进程重新执行时,能从断点处继续执行。 91 | 92 | 可见,PCB 包含信息还是比较多的。 93 | 94 | > 每个 PCB 是如何组织的呢? 95 | 96 | 通常是通过**链表**的方式进行组织,把具有**相同状态的进程链在一起,组成各种队列**。比如: 97 | 98 | - 将所有处于就绪状态的进程链在一起,称为**就绪队列**; 99 | - 把所有因等待某事件而处于等待状态的进程链在一起就组成各种**阻塞队列**; 100 | - 另外,对于运行队列在单核 CPU 系统中则只有一个运行指针了,因为单核 CPU 在某个时间,只能运行一个程序。 101 | 102 | 那么,就绪队列和阻塞队列链表的组织形式如下图: 103 | 104 | image-20230203001059458 105 | 106 | 除了链接的组织方式,还有索引方式,它的工作原理:将同一状态的进程组织在一个索引表中,索引表项指向相应的 PCB,不同状态对应不同的索引表。 107 | 108 | 一般会选择链表,因为可能面临进程创建,销毁等调度导致进程状态发生变化,所以链表能够更加灵活的插入和删除。 109 | 110 | ### 进程的控制 111 | 112 | 我们熟知了进程的状态变迁和进程的数据结构 PCB 后,再来看看进程的**创建、终止、阻塞、唤醒**的过程,这些过程也就是进程的控制。 113 | 114 | **01 创建进程** 115 | 116 | 操作系统允许一个进程创建另一个进程,而且允许子进程继承父进程所拥有的资源。 117 | 118 | 创建进程的过程如下: 119 | 120 | - 申请一个空白的 PCB,并向 PCB 中填写一些控制和管理进程的信息,比如进程的唯一标识等; 121 | - 为该进程分配运行时所必需的资源,比如内存资源; 122 | - 将 PCB 插入到就绪队列,等待被调度运行; 123 | 124 | **02 终止进程** 125 | 126 | 进程可以有 3 种终止方式:正常结束、异常结束以及外界干预(信号 `kill` 掉)。 127 | 128 | 当子进程被终止时,其在父进程处继承的资源应当还给父进程。而当父进程被终止时,该父进程的子进程就变为孤儿进程,会被 1 号进程收养,并由 1 号进程对它们完成状态收集工作。 129 | 130 | 终止进程的过程如下: 131 | 132 | - 查找需要终止的进程的 PCB; 133 | - 如果处于执行状态,则立即终止该进程的执行,然后将 CPU 资源分配给其他进程; 134 | - 如果其还有子进程,则应将该进程的子进程交给 1 号进程接管; 135 | - 将该进程所拥有的全部资源都归还给操作系统; 136 | - 将其从 PCB 所在队列中删除; 137 | 138 | **03 阻塞进程** 139 | 140 | 当进程需要等待某一事件完成时,它可以调用阻塞语句把自己阻塞等待。而一旦被阻塞等待,它只能由另一个进程唤醒。 141 | 142 | 阻塞进程的过程如下: 143 | 144 | - 找到将要被阻塞进程标识号对应的 PCB; 145 | - 如果该进程为运行状态,则保护其现场,将其状态转为阻塞状态,停止运行; 146 | - 将该 PCB 插入到阻塞队列中去; 147 | 148 | **04 唤醒进程** 149 | 150 | 进程由「运行」转变为「阻塞」状态是由于进程必须等待某一事件的完成,所以处于阻塞状态的进程是绝对不可能叫醒自己的。 151 | 152 | 如果某进程正在等待 I/O 事件,需由别的进程发消息给它,则只有当该进程所期待的事件出现时,才由发现者进程用唤醒语句叫醒它。 153 | 154 | 唤醒进程的过程如下: 155 | 156 | - 在该事件的阻塞队列中找到相应进程的 PCB; 157 | - 将其从阻塞队列中移出,并置其状态为就绪状态; 158 | - 把该 PCB 插入到就绪队列中,等待调度程序调度; 159 | 160 | 进程的阻塞和唤醒是一对功能相反的语句,如果某个进程调用了阻塞语句,则必有一个与之对应的唤醒语句。 161 | 162 | ## 线程 163 | 164 | 在早期的操作系统中都是以进程作为独立运行的基本单位,直到后面,计算机科学家们又提出了更小的能独立运行的基本单位,也就是**线程。** 165 | 166 | ### 什么是线程? 167 | 168 | **线程是进程当中的一条执行流程。** 169 | 170 | 同一个进程内多个线程之间可以共享代码段、数据段、打开的文件等资源,但每个线程各自都有一套独立的寄存器和栈,这样可以确保线程的控制流是相对独立的。 171 | 172 | image-20230203142220904 173 | 174 | > 线程的优缺点? 175 | 176 | 线程的优点: 177 | 178 | - 一个进程中可以同时存在多个线程; 179 | - 各个线程之间可以并发执行; 180 | - 各个线程之间可以共享地址空间和文件等资源; 181 | 182 | 线程的缺点: 183 | 184 | - 当进程中的一个线程崩溃时,会导致其所属进程的所有线程崩溃 185 | 186 | ### 线程与进程的比较 187 | 188 | 线程与进程的比较如下: 189 | 190 | - 进程是资源(包括内存、打开的文件等)分配的单位,线程是 CPU 调度的单位; 191 | - 进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈; 192 | - 线程同样具有就绪、阻塞、执行三种基本状态,同样具有状态之间的转换关系; 193 | - 线程能减少并发执行的时间和空间开销; 194 | 195 | 对于,线程相比进程能减少开销,体现在: 196 | 197 | - 线程的创建时间比进程快,因为进程在创建的过程中,还需要资源管理信息,比如内存管理信息、文件管理信息,而线程在创建的过程中,不会涉及这些资源管理信息,而是共享它们; 198 | - 线程的终止时间比进程快,因为线程释放的资源相比进程少很多; 199 | - 同一个进程内的线程切换比进程切换快,因为线程具有相同的地址空间(虚拟内存共享),这意味着同一个进程的线程都具有同一个页表,那么在切换的时候不需要切换页表。而对于进程之间的切换,切换的时候要把页表给切换掉,而页表的切换过程开销是比较大的; 200 | - 由于同一进程的各线程间共享内存和文件资源,那么在线程之间数据传递的时候,就不需要经过内核了,这就使得线程之间的数据交互效率更高了; 201 | 202 | 所以,不管是时间效率,还是空间效率线程比进程都要高。 203 | 204 | ### 线程的上下文切换 205 | 206 | 在前面我们知道了,线程与进程最大的区别在于:**线程是调度的基本单位,而进程则是资源拥有的基本单位**。 207 | 208 | 所以,所谓操作系统的任务调度,实际上的调度对象是线程,而进程只是给线程提供了虚拟内存、全局变量等资源。 209 | 210 | 对于线程和进程,我们可以这么理解: 211 | 212 | - 当进程只有一个线程时,可以认为进程就等于线程; 213 | - 当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源,这些资源在上下文切换时是不需要修改的; 214 | 215 | 另外,线程也有自己的私有数据,比如栈和寄存器等,这些在上下文切换时也是需要保存的。 216 | 217 | > 线程上下文切换的是什么? 218 | 219 | 这还得看线程是不是属于同一个进程: 220 | 221 | - 当两个线程不是属于同一个进程,则切换的过程就跟进程上下文切换一样; 222 | - **当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据**; 223 | 224 | 所以,线程的上下文切换相比进程,开销要小很多。 225 | 226 | ### 线程的实现 227 | 228 | 主要有三种线程的实现方式: 229 | 230 | - **用户线程(User Thread)**:在用户空间实现的线程,不是由内核管理的线程,是由用户态的线程库来完成线程的管理; 231 | - **内核线程(Kernel Thread)**:在内核中实现的线程,是由内核管理的线程; 232 | - **轻量级进程(LightWeight Process)**:在内核中来支持用户线程; 233 | 234 | **用户线程的整个线程管理和调度,操作系统是不直接参与的,而是由用户级线程库函数来完成线程的管理,包括线程的创建、终止、同步和调度等。** 235 | 236 | 用户线程的**优点**: 237 | 238 | - 每个进程都需要有它私有的线程控制块(TCB)列表,用来跟踪记录它各个线程状态信息(PC、栈指针、寄存器),TCB 由用户级线程库函数来维护,可用于不支持线程技术的操作系统; 239 | - 用户线程的切换也是由线程库函数来完成的,无需用户态与内核态的切换,所以速度特别快; 240 | 241 | 用户线程的**缺点**: 242 | 243 | - 由于操作系统不参与线程的调度,如果一个线程发起了系统调用而阻塞,那进程所包含的用户线程都不能执行了。 244 | - 当一个线程开始运行后,除非它主动地交出 CPU 的使用权,否则它所在的进程当中的其他线程无法运行,因为用户态的线程没法打断当前运行中的线程,它没有这个特权,只有操作系统才有,但是用户线程不是由操作系统管理的。 245 | - 由于时间片分配给进程,故与其他进程比,在多线程执行时,每个线程得到的时间片较少,执行会比较慢; 246 | 247 | **内核线程是由操作系统管理的,线程对应的 TCB 自然是放在操作系统里的,这样线程的创建、终止和管理都是由操作系统负责。** 248 | 249 | 内核线程的**优点**: 250 | 251 | - 在一个进程当中,如果某个内核线程发起系统调用而被阻塞,并不会影响其他内核线程的运行; 252 | - 分配给线程,多线程的进程获得更多的 CPU 运行时间; 253 | 254 | 内核线程的**缺点**: 255 | 256 | - 在支持内核线程的操作系统中,由内核来维护进程和线程的上下文信息,如 PCB 和 TCB; 257 | - 线程的创建、终止和切换都是通过系统调用的方式来进行,因此对于系统来说,系统开销比较大; 258 | 259 | **轻量级进程(Light-weight process,LWP)是内核支持的用户线程,一个进程可有一个或多个 LWP,每个 LWP 是跟内核线程一对一映射的,也就是 LWP 都是由一个内核线程支持,而且 LWP 是由内核管理并像普通进程一样被调度**。 260 | 261 | 在大多数系统中,**LWP与普通进程的区别也在于它只有一个最小的执行上下文和调度程序所需的统计信息**。一般来说,一个进程代表程序的一个实例,而 LWP 代表程序的执行线程,因为一个执行线程不像进程那样需要那么多状态信息,所以 LWP 也不带有这样的信息。 262 | 263 | 在 LWP 之上也是可以使用用户线程的,那么 LWP 与用户线程的对应关系就有三种: 264 | 265 | - `1 : 1`,即一个 LWP 对应 一个用户线程; 266 | - `N : 1`,即一个 LWP 对应多个用户线程; 267 | - `M : N`,即多个 LWP 对应多个用户线程; 268 | 269 | 接下来针对上面这三种对应关系说明它们优缺点。先看下图的 LWP 模型: 270 | 271 | image-20230203154118858 272 | 273 | **1 : 1 模式** 274 | 275 | 一个线程对应到一个 LWP 再对应到一个内核线程,如上图的进程 4,属于此模型。 276 | 277 | - 优点:实现并行,当一个 LWP 阻塞,不会影响其他 LWP; 278 | - 缺点:每一个用户线程,就产生一个内核线程,创建线程的开销较大。 279 | 280 | **N : 1 模式** 281 | 282 | 多个用户线程对应一个 LWP 再对应一个内核线程,如上图的进程 2,线程管理是在用户空间完成的,此模式中用户的线程对操作系统不可见。 283 | 284 | - 优点:用户线程要开几个都没问题,且上下文切换发生用户空间,切换的效率较高; 285 | - 缺点:一个用户线程如果阻塞了,则整个进程都将会阻塞,另外在多核 CPU 中,是没办法充分利用 CPU 的。 286 | 287 | **M : N 模式** 288 | 289 | 根据前面的两个模型混搭一起,就形成 `M:N` 模型,该模型提供了两级控制,首先多个用户线程对应到多个 LWP,LWP 再一一对应到内核线程,如上图的进程 3。 290 | 291 | - 优点:综合了前两种优点,大部分的线程上下文发生在用户空间,且多个线程又可以充分利用多核 CPU 的资源。 292 | 293 | **组合模式** 294 | 295 | 如上图的进程 5,此进程结合 `1:1` 模型和 `M:N` 模型。开发人员可以针对不同的应用特点调节内核线程的数目来达到物理并行性和逻辑并行性的最佳方案。 296 | 297 | ### 调度算法 298 | 299 | 不同的调度算法适用的场景也是不同的。 300 | 301 | 接下来,说说在**单核 CPU 系统**中常见的调度算法。 302 | 303 | > 01 先来先服务调度算法 304 | 305 | 最简单的一个调度算法,就是非抢占式的**先来先服务(\*First Come First Serve, FCFS\*)算法**了。 306 | 307 | ![image-20230203154706712](操作系统八股.assets/image-20230203154706712.png) 308 | 309 | 顾名思义,先来后到,**每次从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。** 310 | 311 | 这似乎很公平,但是当一个长作业先运行了,那么后面的短作业等待的时间就会很长,不利于短作业。 312 | 313 | FCFS 对长作业有利,适用于 CPU 繁忙型作业的系统,而不适用于 I/O 繁忙型作业的系统。 314 | 315 | > 02 最短作业优先调度算法 316 | 317 | **最短作业优先(\*Shortest Job First, SJF\*)调度算法**同样也是顾名思义,它会**优先选择运行时间最短的进程来运行**,这有助于提高系统的吞吐量。 318 | 319 | ![image-20230203154722404](操作系统八股.assets/image-20230203154722404.png) 320 | 321 | 这显然对长作业不利,很容易造成一种极端现象。 322 | 323 | 比如,一个长作业在就绪队列等待运行,而这个就绪队列有非常多的短作业,那么就会使得长作业不断的往后推,周转时间变长,致使长作业长期不会被运行。 324 | 325 | > 03 高响应比优先调度算法 326 | 327 | 前面的「先来先服务调度算法」和「最短作业优先调度算法」都没有很好的权衡短作业和长作业。 328 | 329 | 那么,**高响应比优先 (\*Highest Response Ratio Next, HRRN\*)调度算法**主要是权衡了短作业和长作业。 330 | 331 | **每次进行进程调度时,先计算「响应比优先级」,然后把「响应比优先级」最高的进程投入运行**,「响应比优先级」的计算公式: 332 | 333 | image-20230203154742836 334 | 335 | 从上面的公式,可以发现: 336 | 337 | - 如果两个进程的「等待时间」相同时,「要求的服务时间」越短,「响应比」就越高,这样短作业的进程容易被选中运行; 338 | - 如果两个进程「要求的服务时间」相同时,「等待时间」越长,「响应比」就越高,这就兼顾到了长作业进程,因为进程的响应比可以随时间等待的增加而提高,当其等待时间足够长时,其响应比便可以升到很高,从而获得运行的机会; 339 | 340 | > TIP 341 | > 342 | > 很多人问怎么才能知道一个进程要求服务的时间?这不是不可预知的吗? 343 | > 344 | > 对的,这是不可预估的。所以,高响应比优先调度算法是「理想型」的调度算法,现实中是实现不了的。 345 | 346 | > 04 时间片轮转调度算法 347 | 348 | 最古老、最简单、最公平且使用最广的算法就是**时间片轮转(\*Round Robin, RR\*)调度算法**。 349 | 350 | image-20230203154824865 351 | 352 | **每个进程被分配一个时间段,称为时间片(\*Quantum\*),即允许该进程在该时间段中运行。** 353 | 354 | - 如果时间片用完,进程还在运行,那么将会把此进程从 CPU 释放出来,并把 CPU 分配给另外一个进程; 355 | - 如果该进程在时间片结束前阻塞或结束,则 CPU 立即进行切换; 356 | 357 | 另外,时间片的长度就是一个很关键的点: 358 | 359 | - 如果时间片设得太短会导致过多的进程上下文切换,降低了 CPU 效率; 360 | - 如果设得太长又可能引起对短作业进程的响应时间变长。将 361 | 362 | 一般来说,时间片设为 `20ms~50ms` 通常是一个比较合理的折中值。 363 | 364 | > 05 最高优先级调度算法 365 | 366 | 前面的「时间片轮转算法」做了个假设,即让所有的进程同等重要,也不偏袒谁,大家的运行时间都一样。 367 | 368 | 但是,对于多用户计算机系统就有不同的看法了,它们希望调度是有优先级的,即希望调度程序能**从就绪队列中选择最高优先级的进程进行运行,这称为最高优先级(\*Highest Priority First,HPF\*)调度算法**。 369 | 370 | 进程的优先级可以分为,静态优先级和动态优先级: 371 | 372 | - 静态优先级:创建进程时候,就已经确定了优先级了,然后整个运行时间优先级都不会变化; 373 | - 动态优先级:根据进程的动态变化调整优先级,比如如果进程运行时间增加,则降低其优先级,如果进程等待时间(就绪队列的等待时间)增加,则升高其优先级,也就是**随着时间的推移增加等待进程的优先级**。 374 | 375 | 该算法也有两种处理优先级高的方法,非抢占式和抢占式: 376 | 377 | - 非抢占式:当就绪队列中出现优先级高的进程,运行完当前进程,再选择优先级高的进程。 378 | - 抢占式:当就绪队列中出现优先级高的进程,当前进程挂起,调度优先级高的进程运行。 379 | 380 | 但是依然有缺点,可能会导致低优先级的进程永远不会运行。 381 | 382 | > 06 多级反馈队列调度算法 383 | 384 | **多级反馈队列(\*Multilevel Feedback Queue\*)调度算法**是「时间片轮转算法」和「最高优先级算法」的综合和发展。 385 | 386 | 顾名思义: 387 | 388 | - 「多级」表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。 389 | - 「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列; 390 | 391 | image-20230203155105227 392 | 393 | 来看看,它是如何工作的: 394 | 395 | - 设置了多个队列,赋予每个队列不同的优先级,每个**队列优先级从高到低**,同时**优先级越高时间片越短**; 396 | - 新的进程会被放入到第一级队列的末尾,按先来先服务的原则排队等待被调度,如果在第一级队列规定的时间片没运行完成,则将其转入到第二级队列的末尾,以此类推,直至完成; 397 | - 当较高优先级的队列为空,才调度较低优先级的队列中的进程运行。如果进程运行时,有新进程进入较高优先级的队列,则停止当前运行的进程并将其移入到原队列末尾,接着让较高优先级的进程运行; 398 | 399 | 可以发现,对于短作业可能可以在第一级队列很快被处理完。对于长作业,如果在第一级队列处理不完,可以移入下次队列等待被执行,虽然等待的时间变长了,但是运行时间也变更长了,所以该算法很好的**兼顾了长短作业,同时有较好的响应时间。** 400 | 401 | # 进程间有哪些通信方式? 402 | 403 | ## 管道 404 | 405 | **所谓的管道,就是内核里面的一串缓存**。从管道的一端写入的数据,实际上是缓存在内核中的,另一端读取,也就是从内核中读取这段数据。另外,管道传输的数据是无格式的流且大小受限。 406 | 407 | ## 消息队列 408 | 409 | 前面说到管道的通信方式是效率低的,因此管道不适合进程间频繁地交换数据。 410 | 411 | 对于这个问题,**消息队列**的通信模式就可以解决。比如,A 进程要给 B 进程发送消息,A 进程把数据放在对应的消息队列后就可以正常返回了,B 进程需要的时候再去读取数据就可以了。同理,B 进程要给 A 进程发送消息也是如此。 412 | 413 | **消息队列不适合比较大数据的传输**,因为在内核中每个消息体都有一个最大长度的限制,同时所有队列所包含的全部消息体的总长度也是有上限。在 Linux 内核中,会有两个宏定义 `MSGMAX` 和 `MSGMNB`,它们以字节为单位,分别定义了一条消息的最大长度和一个队列的最大长度。 414 | 415 | **消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销**,因为进程写入数据到内核中的消息队列时,会发生从用户态拷贝数据到内核态的过程,同理另一进程读取内核中的消息数据时,会发生从内核态拷贝数据到用户态的过程。 416 | 417 | ## 共享内存 418 | 419 | 消息队列的读取和写入的过程,都会有发生用户态与内核态之间的消息拷贝过程。那**共享内存**的方式,就很好的解决了这一问题。 420 | 421 | 现代操作系统,对于内存管理,采用的是虚拟内存技术,也就是每个进程都有自己独立的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中。所以,即使进程 A 和 进程 B 的虚拟地址是一样的,其实访问的是不同的物理内存地址,对于数据的增删查改互不影响。 422 | 423 | **共享内存的机制,就是拿出一块虚拟地址空间来,映射到相同的物理内存中**。这样这个进程写入的东西,另外一个进程马上就能看到了,都不需要拷贝来拷贝去,传来传去,大大提高了进程间通信的速度。 424 | 425 | image-20230203162644946 426 | 427 | ## 信号量 428 | 429 | 用了共享内存通信方式,带来新的问题,那就是如果多个进程同时修改同一个共享内存,很有可能就冲突了。例如两个进程都同时写一个地址,那先写的那个进程会发现内容被别人覆盖了。 430 | 431 | 为了防止多进程竞争共享资源,而造成的数据错乱,所以需要保护机制,使得共享的资源,在任意时刻只能被一个进程访问。正好,**信号量**就实现了这一保护机制。 432 | 433 | **信号量其实是一个整型的计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据**。 434 | 435 | 信号量表示资源的数量,控制信号量的方式有两种原子操作: 436 | 437 | - 一个是 **P 操作**,这个操作会把信号量减去 1,相减后如果信号量 < 0,则表明资源已被占用,进程需阻塞等待;相减后如果信号量 >= 0,则表明还有资源可使用,进程可正常继续执行。 438 | - 另一个是 **V 操作**,这个操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是会将该进程唤醒运行;相加后如果信号量 > 0,则表明当前没有阻塞中的进程; 439 | 440 | P 操作是用在进入共享资源之前,V 操作是用在离开共享资源之后,这两个操作是必须成对出现的。 441 | 442 | 接下来,举个例子,如果要使得两个进程互斥访问共享内存,我们可以初始化信号量为 `1`。 443 | 444 | image-20230203163726654 445 | 446 | 具体的过程如下: 447 | 448 | - 进程 A 在访问共享内存前,先执行了 P 操作,由于信号量的初始值为 1,故在进程 A 执行 P 操作后信号量变为 0,表示共享资源可用,于是进程 A 就可以访问共享内存。 449 | - 若此时,进程 B 也想访问共享内存,执行了 P 操作,结果信号量变为了 -1,这就意味着临界资源已被占用,因此进程 B 被阻塞。 450 | - 直到进程 A 访问完共享内存,才会执行 V 操作,使得信号量恢复为 0,接着就会唤醒阻塞中的线程 B,使得进程 B 可以访问共享内存,最后完成共享内存的访问后,执行 V 操作,使信号量恢复到初始值 1。 451 | 452 | 可以发现,信号初始化为 `1`,就代表着是**互斥信号量**,它可以保证共享内存在任何时刻只有一个进程在访问,这就很好的保护了共享内存。 453 | 454 | 另外,在多进程里,每个进程并不一定是顺序执行的,它们基本是以各自独立的、不可预知的速度向前推进,但有时候我们又希望多个进程能密切合作,以实现一个共同的任务。 455 | 456 | 例如,进程 A 是负责生产数据,而进程 B 是负责读取数据,这两个进程是相互合作、相互依赖的,进程 A 必须先生产了数据,进程 B 才能读取到数据,所以执行是有前后顺序的。 457 | 458 | 那么这时候,就可以用信号量来实现多进程同步的方式,我们可以初始化信号量为 `0`。 459 | 460 | image-20230203163801981 461 | 462 | 具体过程: 463 | 464 | - 如果进程 B 比进程 A 先执行了,那么执行到 P 操作时,由于信号量初始值为 0,故信号量会变为 -1,表示进程 A 还没生产数据,于是进程 B 就阻塞等待; 465 | - 接着,当进程 A 生产完数据后,执行了 V 操作,就会使得信号量变为 0,于是就会唤醒阻塞在 P 操作的进程 B; 466 | - 最后,进程 B 被唤醒后,意味着进程 A 已经生产了数据,于是进程 B 就可以正常读取数据了。 467 | 468 | 可以发现,信号初始化为 `0`,就代表着是**同步信号量**,它可以保证进程 A 应在进程 B 之前执行。 469 | 470 | ## 信号 471 | 472 | **对于异常情况下的工作模式,就需要用「信号」的方式来通知进程。** 473 | 474 | 信号是进程间通信机制中**唯一的异步通信机制**,因为可以在任何时候发送信号给某一进程,一旦有信号产生,我们就有下面这几种,用户进程对信号的处理方式。 475 | 476 | **1.执行默认操作**。Linux 对每种信号都规定了默认操作,例如,上面列表中的 SIGTERM 信号,就是终止进程的意思。 477 | 478 | **2.捕捉信号**。我们可以为信号定义一个信号处理函数。当信号发生时,我们就执行相应的信号处理函数。 479 | 480 | **3.忽略信号**。当我们不希望处理某些信号的时候,就可以忽略该信号,不做任何处理。有两个信号是应用进程无法捕捉和忽略的,即 `SIGKILL` 和 `SEGSTOP`,它们用于在任何时候中断或结束某一进程。 481 | 482 | ## Socket 483 | 484 | 前面提到的管道、消息队列、共享内存、信号量和信号都是在同一台主机上进行进程间通信,那要想**跨网络与不同主机上的进程之间通信,就需要 Socket 通信了。** 485 | 486 | ## 总结 487 | 488 | 由于每个进程的用户空间都是独立的,不能相互访问,这时就需要借助内核空间来实现进程间通信,原因很简单,每个进程都是共享一个内核空间。 489 | 490 | Linux 内核提供了不少进程间通信的方式,其中最简单的方式就是管道,管道分为「匿名管道」和「命名管道」。 491 | 492 | **匿名管道**顾名思义,它没有名字标识,匿名管道是特殊文件只存在于内存,没有存在于文件系统中,shell 命令中的「`|`」竖线就是匿名管道,通信的数据是**无格式的流并且大小受限**,通信的方式是**单向**的,数据只能在一个方向上流动,如果要双向通信,需要创建两个管道,再来**匿名管道是只能用于存在父子关系的进程间通信**,匿名管道的生命周期随着进程创建而建立,随着进程终止而消失。 493 | 494 | **命名管道**突破了匿名管道只能在亲缘关系进程间的通信限制,因为使用命名管道的前提,需要在文件系统创建一个类型为 p 的设备文件,那么毫无关系的进程就可以通过这个设备文件进行通信。另外,不管是匿名管道还是命名管道,进程写入的数据都是**缓存在内核**中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循**先进先出**原则,不支持 lseek 之类的文件定位操作。 495 | 496 | **消息队列**克服了管道通信的数据是无格式的字节流的问题,消息队列实际上是保存在内核的「消息链表」,消息队列的消息体是可以用户自定义的数据类型,发送数据时,会被分成一个一个独立的消息体,当然接收数据时,也要与发送方发送的消息体的数据类型保持一致,这样才能保证读取的数据是正确的。消息队列通信的速度不是最及时的,毕竟**每次数据的写入和读取都需要经过用户态与内核态之间的拷贝过程。** 497 | 498 | **共享内存**可以解决消息队列通信中用户态与内核态之间数据拷贝过程带来的开销,**它直接分配一个共享空间,每个进程都可以直接访问**,就像访问进程自己的空间一样快捷方便,不需要陷入内核态或者系统调用,大大提高了通信的速度,享有**最快**的进程间通信方式之名。但是便捷高效的共享内存通信,**带来新的问题,多进程竞争同个共享资源会造成数据的错乱。** 499 | 500 | 那么,就需要**信号量**来保护共享资源,以确保任何时刻只能有一个进程访问共享资源,这种方式就是互斥访问。**信号量不仅可以实现访问的互斥性,还可以实现进程间的同步**,信号量其实是一个计数器,表示的是资源个数,其值可以通过两个原子操作来控制,分别是 **P 操作和 V 操作**。 501 | 502 | 与信号量名字很相似的叫**信号**,它俩名字虽然相似,但功能一点儿都不一样。信号是**异步通信机制**,信号可以在应用进程和内核之间直接交互,内核也可以利用信号来通知用户空间的进程发生了哪些系统事件,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令),一旦有信号发生,**进程有三种方式响应信号 1. 执行默认操作、2. 捕捉信号、3. 忽略信号**。有两个信号是应用进程无法捕捉和忽略的,即 `SIGKILL` 和 `SIGSTOP`,这是为了方便我们能在任何时候结束或停止某个进程。 503 | 504 | 前面说到的通信机制,都是工作于同一台主机,如果**要与不同主机的进程间通信,那么就需要 Socket 通信了**。Socket 实际上不仅用于不同的主机进程间通信,还可以用于本地主机进程间通信,可根据创建 Socket 的类型不同,分为三种常见的通信方式,一个是基于 TCP 协议的通信方式,一个是基于 UDP 协议的通信方式,一个是本地进程间通信方式。 505 | 506 | 以上,就是进程间通信的主要机制了。你可能会问了,那线程通信间的方式呢? 507 | 508 | 同个进程下的线程之间都是共享进程的资源,只要是共享变量都可以做到线程间通信,比如全局变量,所以对于线程间关注的不是通信方式,而是关注多线程竞争共享资源的问题,信号量也同样可以在线程间实现互斥与同步: 509 | 510 | - 互斥的方式,可保证任意时刻只有一个线程访问共享资源; 511 | - 同步的方式,可保证线程 A 应在线程 B 之前执行; 512 | 513 | # 多线程冲突了怎么办? 514 | 515 | ### 互斥的概念 516 | 517 | **竞争条件(\*race condition\*)**,当多线程相互竞争操作共享变量时,由于运气不好,即在执行过程中发生了上下文切换,我们得到了错误的结果,事实上,每次运行都可能得到不同的结果,因此输出的结果存在**不确定性(\*indeterminate\*)**。 518 | 519 | 由于多线程执行操作共享变量的这段代码可能会导致竞争状态,因此我们将此段代码称为**临界区(\*critical section\*),它是访问共享资源的代码片段,一定不能给多线程同时执行。** 520 | 521 | 我们希望这段代码是**互斥(\*mutualexclusion\*)的,也就说保证一个线程在临界区执行时,其他线程应该被阻止进入临界区**,说白了,就是这段代码执行过程中,最多只能出现一个线程。 522 | 523 | image-20230203165515837 524 | 525 | 另外,说一下互斥也并不是只针对多线程。在多进程竞争共享资源的时候,也同样是可以使用互斥的方式来避免资源竞争造成的资源混乱。 526 | 527 | ### 同步的概念 528 | 529 | 互斥解决了并发进程/线程对临界区的使用问题。这种基于临界区控制的交互作用是比较简单的,只要一个进程/线程进入了临界区,其他试图想进入临界区的进程/线程都会被阻塞着,直到第一个进程/线程离开了临界区。 530 | 531 | **所谓同步,就是并发进程/线程在一些关键点上可能需要互相等待与互通消息,这种相互制约的等待与互通信息称为进程/线程同步**。 532 | 533 | ## 互斥与同步的实现和使用 534 | 535 | 在进程/线程并发执行的过程中,进程/线程之间存在协作的关系,例如有互斥、同步的关系。 536 | 537 | 为了实现进程/线程间正确的协作,操作系统必须提供实现进程协作的措施和方法,主要的方法有两种: 538 | 539 | - *锁*:加锁、解锁操作; 540 | - *信号量*:P、V 操作; 541 | 542 | 这两个都可以方便地实现进程/线程互斥,而信号量比锁的功能更强一些,它还可以方便地实现进程/线程同步。 543 | 544 | ### 锁 545 | 546 | 使用加锁操作和解锁操作可以解决并发线程/进程的互斥问题。 547 | 548 | 任何想进入临界区的线程,必须先执行加锁操作。若加锁操作顺利通过,则线程可进入临界区;在完成对临界资源的访问后再执行解锁操作,以释放该临界资源。 549 | 550 | image-20230203170706761 551 | 552 | 根据锁的实现不同,可以分为「忙等待锁」和「无忙等待锁」。 553 | 554 | **「忙等待锁」** 555 | 556 | 当获取不到锁时,线程就会一直 while 循环,不做任何事情,所以就被称为「忙等待锁」,也被称为**自旋锁(\*spin lock\*)**。 557 | 558 | **「无等待锁」** 559 | 560 | 无等待锁顾明思议就是获取不到锁的时候,不用自旋。 561 | 562 | 既然不想自旋,那当没获取到锁的时候,就把当前线程放入到锁的等待队列,然后执行调度程序,把 CPU 让给其他线程执行。 563 | 564 | ### 信号量 565 | 566 | 信号量是操作系统提供的一种协调共享资源访问的方法。 567 | 568 | 通常**信号量表示资源的数量**,对应的变量是一个整型(`sem`)变量。 569 | 570 | 另外,还有**两个原子操作的系统调用函数来控制信号量的**,分别是: 571 | 572 | - *P 操作*:将 `sem` 减 `1`,相减后,如果 `sem < 0`,则进程/线程进入阻塞等待,否则继续,表明 P 操作可能会阻塞; 573 | - *V 操作*:将 `sem` 加 `1`,相加后,如果 `sem <= 0`,唤醒一个等待中的进程/线程,表明 V 操作不会阻塞; 574 | 575 | ### 生产者-消费者问题 576 | 577 | ![image-20230203213538415](操作系统八股.assets/image-20230203213538415.png) 578 | 579 | 生产者-消费者问题描述: 580 | 581 | - **生产者**在生成数据后,放在一个缓冲区中; 582 | - **消费者**从缓冲区取出数据处理; 583 | - 任何时刻,**只能有一个**生产者或消费者可以访问缓冲区; 584 | 585 | 我们对问题分析可以得出: 586 | 587 | - 任何时刻只能有一个线程操作缓冲区,说明操作缓冲区是临界代码,**需要互斥**; 588 | - 缓冲区空时,消费者必须等待生产者生成数据;缓冲区满时,生产者必须等待消费者取出数据。说明生产者和消费者**需要同步**。 589 | 590 | 那么我们需要三个信号量,分别是: 591 | 592 | - 互斥信号量 `mutex`:用于互斥访问缓冲区,初始化值为 1; 593 | - 资源信号量 `fullBuffers`:用于消费者询问缓冲区是否有数据,有数据则读取数据,初始化值为 0(表明缓冲区一开始为空); 594 | - 资源信号量 `emptyBuffers`:用于生产者询问缓冲区是否有空位,有空位则生成数据,初始化值为 n (缓冲区大小); 595 | 596 | 具体的实现代码: 597 | 598 | image-20230203213605267 599 | 600 | 如果消费者线程一开始执行 `P(fullBuffers)`,由于信号量 `fullBuffers` 初始值为 0,则此时 `fullBuffers` 的值从 0 变为 -1,说明缓冲区里没有数据,消费者只能等待。 601 | 602 | 接着,轮到生产者执行 `P(emptyBuffers)`,表示减少 1 个空槽,如果当前没有其他生产者线程在临界区执行代码,那么该生产者线程就可以把数据放到缓冲区,放完后,执行 `V(fullBuffers)` ,信号量 `fullBuffers` 从 -1 变成 0,表明有「消费者」线程正在阻塞等待数据,于是阻塞等待的消费者线程会被唤醒。 603 | 604 | 消费者线程被唤醒后,如果此时没有其他消费者线程在读数据,那么就可以直接进入临界区,从缓冲区读取数据。最后,离开临界区后,把空槽的个数 + 1。 605 | 606 | # 怎么避免死锁? 607 | 608 | 死锁只有**同时满足**以下四个条件才会发生: 609 | 610 | - 互斥条件; 611 | - 持有并等待条件; 612 | - 不可剥夺条件; 613 | - 环路等待条件; 614 | 615 | ### 互斥条件 616 | 617 | 互斥条件是指**多个线程不能同时使用同一个资源**。 618 | 619 | ### 持有并等待条件 620 | 621 | 持有并等待条件是指,当线程 A 已经持有了资源 1,又想申请资源 2,而资源 2 已经被线程 C 持有了,所以线程 A 就会处于等待状态,但是**线程 A 在等待资源 2 的同时并不会释放自己已经持有的资源 1**。 622 | 623 | ### 不可剥夺条件 624 | 625 | 不可剥夺条件是指,当线程已经持有了资源 ,**在自己使用完之前不能被其他线程获取**,线程 B 如果也想使用此资源,则只能在线程 A 使用完并释放后才能获取。 626 | 627 | ### 环路等待条件 628 | 629 | 环路等待条件指的是,在死锁发生的时候,**两个线程获取资源的顺序构成了环形链**。 630 | 631 | 比如,线程 A 已经持有资源 2,而想请求资源 1, 线程 B 已经获取了资源 1,而想请求资源 2,这就形成资源请求等待的环形图。 632 | 633 | ## 避免死锁问题的发生 634 | 635 | 前面我们提到,产生死锁的四个必要条件是:互斥条件、持有并等待条件、不可剥夺条件、环路等待条件。 636 | 637 | 那么避免死锁问题就只需要破环其中一个条件就可以,最常见的并且可行的就是**使用资源有序分配法,来破环环路等待条件**。 638 | 639 | 那什么是资源有序分配法呢? 640 | 641 | 线程 A 和 线程 B 获取资源的顺序要一样,当线程 A 是先尝试获取资源 A,然后尝试获取资源 B 的时候,线程 B 同样也是先尝试获取资源 A,然后尝试获取资源 B。也就是说,线程 A 和 线程 B 总是以相同的顺序申请自己想要的资源。 642 | 643 | 我们使用资源有序分配法的方式来修改前面发生死锁的代码,我们可以不改动线程 A 的代码。 644 | 645 | 我们先要清楚线程 A 获取资源的顺序,它是先获取互斥锁 A,然后获取互斥锁 B。 646 | 647 | 所以我们只需将线程 B 改成以相同顺序的获取资源,就可以打破死锁了。 648 | 649 | image-20230203220950140 650 | 651 | # 什么是悲观锁、乐观锁? 652 | 653 | ## 互斥锁与自旋锁 654 | 655 | 最底层的两种就是会「互斥锁和自旋锁」,有很多高级的锁都是基于它们实现的,你可以认为它们是各种锁的地基,所以我们必须清楚它俩之间的区别和应用。 656 | 657 | 加锁的目的就是保证共享资源在任意时间里,只有一个线程访问,这样就可以避免多线程导致共享数据错乱的问题。 658 | 659 | 当已经有一个线程加锁后,其他线程加锁则就会失败,互斥锁和自旋锁对于加锁失败后的处理方式是不一样的: 660 | 661 | - **互斥锁**加锁失败后,线程会**释放 CPU** ,给其他线程; 662 | - **自旋锁**加锁失败后,线程会**忙等待**,直到它拿到锁; 663 | 664 | 互斥锁是一种「独占锁」,比如当线程 A 加锁成功后,此时互斥锁已经被线程 A 独占了,只要线程 A 没有释放手中的锁,线程 B 加锁就会失败,于是就会释放 CPU 让给其他线程,**既然线程 B 释放掉了 CPU,自然线程 B 加锁的代码就会被阻塞**。 665 | 666 | **对于互斥锁加锁失败而阻塞的现象,是由操作系统内核实现的**。当加锁失败时,内核会将线程置为「睡眠」状态,等到锁被释放后,内核会在合适的时机唤醒线程,当这个线程成功获取到锁后,于是就可以继续执行。如下图: 667 | 668 | image-20230203221317632 669 | 670 | 自旋锁是通过 CPU 提供的 `CAS` 函数(*Compare And Swap*),在「用户态」完成加锁和解锁操作,不会主动产生线程上下文切换,所以相比互斥锁来说,会快一些,开销也小一些。 671 | 672 | 一般加锁的过程,包含两个步骤: 673 | 674 | - 第一步,查看锁的状态,如果锁是空闲的,则执行第二步; 675 | - 第二步,将锁设置为当前线程持有; 676 | 677 | CAS 函数就把这两个步骤合并成一条硬件级指令,形成**原子指令**,这样就保证了这两个步骤是不可分割的,要么一次性执行完两个步骤,要么两个步骤都不执行。 678 | 679 | 比如,设锁为变量 lock,整数 0 表示锁是空闲状态,整数 pid 表示线程 ID,那么 CAS(lock, 0, pid) 就表示自旋锁的加锁操作,CAS(lock, pid, 0) 则表示解锁操作。 680 | 681 | 使用自旋锁的时候,当发生多线程竞争锁的情况,加锁失败的线程会「忙等待」,直到它拿到锁。这里的「忙等待」可以用 `while` 循环等待实现,不过最好是使用 CPU 提供的 `PAUSE` 指令来实现「忙等待」,因为可以减少循环等待时的耗电量。 682 | 683 | 自旋锁是最比较简单的一种锁,一直自旋,利用 CPU 周期,直到锁可用。**需要注意,在单核 CPU 上,需要抢占式的调度器(即不断通过时钟中断一个线程,运行其他线程)。否则,自旋锁在单 CPU 上无法使用,因为一个自旋的线程永远不会放弃 CPU。** 684 | 685 | 自旋锁与互斥锁使用层面比较相似,但实现层面上完全不同:**当加锁失败时,互斥锁用「线程切换」来应对,自旋锁则用「忙等待」来应对**。 686 | 687 | ## 读写锁 688 | 689 | **读写锁适用于能明确区分读操作和写操作的场景**。 690 | 691 | 读写锁的工作原理是: 692 | 693 | - 当「写锁」没有被线程持有时,多个线程能够并发地持有读锁,这大大提高了共享资源的访问效率,因为「读锁」是用于读取共享资源的场景,所以多个线程同时持有读锁也不会破坏共享资源的数据。 694 | - 但是,一旦「写锁」被线程持有后,读线程的获取读锁的操作会被阻塞,而且其他写线程的获取写锁的操作也会被阻塞。 695 | 696 | ## 乐观锁与悲观锁 697 | 698 | 前面提到的互斥锁、自旋锁、读写锁,都是属于悲观锁。 699 | 700 | 悲观锁做事比较悲观,它认为**多线程同时修改共享资源的概率比较高,于是很容易出现冲突,所以访问共享资源前,先要上锁**。 701 | 702 | 那相反的,如果多线程同时修改共享资源的概率比较低,就可以采用乐观锁。 703 | 704 | 乐观锁做事比较乐观,它假定冲突的概率很低,它的工作方式是:**先修改完共享资源,再验证这段时间内有没有发生冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作**。 705 | 706 | 放弃后如何重试,这跟业务场景息息相关,虽然重试的成本很高,但是冲突的概率足够低的话,还是可以接受的。 707 | 708 | 可见,乐观锁的心态是,不管三七二十一,先改了资源再说。另外,你会发现**乐观锁全程并没有加锁,所以它也叫无锁编程**。 709 | 710 | 乐观锁虽然去除了加锁解锁的操作,但是一旦发生冲突,重试的成本非常高,所以**只有在冲突概率非常低,且加锁成本非常高的场景时,才考虑使用乐观锁。** 711 | 712 | ## 总结 713 | 714 | 开发过程中,最常见的就是互斥锁的了,互斥锁加锁失败时,会用「线程切换」来应对,当加锁失败的线程再次加锁成功后的这一过程,会有两次线程上下文切换的成本,性能损耗比较大。 715 | 716 | 如果我们明确知道被锁住的代码的执行时间很短,那我们应该选择开销比较小的自旋锁,因为自旋锁加锁失败时,并不会主动产生线程切换,而是一直忙等待,直到获取到锁,那么如果被锁住的代码执行时间很短,那这个忙等待的时间相对应也很短。 717 | 718 | 如果能区分读操作和写操作的场景,那读写锁就更合适了,它允许多个读线程可以同时持有读锁,提高了读的并发性。根据偏袒读方还是写方,可以分为读优先锁和写优先锁,读优先锁并发性很强,但是写线程会被饿死,而写优先锁会优先服务写线程,读线程也可能会被饿死,那为了避免饥饿的问题,于是就有了公平读写锁,它是用队列把请求锁的线程排队,并保证先入先出的原则来对线程加锁,这样便保证了某种线程不会被饿死,通用性也更好点。 719 | 720 | 互斥锁和自旋锁都是最基本的锁,读写锁可以根据场景来选择这两种锁其中的一个进行实现。 721 | 722 | 另外,互斥锁、自旋锁、读写锁都属于悲观锁,悲观锁认为并发访问共享资源时,冲突概率可能非常高,所以在访问共享资源前,都需要先加锁。 723 | 724 | 相反的,如果并发访问共享资源时,冲突概率非常低的话,就可以使用乐观锁,它的工作方式是,在访问共享资源时,不用先加锁,修改完共享资源后,再验证这段时间内有没有发生冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作。 725 | 726 | 但是,一旦冲突概率上升,就不适合使用乐观锁了,因为它解决冲突的重试成本非常高。 727 | 728 | 不管使用的哪种锁,我们的加锁的代码范围应该尽可能的小,也就是加锁的粒度要小,这样执行速度会比较快。再来,使用上了合适的锁,就会快上加快了。 729 | 730 | 例题: 731 | 732 | > CAS 不是乐观锁吗,为什么基于 CAS 实现的自旋锁是悲观锁? 733 | 734 | 乐观锁是先修改同步资源,再验证有没有发生冲突。 735 | 736 | 悲观锁是修改共享数据前,都要先加锁,防止竞争。 737 | 738 | CAS 是乐观锁没错,但是 CAS 和自旋锁不同之处,自旋锁基于 CAS 加了while 或者睡眠 CPU 的操作而产生自旋的效果,加锁失败会忙等待直到拿到锁,自旋锁是要需要事先拿到锁才能修改数据的,所以算悲观锁。 739 | 740 | # 进程调度/页面置换/磁盘调度算法 741 | 742 | ## 进程调度算法 743 | 744 | 进程调度算法也称 CPU 调度算法,毕竟进程是由 CPU 调度的。 745 | 746 | 当 CPU 空闲时,操作系统就选择内存中的某个「就绪状态」的进程,并给其分配 CPU。 747 | 748 | 什么时候会发生 CPU 调度呢?通常有以下情况: 749 | 750 | 1. 当进程从运行状态转到等待状态; 751 | 2. 当进程从运行状态转到就绪状态; 752 | 3. 当进程从等待状态转到就绪状态; 753 | 4. 当进程从运行状态转到终止状态; 754 | 755 | 其中发生在 1 和 4 两种情况下的调度称为「非抢占式调度」,2 和 3 两种情况下发生的调度称为「抢占式调度」。 756 | 757 | 非抢占式的意思就是,当进程正在运行时,它就会一直运行,直到该进程完成或发生某个事件而被阻塞时,才会把 CPU 让给其他进程。 758 | 759 | 而抢占式调度,顾名思义就是进程正在运行的时,可以被打断,使其把 CPU 让给其他进程。那抢占的原则一般有三种,分别是时间片原则、优先权原则、短作业优先原则。 760 | 761 | 接下来,说说常见的调度算法: 762 | 763 | - 先来先服务调度算法 764 | - 最短作业优先调度算法 765 | - 高响应比优先调度算法 766 | - 时间片轮转调度算法 767 | - 最高优先级调度算法 768 | - 多级反馈队列调度算法 769 | 770 | ## 内存页面置换算法 771 | 772 | 在了解内存页面置换算法前,我们得先谈一下**缺页异常(缺页中断)**。 773 | 774 | 当 CPU 访问的页面不在物理内存时,便会产生一个缺页中断,请求操作系统将所缺页调入到物理内存。那它与一般中断的主要区别在于: 775 | 776 | - 缺页中断在指令执行「期间」产生和处理中断信号,而一般中断在一条指令执行「完成」后检查和处理中断信号。 777 | - 缺页中断返回到该指令的开始重新执行「该指令」,而一般中断返回回到该指令的「下一个指令」执行。 778 | 779 | 我们来看一下缺页中断的处理流程,如下图: 780 | 781 | image-20230204142634408 782 | 783 | 1. 在 CPU 里访问一条 Load M 指令,然后 CPU 会去找 M 所对应的页表项。 784 | 2. 如果该页表项的状态位是「有效的」,那 CPU 就可以直接去访问物理内存了,如果状态位是「无效的」,则 CPU 则会发送缺页中断请求。 785 | 3. 操作系统收到了缺页中断,则会执行缺页中断处理函数,先会查找该页面在磁盘中的页面的位置。 786 | 4. 找到磁盘中对应的页面后,需要把该页面换入到物理内存中,但是在换入前,需要在物理内存中找空闲页,如果找到空闲页,就把页面换入到物理内存中。 787 | 5. 页面从磁盘换入到物理内存完成后,则把页表项中的状态位修改为「有效的」。 788 | 6. 最后,CPU 重新执行导致缺页异常的指令。 789 | 790 | 上面所说的过程,第 4 步是能在物理内存找到空闲页的情况,那如果找不到呢? 791 | 792 | 找不到空闲页的话,就说明此时内存已满了,这时候,就需要「页面置换算法」选择一个物理页,如果该物理页有被修改过(脏页),则把它换出到磁盘,然后把该被置换出去的页表项的状态改成「无效的」,最后把正在访问的页面装入到这个物理页中。 793 | 794 | 这里提一下,页表项通常有如下图的字段: 795 | 796 | image-20230204142754599 797 | 798 | 那其中: 799 | 800 | - *状态位*:用于表示该页是否有效,也就是说是否在物理内存中,供程序访问时参考。 801 | - *访问字段*:用于记录该页在一段时间被访问的次数,供页面置换算法选择出页面时参考。 802 | - *修改位*:表示该页在调入内存后是否有被修改过,由于内存中的每一页都在磁盘上保留一份副本,因此,如果没有修改,在置换该页时就不需要将该页写回到磁盘上,以减少系统的开销;如果已经被修改,则将该页重写到磁盘上,以保证磁盘中所保留的始终是最新的副本。 803 | - *硬盘地址*:用于指出该页在硬盘上的地址,通常是物理块号,供调入该页时使用。 804 | 805 | 这里我整理了虚拟内存的管理整个流程,你可以从下面这张图看到: 806 | 807 | image-20230204143313238 808 | 809 | 常见的页面置换算法有如下几种: 810 | 811 | - 最佳页面置换算法(*OPT*) 812 | - 先进先出置换算法(*FIFO*) 813 | - 最近最久未使用的置换算法(*LRU*) 814 | - 时钟页面置换算法(*Lock*) 815 | - 最不常用置换算法(*LFU*) 816 | 817 | ### 最佳页面置换算法 818 | 819 | 最佳页面置换算法基本思路是,**置换在「未来」最长时间不访问的页面**。 820 | 821 | 所以,该算法实现需要计算内存中每个逻辑页面的「下一次」访问时间,然后比较,选择未来最长时间不访问的页面。 822 | 823 | 我们举个例子,假设一开始有 3 个空闲的物理页,然后有请求的页面序列,那它的置换过程如下图: 824 | 825 | ![image-20230204143408791](操作系统八股.assets/image-20230204143408791.png) 826 | 827 | 在这个请求的页面序列中,缺页共发生了 `7` 次(空闲页换入 3 次 + 最优页面置换 4 次),页面置换共发生了 `4` 次。 828 | 829 | 这很理想,但是实际系统中无法实现,因为程序访问页面时是动态的,我们是无法预知每个页面在「下一次」访问前的等待时间。 830 | 831 | 所以,最佳页面置换算法作用是为了衡量你的算法的效率,你的算法效率越接近该算法的效率,那么说明你的算法是高效的。 832 | 833 | ### 先进先出置换算法 834 | 835 | 既然我们无法预知页面在下一次访问前所需的等待时间,那我们可以**选择在内存驻留时间很长的页面进行中置换**,这个就是「先进先出置换」算法的思想。 836 | 837 | 还是以前面的请求的页面序列作为例子,假设使用先进先出置换算法,则过程如下图: 838 | 839 | ![image-20230204143434295](操作系统八股.assets/image-20230204143434295.png) 840 | 841 | 在这个请求的页面序列中,缺页共发生了 `10` 次,页面置换共发生了 `7` 次,跟最佳页面置换算法比较起来,性能明显差了很多。 842 | 843 | ### 最近最久未使用的置换算法 844 | 845 | 最近最久未使用(*LRU*)的置换算法的基本思路是,发生缺页时,**选择最长时间没有被访问的页面进行置换**,也就是说,该算法假设已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用。 846 | 847 | 这种算法近似最优置换算法,最优置换算法是通过「未来」的使用情况来推测要淘汰的页面,而 LRU 则是通过「历史」的使用情况来推测要淘汰的页面。 848 | 849 | 还是以前面的请求的页面序列作为例子,假设使用最近最久未使用的置换算法,则过程如下图: 850 | 851 | ![image-20230204143453279](操作系统八股.assets/image-20230204143453279.png) 852 | 853 | 在这个请求的页面序列中,缺页共发生了 `9` 次,页面置换共发生了 `6` 次,跟先进先出置换算法比较起来,性能提高了一些。 854 | 855 | 虽然 LRU 在理论上是可以实现的,但代价很高。为了完全实现 LRU,需要在内存中维护一个所有页面的链表,最近最多使用的页面在表头,最近最少使用的页面在表尾。 856 | 857 | 困难的是,在每次访问内存时都必须要更新「整个链表」。在链表中找到一个页面,删除它,然后把它移动到表头是一个非常费时的操作。 858 | 859 | 所以,LRU 虽然看上去不错,但是由于开销比较大,实际应用中比较少使用。 860 | 861 | ### 时钟页面置换算法 862 | 863 | 那有没有一种即能优化置换的次数,也能方便实现的算法呢? 864 | 865 | 时钟页面置换算法就可以两者兼得,它跟 LRU 近似,又是对 FIFO 的一种改进。 866 | 867 | 该算法的思路是,把所有的页面都保存在一个类似钟面的「环形链表」中,一个表针指向最老的页面。 868 | 869 | 当发生缺页中断时,算法首先检查表针指向的页面: 870 | 871 | - 如果它的访问位位是 0 就淘汰该页面,并把新的页面插入这个位置,然后把表针前移一个位置; 872 | - 如果访问位是 1 就清除访问位,并把表针前移一个位置,重复这个过程直到找到了一个访问位为 0 的页面为止; 873 | 874 | 我画了一副时钟页面置换算法的工作流程图,你可以在下方看到: 875 | 876 | image-20230204143521125 877 | 878 | 了解了这个算法的工作方式,就明白为什么它被称为时钟(*Clock*)算法了。 879 | 880 | ### 最不常用算法 881 | 882 | 最不常用(*LFU*)算法,这名字听起来很调皮,但是它的意思不是指这个算法不常用,而是**当发生缺页中断时,选择「访问次数」最少的那个页面,并将其淘汰**。 883 | 884 | 它的实现方式是,对每个页面设置一个「访问计数器」,每当一个页面被访问时,该页面的访问计数器就累加 1。在发生缺页中断时,淘汰计数器值最小的那个页面。 885 | 886 | 看起来很简单,每个页面加一个计数器就可以实现了,但是在操作系统中实现的时候,我们需要考虑效率和硬件成本的。 887 | 888 | 要增加一个计数器来实现,这个硬件成本是比较高的,另外如果要对这个计数器查找哪个页面访问次数最小,查找链表本身,如果链表长度很大,是非常耗时的,效率不高。 889 | 890 | 但还有个问题,LFU 算法只考虑了频率问题,没考虑时间的问题,比如有些页面在过去时间里访问的频率很高,但是现在已经没有访问了,而当前频繁访问的页面由于没有这些页面访问的次数高,在发生缺页中断时,就会可能会误伤当前刚开始频繁访问,但访问次数还不高的页面。 891 | 892 | 那这个问题的解决的办法还是有的,可以定期减少访问的次数,比如当发生时间中断时,把过去时间访问的页面的访问次数除以 2,也就说,随着时间的流失,以前的高访问次数的页面会慢慢减少,相当于加大了被置换的概率。 893 | 894 | ## 磁盘调度算法 895 | 896 | 常见的磁盘调度算法有: 897 | 898 | - 先来先服务算法 899 | - 最短寻道时间优先算法 900 | - 扫描算法 901 | - 循环扫描算法 902 | - LOOK 与 C-LOOK 算法 903 | 904 | ### 先来先服务 905 | 906 | 先来先服务(*First-Come,First-Served,FCFS*),顾名思义,先到来的请求,先被服务。 907 | 908 | 那按照这个序列的话: 909 | 910 | 98,183,37,122,14,124,65,67 911 | 912 | 那么,磁盘的写入顺序是从左到右,如下图: 913 | 914 | ![image-20230204143644032](操作系统八股.assets/image-20230204143644032.png) 915 | 916 | 先来先服务算法总共移动了 `640` 个磁道的距离,这么一看这种算法,比较简单粗暴,但是如果大量进程竞争使用磁盘,请求访问的磁道可能会很分散,那先来先服务算法在性能上就会显得很差,因为寻道时间过长。 917 | 918 | ### 最短寻道时间优先 919 | 920 | 最短寻道时间优先(*Shortest Seek First,SSF*)算法的工作方式是,优先选择从当前磁头位置所需寻道时间最短的请求,还是以这个序列为例子: 921 | 922 | 98,183,37,122,14,124,65,67 923 | 924 | 那么,那么根据距离磁头( 53 位置)最近的请求的算法,具体的请求则会是下列从左到右的顺序: 925 | 926 | 65,67,37,14,98,122,124,183 927 | 928 | ![image-20230204143656785](操作系统八股.assets/image-20230204143656785.png) 929 | 930 | 磁头移动的总距离是 `236` 磁道,相比先来先服务性能提高了不少。 931 | 932 | 但这个算法可能存在某些请求的**饥饿**,因为本次例子我们是静态的序列,看不出问题,假设是一个动态的请求,如果后续来的请求都是小于 183 磁道的,那么 183 磁道可能永远不会被响应,于是就产生了饥饿现象,这里**产生饥饿的原因是磁头在一小块区域来回移动**。 933 | 934 | ### 扫描算法 935 | 936 | 最短寻道时间优先算法会产生饥饿的原因在于:磁头有可能再一个小区域内来回得移动。 937 | 938 | 为了防止这个问题,可以规定:**磁头在一个方向上移动,访问所有未完成的请求,直到磁头到达该方向上的最后的磁道,才调换方向,这就是扫描(\*Scan\*)算法**。 939 | 940 | 这种算法也叫做电梯算法,比如电梯保持按一个方向移动,直到在那个方向上没有请求为止,然后改变方向。 941 | 942 | 还是以这个序列为例子,磁头的初始位置是 53: 943 | 944 | 98,183,37,122,14,124,65,67 945 | 946 | 那么,假设扫描调度算先朝磁道号减少的方向移动,具体请求则会是下列从左到右的顺序: 947 | 948 | 37,14,`0`,65,67,98,122,124,183 949 | 950 | ![image-20230204143718506](操作系统八股.assets/image-20230204143718506.png) 951 | 952 | 磁头先响应左边的请求,直到到达最左端( 0 磁道)后,才开始反向移动,响应右边的请求。 953 | 954 | 扫描调度算法性能较好,不会产生饥饿现象,但是存在这样的问题,中间部分的磁道会比较占便宜,中间部分相比其他部分响应的频率会比较多,也就是说每个磁道的响应频率存在差异。 955 | 956 | ### 循环扫描算法 957 | 958 | 扫描算法使得每个磁道响应的频率存在差异,那么要优化这个问题的话,可以总是按相同的方向进行扫描,使得每个磁道的响应频率基本一致。 959 | 960 | 循环扫描(*Circular Scan, CSCAN* )规定:只有磁头朝某个特定方向移动时,才处理磁道访问请求,而返回时直接快速移动至最靠边缘的磁道,也就是复位磁头,这个过程是很快的,并且**返回中途不处理任何请求**,该算法的特点,就是**磁道只响应一个方向上的请求**。 961 | 962 | 还是以这个序列为例子,磁头的初始位置是 53: 963 | 964 | 98,183,37,122,14,124,65,67 965 | 966 | 那么,假设循环扫描调度算先朝磁道增加的方向移动,具体请求会是下列从左到右的顺序: 967 | 968 | 65,67,98,122,124,183,`199`,`0`,14,37 969 | 970 | ![image-20230204143807163](操作系统八股.assets/image-20230204143807163.png) 971 | 972 | 磁头先响应了右边的请求,直到碰到了最右端的磁道 199,就立即回到磁盘的开始处(磁道 0),但这个返回的途中是不响应任何请求的,直到到达最开始的磁道后,才继续顺序响应右边的请求。 973 | 974 | 循环扫描算法相比于扫描算法,对于各个位置磁道响应频率相对比较平均。 975 | 976 | ### LOOK 与 C-LOOK算法 977 | 978 | 我们前面说到的扫描算法和循环扫描算法,都是磁头移动到磁盘「最始端或最末端」才开始调换方向。 979 | 980 | 那这其实是可以优化的,优化的思路就是**磁头在移动到「最远的请求」位置,然后立即反向移动。** 981 | 982 | 那针对 SCAN 算法的优化则叫 LOOK 算法,它的工作方式,磁头在每个方向上仅仅移动到最远的请求位置,然后立即反向移动,而不需要移动到磁盘的最始端或最末端,**反向移动的途中会响应请求**。 983 | 984 | ![image-20230204143820697](操作系统八股.assets/image-20230204143820697.png) 985 | 986 | 而针 C-SCAN 算法的优化则叫 C-LOOK,它的工作方式,磁头在每个方向上仅仅移动到最远的请求位置,然后立即反向移动,而不需要移动到磁盘的最始端或最末端,**反向移动的途中不会响应请求**。 987 | 988 | ![image-20230204143830688](操作系统八股.assets/image-20230204143830688.png) 989 | 990 | # 文件系统全家桶 991 | 992 | ## 文件系统的基本组成 993 | 994 | Linux 文件系统会为每个文件分配两个数据结构:**索引节点(\*index node\*)和目录项(\*directory entry\*)**,它们主要用来记录文件的元信息和目录层次结构。 995 | 996 | - 索引节点,也就是 *inode*,用来记录文件的元信息,比如 inode 编号、文件大小、访问权限、创建时间、修改时间、**数据在磁盘的位置**等等。索引节点是文件的**唯一**标识,它们之间一一对应,也同样都会被存储在硬盘中,所以**索引节点同样占用磁盘空间**。 997 | - 目录项,也就是 *dentry*,用来记录文件的名字、**索引节点指针**以及与其他目录项的层级关联关系。多个目录项关联起来,就会形成目录结构,但它与索引节点不同的是,**目录项是由内核维护的一个数据结构,不存放于磁盘,而是缓存在内存**。 -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126145202123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126145202123.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126145220689.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126145220689.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126145614195.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126145614195.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126145639682.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126145639682.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126145724420.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126145724420.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126145804277.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126145804277.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126145919764.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126145919764.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126150006442.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126150006442.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126150247839.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126150247839.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126150736494.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126150736494.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126150853404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126150853404.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126150950542.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126150950542.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151036131.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151036131.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151059580.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151059580.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151116758.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151116758.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151132578.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151132578.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151144335.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151144335.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151228887.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151228887.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151307696.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151307696.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151345159.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151345159.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151359357.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151359357.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126151831836.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126151831836.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126162417520.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126162417520.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126171413301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126171413301.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126171450973.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126171450973.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126190019529.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126190019529.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126190132484.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126190132484.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126190420778.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126190420778.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126190513685.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126190513685.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126190808303.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126190808303.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126190941783.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126190941783.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126192500961.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126192500961.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230126192814076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230126192814076.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201140944524.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201140944524.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201141056876.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201141056876.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201141210092.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201141210092.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201145201966.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201145201966.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201163118587.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201163118587.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201201111914.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201201111914.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201201153169.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201201153169.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201201504557.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201201504557.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201201611677.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201201611677.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201232550574.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201232550574.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201233607275.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201233607275.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230201233724355.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230201233724355.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202001455580.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202001455580.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202141157456.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202141157456.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202141230025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202141230025.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202141330954.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202141330954.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202141530611.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202141530611.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202142808507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202142808507.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202142907076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202142907076.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202142925512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202142925512.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202142943183.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202142943183.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202143914913.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202143914913.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202144053005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202144053005.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202144156005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202144156005.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202150851545.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202150851545.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202151753926.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202151753926.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202151927972.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202151927972.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202151940614.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202151940614.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152017320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152017320.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152027864.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152027864.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152123506.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152123506.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152324041.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152324041.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152332562.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152332562.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152354957.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152354957.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152411431.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152411431.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152717672.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152717672.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202152818780.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202152818780.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202153827325.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202153827325.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202153920169.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202153920169.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202153945231.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202153945231.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202154024196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202154024196.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202154234836.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202154234836.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202155810856.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202155810856.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202155838360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202155838360.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202160019143.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202160019143.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202160146438.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202160146438.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202210614068.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202210614068.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202212457439.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202212457439.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202212640247.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202212640247.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202212656485.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202212656485.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202212840841.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202212840841.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202213116722.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202213116722.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202213138742.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202213138742.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202215911544.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202215911544.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202220026412.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202220026412.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202220049827.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202220049827.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202220236535.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202220236535.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202222440843.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202222440843.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202222526258.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202222526258.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202222709619.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202222709619.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202222820668.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202222820668.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202222843196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202222843196.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202224150400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202224150400.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202224211893.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202224211893.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202224246159.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202224246159.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202225809040.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202225809040.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202233949212.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202233949212.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202234005218.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202234005218.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202234046383.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202234046383.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.assets/image-20230202234202774.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinglin/JavaInterview/95d03cae493ad5f5ee76392868eaa2a30ec9af83/计算机网络问题整理/计网八股.assets/image-20230202234202774.png -------------------------------------------------------------------------------- /计算机网络问题整理/计网八股.md: -------------------------------------------------------------------------------- 1 | 参考内容来自:https://xiaolincoding.com/network/ 2 | 3 | ## 网络模型 4 | 5 | 为了使得多种设备能通过网络相互通信,和为了解决各种不同设备在网络互联中的兼容性问题,国际标准化组织制定了开放式系统互联通信参考模型(*Open System Interconnection Reference Model*),也就是 OSI 网络模型,该模型主要有 7 层,分别是应用层、表示层、会话层、传输层、网络层、数据链路层以及物理层。 6 | 7 | 每一层负责的职能都不同,如下: 8 | 9 | - 应用层,负责给应用程序提供统一的接口; 10 | - 表示层,负责把数据转换成兼容另一个系统能识别的格式; 11 | - 会话层,负责建立、管理和终止表示层实体之间的通信会话; 12 | - 传输层,负责端到端的数据传输; 13 | - 网络层,负责数据的路由、转发、分片; 14 | - 数据链路层,负责数据的封帧和差错检测,以及 MAC 寻址; 15 | - 物理层,负责在物理网络中传输数据帧; 16 | 17 | TCP/IP 网络模型共有 4 层,分别是应用层、传输层、网络层和网络接口层,每一层负责的职能如下: 18 | 19 | - 应用层,负责向用户提供一组应用程序,比如 HTTP、DNS、FTP 等; 20 | - 传输层,负责端到端的通信,比如 TCP、UDP 等; 21 | - 网络层,负责网络包的封装、分片、路由、转发,比如 IP、ICMP 等; 22 | - 网络接口层,负责网络包在物理网络中的传输,比如网络包的封帧、 MAC 寻址、差错检测,以及通过网卡传输网络帧等; 23 | 24 | TCP/IP 网络模型相比 OSI 网络模型简化了不少,也更加易记,它们之间的关系如下图: 25 | 26 | image-20230126150247839 27 | 28 | ## GET 与 POST 29 | 30 | ### GET 和 POST 有什么区别? 31 | 32 | 根据 RFC 规范,**GET 的语义是从服务器获取指定的资源**,这个资源可以是静态的文本、页面、图片视频等。GET 请求的参数位置一般是写在 URL 中,URL 规定只能支持 ASCII,所以 GET 请求的参数只允许 ASCII 字符 ,而且浏览器会对 URL 的长度有限制(HTTP协议本身对 URL长度并没有做任何规定)。 33 | 34 | 根据 RFC 规范,**POST 的语义是根据请求负荷(报文body)对指定的资源做出处理**,具体的处理方式视资源类型而不同。POST 请求携带数据的位置一般是写在报文 body 中,body 中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对 body 大小做限制。 35 | 36 | GET 和 POST 还有一个重大区别,简单的说:GET产生一个TCP数据包;POST产生两个TCP数据包。长的说:对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据)而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。 37 | 38 | ## HTTP 与 HTTPS 39 | 40 | ### HTTP 与 HTTPS 有哪些区别? 41 | 42 | - HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。 43 | - HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。 44 | - 两者的默认端口不一样,HTTP 默认端口号是 80,HTTPS 默认端口号是 443。 45 | - HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。 46 | 47 | ## HTTP/1.1、HTTP/2、HTTP/3 演变 48 | 49 | ### HTTP/1.1 相比 HTTP/1.0 提高了什么性能? 50 | 51 | HTTP/1.1 相比 HTTP/1.0 性能上的改进: 52 | 53 | - 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。 54 | - 支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。 55 | 56 | 但 HTTP/1.1 还是有性能瓶颈: 57 | 58 | - 请求 / 响应头部(Header)未经压缩就发送,首部信息越多延迟越大。只能压缩 `Body` 的部分; 59 | - 发送冗长的首部。每次互相发送相同的首部造成的浪费较多; 60 | - 服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是队头阻塞; 61 | - 没有请求优先级控制; 62 | - 请求只能从客户端开始,服务器只能被动响应。 63 | 64 | ### HTTP/2 做了什么优化? 65 | 66 | HTTP/2 协议是基于 HTTPS 的,所以 HTTP/2 的安全性也是有保障的。 67 | 68 | ![image-20230126150736494](计网八股.assets/image-20230126150736494.png) 69 | 70 | 那 HTTP/2 相比 HTTP/1.1 性能上的改进: 71 | 72 | - 头部压缩 73 | - 二进制格式 74 | - 并发传输 75 | - 服务器主动推送资源 76 | 77 | *1. 头部压缩* 78 | 79 | HTTP/2 会**压缩头**(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你**消除重复的部分**。 80 | 81 | 这就是所谓的 `HPACK` 算法:在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就**提高速度**了。 82 | 83 | *2. 二进制格式* 84 | 85 | HTTP/2 不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了**二进制格式**,头信息和数据体都是二进制,并且统称为帧(frame):**头信息帧(Headers Frame)和数据帧(Data Frame)**。 86 | 87 | image-20230126150853404 88 | 89 | *3. 并发传输* 90 | 91 | 我们都知道 HTTP/1.1 的实现是基于请求-响应模型的。同一个连接中,HTTP 完成一个事务(请求与响应),才能处理下一个事务,也就是说在发出请求等待响应的过程中,是没办法做其他事情的,如果响应迟迟不来,那么后续的请求是无法发送的,也造成了**队头阻塞**的问题。 92 | 93 | 而 HTTP/2 就很牛逼了,引出了 Stream 概念,多个 Stream 复用在一条 TCP 连接。 94 | 95 | image-20230126150950542 96 | 97 | 从上图可以看到,1 个 TCP 连接包含多个 Stream,Stream 里可以包含 1 个或多个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成。Message 里包含一条或者多个 Frame,Frame 是 HTTP/2 最小单位,以二进制压缩格式存放 HTTP/1 中的内容(头部和包体)。 98 | 99 | **针对不同的 HTTP 请求用独一无二的 Stream ID 来区分,接收端可以通过 Stream ID 有序组装成 HTTP 消息,不同 Stream 的帧是可以乱序发送的,因此可以并发不同的 Stream ,也就是 HTTP/2 可以并行交错地发送请求和响应**。 100 | 101 | 比如下图,服务端**并行交错地**发送了两个响应: Stream 1 和 Stream 3,这两个 Stream 都是跑在一个 TCP 连接上,客户端收到后,会根据相同的 Stream ID 有序组装成 HTTP 消息。 102 | 103 | ![image-20230126151036131](计网八股.assets/image-20230126151036131.png) 104 | 105 | *4、服务器推送* 106 | 107 | HTTP/2 还在一定程度上改善了传统的「请求 - 应答」工作模式,服务端不再是被动地响应,可以**主动**向客户端发送消息。 108 | 109 | 客户端和服务器**双方都可以建立 Stream**, Stream ID 也是有区别的,客户端建立的 Stream 必须是奇数号,而服务器建立的 Stream 必须是偶数号。 110 | 111 | 比如下图,Stream 1 是客户端向服务端请求的资源,属于客户端建立的 Stream,所以该 Stream 的 ID 是奇数(数字 1);Stream 2 和 4 都是服务端主动向客户端推送的资源,属于服务端建立的 Stream,所以这两个 Stream 的 ID 是偶数(数字 2 和 4)。 112 | 113 | image-20230126151059580 114 | 115 | 再比如,客户端通过 HTTP/1.1 请求从服务器那获取到了 HTML 文件,而 HTML 可能还需要依赖 CSS 来渲染页面,这时客户端还要再发起获取 CSS 文件的请求,需要两次消息往返,如下图左边部分: 116 | 117 | ![image-20230126151116758](计网八股.assets/image-20230126151116758.png) 118 | 119 | 如上图右边部分,在 HTTP/2 中,客户端在访问 HTML 时,服务器可以直接主动推送 CSS 文件,减少了消息传递的次数。 120 | 121 | > HTTP/2 有什么缺陷? 122 | 123 | HTTP/2 通过 Stream 的并发能力,解决了 HTTP/1 队头阻塞的问题,看似很完美了,但是 HTTP/2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP 这一层面,而是在 TCP 这一层。 124 | 125 | **HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。** 126 | 127 | image-20230126151132578 128 | 129 | 举个例子,如下图: 130 | 131 | image-20230126151144335 132 | 133 | 图中发送方发送了很多个 packet,每个 packet 都有自己的序号,你可以认为是 TCP 的序列号,其中 packet 3 在网络中丢失了,即使 packet 4-6 被接收方收到后,由于内核中的 TCP 数据不是连续的,于是接收方的应用层就无法从内核中读取到,只有等到 packet 3 重传后,接收方的应用层才可以从内核中读取到数据,这就是 HTTP/2 的队头阻塞问题,是在 TCP 层面发生的。 134 | 135 | 所以,一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的**所有的 HTTP 请求都必须等待这个丢了的包被重传回来**。 136 | 137 | ### HTTP/3 做了哪些优化? 138 | 139 | 前面我们知道了 HTTP/1.1 和 HTTP/2 都有队头阻塞的问题: 140 | 141 | - HTTP/1.1 中的管道( pipeline)虽然解决了请求的队头阻塞,但是**没有解决响应的队头阻塞**,因为服务端需要按顺序响应收到的请求,如果服务端处理某个请求消耗的时间比较长,那么只能等响应完这个请求后, 才能处理下一个请求,这属于 HTTP 层队头阻塞。 142 | - HTTP/2 虽然通过多个请求复用一个 TCP 连接解决了 HTTP 的队头阻塞 ,但是**一旦发生丢包,就会阻塞住所有的 HTTP 请求**,这属于 TCP 层队头阻塞。 143 | 144 | HTTP/2 队头阻塞的问题是因为 TCP,所以 **HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!** 145 | 146 | image-20230126151228887 147 | 148 | UDP 发送是不管顺序,也不管丢包的,所以不会出现像 HTTP/2 队头阻塞的问题。大家都知道 UDP 是不可靠传输的,但基于 UDP 的 **QUIC 协议** 可以实现类似 TCP 的可靠性传输。 149 | 150 | QUIC 有以下 3 个特点。 151 | 152 | - 无队头阻塞 153 | - 更快的连接建立 154 | - 连接迁移 155 | 156 | *1、无队头阻塞* 157 | 158 | QUIC 协议也有类似 HTTP/2 Stream 与多路复用的概念,也是可以在同一条连接上并发传输多个 Stream,Stream 可以认为就是一条 HTTP 请求。 159 | 160 | QUIC 有自己的一套机制可以保证传输的可靠性的。**当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响,因此不存在队头阻塞问题**。这与 HTTP/2 不同,HTTP/2 只要某个流中的数据包丢失了,其他流也会因此受影响。 161 | 162 | 所以,QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响。 163 | 164 | ![image-20230126151307696](计网八股.assets/image-20230126151307696.png) 165 | 166 | *2、更快的连接建立* 167 | 168 | 对于 HTTP/1 和 HTTP/2 协议,TCP 和 TLS 是分层的,分别属于内核实现的传输层、openssl 库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先 TCP 握手,再 TLS 握手。 169 | 170 | HTTP/3 在传输数据前虽然需要 QUIC 协议握手,但是这个握手过程只需要 1 RTT,握手的目的是为确认双方的「连接 ID」,连接迁移就是基于连接 ID 实现的。 171 | 172 | 但是 HTTP/3 的 QUIC 协议并不是与 TLS 分层,而是 QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的“记录”,再加上 QUIC 使用的是 TLS/1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商,如下图: 173 | 174 | ![image-20230126151345159](计网八股.assets/image-20230126151345159.png) 175 | 176 | 甚至,在第二次连接的时候,应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送,达到 0-RTT 的效果。 177 | 178 | 如下图右边部分,HTTP/3 当会话恢复时,有效负载数据与第一个数据包一起发送,可以做到 0-RTT(下图的右下角): 179 | 180 | image-20230126151359357 181 | 182 | *3、连接迁移* 183 | 184 | 基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。 185 | 186 | image-20230126151831836 187 | 188 | 那么**当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立连接**。而建立连接的过程包含 TCP 三次握手和 TLS 四次握手的时延,以及 TCP 慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。 189 | 190 | 而 QUIC 协议没有用四元组的方式来“绑定”连接,而是通过**连接 ID** 来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了**连接迁移**的功能。 191 | 192 | 所以, QUIC 是一个在 UDP 之上的**伪** TCP + TLS + HTTP/2 的多路复用的协议。 193 | 194 | QUIC 是新协议,对于很多网络设备,根本不知道什么是 QUIC,只会当做 UDP,这样会出现新的问题,因为有的网络设备是会丢掉 UDP 包的,而 QUIC 是基于 UDP 实现的,那么如果网络设备无法识别这个是 QUIC 包,那么就会当作 UDP包,然后被丢弃。 195 | 196 | HTTP/3 现在普及的进度非常的缓慢,不知道未来 UDP 是否能够逆袭 TCP。 197 | 198 | ## TCP 连接建立 199 | 200 | ### TCP 三次握手过程是怎样的? 201 | 202 | TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而**建立连接是通过三次握手来进行的**。三次握手的过程如下图: 203 | 204 | image-20230126145202123 205 | 206 | - 一开始,客户端和服务端都处于 `CLOSE` 状态。先是服务端主动监听某个端口,处于 `LISTEN` 状态 207 | 208 | image-20230126145220689 209 | 210 | - 客户端会随机初始化序号(`client_isn`),将此序号置于 TCP 首部的「序号」字段中,同时把 `SYN` 标志位置为 `1`,表示 `SYN` 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 `SYN-SENT` 状态。 211 | 212 | image-20230126145614195 213 | 214 | - 服务端收到客户端的 `SYN` 报文后,首先服务端也随机初始化自己的序号(`server_isn`),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 `client_isn + 1`, 接着把 `SYN` 和 `ACK` 标志位置为 `1`。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 `SYN-RCVD` 状态。 215 | 216 | image-20230126145639682 217 | 218 | - 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 `ACK` 标志位置为 `1` ,其次「确认应答号」字段填入 `server_isn + 1` ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 `ESTABLISHED` 状态。 219 | - 服务端收到客户端的应答报文后,也进入 `ESTABLISHED` 状态。 220 | 221 | 从上面的过程可以发现**第三次握手是可以携带数据的,前两次握手是不可以携带数据的**,这也是面试常问的题。 222 | 223 | 一旦完成三次握手,双方都处于 `ESTABLISHED` 状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。 224 | 225 | ### 为什么是三次握手?不是两次、四次? 226 | 227 | 相信大家比较常回答的是:“因为三次握手才能保证双方具有接收和发送的能力。” 228 | 229 | 这回答是没问题,但这回答是片面的,并没有说出主要的原因。 230 | 231 | 在前面我们知道了什么是 **TCP 连接**: 232 | 233 | - 用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 **Socket、序列号和窗口大小**称为连接。 234 | 235 | 所以,重要的是**为什么三次握手才可以初始化 Socket、序列号和窗口大小并建立 TCP 连接。** 236 | 237 | 接下来,以三个方面分析三次握手的原因: 238 | 239 | - 三次握手才可以阻止重复历史连接的初始化(主要原因) 240 | - 三次握手才可以同步双方的初始序列号 241 | - 三次握手才可以避免资源浪费 242 | 243 | 简单来说,三次握手的**首要原因是为了防止旧的重复连接初始化造成混乱。** 244 | 245 | 我们考虑一个场景,客户端先发送了 SYN(seq = 90)报文,然后客户端宕机了,而且这个 SYN 报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向服务端建立连接,发送了 SYN(seq = 100)报文(*注意!不是重传 SYN,重传的 SYN 的序列号是一样的*)。 246 | 247 | 看看三次握手是如何阻止历史连接的: 248 | 249 | ![image-20230126145724420](计网八股.assets/image-20230126145724420.png) 250 | 251 | 客户端连续发送多次 SYN(都是同一个四元组)建立连接的报文,在**网络拥堵**情况下: 252 | 253 | - 一个「旧 SYN 报文」比「最新的 SYN」 报文早到达了服务端,那么此时服务端就会回一个 `SYN + ACK` 报文给客户端,此报文中的确认号是 91(90+1)。 254 | - 客户端收到后,发现自己期望收到的确认号应该是 100 + 1,而不是 90 + 1,于是就会回 RST 报文。 255 | - 服务端收到 RST 报文后,就会释放连接。 256 | - 后续最新的 SYN 抵达了服务端后,客户端与服务端就可以正常的完成三次握手了。 257 | 258 | 上述中的「旧 SYN 报文」称为历史连接,TCP 使用三次握手建立连接的**最主要原因就是防止「历史连接」初始化了连接**。 259 | 260 | > TIP 261 | > 262 | > 有很多人问,如果服务端在收到 RST 报文之前,先收到了「新 SYN 报文」,也就是服务端收到客户端报文的顺序是:「旧 SYN 报文」->「新 SYN 报文」,此时会发生什么? 263 | > 264 | > 当服务端第一次收到 SYN 报文,也就是收到 「旧 SYN 报文」时,就会回复 `SYN + ACK` 报文给客户端,此报文中的确认号是 91(90+1)。 265 | > 266 | > 然后这时再收到「新 SYN 报文」时,就会回 [Challenge Ack (opens new window)](https://xiaolincoding.com/network/3_tcp/challenge_ack.html)报文给客户端,**这个 ack 报文并不是确认收到「新 SYN 报文」的,而是上一次的 ack 确认号**,也就是91(90+1)。所以客户端收到此 ACK 报文时,发现自己期望收到的确认号应该是 101,而不是 91,于是就会回 RST 报文。 267 | 268 | **如果是两次握手连接,就无法阻止历史连接**,那为什么 TCP 两次握手为什么无法阻止历史连接呢? 269 | 270 | 我先直接说结论,主要是因为**在两次握手的情况下,服务端没有中间状态给客户端来阻止历史连接,导致服务端可能建立一个历史连接,造成资源浪费**。 271 | 272 | 你想想,在两次握手的情况下,服务端在收到 SYN 报文后,就进入 ESTABLISHED 状态,意味着这时可以给对方发送数据,但是客户端此时还没有进入 ESTABLISHED 状态,假设这次是历史连接,客户端判断到此次连接为历史连接,那么就会回 RST 报文来断开连接,而服务端在第一次握手的时候就进入 ESTABLISHED 状态,所以它可以发送数据的,但是它并不知道这个是历史连接,它只有在收到 RST 报文后,才会断开连接。 273 | 274 | ![image-20230126145804277](计网八股.assets/image-20230126145804277.png) 275 | 276 | 可以看到,如果采用两次握手建立 TCP 连接的场景下,服务端在向客户端发送数据前,并没有阻止掉历史连接,导致服务端建立了一个历史连接,又白白发送了数据,妥妥地浪费了服务端的资源。 277 | 278 | 因此,**要解决这种现象,最好就是在服务端发送数据前,也就是建立连接之前,要阻止掉历史连接,这样就不会造成资源浪费,而要实现这个功能,就需要三次握手**。 279 | 280 | 所以,**TCP 使用三次握手建立连接的最主要原因是防止「历史连接」初始化了连接。** 281 | 282 | *原因二:同步双方初始序列号* 283 | 284 | TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用: 285 | 286 | - 接收方可以去除重复的数据; 287 | - 接收方可以根据数据包的序列号按序接收; 288 | - 可以标识发送出去的数据包中, 哪些是已经被对方收到的(通过 ACK 报文中的序列号知道); 289 | 290 | 可见,序列号在 TCP 连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的 `SYN` 报文的时候,需要服务端回一个 `ACK` 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,**这样一来一回,才能确保双方的初始序列号能被可靠的同步。** 291 | 292 | image-20230126145919764 293 | 294 | 四次握手其实也能够可靠的同步双方的初始化序号,但由于**第二步和第三步可以优化成一步**,所以就成了「三次握手」。 295 | 296 | 而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。 297 | 298 | *原因三:避免资源浪费* 299 | 300 | 如果只有「两次握手」,当客户端发生的 `SYN` 报文在网络中阻塞,客户端没有接收到 `ACK` 报文,就会重新发送 `SYN` ,**由于没有第三次握手,服务端不清楚客户端是否收到了自己回复的 `ACK` 报文,所以服务端每收到一个 `SYN` 就只能先主动建立一个连接**,这会造成什么情况呢? 301 | 302 | 如果客户端发送的 `SYN` 报文在网络中阻塞了,重复发送多次 `SYN` 报文,那么服务端在收到请求后就会**建立多个冗余的无效链接,造成不必要的资源浪费。** 303 | 304 | ![image-20230126150006442](计网八股.assets/image-20230126150006442.png) 305 | 306 | 即两次握手会造成消息滞留情况下,服务端重复接受无用的连接请求 `SYN` 报文,而造成重复分配资源。 307 | 308 | > TIP 309 | > 310 | > 很多人问,两次握手不是也可以根据上下文信息丢弃 syn 历史报文吗? 311 | > 312 | > 我这里两次握手是假设「由于没有第三次握手,服务端不清楚客户端是否收到了自己发送的建立连接的 `ACK` 确认报文,所以每收到一个 `SYN` 就只能先主动建立一个连接」这个场景。 313 | > 314 | > 当然你要实现成类似三次握手那样,根据上下文丢弃 syn 历史报文也是可以的,两次握手没有具体的实现,怎么假设都行。 315 | 316 | **小结** 317 | 318 | TCP 建立连接时,通过三次握手**能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号**。序列号能够保证数据包不重复、不丢弃和按序传输。 319 | 320 | 不使用「两次握手」和「四次握手」的原因: 321 | 322 | - 「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号; 323 | - 「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。 324 | 325 | ### 第一次握手丢失了,会发生什么? 326 | 327 | 当客户端想和服务端建立 TCP 连接的时候,首先第一个发的就是 SYN 报文,然后进入到 `SYN_SENT` 状态。 328 | 329 | 在这之后,如果客户端迟迟收不到服务端的 SYN-ACK 报文(第二次握手),就会触发「超时重传」机制,重传 SYN 报文,而且**重传的 SYN 报文的序列号都是一样的**。 330 | 331 | 不同版本的操作系统可能超时时间不同,有的 1 秒的,也有 3 秒的,这个超时时间是写死在内核里的,如果想要更改则需要重新编译内核,比较麻烦。 332 | 333 | 当客户端在 1 秒后没收到服务端的 SYN-ACK 报文后,客户端就会重发 SYN 报文,那到底重发几次呢? 334 | 335 | 在 Linux 里,客户端的 SYN 报文最大重传次数由 `tcp_syn_retries`内核参数控制,这个参数是可以自定义的,默认值一般是 5。 336 | 337 | ```cmd 338 | # cat /proc/sys/net/ipv4/tcp_syn_retries 339 | 5 340 | ``` 341 | 342 | 通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。没错,**每次超时的时间是上一次的 2 倍**。 343 | 344 | 当第五次超时重传后,会继续等待 32 秒,如果服务端仍然没有回应 ACK,客户端就不再发送 SYN 包,然后断开 TCP 连接。 345 | 346 | 所以,总耗时是 1+2+4+8+16+32=63 秒,大约 1 分钟左右。 347 | 348 | ### 第二次握手丢失了,会发生什么? 349 | 350 | 当服务端收到客户端的第一次握手后,就会回 SYN-ACK 报文给客户端,这个就是第二次握手,此时服务端会进入 `SYN_RCVD` 状态。 351 | 352 | 第二次握手的 `SYN-ACK` 报文其实有两个目的 : 353 | 354 | - 第二次握手里的 ACK, 是对第一次握手的确认报文; 355 | - 第二次握手里的 SYN,是服务端发起建立 TCP 连接的报文; 356 | 357 | 所以,如果第二次握手丢了,就会发生比较有意思的事情,具体会怎么样呢? 358 | 359 | 因为第二次握手报文里是包含对客户端的第一次握手的 ACK 确认报文,所以,如果客户端迟迟没有收到第二次握手,那么客户端就觉得可能自己的 SYN 报文(第一次握手)丢失了,于是**客户端就会触发超时重传机制,重传 SYN 报文**。 360 | 361 | 然后,因为第二次握手中包含服务端的 SYN 报文,所以当客户端收到后,需要给服务端发送 ACK 确认报文(第三次握手),服务端才会认为该 SYN 报文被客户端收到了。 362 | 363 | 那么,如果第二次握手丢失了,服务端就收不到第三次握手,于是**服务端这边会触发超时重传机制,重传 SYN-ACK 报文**。 364 | 365 | 在 Linux 下,SYN-ACK 报文的最大重传次数由 `tcp_synack_retries`内核参数决定,默认值是 5。 366 | 367 | 因此,当第二次握手丢失了,客户端和服务端都会重传: 368 | 369 | - 客户端会重传 SYN 报文,也就是第一次握手,最大重传次数由 `tcp_syn_retries`内核参数决定; 370 | - 服务端会重传 SYN-ACK 报文,也就是第二次握手,最大重传次数由 `tcp_synack_retries` 内核参数决定。 371 | 372 | ### 第三次握手丢失了,会发生什么? 373 | 374 | 客户端收到服务端的 SYN-ACK 报文后,就会给服务端回一个 ACK 报文,也就是第三次握手,此时客户端状态进入到 `ESTABLISH` 状态。 375 | 376 | 因为这个第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN-ACK 报文,直到收到第三次握手,或者达到最大重传次数。 377 | 378 | 注意,**ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文**。 379 | 380 | ## TCP 连接断开 381 | 382 | ### TCP 四次挥手过程是怎样的? 383 | 384 | 天下没有不散的宴席,对于 TCP 连接也是这样, TCP 断开连接是通过**四次挥手**方式。 385 | 386 | 双方都可以主动断开连接,断开连接后主机中的「资源」将被释放,四次挥手的过程如下图: 387 | 388 | image-20230126162417520 389 | 390 | - 客户端打算关闭连接,此时会发送一个 TCP 首部 `FIN` 标志位被置为 `1` 的报文,也即 `FIN` 报文,之后客户端进入 `FIN_WAIT_1` 状态。 391 | - 服务端收到该报文后,就向客户端发送 `ACK` 应答报文,接着服务端进入 `CLOSE_WAIT` 状态。 392 | - 客户端收到服务端的 `ACK` 应答报文后,之后进入 `FIN_WAIT_2` 状态。 393 | - 等待服务端处理完数据后,也向客户端发送 `FIN` 报文,之后服务端进入 `LAST_ACK` 状态。 394 | - 客户端收到服务端的 `FIN` 报文后,回一个 `ACK` 应答报文,之后进入 `TIME_WAIT` 状态 395 | - 服务端收到了 `ACK` 应答报文后,就进入了 `CLOSE` 状态,至此服务端已经完成连接的关闭。 396 | - 客户端在经过 `2MSL` 一段时间后,自动进入 `CLOSE` 状态,至此客户端也完成连接的关闭。 397 | 398 | 你可以看到,每个方向都需要**一个 FIN 和一个 ACK**,因此通常被称为**四次挥手**。 399 | 400 | 这里一点需要注意是:**主动关闭连接的,才有 TIME_WAIT 状态。** 401 | 402 | ### 为什么挥手需要四次? 403 | 404 | 再来回顾下四次挥手双方发 `FIN` 包的过程,就能理解为什么需要四次了。 405 | 406 | - 关闭连接时,客户端向服务端发送 `FIN` 时,仅仅表示客户端不再发送数据了但是还能接收数据。 407 | - 服务端收到客户端的 `FIN` 报文时,先回一个 `ACK` 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 `FIN` 报文给客户端来表示同意现在关闭连接。 408 | 409 | 从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 `ACK` 和 `FIN` 一般都会分开发送,因此是需要四次挥手。 410 | 411 | ### 第一次挥手丢失了,会发生什么? 412 | 413 | 当客户端(主动关闭方)调用 close 函数后,就会向服务端发送 FIN 报文,试图与服务端断开连接,此时客户端的连接进入到 `FIN_WAIT_1` 状态。 414 | 415 | 正常情况下,如果能及时收到服务端(被动关闭方)的 ACK,则会很快变为 `FIN_WAIT2`状态。 416 | 417 | 如果第一次挥手丢失了,那么客户端迟迟收不到被动方的 ACK 的话,也就会触发超时重传机制,重传 FIN 报文,重发次数由 `tcp_orphan_retries` 参数控制。 418 | 419 | 当客户端重传 FIN 报文的次数超过 `tcp_orphan_retries` 后,就不再发送 FIN 报文,则会在等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到第二次挥手,那么直接进入到 `close` 状态。 420 | 421 | 举个例子,假设 tcp_orphan_retries 参数值为 3,当第一次挥手一直丢失时,发生的过程如下图: 422 | 423 | image-20230126171413301 424 | 425 | 具体过程: 426 | 427 | - 当客户端超时重传 3 次 FIN 报文后,由于 tcp_orphan_retries 为 3,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到服务端的第二次挥手(ACK报文),那么客户端就会断开连接。 428 | 429 | ### 第二次挥手丢失了,会发生什么? 430 | 431 | 当服务端收到客户端的第一次挥手后,就会先回一个 ACK 确认报文,此时服务端的连接进入到 `CLOSE_WAIT` 状态。 432 | 433 | 在前面我们也提了,ACK 报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传 FIN 报文,直到收到服务端的第二次挥手,或者达到最大的重传次数。 434 | 435 | 举个例子,假设 tcp_orphan_retries 参数值为 2,当第二次挥手一直丢失时,发生的过程如下图: 436 | 437 | image-20230126171450973 438 | 439 | 具体过程: 440 | 441 | - 当客户端超时重传 2 次 FIN 报文后,由于 tcp_orphan_retries 为 2,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到服务端的第二次挥手(ACK 报文),那么客户端就会断开连接。 442 | 443 | 这里提一下,当客户端收到第二次挥手,也就是收到服务端发送的 ACK 报文后,客户端就会处于 `FIN_WAIT2` 状态,在这个状态需要等服务端发送第三次挥手,也就是服务端的 FIN 报文。 444 | 445 | 对于 close 函数关闭的连接,由于无法再发送和接收数据,所以`FIN_WAIT2` 状态不可以持续太久,而 `tcp_fin_timeout` 控制了这个状态下连接的持续时长,默认值是 60 秒。 446 | 447 | 这意味着对于调用 close 关闭的连接,如果在 60 秒后还没有收到 FIN 报文,客户端(主动关闭方)的连接就会直接关闭,如下图: 448 | 449 | image-20230126190019529 450 | 451 | 但是注意,如果主动关闭方使用 shutdown 函数关闭连接,指定了只关闭发送方向,而接收方向并没有关闭,那么意味着主动关闭方还是可以接收数据的。 452 | 453 | 此时,如果主动关闭方一直没收到第三次挥手,那么主动关闭方的连接将会一直处于 `FIN_WAIT2` 状态(`tcp_fin_timeout` 无法控制 shutdown 关闭的连接)。如下图: 454 | 455 | image-20230126190132484 456 | 457 | ### 第三次挥手丢失了,会发生什么? 458 | 459 | 当服务端(被动关闭方)收到客户端(主动关闭方)的 FIN 报文后,内核会自动回复 ACK,同时连接处于 `CLOSE_WAIT` 状态,顾名思义,它表示等待应用进程调用 close 函数关闭连接。 460 | 461 | 此时,内核是没有权利替代进程关闭连接,必须由进程主动调用 close 函数来触发服务端发送 FIN 报文。 462 | 463 | 服务端处于 CLOSE_WAIT 状态时,调用了 close 函数,内核就会发出 FIN 报文,同时连接进入 LAST_ACK 状态,等待客户端返回 ACK 来确认连接关闭。 464 | 465 | 如果迟迟收不到这个 ACK,服务端就会重发 FIN 报文,重发次数仍然由 `tcp_orphan_retrie`s 参数控制,这与客户端重发 FIN 报文的重传次数控制方式是一样的。 466 | 467 | 举个例子,假设 `tcp_orphan_retrie`s = 3,当第三次挥手一直丢失时,发生的过程如下图: 468 | 469 | image-20230126190420778 470 | 471 | 具体过程: 472 | 473 | - 当服务端重传第三次挥手报文的次数达到了 3 次后,由于 tcp_orphan_retries 为 3,达到了重传最大次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到客户端的第四次挥手(ACK报文),那么服务端就会断开连接。 474 | - 客户端因为是通过 close 函数关闭连接的,处于 FIN_WAIT_2 状态是有时长限制的,如果 tcp_fin_timeout 时间内还是没能收到服务端的第三次挥手(FIN 报文),那么客户端就会断开连接。 475 | 476 | ### 第四次挥手丢失了,会发生什么? 477 | 478 | 当客户端收到服务端的第三次挥手的 FIN 报文后,就会回 ACK 报文,也就是第四次挥手,此时客户端连接进入 `TIME_WAIT` 状态。 479 | 480 | 在 Linux 系统,TIME_WAIT 状态会持续 2MSL 后才会进入关闭状态。 481 | 482 | 然后,服务端(被动关闭方)没有收到 ACK 报文前,还是处于 LAST_ACK 状态。 483 | 484 | 如果第四次挥手的 ACK 报文没有到达服务端,服务端就会重发 FIN 报文,重发次数仍然由前面介绍过的 `tcp_orphan_retries` 参数控制。 485 | 486 | 举个例子,假设 tcp_orphan_retries 为 2,当第四次挥手一直丢失时,发生的过程如下: 487 | 488 | image-20230126190513685 489 | 490 | 具体过程: 491 | 492 | - 当服务端重传第三次挥手报文达到 2 时,由于 tcp_orphan_retries 为 2, 达到了最大重传次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到客户端的第四次挥手(ACK 报文),那么服务端就会断开连接。 493 | - 客户端在收到第三次挥手后,就会进入 TIME_WAIT 状态,开启时长为 2MSL 的定时器,如果途中再次收到第三次挥手(FIN 报文)后,就会重置定时器,当等待 2MSL 时长后,客户端就会断开连接。 494 | 495 | ### 为什么 TIME_WAIT 等待的时间是 2MSL? 496 | 497 | `MSL` 是 Maximum Segment Lifetime,**报文最大生存时间**,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 `TTL` 字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。 498 | 499 | MSL 与 TTL 的区别: MSL 的单位是时间,而 TTL 是经过路由跳数。所以 **MSL 应该要大于等于 TTL 消耗为 0 的时间**,以确保报文已被自然消亡。 500 | 501 | **TTL 的值一般是 64,Linux 将 MSL 设置为 30 秒,意味着 Linux 认为数据报文经过 64 个路由器的时间不会超过 30 秒,如果超过了,就认为报文已经消失在网络中了**。 502 | 503 | TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以**一来一回需要等待 2 倍的时间**。 504 | 505 | 比如,如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 `FIN` 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。 506 | 507 | 可以看到 **2MSL时长** 这其实是相当于**至少允许报文丢失一次**。比如,若 ACK 在一个 MSL 内丢失,这样被动方重发的 FIN 会在第 2 个 MSL 内到达,TIME_WAIT 状态的连接可以应对。 508 | 509 | 为什么不是 4 或者 8 MSL 的时长呢?你可以想象一个丢包率达到百分之一的糟糕网络,连续两次丢包的概率只有万分之一,这个概率实在是太小了,忽略它比解决它更具性价比。 510 | 511 | `2MSL` 的时间是从**客户端接收到 FIN 后发送 ACK 开始计时的**。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 **2MSL 时间将重新计时**。 512 | 513 | 在 Linux 系统里 `2MSL` 默认是 `60` 秒,那么一个 `MSL` 也就是 `30` 秒。**Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒**。 514 | 515 | ### 为什么需要 TIME_WAIT 状态? 516 | 517 | 主动发起关闭连接的一方,才会有 `TIME-WAIT` 状态。 518 | 519 | 需要 TIME-WAIT 状态,主要是两个原因: 520 | 521 | - 防止历史连接中的数据,被后面相同四元组的连接错误的接收; 522 | - 保证「被动关闭连接」的一方,能被正确的关闭; 523 | 524 | *原因一:防止历史连接中的数据,被后面相同四元组的连接错误的接收* 525 | 526 | 为了能更好的理解这个原因,我们先来了解序列号(SEQ)和初始序列号(ISN)。 527 | 528 | - **序列号**,是 TCP 一个头部字段,标识了 TCP 发送端到 TCP 接收端的数据流的一个字节,因为 TCP 是面向字节流的可靠协议,为了保证消息的顺序性和可靠性,TCP 为每个传输方向上的每个字节都赋予了一个编号,以便于传输成功后确认、丢失后重传以及在接收端保证不会乱序。**序列号是一个 32 位的无符号数,因此在到达 4G 之后再循环回到 0**。 529 | - **初始序列号**,在 TCP 建立连接的时候,客户端和服务端都会各自生成一个初始序列号,它是基于时钟生成的一个随机数,来保证每个连接都拥有不同的初始序列号。**初始化序列号可被视为一个 32 位的计数器,该计数器的数值每 4 微秒加 1,循环一次需要 4.55 小时**。 530 | 531 | **序列号和初始化序列号并不是无限递增的,会发生回绕为初始值的情况,这意味着无法根据序列号来判断新老数据**。 532 | 533 | 假设 TIME-WAIT 没有等待时间或时间过短,被延迟的数据包抵达后会发生什么呢? 534 | 535 | ![image-20230126190808303](计网八股.assets/image-20230126190808303.png) 536 | 537 | 如上图: 538 | 539 | - 服务端在关闭连接之前发送的 `SEQ = 301` 报文,被网络延迟了。 540 | - 接着,服务端以相同的四元组重新打开了新连接,前面被延迟的 `SEQ = 301` 这时抵达了客户端,而且该数据报文的序列号刚好在客户端接收窗口内,因此客户端会正常接收这个数据报文,但是这个数据报文是上一个连接残留下来的,这样就产生数据错乱等严重的问题。 541 | 542 | 为了防止历史连接中的数据,被后面相同四元组的连接错误的接收,因此 TCP 设计了 TIME_WAIT 状态,状态会持续 `2MSL` 时长,这个时间**足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。** 543 | 544 | *原因二:保证「被动关闭连接」的一方,能被正确的关闭* 545 | 546 | TIME-WAIT 作用是**等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。** 547 | 548 | 如果客户端(主动关闭方)最后一次 ACK 报文(第四次挥手)在网络中丢失了,那么按照 TCP 可靠性原则,服务端(被动关闭方)会重发 FIN 报文。 549 | 550 | 假设客户端没有 TIME_WAIT 状态,而是在发完最后一次回 ACK 报文就直接进入 CLOSE 状态,如果该 ACK 报文丢失了,服务端则重传的 FIN 报文,而这时客户端已经进入到关闭状态了,在收到服务端重传的 FIN 报文后,就会回 RST 报文。 551 | 552 | 服务端收到这个 RST 并将其解释为一个错误(Connection reset by peer),这对于一个可靠的协议来说不是一个优雅的终止方式。 553 | 554 | 为了防止这种情况出现,客户端必须等待足够长的时间,确保服务端能够收到 ACK,如果服务端没有收到 ACK,那么就会触发 TCP 重传机制,服务端会重新发送一个 FIN,这样一去一来刚好两个 MSL 的时间。 555 | 556 | image-20230126190941783 557 | 558 | 客户端在收到服务端重传的 FIN 报文时,TIME_WAIT 状态的等待时间,会重置回 2MSL。 559 | 560 | ### 如果已经建立了连接,但是客户端突然出现故障了怎么办? 561 | 562 | 客户端出现故障指的是客户端的主机发生了宕机,或者断电的场景。发生这种情况的时候,如果服务端一直不会发送数据给客户端,那么服务端是永远无法感知到客户端宕机这个事件的,也就是服务端的 TCP 连接将一直处于 `ESTABLISH` 状态,占用着系统资源。 563 | 564 | 为了避免这种情况,TCP 搞了个**保活机制**。这个机制的原理是这样的: 565 | 566 | 定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。 567 | 568 | 在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值: 569 | 570 | ```shell 571 | net.ipv4.tcp_keepalive_time=7200 572 | net.ipv4.tcp_keepalive_intvl=75 573 | net.ipv4.tcp_keepalive_probes=9 574 | ``` 575 | 576 | - tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制 577 | - tcp_keepalive_intvl=75:表示每次检测间隔 75 秒; 578 | - tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。 579 | 580 | 也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。 581 | 582 | image-20230126192500961 583 | 584 | 注意,应用程序若想使用 TCP 保活机制需要通过 socket 接口设置 `SO_KEEPALIVE` 选项才能够生效,如果没有设置,那么就无法使用 TCP 保活机制。 585 | 586 | 如果开启了 TCP 保活,需要考虑以下几种情况: 587 | 588 | - 第一种,对端程序是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 **TCP 保活时间会被重置**,等待下一个 TCP 保活时间的到来。 589 | - 第二种,对端主机宕机并重启。当 TCP 保活的探测报文发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,**会产生一个 RST 报文**,这样很快就会发现 TCP 连接已经被重置。 590 | - 第三种,是对端主机宕机(*注意不是进程崩溃,进程崩溃后操作系统在回收进程资源的时候,会发送 FIN 报文,而主机宕机则是无法感知的,所以需要 TCP 保活机制来探测对方是不是发生了主机宕机*),或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,**TCP 会报告该 TCP 连接已经死亡**。 591 | 592 | TCP 保活的这个机制检测的时间是有点长,我们可以自己在应用层实现一个心跳机制。 593 | 594 | 比如,web 服务软件一般都会提供 `keepalive_timeout` 参数,用来指定 HTTP 长连接的超时时间。如果设置了 HTTP 长连接的超时时间是 60 秒,web 服务软件就会**启动一个定时器**,如果客户端在完成一个 HTTP 请求后,在 60 秒内都没有再发起新的请求,**定时器的时间一到,就会触发回调函数来释放该连接。** 595 | 596 | image-20230126192814076 597 | 598 | ### 如果已经建立了连接,但是服务端的进程崩溃会发生什么? 599 | 600 | TCP 的连接信息是由内核维护的,所以当服务端的进程崩溃后,内核需要回收该进程的所有 TCP 连接资源,于是内核会发送第一次挥手 FIN 报文,后续的挥手过程也都是在内核完成,并不需要进程的参与,所以即使服务端的进程退出了,还是能与客户端完成 TCP 四次挥手的过程。 601 | 602 | # TCP 四次挥手,可以变成三次吗? 603 | 604 | **TCP 四次挥手中,能不能把第二次的 ACK 报文, 放到第三次 FIN 报文一起发送?** 605 | 606 | 虽然我们在学习 TCP 挥手时,学到的是需要四次来完成 TCP 挥手,但是**在一些情况下, TCP 四次挥手是可以变成 TCP 三次挥手的**。 607 | 608 | image-20230202225809040 609 | 610 | ### 为什么 TCP 挥手需要四次呢? 611 | 612 | 服务器收到客户端的 FIN 报文时,内核会马上回一个 ACK 应答报文,**但是服务端应用程序可能还有数据要发送,所以并不能马上发送 FIN 报文,而是将发送 FIN 报文的控制权交给服务端应用程序**: 613 | 614 | - 如果服务端应用程序有数据要发送的话,就发完数据后,才调用关闭连接的函数; 615 | - 如果服务端应用程序没有数据要发送的话,可以直接调用关闭连接的函数, 616 | 617 | 从上面过程可知,**是否要发送第三次挥手的控制权不在内核,而是在被动关闭方(上图的服务端)的应用程序,因为应用程序可能还有数据要发送,由应用程序决定什么时候调用关闭连接的函数,当调用了关闭连接的函数,内核就会发送 FIN 报文了,**所以服务端的 ACK 和 FIN 一般都会分开发送。 618 | 619 | > FIN 报文一定得调用关闭连接的函数,才会发送吗? 620 | 621 | 不一定。 622 | 623 | 如果进程退出了,不管是不是正常退出,还是异常退出(如进程崩溃),内核都会发送 FIN 报文,与对方完成四次挥手。 624 | 625 | ### 粗暴关闭 vs 优雅关闭 626 | 627 | 前面介绍 TCP 四次挥手的时候,并没有详细介绍关闭连接的函数,其实关闭的连接的函数有两种函数: 628 | 629 | - close 函数,同时 socket 关闭发送方向和读取方向,也就是 socket 不再有发送和接收数据的能力。如果有多进程/多线程共享同一个 socket,如果有一个进程调用了 close 关闭只是让 socket 引用计数 -1,并不会导致 socket 不可用,同时也不会发出 FIN 报文,其他进程还是可以正常读写该 socket,直到引用计数变为 0,才会发出 FIN 报文。 630 | - shutdown 函数,可以指定 socket 只关闭发送方向而不关闭读取方向,也就是 socket 不再有发送数据的能力,但是还是具有接收数据的能力。如果有多进程/多线程共享同一个 socket,shutdown 则不管引用计数,直接使得该 socket 不可用,然后发出 FIN 报文,如果有别的进程企图使用该 socket,将会受到影响。 631 | 632 | 如果客户端是用 close 函数来关闭连接,那么在 TCP 四次挥手过程中,如果收到了服务端发送的数据,由于客户端已经不再具有发送和接收数据的能力,所以客户端的内核会回 RST 报文给服务端,然后内核会释放连接,这时就不会经历完成的 TCP 四次挥手,所以我们常说,调用 close 是粗暴的关闭。 633 | 634 | 如果客户端是用 close 函数来关闭连接,那么在 TCP 四次挥手过程中,如果收到了服务端发送的数据,由于客户端已经不再具有发送和接收数据的能力,所以客户端的内核会回 RST 报文给服务端,然后内核会释放连接,这时就不会经历完成的 TCP 四次挥手,所以我们常说,调用 close 是粗暴的关闭。 635 | 636 | ![image-20230202233949212](计网八股.assets/image-20230202233949212.png) 637 | 638 | 当服务端收到 RST 后,内核就会释放连接,当服务端应用程序再次发起读操作或者写操作时,就能感知到连接已经被释放了: 639 | 640 | - 如果是读操作,则会返回 RST 的报错,也就是我们常见的Connection reset by peer。 641 | - 如果是写操作,那么程序会产生 SIGPIPE 信号,应用层代码可以捕获并处理信号,如果不处理,则默认情况下进程会终止,异常退出。 642 | 643 | 相对的,shutdown 函数因为可以指定只关闭发送方向而不关闭读取方向,所以即使在 TCP 四次挥手过程中,如果收到了服务端发送的数据,客户端也是可以正常读取到该数据的,然后就会经历完整的 TCP 四次挥手,所以我们常说,调用 shutdown 是优雅的关闭。 644 | 645 | ![image-20230202234005218](计网八股.assets/image-20230202234005218.png) 646 | 647 | 但是注意,shutdown 函数也可以指定「只关闭读取方向,而不关闭发送方向」,但是这时候内核是不会发送 FIN 报文的,因为发送 FIN 报文是意味着我方将不再发送任何数据,而 shutdown 如果指定「不关闭发送方向」,就意味着 socket 还有发送数据的能力,所以内核就不会发送 FIN。 648 | 649 | ## 什么情况会出现三次挥手? 650 | 651 | 当被动关闭方(上图的服务端)在 TCP 挥手过程中,「**没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。** 652 | 653 | image-20230202234046383 654 | 655 | 然后因为 TCP 延迟确认机制是默认开启的,所以导致我们抓包时,看见三次挥手的次数比四次挥手还多。 656 | 657 | > 什么是 TCP 延迟确认机制? 658 | 659 | 当发送没有携带数据的 ACK,它的网络效率也是很低的,因为它也有 40 个字节的 IP 头 和 TCP 头,但却没有携带数据报文。 为了解决 ACK 传输效率低问题,所以就衍生出了 **TCP 延迟确认**。 TCP 延迟确认的策略: 660 | 661 | - 当有响应数据要发送时,ACK 会随着响应数据一起立刻发送给对方 662 | - 当没有响应数据要发送时,ACK 将会延迟一段时间,以等待是否有响应数据可以一起发送 663 | - 如果在延迟等待发送 ACK 期间,对方的第二个数据报文又到达了,这时就会立刻发送 ACK 664 | 665 | image-20230202234202774 666 | 667 | ## 重传机制 668 | 669 | 常见的重传机制: 670 | 671 | - 超时重传 672 | - 快速重传 673 | - SACK 674 | - D-SACK 675 | 676 | ### 超时重传 677 | 678 | 重传机制的其中一个方式,就是在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 `ACK` 确认应答报文,就会重发该数据,也就是我们常说的**超时重传**。 679 | 680 | TCP 会在以下两种情况发生超时重传: 681 | 682 | - 数据包丢失 683 | - 确认应答丢失 684 | 685 | image-20230201140944524 686 | 687 | 什么是 `RTT`(Round-Trip Time 往返时延),从下图我们就可以知道: 688 | 689 | image-20230201141056876 690 | 691 | `RTT` 指的是**数据发送时刻到接收到确认的时刻的差值**,也就是包的往返时间。 692 | 693 | 超时重传时间是以 `RTO` (Retransmission Timeout 超时重传时间)表示。 694 | 695 | 假设在重传的情况下,超时时间 `RTO` 「较长或较短」时,会发生什么事情呢? 696 | 697 | 上图中有两种超时时间不同的情况: 698 | 699 | - 当超时时间 **RTO 较大**时,重发就慢,丢了老半天才重发,没有效率,性能差; 700 | - 当超时时间 **RTO 较小**时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。 701 | 702 | 精确的测量超时时间 `RTO` 的值是非常重要的,这可让我们的重传机制更高效。 703 | 704 | 根据上述的两种情况,我们可以得知,**超时重传时间 RTO 的值应该略大于报文往返 RTT 的值**。 705 | 706 | image-20230201141210092 707 | 708 | ### 快速重传 709 | 710 | TCP 还有另外一种**快速重传(Fast Retransmit)机制**,它**不以时间为驱动,而是以数据驱动重传**。 711 | 712 | 快速重传机制,是如何工作的呢?其实很简单,一图胜千言。 713 | 714 | image-20230201145201966 715 | 716 | 快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。 717 | 718 | 快速重传机制只解决了一个问题,就是超时时间的问题,但是它依然面临着另外一个问题。就是**重传的时候,是重传一个,还是重传所有的问题。** 719 | 720 | 举个例子,假设发送方发了 6 个数据,编号的顺序是 Seq1 ~ Seq6 ,但是 Seq2、Seq3 都丢失了,那么接收方在收到 Seq4、Seq5、Seq6 时,都是回复 ACK2 给发送方,但是发送方并不清楚这连续的 ACK2 是接收方收到哪个报文而回复的, 那是选择重传 Seq2 一个报文,还是重传 Seq2 之后已发送的所有报文呢(Seq2、Seq3、 Seq4、Seq5、 Seq6) 呢? 721 | 722 | - 如果只选择重传 Seq2 一个报文,那么重传的效率很低。因为对于丢失的 Seq3 报文,还得在后续收到三个重复的 ACK3 才能触发重传。 723 | - 如果选择重传 Seq2 之后已发送的所有报文,虽然能同时重传已丢失的 Seq2 和 Seq3 报文,但是 Seq4、Seq5、Seq6 的报文是已经被接收过了,对于重传 Seq4 ~Seq6 折部分数据相当于做了一次无用功,浪费资源。 724 | 725 | 可以看到,不管是重传一个报文,还是重传已发送的报文,都存在问题。 726 | 727 | 为了解决不知道该重传哪些 TCP 报文,于是就有 `SACK` 方法。 728 | 729 | ### SACK 方法 730 | 731 | 还有一种实现重传机制的方式叫:`SACK`( Selective Acknowledgment), **选择性确认**。 732 | 733 | 这种方式需要在 TCP 头部「选项」字段里加一个 `SACK` 的东西,它**可以将已收到的数据的信息发送给「发送方」**,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以**只重传丢失的数据**。 734 | 735 | 如下图,发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制,通过 `SACK` 信息发现只有 `200~299` 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复。 736 | 737 | image-20230201163118587 738 | 739 | ### Duplicate SACK 740 | 741 | Duplicate SACK 又称 `D-SACK`,其主要**使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。** 742 | 743 | 下面举例两个栗子,来说明 `D-SACK` 的作用。 744 | 745 | *例子一号:ACK 丢包* 746 | 747 | image-20230201201111914 748 | 749 | - 「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,所以发送方超时后,重传第一个数据包(3000 ~ 3499) 750 | - **于是「接收方」发现数据是重复收到的,于是回了一个 SACK = 3000~3500**,告诉「发送方」 3000~3500 的数据早已被接收了,因为 ACK 都到了 4000 了,已经意味着 4000 之前的所有数据都已收到,所以这个 SACK 就代表着 `D-SACK`。 751 | - 这样「发送方」就知道了,数据没有丢,是「接收方」的 ACK 确认报文丢了。 752 | 753 | *例子二号:网络延时* 754 | 755 | image-20230201201153169 756 | 757 | - 数据包(1000~1499) 被网络延迟了,导致「发送方」没有收到 Ack 1500 的确认报文。 758 | - 而后面报文到达的三个相同的 ACK 确认报文,就触发了快速重传机制,但是在重传后,被延迟的数据包(1000~1499)又到了「接收方」; 759 | - **所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 D-SACK,表示收到了重复的包。** 760 | - 这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而是因为网络延迟了。 761 | 762 | 可见,`D-SACK` 有这么几个好处: 763 | 764 | 1. 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了; 765 | 2. 可以知道是不是「发送方」的数据包被网络延迟了; 766 | 3. 可以知道网络中是不是把「发送方」的数据包给复制了; 767 | 768 | ## 滑动窗口 769 | 770 | 我们都知道 TCP 是每发送一个数据,都要进行一次确认应答。当上一个数据包收到了应答了, 再发送下一个。 771 | 772 | 这个模式就有点像我和你面对面聊天,你一句我一句。但这种方式的缺点是效率比较低的。 773 | 774 | 如果你说完一句话,我在处理其他事情,没有及时回复你,那你不是要干等着我做完其他事情后,我回复你,你才能说下一句话,很显然这不现实。 775 | 776 | image-20230201201504557 777 | 778 | 所以,这样的传输方式有一个缺点:数据包的**往返时间越长,通信的效率就越低**。 779 | 780 | 为解决这个问题,TCP 引入了**窗口**这个概念。即使在往返时间较长的情况下,它也不会降低网络通信的效率。 781 | 782 | 那么有了窗口,就可以指定窗口大小,窗口大小就是指**无需等待确认应答,而可以继续发送数据的最大值**。 783 | 784 | 窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。 785 | 786 | 假设窗口大小为 `3` 个 TCP 段,那么发送方就可以「连续发送」 `3` 个 TCP 段,并且中途若有 ACK 丢失,可以通过「下一个确认应答进行确认」。如下图: 787 | 788 | image-20230201201611677 789 | 790 | 图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫**累计确认**或者**累计应答**。 791 | 792 | > 窗口大小由哪一方决定? 793 | 794 | TCP 头里有一个字段叫 `Window`,也就是窗口大小。 795 | 796 | **这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。** 797 | 798 | 所以,通常窗口的大小是由接收方的窗口大小来决定的。 799 | 800 | 发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。 801 | 802 | > 发送方的滑动窗口 803 | 804 | 我们先来看看发送方的窗口,下图就是发送方缓存的数据,根据处理的情况分成四个部分,其中深蓝色方框是发送窗口,紫色方框是可用窗口: 805 | 806 | image-20230201232550574 807 | 808 | - \#1 是已发送并收到 ACK确认的数据:1~31 字节 809 | - \#2 是已发送但未收到 ACK确认的数据:32~45 字节 810 | - \#3 是未发送但总大小在接收方处理范围内(接收方还有空间):46~51字节 811 | - \#4 是未发送但总大小超过接收方处理范围(接收方没有空间):52字节以后 812 | 813 | 在下图,当发送方把数据「全部」都一下发送出去后,可用窗口的大小就为 0 了,表明可用窗口耗尽,在没收到 ACK 确认之前是无法继续发送数据了。 814 | 815 | image-20230201233607275 816 | 817 | 在下图,当收到之前发送的数据 `32~36` 字节的 ACK 确认应答后,如果发送窗口的大小没有变化,则**滑动窗口往右边移动 5 个字节,因为有 5 个字节的数据被应答确认**,接下来 `52~56` 字节又变成了可用窗口,那么后续也就可以发送 `52~56` 这 5 个字节的数据了。 818 | 819 | image-20230201233724355 820 | 821 | TCP 滑动窗口方案使用三个指针来跟踪在四个传输类别中的每一个类别中的字节。其中两个指针是绝对指针(指特定的序列号),一个是相对指针(需要做偏移)。 822 | 823 | image-20230202001455580 824 | 825 | - `SND.WND`:表示发送窗口的大小(大小是由接收方指定的); 826 | - `SND.UNA`(*Send Unacknoleged*):是一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号,也就是 #2 的第一个字节。 827 | - `SND.NXT`:也是一个绝对指针,它指向未发送但可发送范围的第一个字节的序列号,也就是 #3 的第一个字节。 828 | - 指向 #4 的第一个字节是个相对指针,它需要 `SND.UNA` 指针加上 `SND.WND` 大小的偏移量,就可以指向 #4 的第一个字节了。 829 | 830 | 那么可用窗口大小的计算就可以是: 831 | 832 | **可用窗口大小 = SND.WND -(SND.NXT - SND.UNA)** 833 | 834 | > 接收方的滑动窗口 835 | 836 | 接下来我们看看接收方的窗口,接收窗口相对简单一些,根据处理的情况划分成三个部分: 837 | 838 | - \#1 + #2 是已成功接收并确认的数据(等待应用进程读取); 839 | - \#3 是未收到数据但可以接收的数据; 840 | - \#4 未收到数据并不可以接收的数据; 841 | 842 | image-20230202141330954 843 | 844 | 其中三个接收部分,使用两个指针进行划分: 845 | 846 | - `RCV.WND`:表示接收窗口的大小,它会通告给发送方。 847 | - `RCV.NXT`:是一个指针,它指向期望从发送方发送来的下一个数据字节的序列号,也就是 #3 的第一个字节。 848 | - 指向 #4 的第一个字节是个相对指针,它需要 `RCV.NXT` 指针加上 `RCV.WND` 大小的偏移量,就可以指向 #4 的第一个字节了。 849 | 850 | > 接收窗口和发送窗口的大小是相等的吗? 851 | 852 | 并不是完全相等,接收窗口的大小是**约等于**发送窗口的大小的。 853 | 854 | 因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。 855 | 856 | ## 流量控制 857 | 858 | 发送方不能无脑的发数据给接收方,要考虑接收方处理能力。 859 | 860 | 如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。 861 | 862 | 为了解决这种现象发生,**TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。** 863 | 864 | 下面举个栗子,为了简单起见,假设以下场景: 865 | 866 | - 客户端是接收方,服务端是发送方 867 | - 假设接收窗口和发送窗口相同,都为 `200` 868 | - 假设两个设备在整个传输过程中都保持相同的窗口大小,不受外界影响 869 | 870 | ![image-20230202141530611](计网八股.assets/image-20230202141530611.png) 871 | 872 | 根据上图的流量控制,说明下每个过程: 873 | 874 | 1. 客户端向服务端发送请求数据报文。这里要说明下,本次例子是把服务端作为发送方,所以没有画出服务端的接收窗口。 875 | 2. 服务端收到请求报文后,发送确认报文和 80 字节的数据,于是可用窗口 `Usable` 减少为 120 字节,同时 `SND.NXT` 指针也向右偏移 80 字节后,指向 321,**这意味着下次发送数据的时候,序列号是 321。** 876 | 3. 客户端收到 80 字节数据后,于是接收窗口往右移动 80 字节,`RCV.NXT` 也就指向 321,**这意味着客户端期望的下一个报文的序列号是 321**,接着发送确认报文给服务端。 877 | 4. 服务端再次发送了 120 字节数据,于是可用窗口耗尽为 0,服务端无法再继续发送数据。 878 | 5. 客户端收到 120 字节的数据后,于是接收窗口往右移动 120 字节,`RCV.NXT` 也就指向 441,接着发送确认报文给服务端。 879 | 6. 服务端收到对 80 字节数据的确认报文后,`SND.UNA` 指针往右偏移后指向 321,于是可用窗口 `Usable` 增大到 80。 880 | 7. 服务端收到对 120 字节数据的确认报文后,`SND.UNA` 指针往右偏移后指向 441,于是可用窗口 `Usable` 增大到 200。 881 | 8. 服务端可以继续发送了,于是发送了 160 字节的数据后,`SND.NXT` 指向 601,于是可用窗口 `Usable` 减少到 40。 882 | 9. 客户端收到 160 字节后,接收窗口往右移动了 160 字节,`RCV.NXT` 也就是指向了 601,接着发送确认报文给服务端。 883 | 10. 服务端收到对 160 字节数据的确认报文后,发送窗口往右移动了 160 字节,于是 `SND.UNA` 指针偏移了 160 后指向 601,可用窗口 `Usable` 也就增大至了 200。 884 | 885 | ### 操作系统缓冲区与滑动窗口的关系 886 | 887 | 前面的流量控制例子,我们假定了发送窗口和接收窗口是不变的,但是实际上,发送窗口和接收窗口中所存放的字节数,都是放在操作系统内存缓冲区中的,而操作系统的缓冲区,会**被操作系统调整**。 888 | 889 | 当应用进程没办法及时读取缓冲区的内容时,也会对我们的缓冲区造成影响。 890 | 891 | > 那操作系统的缓冲区,是如何影响发送窗口和接收窗口的呢? 892 | 893 | *我们先来看看第一个例子。* 894 | 895 | 当应用程序没有及时读取缓存时,发送窗口和接收窗口的变化。 896 | 897 | 考虑以下场景: 898 | 899 | - 客户端作为发送方,服务端作为接收方,发送窗口和接收窗口初始大小为 `360`; 900 | - 服务端非常的繁忙,当收到客户端的数据时,应用层不能及时读取数据。 901 | 902 | ![image-20230202142907076](计网八股.assets/image-20230202142907076.png) 903 | 904 | 根据上图的流量控制,说明下每个过程: 905 | 906 | 1. 客户端发送 140 字节数据后,可用窗口变为 220 (360 - 140)。 907 | 2. 服务端收到 140 字节数据,**但是服务端非常繁忙,应用进程只读取了 40 个字节,还有 100 字节占用着缓冲区,于是接收窗口收缩到了 260 (360 - 100)**,最后发送确认信息时,将窗口大小通告给客户端。 908 | 3. 客户端收到确认和窗口通告报文后,发送窗口减少为 260。 909 | 4. 客户端发送 180 字节数据,此时可用窗口减少到 80。 910 | 5. 服务端收到 180 字节数据,**但是应用程序没有读取任何数据,这 180 字节直接就留在了缓冲区,于是接收窗口收缩到了 80 (260 - 180)**,并在发送确认信息时,通过窗口大小给客户端。 911 | 6. 客户端收到确认和窗口通告报文后,发送窗口减少为 80。 912 | 7. 客户端发送 80 字节数据后,可用窗口耗尽。 913 | 8. 服务端收到 80 字节数据,**但是应用程序依然没有读取任何数据,这 80 字节留在了缓冲区,于是接收窗口收缩到了 0**,并在发送确认信息时,通过窗口大小给客户端。 914 | 9. 客户端收到确认和窗口通告报文后,发送窗口减少为 0。 915 | 916 | 可见最后窗口都收缩为 0 了,也就是发生了窗口关闭。当发送方可用窗口变为 0 时,发送方实际上会定时发送窗口探测报文,以便知道接收方的窗口是否发生了改变,这个内容后面会说,这里先简单提一下。 917 | 918 | *我们先来看看第二个例子。* 919 | 920 | 当服务端系统资源非常紧张的时候,操作系统可能会直接减少了接收缓冲区大小,这时应用程序又无法及时读取缓存数据,那么这时候就有严重的事情发生了,会出现数据包丢失的现象。 921 | 922 | ![image-20230202142808507](计网八股.assets/image-20230202142808507.png) 923 | 924 | 说明下每个过程: 925 | 926 | 1. 客户端发送 140 字节的数据,于是可用窗口减少到了 220。 927 | 2. **服务端因为现在非常的繁忙,操作系统于是就把接收缓存减少了 120 字节,当收到 140 字节数据后,又因为应用程序没有读取任何数据,所以 140 字节留在了缓冲区中,于是接收窗口大小从 360 收缩成了 100**,最后发送确认信息时,通告窗口大小给对方。 928 | 3. 此时客户端因为还没有收到服务端的通告窗口报文,所以不知道此时接收窗口收缩成了 100,客户端只会看自己的可用窗口还有 220,所以客户端就发送了 180 字节数据,于是可用窗口减少到 40。 929 | 4. 服务端收到了 180 字节数据时,**发现数据大小超过了接收窗口的大小,于是就把数据包丢失了。** 930 | 5. 客户端收到第 2 步时,服务端发送的确认报文和通告窗口报文,尝试减少发送窗口到 100,把窗口的右端向左收缩了 80,此时可用窗口的大小就会出现诡异的负值。 931 | 932 | 所以,如果发生了先减少缓存,再收缩窗口,就会出现丢包的现象。 933 | 934 | **为了防止这种情况发生,TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间再减少缓存,这样就可以避免了丢包情况。** 935 | 936 | ### 窗口关闭 937 | 938 | 在前面我们都看到了,TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制。 939 | 940 | **如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。** 941 | 942 | > 窗口关闭潜在的危险 943 | 944 | 接收方向发送方通告窗口大小时,是通过 `ACK` 报文来通告的。 945 | 946 | 那么,当发生窗口关闭时,接收方处理完数据后,会向发送方通告一个窗口非 0 的 ACK 报文,如果这个通告窗口的 ACK 报文在网络中丢失了,那麻烦就大了。 947 | 948 | image-20230202142925512 949 | 950 | 这会导致发送方一直等待接收方的非 0 窗口通知,接收方也一直等待发送方的数据,如不采取措施,这种相互等待的过程,会造成了死锁的现象。 951 | 952 | > TCP 是如何解决窗口关闭时,潜在的死锁现象呢? 953 | 954 | 为了解决这个问题,TCP 为每个连接设有一个持续定时器,**只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。** 955 | 956 | 如果持续计时器超时,就会发送**窗口探测 ( Window probe ) 报文**,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。 957 | 958 | image-20230202142943183 959 | 960 | - 如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器; 961 | - 如果接收窗口不是 0,那么死锁的局面就可以被打破了。 962 | 963 | 窗口探测的次数一般为 3 次,每次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 `RST` 报文来中断连接。 964 | 965 | ## 拥塞控制 966 | 967 | > 为什么要有拥塞控制呀,不是有流量控制了吗? 968 | 969 | 前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么。 970 | 971 | 一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。 972 | 973 | **在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大....** 974 | 975 | 所以,TCP 不能忽略网络上发生的事,它被设计成一个无私的协议,当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量。 976 | 977 | 于是,就有了**拥塞控制**,控制的目的就是**避免「发送方」的数据填满整个网络。** 978 | 979 | 为了在「发送方」调节所要发送数据的量,定义了一个叫做「**拥塞窗口**」的概念。 980 | 981 | > 什么是拥塞窗口?和发送窗口有什么关系呢? 982 | 983 | **拥塞窗口 cwnd**是发送方维护的一个的状态变量,它会根据**网络的拥塞程度动态变化的**。 984 | 985 | 我们在前面提到过发送窗口 `swnd` 和接收窗口 `rwnd` 是约等于的关系,那么由于加入了拥塞窗口的概念后,此时发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。 986 | 987 | 拥塞窗口 `cwnd` 变化的规则: 988 | 989 | - 只要网络中没有出现拥塞,`cwnd` 就会增大; 990 | - 但网络中出现了拥塞,`cwnd` 就减少; 991 | 992 | > 那么怎么知道当前网络是否出现了拥塞呢? 993 | 994 | 其实只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是**发生了超时重传,就会认为网络出现了拥塞。** 995 | 996 | > 拥塞控制有哪些控制算法? 997 | 998 | 拥塞控制主要是四个算法: 999 | 1000 | - 慢启动 1001 | - 拥塞避免 1002 | - 拥塞发生 1003 | - 快速恢复 1004 | 1005 | ### 慢启动 1006 | 1007 | TCP 在刚建立连接完成后,首先是有个慢启动的过程,这个慢启动的意思就是一点一点的提高发送数据包的数量,如果一上来就发大量的数据,这不是给网络添堵吗? 1008 | 1009 | 慢启动的算法记住一个规则就行:**当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。** 1010 | 1011 | 这里假定拥塞窗口 `cwnd` 和发送窗口 `swnd` 相等,下面举个栗子: 1012 | 1013 | - 连接建立完成后,一开始初始化 `cwnd = 1`,表示可以传一个 `MSS` 大小的数据。 1014 | - 当收到一个 ACK 确认应答后,cwnd 增加 1,于是一次能够发送 2 个 1015 | - 当收到 2 个的 ACK 确认应答后, cwnd 增加 2,于是就可以比之前多发2 个,所以这一次能够发送 4 个 1016 | - 当这 4 个的 ACK 确认到来的时候,每个确认 cwnd 增加 1, 4 个确认 cwnd 增加 4,于是就可以比之前多发 4 个,所以这一次能够发送 8 个。 1017 | 1018 | 慢启动算法的变化过程如下图: 1019 | 1020 | image-20230202143914913 1021 | 1022 | 可以看出慢启动算法,发包的个数是**指数性的增长**。 1023 | 1024 | > 那慢启动涨到什么时候是个头呢? 1025 | 1026 | 有一个叫慢启动门限 `ssthresh` (slow start threshold)状态变量。 1027 | 1028 | - 当 `cwnd` < `ssthresh` 时,使用慢启动算法。 1029 | - 当 `cwnd` >= `ssthresh` 时,就会使用「拥塞避免算法」。 1030 | 1031 | ### 拥塞避免算法 1032 | 1033 | 前面说道,当拥塞窗口 `cwnd` 「超过」慢启动门限 `ssthresh` 就会进入拥塞避免算法。 1034 | 1035 | 一般来说 `ssthresh` 的大小是 `65535` 字节。 1036 | 1037 | 那么进入拥塞避免算法后,它的规则是:**每当收到一个 ACK 时,cwnd 增加 1/cwnd。** 1038 | 1039 | 接上前面的慢启动的栗子,现假定 `ssthresh` 为 `8`: 1040 | 1041 | - 当 8 个 ACK 应答确认到来时,每个确认增加 1/8,8 个 ACK 确认 cwnd 一共增加 1,于是这一次能够发送 9 个 `MSS` 大小的数据,变成了**线性增长。** 1042 | 1043 | 拥塞避免算法的变化过程如下图: 1044 | 1045 | image-20230202144053005 1046 | 1047 | 所以,我们可以发现,拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,但是增长速度缓慢了一些。 1048 | 1049 | 就这么一直增长着后,网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。 1050 | 1051 | 当触发了重传机制,也就进入了「拥塞发生算法」。 1052 | 1053 | ### 拥塞发生 1054 | 1055 | 当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种: 1056 | 1057 | - 超时重传 1058 | - 快速重传 1059 | 1060 | 这两种使用的拥塞发送算法是不同的,接下来分别来说说。 1061 | 1062 | > 发生超时重传的拥塞发生算法 1063 | 1064 | 当发生了「超时重传」,则就会使用拥塞发生算法。 1065 | 1066 | 这个时候,ssthresh 和 cwnd 的值会发生变化: 1067 | 1068 | - `ssthresh` 设为 `cwnd/2`, 1069 | - `cwnd` 重置为 `1` (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1) 1070 | 1071 | image-20230202144156005 1072 | 1073 | 接着,就重新开始慢启动,慢启动是会突然减少数据流的。这真是一旦「超时重传」,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿。 1074 | 1075 | 就好像本来在秋名山高速漂移着,突然来个紧急刹车,轮胎受得了吗。。。 1076 | 1077 | > 发生快速重传的拥塞发生算法 1078 | 1079 | 还有更好的方式,前面我们讲过「快速重传算法」。当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。 1080 | 1081 | TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 `ssthresh` 和 `cwnd` 变化如下: 1082 | 1083 | - `cwnd = cwnd/2` ,也就是设置为原来的一半; 1084 | - `ssthresh = cwnd`; 1085 | - 进入快速恢复算法 1086 | 1087 | ### 快速恢复 1088 | 1089 | 快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 `RTO` 超时那么强烈。 1090 | 1091 | 正如前面所说,进入快速恢复之前,`cwnd` 和 `ssthresh` 已被更新了: 1092 | 1093 | - `cwnd = cwnd/2` ,也就是设置为原来的一半; 1094 | - `ssthresh = cwnd`; 1095 | 1096 | 然后,进入快速恢复算法如下: 1097 | 1098 | - 拥塞窗口 `cwnd = ssthresh + 3` ( 3 的意思是确认有 3 个数据包被收到了); 1099 | - 重传丢失的数据包; 1100 | - 如果再收到重复的 ACK,那么 cwnd 增加 1; 1101 | - 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态; 1102 | 1103 | 快速恢复算法的变化过程如下图: 1104 | 1105 | image-20230202150851545 1106 | 1107 | 也就是没有像「超时重传」一夜回到解放前,而是还在比较高的值,后续呈线性增长。 1108 | 1109 | ## IP 基本认识 1110 | 1111 | IP 在 TCP/IP 参考模型中处于第三层,也就是**网络层**。 1112 | 1113 | 网络层的主要作用是:**实现主机与主机之间的通信,也叫点对点(end to end)通信。** 1114 | 1115 | > 网络层与数据链路层有什么关系呢? 1116 | 1117 | 有的小伙伴分不清 IP(网络层) 和 MAC (数据链路层)之间的区别和关系。 1118 | 1119 | 其实很容易区分,在上面我们知道 IP 的作用是主机之间通信用的,而 **MAC 的作用则是实现「直连」的两个设备之间通信,而 IP 则负责在「没有直连」的两个网络之间进行通信传输。** 1120 | 1121 | image-20230202151753926 1122 | 1123 | 在网络中数据包传输中也是如此,**源IP地址和目标IP地址在传输过程中是不会变化的(前提:没有使用 NAT 网络),只有源 MAC 地址和目标 MAC 一直在变化。** 1124 | 1125 | ------ 1126 | 1127 | ## IP 地址的基础知识 1128 | 1129 | 在 TCP/IP 网络通信时,为了保证能正常通信,每个设备都需要配置正确的 IP 地址,否则无法实现正常的通信。 1130 | 1131 | IP 地址(IPv4 地址)由 `32` 位正整数来表示,IP 地址在计算机是以二进制的方式处理的。 1132 | 1133 | 而人类为了方便记忆采用了**点分十进制**的标记方式,也就是将 32 位 IP 地址以每 8 位为组,共分为 `4` 组,每组以「`.`」隔开,再将每组转换成十进制。 1134 | 1135 | image-20230202151927972 1136 | 1137 | 那么,IP 地址最大值也就是 1138 | 1139 | image-20230202151940614 1140 | 1141 | 也就说,最大允许 43 亿台计算机连接到网络。 1142 | 1143 | 实际上,IP 地址并不是根据主机台数来配置的,而是以网卡。像服务器、路由器等设备都是有 2 个以上的网卡,也就是它们会有 2 个以上的 IP 地址。 1144 | 1145 | image-20230202152027864 1146 | 1147 | 因此,让 43 亿台计算机全部连网其实是不可能的,更何况 IP 地址是由「网络标识」和「主机标识」这两个部分组成的,所以实际能够连接到网络的计算机个数更是少了很多。 1148 | 1149 | > 可能有的小伙伴提出了疑问,现在不仅电脑配了 IP 地址, 手机、IPad 等电子设备都配了 IP 呀,照理来说肯定会超过 43 亿啦,那是怎么能够支持这么多 IP 的呢? 1150 | 1151 | 因为会根据一种可以更换 IP 地址的技术 `NAT`,使得可连接计算机数超过 43 亿台。 `NAT` 技术后续会进一步讨论和说明。 1152 | 1153 | ### IP 地址的分类 1154 | 1155 | 互联网诞生之初,IP 地址显得很充裕,于是计算机科学家们设计了**分类地址**。 1156 | 1157 | IP 地址分类成了 5 种类型,分别是 A 类、B 类、C 类、D 类、E 类。 1158 | 1159 | image-20230202152324041 1160 | 1161 | 上图中黄色部分为分类号,用以区分 IP 地址类别。 1162 | 1163 | > 什么是 A、B、C 类地址? 1164 | 1165 | 其中对于 A、B、C 类主要分为两个部分,分别是**网络号和主机号**。这很好理解,好比小林是 A 小区 1 栋 101 号,你是 B 小区 1 栋 101 号。 1166 | 1167 | 我们可以用下面这个表格, 就能很清楚的知道 A、B、C 分类对应的地址范围、最大主机个数。 1168 | 1169 | image-20230202152332562 1170 | 1171 | > A、B、C 分类地址最大主机个数是如何计算的呢? 1172 | 1173 | 最大主机个数,就是要看主机号的位数,如 C 类地址的主机号占 8 位,那么 C 类地址的最大主机个数: 1174 | 1175 | image-20230202152354957 1176 | 1177 | 为什么要减 2 呢? 1178 | 1179 | 因为在 IP 地址中,有两个 IP 是特殊的,分别是主机号全为 1 和 全为 0 地址。 1180 | 1181 | ![image-20230202152411431](计网八股.assets/image-20230202152411431.png) 1182 | 1183 | - 主机号全为 1 指定某个网络下的所有主机,用于广播 1184 | - 主机号全为 0 指定某个网络 1185 | 1186 | 因此,在分配过程中,应该去掉这两种情况。 1187 | 1188 | > 广播地址用于什么? 1189 | 1190 | 广播地址用于在**同一个链路中相互连接的主机之间发送数据包**。 1191 | 1192 | 学校班级中就有广播的例子,在准备上课的时候,通常班长会喊:“上课, 全体起立!”,班里的同学听到这句话是不是全部都站起来了?这个句话就有广播的含义。 1193 | 1194 | 当主机号全为 1 时,就表示该网络的广播地址。例如把 `172.20.0.0/16` 用二进制表示如下: 1195 | 1196 | 10101100.00010100.00000000.00000000 1197 | 1198 | 将这个地址的**主机部分全部改为 1**,则形成广播地址: 1199 | 1200 | 10101100.00010100.`11111111.11111111` 1201 | 1202 | 再将这个地址用十进制表示,则为 `172.20.255.255`。 1203 | 1204 | 广播地址可以分为本地广播和直接广播两种。 1205 | 1206 | - **在本网络内广播的叫做本地广播**。例如网络地址为 192.168.0.0/24 的情况下,广播地址是 192.168.0.255 。因为这个广播地址的 IP 包会被路由器屏蔽,所以不会到达 192.168.0.0/24 以外的其他链路上。 1207 | - **在不同网络之间的广播叫做直接广播**。例如网络地址为 192.168.0.0/24 的主机向 192.168.1.255/24 的目标地址发送 IP 包。收到这个包的路由器,将数据转发给 192.168.1.0/24,从而使得所有 192.168.1.1~192.168.1.254 的主机都能收到这个包(由于直接广播有一定的安全问题,多数情况下会在路由器上设置为不转发。) 。 1208 | 1209 | > 什么是 D、E 类地址? 1210 | 1211 | 而 D 类和 E 类地址是没有主机号的,所以不可用于主机 IP,D 类常被用于**多播**,E 类是预留的分类,暂时未使用。 1212 | 1213 | image-20230202152717672 1214 | 1215 | > 多播地址用于什么? 1216 | 1217 | 多播用于**将包发送给特定组内的所有主机。** 1218 | 1219 | 还是举班级的栗子,老师说:“最后一排的同学,上来做这道数学题。”,老师指定的是最后一排的同学,也就是多播的含义了。 1220 | 1221 | 由于广播无法穿透路由,若想给其他网段发送同样的包,就可以使用可以穿透路由的多播。 1222 | 1223 | image-20230202152818780 1224 | 1225 | 多播使用的 D 类地址,其前四位是 `1110` 就表示是多播地址,而剩下的 28 位是多播的组编号。 1226 | 1227 | 从 224.0.0.0 ~ 239.255.255.255 都是多播的可用范围,其划分为以下三类: 1228 | 1229 | - 224.0.0.0 ~ 224.0.0.255 为预留的组播地址,只能在局域网中,路由器是不会进行转发的。 1230 | - 224.0.1.0 ~ 238.255.255.255 为用户可用的组播地址,可以用于 Internet 上。 1231 | - 239.0.0.0 ~ 239.255.255.255 为本地管理组播地址,可供内部网在内部使用,仅在特定的本地范围内有效。 1232 | 1233 | > IP 分类的优点 1234 | 1235 | 不管是路由器还是主机解析到一个 IP 地址时候,我们判断其 IP 地址的首位是否为 0,为 0 则为 A 类地址,那么就能很快的找出网络地址和主机地址。 1236 | 1237 | > IP 分类的缺点 1238 | 1239 | *缺点一* 1240 | 1241 | **同一网络下没有地址层次**,比如一个公司里用了 B 类地址,但是可能需要根据生产环境、测试环境、开发环境来划分地址层次,而这种 IP 分类是没有地址层次划分的功能,所以这就**缺少地址的灵活性**。 1242 | 1243 | *缺点二* 1244 | 1245 | A、B、C类有个尴尬处境,就是**不能很好的与现实网络匹配**。 1246 | 1247 | - C 类地址能包含的最大主机数量实在太少了,只有 254 个,估计一个网吧都不够用。 1248 | - 而 B 类地址能包含的最大主机数量又太多了,6 万多台机器放在一个网络下面,一般的企业基本达不到这个规模,闲着的地址就是浪费。 1249 | 1250 | 这两个缺点,都可以在 `CIDR` 无分类地址解决。 1251 | 1252 | ### 无分类地址 CIDR 1253 | 1254 | 正因为 IP 分类存在许多缺点,所以后面提出了无分类地址的方案,即 `CIDR`。 1255 | 1256 | 这种方式不再有分类地址的概念,32 比特的 IP 地址被划分为两部分,前面是**网络号**,后面是**主机号**。 1257 | 1258 | > 怎么划分网络号和主机号的呢? 1259 | 1260 | 表示形式 `a.b.c.d/x`,其中 `/x` 表示前 x 位属于**网络号**, x 的范围是 `0 ~ 32`,这就使得 IP 地址更加具有灵活性。 1261 | 1262 | 比如 10.100.122.2/24,这种地址表示形式就是 CIDR,/24 表示前 24 位是网络号,剩余的 8 位是主机号。 1263 | 1264 | image-20230202153827325 1265 | 1266 | 还有另一种划分网络号与主机号形式,那就是**子网掩码**,掩码的意思就是掩盖掉主机号,剩余的就是网络号。 1267 | 1268 | **将子网掩码和 IP 地址按位计算 AND,就可得到网络号。** 1269 | 1270 | image-20230202153920169 1271 | 1272 | > 为什么要分离网络号和主机号? 1273 | 1274 | 因为两台计算机要通讯,首先要判断是否处于同一个广播域内,即网络地址是否相同。如果网络地址相同,表明接受方在本网络上,那么可以把数据包直接发送到目标主机。 1275 | 1276 | 路由器寻址工作中,也就是通过这样的方式来找到对应的网络号的,进而把数据包转发给对应的网络内。 1277 | 1278 | image-20230202153945231 1279 | 1280 | > 怎么进行子网划分? 1281 | 1282 | 在上面我们知道可以通过子网掩码划分出网络号和主机号,那实际上子网掩码还有一个作用,那就是**划分子网**。 1283 | 1284 | **子网划分实际上是将主机地址分为两个部分:子网网络地址和子网主机地址**。形式如下: 1285 | 1286 | image-20230202154024196 1287 | 1288 | - 未做子网划分的 ip 地址:网络地址+主机地址 1289 | - 做子网划分后的 ip 地址:网络地址+(子网网络地址+子网主机地址) 1290 | 1291 | 假设对 C 类地址进行子网划分,网络地址 192.168.1.0,使用子网掩码 255.255.255.192 对其进行子网划分。 1292 | 1293 | C 类地址中前 24 位是网络号,最后 8 位是主机号,根据子网掩码可知**从 8 位主机号中借用 2 位作为子网号**。 1294 | 1295 | image-20230202154234836 1296 | 1297 | 由于子网网络地址被划分成 2 位,那么子网地址就有 4 个,分别是 00、01、10、11,具体划分如下图: 1298 | 1299 | ![image-20230202155810856](计网八股.assets/image-20230202155810856.png) 1300 | 1301 | 划分后的 4 个子网如下表格: 1302 | 1303 | image-20230202155838360 1304 | 1305 | ### 公有 IP 地址与私有 IP 地址 1306 | 1307 | 在 A、B、C 分类地址,实际上有分公有 IP 地址和私有 IP 地址。 1308 | 1309 | image-20230202160019143 1310 | 1311 | 平时我们办公室、家里、学校用的 IP 地址,一般都是私有 IP 地址。因为这些地址允许组织内部的 IT 人员自己管理、自己分配,而且可以重复。因此,你学校的某个私有 IP 地址和我学校的可以是一样的。 1312 | 1313 | 就像每个小区都有自己的楼编号和门牌号,你小区家可以叫 1 栋 101 号,我小区家也可以叫 1 栋 101,没有任何问题。但一旦出了小区,就需要带上中山路 666 号(公网 IP 地址),是国家统一分配的,不能两个小区都叫中山路 666。 1314 | 1315 | 所以,公有 IP 地址是有个组织统一分配的,假设你要开一个博客网站,那么你就需要去申请购买一个公有 IP,这样全世界的人才能访问。并且公有 IP 地址基本上要在整个互联网范围内保持唯一。 1316 | 1317 | image-20230202160146438 1318 | 1319 | ### IP 地址与路由控制 1320 | 1321 | IP地址的**网络地址**这一部分是用于进行路由控制。 1322 | 1323 | 路由控制表中记录着网络地址与下一步应该发送至路由器的地址。在主机和路由器上都会有各自的路由器控制表。 1324 | 1325 | 在发送 IP 包时,首先要确定 IP 包首部中的目标地址,再从路由控制表中找到与该地址具有**相同网络地址**的记录,根据该记录将 IP 包转发给相应的下一个路由器。如果路由控制表中存在多条相同网络地址的记录,就选择相同位数最多的网络地址,也就是最长匹配。 1326 | 1327 | 下面以下图的网络链路作为例子说明: 1328 | 1329 | ![image-20230202210614068](计网八股.assets/image-20230202210614068.png) 1330 | 1331 | 1. 主机 A 要发送一个 IP 包,其源地址是 `10.1.1.30` 和目标地址是 `10.1.2.10`,由于没有在主机 A 的路由表找到与目标地址 `10.1.2.10` 相同的网络地址,于是包被转发到默认路由(路由器 `1` ) 1332 | 2. 路由器 `1` 收到 IP 包后,也在路由器 `1` 的路由表匹配与目标地址相同的网络地址记录,发现匹配到了,于是就把 IP 数据包转发到了 `10.1.0.2` 这台路由器 `2` 1333 | 3. 路由器 `2` 收到后,同样对比自身的路由表,发现匹配到了,于是把 IP 包从路由器 `2` 的 `10.1.2.1` 这个接口出去,最终经过交换机把 IP 数据包转发到了目标主机 1334 | 1335 | > 环回地址是不会流向网络 1336 | 1337 | 环回地址是在同一台计算机上的程序之间进行网络通信时所使用的一个默认地址。 1338 | 1339 | 计算机使用一个特殊的 IP 地址 **127.0.0.1 作为环回地址**。与该地址具有相同意义的是一个叫做 `localhost` 的主机名。使用这个 IP 或主机名时,数据包不会流向网络。 1340 | 1341 | ### IP 分片与重组 1342 | 1343 | 每种数据链路的最大传输单元 `MTU` 都是不相同的,如 FDDI 数据链路 MTU 4352、以太网的 MTU 是 1500 字节等。 1344 | 1345 | 每种数据链路的 MTU 之所以不同,是因为每个不同类型的数据链路的使用目的不同。使用目的不同,可承载的 MTU 也就不同。 1346 | 1347 | 其中,我们最常见数据链路是以太网,它的 MTU 是 `1500` 字节。 1348 | 1349 | 那么当 IP 数据包大小大于 MTU 时, IP 数据包就会被分片。 1350 | 1351 | 经过分片之后的 IP 数据报在被重组的时候,只能由目标主机进行,路由器是不会进行重组的。 1352 | 1353 | 假设发送方发送一个 4000 字节的大数据报,若要传输在以太网链路,则需要把数据报分片成 3 个小数据报进行传输,再交由接收方重组成大数据报。 1354 | 1355 | ![image-20230202212457439](计网八股.assets/image-20230202212457439.png) 1356 | 1357 | 在分片传输中,一旦某个分片丢失,则会造成整个 IP 数据报作废,所以 TCP 引入了 `MSS` 也就是在 TCP 层进行分片不由 IP 层分片,那么对于 UDP 我们尽量不要发送一个大于 `MTU` 的数据报文。 1358 | 1359 | ### IPv6 基本认识 1360 | 1361 | IPv4 的地址是 32 位的,大约可以提供 42 亿个地址,但是早在 2011 年 IPv4 地址就已经被分配完了。 1362 | 1363 | 但是 IPv6 的地址是 `128` 位的,这可分配的地址数量是大的惊人,说个段子 **IPv6 可以保证地球上的每粒沙子都能被分配到一个 IP 地址。** 1364 | 1365 | 但 IPv6 除了有更多的地址之外,还有更好的安全性和扩展性,说简单点就是 IPv6 相比于 IPv4 能带来更好的网络体验。 1366 | 1367 | 但是因为 IPv4 和 IPv6 不能相互兼容,所以不但要我们电脑、手机之类的设备支持,还需要网络运营商对现有的设备进行升级,所以这可能是 IPv6 普及率比较慢的一个原因。 1368 | 1369 | > IPv6 的亮点 1370 | 1371 | IPv6 不仅仅只是可分配的地址变多了,它还有非常多的亮点。 1372 | 1373 | - IPv6 可自动配置,即使没有 DHCP 服务器也可以实现自动分配IP地址,真是**便捷到即插即用**啊。 1374 | - IPv6 包头包首部长度采用固定的值 `40` 字节,去掉了包头校验和,简化了首部结构,减轻了路由器负荷,大大**提高了传输的性能**。 1375 | - IPv6 有应对伪造 IP 地址的网络安全功能以及防止线路窃听的功能,大大**提升了安全性**。 1376 | - **...** (由你发现更多的亮点) 1377 | 1378 | > IPv6 地址的标识方法 1379 | 1380 | IPv4 地址长度共 32 位,是以每 8 位作为一组,并用点分十进制的表示方式。 1381 | 1382 | IPv6 地址长度是 128 位,是以每 16 位作为一组,每组用冒号 「:」 隔开。 1383 | 1384 | ![image-20230202212640247](计网八股.assets/image-20230202212640247.png) 1385 | 1386 | 如果出现连续的 0 时还可以将这些 0 省略,并用两个冒号 「::」隔开。但是,一个 IP 地址中只允许出现一次两个连续的冒号。 1387 | 1388 | ![image-20230202212656485](计网八股.assets/image-20230202212656485.png) 1389 | 1390 | > IPv6 地址的结构 1391 | 1392 | IPv6 类似 IPv4,也是通过 IP 地址的前几位标识 IP 地址的种类。 1393 | 1394 | IPv6 的地址主要有以下类型地址: 1395 | 1396 | - 单播地址,用于一对一的通信 1397 | - 组播地址,用于一对多的通信 1398 | - 任播地址,用于通信最近的节点,最近的节点是由路由协议决定 1399 | - 没有广播地址 1400 | 1401 | image-20230202212840841 1402 | 1403 | > IPv6 单播地址类型 1404 | 1405 | 对于一对一通信的 IPv6 地址,主要划分了三类单播地址,每类地址的有效范围都不同。 1406 | 1407 | - 在同一链路单播通信,不经过路由器,可以使用**链路本地单播地址**,IPv4 没有此类型 1408 | - 在内网里单播通信,可以使用**唯一本地地址**,相当于 IPv4 的私有 IP 1409 | - 在互联网通信,可以使用**全局单播地址**,相当于 IPv4 的公有 IP 1410 | 1411 | image-20230202213116722 1412 | 1413 | ### IPv4 首部与 IPv6 首部 1414 | 1415 | IPv4 首部与 IPv6 首部的差异如下图: 1416 | 1417 | ![image-20230202213138742](计网八股.assets/image-20230202213138742.png) 1418 | 1419 | IPv6 相比 IPv4 的首部改进: 1420 | 1421 | - **取消了首部校验和字段。** 因为在数据链路层和传输层都会校验,因此 IPv6 直接取消了 IP 的校验。 1422 | - **取消了分片/重新组装相关字段。** 分片与重组是耗时的过程,IPv6 不允许在中间路由器进行分片与重组,这种操作只能在源与目标主机,这将大大提高了路由器转发的速度。 1423 | - **取消选项字段。** 选项字段不再是标准 IP 首部的一部分了,但它并没有消失,而是可能出现在 IPv6 首部中的「下一个首部」指出的位置上。删除该选项字段使的 IPv6 的首部成为固定长度的 `40` 字节。 1424 | 1425 | ## IP 协议相关技术 1426 | 1427 | 跟 IP 协议相关的技术也不少,接下来说说与 IP 协议相关的重要且常见的技术。 1428 | 1429 | - DNS 域名解析 1430 | - ARP 与 RARP 协议 1431 | - DHCP 动态获取 IP 地址 1432 | - NAT 网络地址转换 1433 | - ICMP 互联网控制报文协议 1434 | - IGMP 因特网组管理协议 1435 | 1436 | ### DNS 1437 | 1438 | 我们在上网的时候,通常使用的方式是域名,而不是 IP 地址,因为域名方便人类记忆。 1439 | 1440 | 那么实现这一技术的就是 **DNS 域名解析**,DNS 可以将域名网址自动转换为具体的 IP 地址。 1441 | 1442 | > 域名的层级关系 1443 | 1444 | DNS 中的域名都是用**句点**来分隔的,比如 `www.server.com`,这里的句点代表了不同层次之间的**界限**。 1445 | 1446 | 在域名中,**越靠右**的位置表示其层级**越高**。 1447 | 1448 | ### ARP 1449 | 1450 | 在传输一个 IP 数据报的时候,确定了源 IP 地址和目标 IP 地址后,就会通过主机「路由表」确定 IP 数据包下一跳。然而,网络层的下一层是数据链路层,所以我们还要知道「下一跳」的 MAC 地址。 1451 | 1452 | 由于主机的路由表中可以找到下一跳的 IP 地址,所以可以通过 **ARP 协议**,求得下一跳的 MAC 地址。 1453 | 1454 | > 那么 ARP 又是如何知道对方 MAC 地址的呢? 1455 | 1456 | 简单地说,ARP 是借助 **ARP 请求与 ARP 响应**两种类型的包确定 MAC 地址的。 1457 | 1458 | image-20230202215911544 1459 | 1460 | - 主机会通过**广播发送 ARP 请求**,这个包中包含了想要知道的 MAC 地址的主机 IP 地址。 1461 | - 当同个链路中的所有设备收到 ARP 请求时,会去拆开 ARP 请求包里的内容,如果 ARP 请求包中的目标 IP 地址与自己的 IP 地址一致,那么这个设备就将自己的 MAC 地址塞入 **ARP 响应包**返回给主机。 1462 | 1463 | 操作系统通常会把第一次通过 ARP 获取的 MAC 地址缓存起来,以便下次直接从缓存中找到对应 IP 地址的 MAC 地址。 1464 | 1465 | 不过,MAC 地址的缓存是有一定期限的,超过这个期限,缓存的内容将被清除。 1466 | 1467 | > RARP 协议你知道是什么吗? 1468 | 1469 | ARP 协议是已知 IP 地址求 MAC 地址,那 RARP 协议正好相反,它是**已知 MAC 地址求 IP 地址**。例如将打印机服务器等小型嵌入式设备接入到网络时就经常会用得到。 1470 | 1471 | 通常这需要架设一台 `RARP` 服务器,在这个服务器上注册设备的 MAC 地址及其 IP 地址。然后再将这个设备接入到网络,接着: 1472 | 1473 | - 该设备会发送一条「我的 MAC 地址是XXXX,请告诉我,我的IP地址应该是什么」的请求信息。 1474 | - RARP 服务器接到这个消息后返回「MAC地址为 XXXX 的设备,IP地址为 XXXX」的信息给这个设备。 1475 | 1476 | 最后,设备就根据从 RARP 服务器所收到的应答信息设置自己的 IP 地址。 1477 | 1478 | image-20230202220026412 1479 | 1480 | ### DHCP 1481 | 1482 | DHCP 在生活中我们是很常见的了,我们的电脑通常都是通过 DHCP 动态获取 IP 地址,大大省去了配 IP 信息繁琐的过程。 1483 | 1484 | 接下来,我们来看看我们的电脑是如何通过 4 个步骤的过程,获取到 IP 的。 1485 | 1486 | ![image-20230202220049827](计网八股.assets/image-20230202220049827.png) 1487 | 1488 | 先说明一点,DHCP 客户端进程监听的是 68 端口号,DHCP 服务端进程监听的是 67 端口号。 1489 | 1490 | 这 4 个步骤: 1491 | 1492 | - 客户端首先发起 **DHCP 发现报文(DHCP DISCOVER)** 的 IP 数据报,由于客户端没有 IP 地址,也不知道 DHCP 服务器的地址,所以使用的是 UDP **广播**通信,其使用的广播目的地址是 255.255.255.255(端口 67) 并且使用 0.0.0.0(端口 68) 作为源 IP 地址。DHCP 客户端将该 IP 数据报传递给链路层,链路层然后将帧广播到所有的网络中设备。 1493 | - DHCP 服务器收到 DHCP 发现报文时,用 **DHCP 提供报文(DHCP OFFER)** 向客户端做出响应。该报文仍然使用 IP 广播地址 255.255.255.255,该报文信息携带服务器提供可租约的 IP 地址、子网掩码、默认网关、DNS 服务器以及 **IP 地址租用期**。 1494 | - 客户端收到一个或多个服务器的 DHCP 提供报文后,从中选择一个服务器,并向选中的服务器发送 **DHCP 请求报文(DHCP REQUEST**进行响应,回显配置的参数。 1495 | - 最后,服务端用 **DHCP ACK 报文**对 DHCP 请求报文进行响应,应答所要求的参数。 1496 | 1497 | 一旦客户端收到 DHCP ACK 后,交互便完成了,并且客户端能够在租用期内使用 DHCP 服务器分配的 IP 地址。 1498 | 1499 | 如果租约的 DHCP IP 地址快期后,客户端会向服务器发送 DHCP 请求报文: 1500 | 1501 | - 服务器如果同意继续租用,则用 DHCP ACK 报文进行应答,客户端就会延长租期。 1502 | - 服务器如果不同意继续租用,则用 DHCP NACK 报文,客户端就要停止使用租约的 IP 地址。 1503 | 1504 | 可以发现,DHCP 交互中,**全程都是使用 UDP 广播通信**。 1505 | 1506 | > 咦,用的是广播,那如果 DHCP 服务器和客户端不是在同一个局域网内,路由器又不会转发广播包,那不是每个网络都要配一个 DHCP 服务器? 1507 | 1508 | 所以,为了解决这一问题,就出现了 **DHCP 中继代理**。有了 DHCP 中继代理以后,**对不同网段的 IP 地址分配也可以由一个 DHCP 服务器统一进行管理。** 1509 | 1510 | image-20230202220236535 1511 | 1512 | - DHCP 客户端会向 DHCP 中继代理发送 DHCP 请求包,而 DHCP 中继代理在收到这个广播包以后,再以**单播**的形式发给 DHCP 服务器。 1513 | - 服务器端收到该包以后再向 DHCP 中继代理返回应答,并由 DHCP 中继代理将此包广播给 DHCP 客户端 。 1514 | 1515 | 因此,DHCP 服务器即使不在同一个链路上也可以实现统一分配和管理IP地址。 1516 | 1517 | ### NAT 1518 | 1519 | IPv4 的地址是非常紧缺的,在前面我们也提到可以通过无分类地址来减缓 IPv4 地址耗尽的速度,但是互联网的用户增速是非常惊人的,所以 IPv4 地址依然有被耗尽的危险。 1520 | 1521 | 于是,提出了一种**网络地址转换 NAT** 的方法,再次缓解了 IPv4 地址耗尽的问题。 1522 | 1523 | 简单的来说 NAT 就是同个公司、家庭、教室内的主机对外部通信时,把私有 IP 地址转换成公有 IP 地址。 1524 | 1525 | ![image-20230202222440843](计网八股.assets/image-20230202222440843.png) 1526 | 1527 | > 那不是 N 个私有 IP 地址,你就要 N 个公有 IP 地址?这怎么就缓解了 IPv4 地址耗尽的问题?这不瞎扯吗? 1528 | 1529 | 确实是,普通的 NAT 转换没什么意义。 1530 | 1531 | 由于绝大多数的网络应用都是使用传输层协议 TCP 或 UDP 来传输数据的。 1532 | 1533 | 因此,可以把 IP 地址 + 端口号一起进行转换。 1534 | 1535 | 这样,就用一个全球 IP 地址就可以了,这种转换技术就叫**网络地址与端口转换 NAPT。** 1536 | 1537 | 很抽象?来,看下面的图解就能瞬间明白了。 1538 | 1539 | ![image-20230202222526258](计网八股.assets/image-20230202222526258.png) 1540 | 1541 | 图中有两个客户端 192.168.1.10 和 192.168.1.11 同时与服务器 183.232.231.172 进行通信,并且这两个客户端的本地端口都是 1025。 1542 | 1543 | 此时,**两个私有 IP 地址都转换 IP 地址为公有地址 120.229.175.121,但是以不同的端口号作为区分。** 1544 | 1545 | 于是,生成一个 NAPT 路由器的转换表,就可以正确地转换地址跟端口的组合,令客户端 A、B 能同时与服务器之间进行通信。 1546 | 1547 | 这种转换表在 NAT 路由器上自动生成。例如,在 TCP 的情况下,建立 TCP 连接首次握手时的 SYN 包一经发出,就会生成这个表。而后又随着收到关闭连接时发出 FIN 包的确认应答从表中被删除。 1548 | 1549 | > NAT 那么牛逼,难道就没缺点了吗? 1550 | 1551 | 当然有缺陷,肯定没有十全十美的方案。 1552 | 1553 | 由于 NAT/NAPT 都依赖于自己的转换表,因此会有以下的问题: 1554 | 1555 | - 外部无法主动与 NAT 内部服务器建立连接,因为 NAPT 转换表没有转换记录。 1556 | - 转换表的生成与转换操作都会产生性能开销。 1557 | - 通信过程中,如果 NAT 路由器重启了,所有的 TCP 连接都将被重置。 1558 | 1559 | > 如何解决 NAT 潜在的问题呢? 1560 | 1561 | 解决的方法主要有两种方法。 1562 | 1563 | *第一种就是改用 IPv6* 1564 | 1565 | IPv6 可用范围非常大,以至于每台设备都可以配置一个公有 IP 地址,就不搞那么多花里胡哨的地址转换了,但是 IPv6 普及速度还需要一些时间。 1566 | 1567 | *第二种 NAT 穿透技术* 1568 | 1569 | NAT 穿越技术拥有这样的功能,它能够让网络应用程序主动发现自己位于 NAT 设备之后,并且会主动获得 NAT 设备的公有 IP,并为自己建立端口映射条目,注意这些都是 NAT设备后的应用程序自动完成的。 1570 | 1571 | 也就是说,在 NAT 穿透技术中,NAT设备后的应用程序处于主动地位,它已经明确地知道 NAT 设备要修改它外发的数据包,于是它主动配合 NAT 设备的操作,主动地建立好映射,这样就不像以前由 NAT 设备来建立映射了。 1572 | 1573 | 说人话,就是客户端主动从 NAT 设备获取公有 IP 地址,然后自己建立端口映射条目,然后用这个条目对外通信,就不需要 NAT 设备来进行转换了。 1574 | 1575 | ### ICMP 1576 | 1577 | ICMP 全称是 **Internet Control Message Protocol**,也就是**互联网控制报文协议**。 1578 | 1579 | 里面有个关键词 —— **控制**,如何控制的呢? 1580 | 1581 | 网络包在复杂的网络传输环境里,常常会遇到各种问题。 1582 | 1583 | 当遇到问题的时候,总不能死个不明不白,没头没脑的作风不是计算机网络的风格。所以需要传出消息,报告遇到了什么问题,这样才可以调整传输策略,以此来控制整个局面。 1584 | 1585 | > ICMP 功能都有啥? 1586 | 1587 | `ICMP` 主要的功能包括:**确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。** 1588 | 1589 | 在 `IP` 通信中如果某个 `IP` 包因为某种原因未能达到目标地址,那么这个具体的原因将**由 ICMP 负责通知**。 1590 | 1591 | image-20230202222709619 1592 | 1593 | 如上图例子,主机 `A` 向主机 `B` 发送了数据包,由于某种原因,途中的路由器 `2` 未能发现主机 `B` 的存在,这时,路由器 `2` 就会向主机 `A` 发送一个 `ICMP` 目标不可达数据包,说明发往主机 `B` 的包未能成功。 1594 | 1595 | ICMP 的这种通知消息会使用 `IP` 进行发送 。 1596 | 1597 | 因此,从路由器 `2` 返回的 ICMP 包会按照往常的路由控制先经过路由器 `1` 再转发给主机 `A` 。收到该 ICMP 包的主机 `A` 则分解 ICMP 的首部和数据域以后得知具体发生问题的原因。 1598 | 1599 | > ICMP 类型 1600 | 1601 | ICMP 大致可以分为两大类: 1602 | 1603 | - 一类是用于诊断的查询消息,也就是「**查询报文类型**」 1604 | - 另一类是通知出错原因的错误消息,也就是「**差错报文类型**」 1605 | 1606 | image-20230202222820668 1607 | 1608 | ### IGMP 1609 | 1610 | ICMP 跟 IGMP 是一点关系都没有的,就好像周杰与周杰伦的区别,大家不要混淆了。 1611 | 1612 | 在前面我们知道了组播地址,也就是 D 类地址,既然是组播,那就说明是只有一组的主机能收到数据包,不在一组的主机不能收到数组包,怎么管理是否是在一组呢?那么,就需要 `IGMP` 协议了。 1613 | 1614 | image-20230202222843196 1615 | 1616 | **IGMP 是因特网组管理协议,工作在主机(组播成员)和最后一跳路由之间**,如上图中的蓝色部分。 1617 | 1618 | - IGMP 报文向路由器申请加入和退出组播组,默认情况下路由器是不会转发组播包到连接中的主机,除非主机通过 IGMP 加入到组播组,主机申请加入到组播组时,路由器就会记录 IGMP 路由器表,路由器后续就会转发组播包到对应的主机了。 1619 | - IGMP 报文采用 IP 封装,IP 头部的协议号为 2,而且 TTL 字段值通常为 1,因为 IGMP 是工作在主机与连接的路由器之间。 1620 | 1621 | > IGMP 工作机制 1622 | 1623 | IGMP 分为了三个版本分别是,IGMPv1、IGMPv2、IGMPv3。 1624 | 1625 | 接下来,以 `IGMPv2` 作为例子,说说**常规查询与响应和离开组播组**这两个工作机制。 1626 | 1627 | *常规查询与响应工作机制* 1628 | 1629 | image-20230202224150400 1630 | 1631 | 1. 路由器会周期性发送目的地址为 `224.0.0.1`(表示同一网段内所有主机和路由器) **IGMP 常规查询报文**。 1632 | 2. 主机1 和 主机 3 收到这个查询,随后会启动「报告延迟计时器」,计时器的时间是随机的,通常是 0~10 秒,计时器超时后主机就会发送 **IGMP 成员关系报告报文**(源 IP 地址为自己主机的 IP 地址,目的 IP 地址为组播地址)。如果在定时器超时之前,收到同一个组内的其他主机发送的成员关系报告报文,则自己不再发送,这样可以减少网络中多余的 IGMP 报文数量。 1633 | 3. 路由器收到主机的成员关系报文后,就会在 IGMP 路由表中加入该组播组,后续网络中一旦该组播地址的数据到达路由器,它会把数据包转发出去。 1634 | 1635 | *离开组播组工作机制* 1636 | 1637 | 离开组播组的情况一,网段中仍有该组播组: 1638 | 1639 | image-20230202224211893 1640 | 1641 | 1. 主机 1 要离开组 224.1.1.1,发送 IGMPv2 离组报文,报文的目的地址是 224.0.0.2(表示发向网段内的所有路由器) 1642 | 2. 路由器 收到该报文后,以 1 秒为间隔连续发送 IGMP 特定组查询报文(共计发送 2 个),以便确认该网络是否还有 224.1.1.1 组的其他成员。 1643 | 3. 主机 3 仍然是组 224.1.1.1 的成员,因此它立即响应这个特定组查询。路由器知道该网络中仍然存在该组播组的成员,于是继续向该网络转发 224.1.1.1 的组播数据包。 1644 | 1645 | 离开组播组的情况二,网段中没有该组播组: 1646 | 1647 | image-20230202224246159 1648 | 1649 | 1. 主机 1 要离开组播组 224.1.1.1,发送 IGMP 离组报文。 1650 | 2. 路由器收到该报文后,以 1 秒为间隔连续发送 IGMP 特定组查询报文(共计发送 2 个)。此时在该网段内,组 224.1.1.1 已经没有其他成员了,因此没有主机响应这个查询。 1651 | 3. 一定时间后,路由器认为该网段中已经没有 224.1.1.1 组播组成员了,将不会再向这个网段转发该组播地址的数据包。 1652 | --------------------------------------------------------------------------------