Parcourir la source

修改了面经

seamew il y a 1 an
Parent
commit
9abbbb2a2b

+ 13 - 0
面经/2023年暑期实习/字节一面.md

@@ -0,0 +1,13 @@
+## 一面
+
+1. tcp三次握手四次挥手
+1. 为什么三次握手,不是两次
+2. kafka为什么这么快
+3. 什么是零拷贝
+4. 进程和线程的区别
+5. 进程和线程的上下文切换
+6. mysql事务隔离级别
+7. mysql事务隔离级别的实现
+8. InnoDB 与 myisam的区别
+8. mysql低层为什么用B+树
+8. 为什么用B树不用二叉树

+ 18 - 7
面经/工作/秋招.md

@@ -1,8 +1,19 @@
-| 公司     | 网站                                                    | 投递时间 | 进度 |
-| -------- | ------------------------------------------------------- | -------- | ---- |
-| 百度     | https://talent.baidu.com/jobs/center                    | 7月10号  | 初筛 |
-| 科大讯飞 | https://campus.iflytek.com/official-pc/delivery         | 7月6号   | 初筛 |
-| oppo     | https://careers.oppo.com/university/oppo/center/history | 7月3号   | 初筛 |
-| 米哈游   | https://campus.mihoyo.com/#/campus/applyRecord          | 7月5号   | 初筛 |
-| 大疆     | https://we.dji.com/zh-CN/user                           | 7月7号   | 初筛 |
+| 公司         | 网站                                                         | 投递时间 | 进度 |
+| ------------ | ------------------------------------------------------------ | -------- | ---- |
+| 百度         | https://talent.baidu.com/jobs/center                         | 7月10号  | 初筛 |
+| 科大讯飞     | https://campus.iflytek.com/official-pc/delivery              | 7月6号   | 初筛 |
+| oppo         | https://careers.oppo.com/university/oppo/center/history      | 7月3号   | 初筛 |
+| 米哈游       | https://campus.mihoyo.com/#/campus/applyRecord               | 7月5号   | 初筛 |
+| 大疆         | https://we.dji.com/zh-CN/user                                | 7月7号   | 初筛 |
+| 小红书       | https://job.xiaohongshu.com/record/campus                    | 7月17号  | 笔试 |
+| 联想         | https://talent.lenovo.com.cn/account/apply                   | 7月17号  | 初筛 |
+| 字节         | https://jobs.bytedance.com/campus/position/application       | 7月17号  | 一面 |
+| 三一重工     | https://sanycampus.m.zhiye.com/#/jobs?code=def8ad56-4f17-48e0-a3bb-d85f9983ca96 | 7月17号  | 初筛 |
+| 长鑫存储     | http://jobs.cxmt.com/Campus                                  | 7月17号  | 初筛 |
+| 网易         | https://campus.163.com/app/personal/apply                    | 7月17号  | 初筛 |
+| 数禾         | https://shuhegroup1.zhiye.com/personal/deliveryRecord        | 7月17号  | 初筛 |
+| 搜狐         | https://app.mokahr.com/campus_apply/cyou-inc/42233?recommendCode=DSJ9ZJhr#/jobs | 7月17号  | 初筛 |
+| 远景科技集团 | https://app.mokahr.com/campus-recruitment/envisiongroup/43123?type=school#/candidateHome/applications | 7月20号  | 初筛 |
+| 多益         | https://xz.duoyi.com/v40/#/positions                         | 7月20号  | 初筛 |
+| 地平线       | https://wecruit.hotjob.cn/SU62d915040dcad43c775ec12c/mc/position/campus?acotycoCode=gmscfw&recruitType=1 | 7月20号  | 初筛 |
 

+ 79 - 5
面经/问答/Mysql.md

@@ -11,8 +11,7 @@
 2. 优化 SQL 语句:优化 SQL 语句可以通过多种方式实现,例如避免使用通配符查询,优化复杂查询语句,避免不必要的连接查询和子查询,合理使用聚合函数和 GROUP BY 子句等。可以通过使用数据库性能分析工具(如 MySQL 自带的慢查询日志)来定位慢查询,并对其进行优化。
 3. 缓存和缓冲技术:通过使用缓存和缓冲技术可以减轻数据库的负载,提高查询性能。可以使用数据库自带的查询缓存功能,或者使用外部缓存技术如 Redis、Memcached 等进行数据缓存,减少对数据库的频繁查询。
 4. 合理配置数据库参数:MySQL 有丰富的配置参数可以进行调优,例如调整缓冲池大小、设置合理的连接数、开启合适的日志等,可以根据实际需求和硬件资源来配置数据库参数,以达到最佳性能。
-5. 定期维护数据库:数据库的性能和稳定性需要定期进行维护,包括定期清理无用的数据和索引,进行数据库备份和日志管理,定期更新统计信息等。定期维护可以保持数据库的健康状态,提升查询性能。
-6. 可以考虑使用分库分表技术,例如对于用户中奖详情单我选择了分库分表,因为有多个用户和不同活动同时竞争这个表,数据库访问压力比较大,所以就要选择分库分表
+6. 可以考虑使用分库分表技术
 
 ## mysql底层数据结构
 
@@ -47,9 +46,12 @@
 在 MySQL 中定义联合索引时,需要注意以下几点:
 
 1. 列顺序:联合索引中的列顺序非常重要,它会影响索引的效果。通常情况下,应该将被频繁用于查询的列放在联合索引的前面,这样可以使索引在查询时更加有效。例如,如果查询中经常同时使用列 A 和列 B 进行查询,那么联合索引应该按照 (A, B) 的顺序定义,而不是 (B, A)。
+
 2. 列选择:不要将过多的列包含在联合索引中,因为索引的大小对数据库性能有影响。过大的索引可能会导致磁盘 I/O 操作增加,并且会占用额外的存储空间,导致性能下降
-3. 索引选择:不是所有的列都适合联合索引。联合索引在使用时会按照索引列的顺序进行匹配,因此只有查询中包含了索引的前缀列时,索引才能被充分利用。因此,应该选择那些在查询中经常一起使用的列进行联合索引定义。
-4. 更新性能:联合索引在更新操作时可能会导致性能下降。因为联合索引涉及多个列,更新其中的一个列可能需要更新整个索引。因此,在定义联合索引时应该考虑更新性能的影响,避免频繁的更新操作导致性能下降。
+
+   * 索引选择:不是所有的列都适合联合索引。联合索引在使用时会按照索引列的顺序进行匹配,因此只有查询中包含了索引的前缀列时,索引才能被充分利用。因此,应该选择那些在查询中经常一起使用的列进行联合索引定义。
+
+   * 更新性能:联合索引在更新操作时可能会导致性能下降。因为联合索引涉及多个列,更新其中的一个列可能需要更新整个索引。因此,在定义联合索引时应该考虑更新性能的影响,避免频繁的更新操作导致性能下降。
 
 ## 数据库ACID
 
@@ -71,9 +73,16 @@ InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?
 
 1. 读未提交 (Read Uncommitted):最低的隔离级别,事务可以读取到其他事务未提交的数据。这种隔离级别可能导致脏读(Dirty Read),即读取到其他事务未提交的数据,可能会引起数据的不一致性。
 2. 读提交 (Read Committed):事务只能读取到其他事务已经提交的数据,保证了数据的一致性。但在该隔离级别下,可能会出现不可重复读(Non-repeatable Read)问题,即在同一事务内多次读取同一行数据时,可能会得到不同的结果。
-3. 可重复读 (Repeatable Read):事务在执行期间对同一行数据进行读取时,会保持一致的结果。其他事务对该行数据的修改只有在当前事务提交后才能生效。这种隔离级别可以避免不可重复读问题,但仍然可能出现幻读(Phantom Read)问题,即在同一事务内多次执行相同的查询时,可能会得到不同的结果。
+3. 可重复读 (Repeatable Read):事务在执行期间对同一行数据进行读取时,会保持一致的结果。其他事务对该行数据的修改只有在当前事务提交后才能生效。这种隔离级别可以避免不可重复读问题,但仍然可能出现幻读(Phantom Read)问题,即在同一事务内多次执行相同的查询时,可能会得到不同的结果。(事务B插入一个数据,事务A进行更新,然后A事务就会读取到这个新插入的数据)
 4. 串行化 (Serializable):最高的隔离级别,事务会对数据库中的数据加锁,防止其他事务对数据的并发访问。这种隔离级别可以避免幻读问题,但可能会导致性能下降,因为事务需要等待其他事务释放锁。
 
+## 数据库隔离级别如何实现
+
+* 读未提交:因为可以读到未提交事务修改的数据,所以直接读取最新的数据就好了;
+* 读提交:通过 **Read View 来实现的**,**「读提交」隔离级别是在「每个语句执行前」都会重新生成一个 Read View**
+* 可重复读:通过 **Read View 来实现的**,**「可重复读」隔离级别是「启动事务时」生成一个 Read View,然后整个事务期间都在用这个 Read View**。
+* 串行化:通过加读写锁的方式来避免并行访问;
+
 ## 数据库MVCC
 
 MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种数据库管理系统(DBMS)中的并发控制机制,用于在多用户并发访问数据库时保障事务的隔离性和一致性。MVCC通过在数据库中为每个事务创建多个版本的数据副本,使多个事务可以同时读取和修改数据库中的数据,而不会相互干扰。
@@ -98,3 +107,68 @@ MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种数
 - 按「物理存储」分类:**聚簇索引(主键索引)、二级索引(辅助索引)(非聚簇索引)**。
 - 按「字段特性」分类:**主键索引、唯一索引、普通索引、前缀索引**。
 - 按「字段个数」分类:**单列索引、联合索引**。
+
+## MySQL为什么使用B+树而不是使用B树
+
+- B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O次数会更少。
+- B+ 树有大量的冗余节点(所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删除的效率都更高,比如删除根节点的时候,不会像 B 树那样会发生复杂的树的变化;
+- B+ 树叶子节点之间用链表连接了起来,有利于范围查询,而 B 树要实现范围查询,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。
+
+## MySQL 有哪些锁?
+
+###  表级锁
+
+* 表锁
+* 元数据锁
+* 意向锁
+
+当执行插入、更新、删除操作,需要先对表加上「意向独占锁」,然后对该记录加独占锁。
+
+*  AUTO-INC 锁
+
+### 行级锁
+
+*  Record Lock
+
+**记录锁,锁住单行数据**
+
+*  Gap Lock
+
+**间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻影记录而提出的**
+
+*  Next-Key Lock
+
+**next-key lock 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的**
+
+* 插入意向锁
+
+一个事务在插入一条记录的时候,需要判断插入位置是否已被其他事务加了间隙锁(next-key lock 也包含间隙锁)。
+
+如果有的话,插入操作就会发生**阻塞**,直到拥有间隙锁的那个事务提交为止(释放间隙锁的时刻),在此期间会生成一个**插入意向锁**,表明有事务想在某个区间插入新记录,但是现在处于等待状态。
+
+## InnoDB 和 MyISAM 引擎的区别
+
+InnoDB和MyISAM是MySQL数据库中常见的两种存储引擎,它们有一些重要的区别。
+
+1. 事务支持:InnoDB引擎支持事务,可以使用ACID(原子性、一致性、隔离性和持久性)特性来确保数据的完整性和一致性。而MyISAM引擎不支持事务,它的操作是自动提交的,无法回滚。
+2. 锁的等级:InnoDB引擎在高并发读写操作下表现更好。它支持行级锁定,可以同时处理多个并发操作,从而提高了数据库的并发性能。相比之下,MyISAM引擎只支持表级锁定,当某个操作在修改表时,其他操作必须等待,可能导致并发性能下降。
+3. 外键约束:InnoDB引擎通过外键约束来保持数据完整性,可以定义外键关系。而MyISAM引擎不支持外键约束,需要在应用层面来维护数据的完整性。
+4. 崩溃恢复:InnoDB引擎具有崩溃恢复能力,可以保证数据库在崩溃后更好地恢复。MyISAM引擎在崩溃后恢复较差,可能会导致数据丢失或损坏。(因为使用了表级锁,非聚簇索引。而InnoDB使用redo log恢复)
+5. 全文搜索功能:MyISAM引擎支持全文索引和全文搜索功能,可以对文本进行高效的全文搜索。而InnoDB引擎在MySQL 5.6版本之前不支持全文索引,需要借助其他插件或引擎来实现全文搜索。
+5. 索引结构:InnoDB引擎使用聚簇索引(Clustered Index),将数据存储在主键的索引树中,可以提高查询效率。而MyISAM引擎使用非聚簇索引(Non-clustered Index),索引文件和数据文件分开存储。
+
+## 什么是全文索引
+
+使用`like%xxx%`进行模糊查询时,字段的索引就会失效。因此,在数据量大的情况下,通过此种方式查询的效率极低。这个时候,就可通过全文索引(Full-Text Search)来进行优化。
+
+全文索引(Full-Text Search)是将存储于数据库中的整本书或整篇文章中的任意信息查找出来的技术。它可以根据需要获得全文中有关章、节、段、句、词等信息,也可以进行各种统计和分析。
+
+全文索引一般是通过倒排索引实现的,倒排索引如同 B+Tree 一样,也是一种索引结构。它在辅助表中存储了单词与单词自身在一个或多个文档中所在位置之间的映射,这通常利用关联数组实现,拥有两种表现形式:
+
+```sql
+    inverted file index:{单词,单词所在文档的id}
+
+    full inverted index:{单词,(单词所在文档的id,再具体文档中的位置)}
+```
+
+创建索引之后可以在索引中查找与关键词匹配的文档列表。可以使用布尔检索、向量空间模型或其他检索算法来计算文档与关键词的相关性,并返回匹配度高的文档结果。

BIN
面经/问答/assets/1cc7401143e79383ead96582ac11b615.png


BIN
面经/问答/assets/454a8228a6549176ad7e0484fba3c92b.png


BIN
面经/问答/assets/8febac10b14bed16cb96d1d944cd08da.png


BIN
面经/问答/assets/98987d9417b2bab43087f45fc959d32a-20230309232253633.png


BIN
面经/问答/assets/cc208c2931b4e889d1a58cb655537767.png


BIN
面经/问答/assets/f67379b60d151262753fec3b817b8617-20230309232312657.png


BIN
面经/问答/assets/jmm.png


+ 1 - 1
面经/问答/kafka.md

@@ -14,7 +14,7 @@ kafka的存储方案是**顺序追加写日志 + 稀疏哈希索引**
 
 /index文件中会生成三份文件XX.log,XX.index,XX.timeindex,日志是分块存储的,时间戳索引从XX.timeindex中找到对应的offset,再从XX.index中索引XX.log。
 
-### 页缓存
+### 页缓存--pagecache
 
 页缓存相对来说比较简单,页缓存在操作系统层面是保存数据的一个基本单位,Kafka 避免使用 JVM,直接使用操作系统的页缓存特性提高处理速度,进而避免了JVM GC 带来的性能损耗。
 

+ 205 - 0
面经/问答/redis.md

@@ -0,0 +1,205 @@
+## redis过期删除策略
+
+Redis 是可以对 key 设置过期时间的,因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略。
+
+在说 Redis 过期删除策略之前,先跟大家介绍下,常见的三种过期删除策略:
+
+### 定时删除
+
+在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作**
+
+定时删除策略的**优点**:
+
+- 可以保证过期 key 会被尽快删除,也就是内存可以被尽快地释放。因此,定时删除对内存是最友好的。
+
+定时删除策略的**缺点**:
+
+- 在过期 key 比较多的情况下,删除过期 key 可能会占用相当一部分 CPU 时间,在内存不紧张但 CPU 时间紧张的情况下,将 CPU 时间用于删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。所以,定时删除策略对 CPU 不友好。
+
+### 惰性删除
+
+不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。**
+
+惰性删除策略的**优点**:
+
+- 因为每次访问时,才会检查 key 是否过期,所以此策略只会使用很少的系统资源,因此,惰性删除策略对 CPU 时间最友好。
+
+惰性删除策略的**缺点**:
+
+- 如果一个 key 已经过期,而这个 key 又仍然保留在数据库中,那么只要这个过期 key 一直没有被访问,它所占用的内存就不会释放,造成了一定的内存空间浪费。所以,惰性删除策略对内存不友好。
+
+### 定期删除:
+每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。
+
+定期删除策略的**优点**:
+
+- 通过限制删除操作执行的时长和频率,来减少删除操作对 CPU 的影响,同时也能删除一部分过期的数据减少了过期键对空间的无效占用。
+
+定期删除策略的**缺点**:
+
+- 内存清理方面没有定时删除效果好,同时没有惰性删除使用的系统资源少。
+- 难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好;如果执行的太少,那又和惰性删除一样了,过期 key 占用的内存不会及时得到释放。
+
+**Redis 选择「惰性删除+定期删除」这两种策略配和使用**
+
+## redis内存淘汰策略
+
+*1、不进行数据淘汰的策略*
+
+**noeviction**(Redis3.0之后,默认的内存淘汰策略) :它表示当运行内存超过最大设置内存时,不淘汰任何数据,这时如果有新的数据写入,则会触发 OOM,但是如果没用数据写入的话,只是单纯的查询或者删除操作的话,还是可以正常工作。
+
+*2、进行数据淘汰的策略*
+
+针对「进行数据淘汰」这一类策略,又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。
+
+在设置了过期时间的数据中进行淘汰:
+
+- **volatile-random**:随机淘汰设置了过期时间的任意键值;
+- **volatile-ttl**:优先淘汰更早过期的键值。
+- **volatile-lru**(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,最久未使用的键值;
+- **volatile-lfu**(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,最少使用的键值
+
+##  Redis 持久化
+
+Redis 共有三种数据持久化的方式:
+
+- **AOF 日志**:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
+- **RDB 快照**:将某一时刻的内存数据,以二进制的方式写入磁盘;
+- **混合持久化方式**:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点;
+
+### AOF
+
+![img](assets/98987d9417b2bab43087f45fc959d32a-20230309232253633.png)
+
+> AOF 日志过大,会触发什么机制?
+
+Redis 为了避免 AOF 文件越写越大,提供了 **AOF 重写机制**,当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。
+
+AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。
+
+### RDB
+
+RDB 快照就是记录某一个瞬间的内存数据,记录的是实际数据,
+
+Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave,他们的区别就在于是否在「主线程」里执行:
+
+- 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,**会阻塞主线程**;
+- 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以**避免主线程的阻塞**;
+
+Redis 还可以通过配置文件的选项来实现每隔一段时间自动执行一次 bgsave 命令,默认会提供以下配置:
+
+```c
+save 900 1
+save 300 10
+save 60 10000
+```
+
+执行 bgsave 过程中,Redis 依然**可以继续处理操作命令**的。关键的技术就在于**写时复制技术**
+
+如果主线程(父进程)要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,于是这块数据的物理内存就会被复制一份(键值对 A'),然后主线程在这个数据副本(键值对 A')进行修改操作。与此同时,bgsave 子进程可以继续把原来的数据(键值对 A)写入到 RDB 文件。
+
+###  为什么会有混合持久化?
+
+Redis 4.0 提出了**混合使用 AOF 日志和内存快照**
+
+AOF 文件的**前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据**。
+
+![img](assets/f67379b60d151262753fec3b817b8617-20230309232312657.png)
+
+## Redis安全控制
+
+### 缓存穿透--没有key
+
+产生的背景:
+
+缓存穿透是指使用不存在的key进行大量的高并发查询,导致缓存无法命中,每次请求都要都要穿透到后端数据库查询(同时也查不到),使得数据库的压力非常大,甚至导致数据库服务压死;**既不在缓存中,也不在数据库中**
+
+解决方案:
+
+1. 接口层实现api限流、用户授权、id检查等;
+2. 从缓存和数据库都取不到数据的话,一样将数据库空值放入缓存中,设置30s有效期避免使用同一个id对数据库攻击压力大;
+3. 布隆过滤器
+
+### 缓存击穿--单个key
+
+产生背景:
+
+在高并发的情况下,当一个缓存key过期时,因为访问该key请求较大,多个请求同时发现缓存过期,因此对多个请求同时数据库查询、同时向Redis写入缓存数据,这样会导致数据库的压力非常大;
+
+解决方案:
+
+1. 使用分布式锁
+
+保证在分布式情况下,使用分布式锁保证对于每个key同时只允许只有一个线程查询到后端服务,其他没有获取到锁的权限,只需要等待即可;这种高并发压力直接转移到分布式锁上,对分布式锁的压力非常大。
+
+2. 使用本地锁
+
+使用本地锁与分布式锁机制一样,只不过分布式锁适应于服务集群、本地锁仅限于单个服务使用。
+
+3. 软过过期 
+
+设置热点数据永不过期或者异步延长过期时间;
+
+4. 布隆过滤器
+
+ 
+
+### 缓存雪崩--多个key
+
+缓存雪崩指缓存服务器重启或者大量的缓存集中在某个时间段失效,突然给数据库产生了巨大的压力,甚至击垮数据库的情况。
+
+解决思路:对不用的数据使用不同的失效时间,加上随机数
+
+## 数据库和缓存如何保证一致性?
+
+### 先更新数据库,再更新缓存
+
+![图片](assets/8febac10b14bed16cb96d1d944cd08da.png)
+
+**出现了缓存和数据库中的数据不一致的现象**。
+
+### 先更新缓存,再更新数据库
+
+![图片](assets/454a8228a6549176ad7e0484fba3c92b.png)
+
+**出现了缓存和数据库中的数据不一致的现象**。
+
+### 先删除缓存,再更新数据库
+
+![图片](assets/cc208c2931b4e889d1a58cb655537767.png)
+
+**先删除缓存,再更新数据库,在「读 + 写」并发的时候,还是会出现缓存和数据库的数据不一致的问题**。
+
+### 先更新数据库,再删除缓存
+
+![图片](assets/1cc7401143e79383ead96582ac11b615.png)
+
+##  说说常见的缓存更新策略?
+
+### Cache Aside(旁路缓存)策略
+
+**写策略的步骤:**
+
+- 先更新数据库中的数据,再删除缓存中的数据。
+
+**读策略的步骤:**
+
+- 如果读取的数据命中了缓存,则直接返回数据;
+- 如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。
+
+### Read/Write Through(读穿 / 写穿)策略
+
+***1、Read Through 策略***
+
+先查询缓存中数据是否存在,如果存在则直接返回,如果不存在,则由缓存组件负责从数据库查询数据,并将结果写入到缓存组件,最后缓存组件将数据返回给应用。
+
+***2、Write Through 策略***
+
+当有数据更新的时候,先查询要写入的数据在缓存中是否已经存在:
+
+- 如果缓存中数据已经存在,则更新缓存中的数据,并且由缓存组件同步更新到数据库中,然后缓存组件告知应用程序更新完成。
+- 如果缓存中数据不存在,直接更新数据库,然后返回;
+
+### Write Back(写回)策略;
+
+Write Back(写回)策略在更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不会更新数据库。对于数据库的更新,会通过批量异步更新的方式进行。

+ 57 - 0
面经/问答/spring.md

@@ -0,0 +1,57 @@
+## 什么是spring
+
+Spring 是一款开源的轻量级 Java 开发框架,它使创建 Java 企业应用程序变得容易。它提供了在企业环境中使用 Java 语言所需的一切,并可以根据应用程序的需求灵活地创建多种架构。
+
+Spring 提供的核心功能主要是 IoC 和 AOP
+
+## Spring IoC
+
+**IoC(Inversion of Control:控制反转)** 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。
+
+**为什么叫控制反转?**
+
+- **控制**:指的是对象创建(实例化、管理)的权力
+- **反转**:控制权交给外部环境(Spring 框架、IoC 容器)
+
+## 依赖查找和依赖注入的区别是什么?
+
+* 依赖查找:主动查找依赖的方式。依赖查找的对象包含spring beandefinition以及单例对象
+* 依赖注入:spring容易自动依赖绑定的方式。依赖注入还包含Resolvable Dependency以及@value对象
+
+## Spring Bean 作用域
+
+| 作用域        | 说明                                                       |
+| ------------- | ---------------------------------------------------------- |
+| **singleton** | 默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例 |
+| **prototype** | 原型作用域,每次依赖查找和依赖注入生成新 Bean 对象         |
+| request       | 将 Spring Bean 存储在 ServletRequest 上下文中              |
+| session       | 将 Spring Bean 存储在 HttpSession 中                       |
+| application   | 将 Spring Bean 存储在 ServletContext 中                    |
+
+## @Autowired 和 @Resource 的区别是什么?
+
+* `@Autowired` 是 Spring 提供的注解,`@Resource` 是 JDK 提供的注解。
+
+* `Autowired` 默认的注入方式为`byType`(根据类型进行匹配),`@Resource`默认注入方式为 `byName`(根据名称进行匹配)。
+
+* 当一个接口存在多个实现类的情况下,`@Autowired` 和`@Resource`都需要通过名称才能正确匹配到对应的 Bean。`Autowired` 可以通过 `@Qualifier` 注解来显式指定名称,`@Resource`可以通过 `name` 属性来显式指定名称。
+
+## Bean 是线程安全的吗?
+
+Spring 框架中的 Bean 是否线程安全,取决于其作用域和状态。
+
+大部分 Bean 实际都是无状态(没有定义可变的成员变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。
+
+对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法:
+
+1. 在 Bean 中尽量避免定义可变的成员变量。
+2. 在类中定义一个 `ThreadLocal` 成员变量,将需要的可变成员变量保存在 `ThreadLocal` 中(推荐的一种方式)。 
+
+## Spring Boot自动装配原理
+
+1. 在Spring Boot项目中有一个注解`@SpringBootApplication`,这个注解是对三个注解进行了封装:`@SpringBootConfiguration`、`@EnableAutoConfiguration`、`@ComponentScan`
+   其中`@EnableAutoConfiguration`是实现自动化配置的核心注解。
+2. 该注解通过`@Import`注解导入`AutoConfigurationImportSelector`,这个类实现了一个导入器接口`ImportSelector`。在该接口中存在一个方法`selectImports`,
+3. 该方法的返回值是一个数组,数组中存储的就是要被导入到`spring`容器中的类的全类名。在`AutoConfigurationImportSelector`类中重写了这个方法,
+4. 该方法内部就是读取了项目的`classpath`路径下`META-INF/spring.factories`文件中的所配置的类的全类名。
+5. 在这些配置类中所定义的`Bean`会根据条件注解所指定的条件来决定是否需要将其导入到`Spring`容器中。

+ 10 - 0
面经/问答/基础.md

@@ -26,3 +26,13 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种
 线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换
 
 ![image-20230508161022413](assets/image-20230508161022413.png)
+
+## JMM内存管理
+
+**Java 内存模型(JMM)** 抽象了线程和主内存之间的关系,就比如说线程之间的共享变量必须存储在主内存中。
+
+**主内存**:所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量)
+
+**本地内存**:每个线程都有一个私有的本地内存来存储共享变量的副本,并且,每个线程只能访问自己的本地内存,无法访问其他线程的本地内存。本地内存是 JMM 抽象出来的一个概念,存储了主内存中的共享变量副本。
+
+![JMM(Java 内存模型)](assets/jmm.png)

+ 32 - 2
面经/问答/操作系统.md

@@ -90,8 +90,8 @@ TLB即为页表缓存、转址旁路缓存、快表等。有了 TLB 后,那么
 
 ## 进程和线程
 
-* **进程(Process)** 是指计算机中正在运行的一个程序实例。
-* **线程(Thread)** 也被称为轻量级进程,更加轻量。多个线程可以在同一个进程中同时执行,并且共享进程的资源比如内存空间、文件句柄、网络连接等。
+* **进程(Process)** 是指计算机中正在运行的一个程序实例。操作系统的资源分配单位,进程是资源(包括内存、打开的文件等)分配的单位,
+* **线程(Thread)** 也被称为轻量级进程,更加轻量。多个线程可以在同一个进程中同时执行,并且共享进程的资源比如内存空间、文件句柄、网络连接等。CPU资源的最小分配单位。线程是 CPU 调度的单位;
 
 ### PCB 是什么?包含哪些信息?
 
@@ -152,6 +152,36 @@ TLB即为页表缓存、转址旁路缓存、快表等。有了 TLB 后,那么
 4. **屏障**:屏障是一种同步原语,用于等待多个线程到达某个点再一起继续执行。当一个线程到达屏障时,它会停止执行并等待其他线程到达屏障,直到所有线程都到达屏障后,它们才会一起继续执行。
 5. **事件** :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
 
+## 进程与线程的上下文切换
+
+### 进程
+
+**一个进程切换到另一个进程运行,称为进程的上下文切换**
+
+**进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的资源。**
+
+> 发生进程上下文切换有哪些场景?
+
+- 为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。这样,当某个进程的时间片耗尽了,进程就从运行状态变为就绪状态,系统从就绪队列选择另外一个进程运行;
+- 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行;
+- 当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度;
+- 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行;
+- 发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序;
+
+### 线程
+
+**线程是调度的基本单位,而进程则是资源拥有的基本单位**。
+
+对于线程和进程,我们可以这么理解:
+
+- 当进程只有一个线程时,可以认为进程就等于线程;
+- 当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源,这些资源在上下文切换时是不需要修改的;
+
+> 线程上下文切换的是什么?
+
+- 当两个线程不是属于同一个进程,则切换的过程就跟进程上下文切换一样;
+- **当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据**;
+
 ## 进程生命周期
 
 我们一般把进程大致分为 5 种状态,这一点和线程很像!

+ 9 - 5
面经/问答/计网.md

@@ -21,8 +21,8 @@ TCP 是**面向连接的、可靠的、基于字节流**的传输层通信协议
 
 ## tcp拥塞控制的几个策略
 
-1. 慢启动:在连接刚建立时,TCP发送方会以较小的拥塞窗口(cwnd)开始发送数据,并根据每次接收到的确认(ACK)增加cwnd的大小,从而逐渐增加发送速率,达到网络带宽的最佳利用。
-2. 拥塞避免:当 `cwnd` >= `ssthresh` 时,就会使用「拥塞避免算法」,TCP发送方会切换到拥塞避免模式,此时每次接收到一个ACK时,cwnd的大小只会增加一个MSS(最大报文段长度),而不是每次都加倍。这样可以避免发送过多的数据导致网络拥塞。
+1. 慢启动:在连接刚建立时,TCP发送方会以较小的拥塞窗口(cwnd)开始发送数据,并根据每次接收到的确认(ACK)增加cwnd(拥塞窗口)的大小,从而逐渐增加发送速率,达到网络带宽的最佳利用。
+2. 拥塞避免:当 `cwnd` >= `ssthresh` (阈值)时,就会使用「拥塞避免算法」,TCP发送方会切换到拥塞避免模式,此时每次接收到一个ACK时,cwnd的大小只会增加一个MSS(最大报文段长度),而不是每次都加倍。这样可以避免发送过多的数据导致网络拥塞。
 3. 拥塞发生:当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:
    1. 快速重传:当TCP发送方连续收到3个重复的ACK时,TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 `ssthresh` 和 `cwnd` 变化如下:
       - `cwnd = cwnd/2` ,也就是设置为原来的一半;
@@ -180,8 +180,8 @@ ACK报文是TCP用于确认接收到数据的报文,用于确认对方发送
 
 ### 第二次挥手丢失了,会发生什么?
 
-* 客户端:ACK 报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制
-* 服务端:服务端会主动调用close函数
+* 客户端:ACK 报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,等到达一定时间之后关闭
+* 服务端:服务端处于 close_wait 状态,直到超时关闭
 
 ### 第三次挥手丢失了,会发生什么?
 
@@ -215,6 +215,10 @@ TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存
 - 第二个场景:HTTP 长连接超时
 - 第三个场景:HTTP 长连接的请求数量达到上限
 
+## 服务器出现大量 CLOSE_WAIT 状态的原因有哪些?
+
+当服务端出现大量 CLOSE_WAIT 状态的连接的时候,说明服务端的程序没有调用 close 函数关闭连接。
+
 ## 四次挥手可以变成三次吗?
 
 答案是可以
@@ -358,7 +362,7 @@ TCP握手之后HTTPS需要四次握手建立连接。
 
 至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。
 
-## HTTPS特点
+## HTTP2的特点
 
 1. 优化stream