Files
StudyNote/面试/八股文/面试鸭.md
2026-02-13 23:38:38 +08:00

1470 lines
42 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mysql
## Mysql的索引类型有那些
1. ### `从数据结构`
- **`B+树索引`**
- 适用于**范围查询** Between 和 **精确查询** in
- 支持有序数字的查 排 聚合操作 是**Mysql的默认索引**
- **`哈希索引`**
- 基于哈希表的结构
- **不适用于范围查询 **但是适用于精确查询 & **查的非常快**
- **`倒排索引`** ->全文索引
- **把“文档→单词”的形式变为“单词→文档”的形式**
- **`R-树索引`** ->多维空间树
- 为空间(多维)查询准备的
- 计算地理位置 区域查询
2. **```从InnoDB B+树来看```**
- **`聚簇索引`**
- 也叫**主键索引**
- 名字由来-> 索引的叶子节点存储完整的数据行数据 -翻译->索引的结构和数据的物理存储是**深度绑定**的 —-—> 索引的叶子节点本身就是数据行的物理存储位置。
- 拿到了主键也就拿到了 这个数据所有
- 索引的叶子节点存储的是数据行 可以直接访问所有数据
- 适合 范围查 &排序
- **`非~ `** -->>二级索引
- 仅仅和主键绑定 拿到了也只是拿到了主键和非~的 数值 想要找到其他的字段 你还要根据主键找(这一步也叫回表)
3. **`索引的性质`**
- **`普通索引`**
- **非主键**&**非唯一**索引
- ```mysql
ALTER TABLE `table_name` ADD INDEX index_name ( `column` )
```
- **`联合索引`**
- 多个列合在一起的普通索引
- 为了提高多个查询条件的性能
- 要按照顺序
- ```mysql
ALTER TABLE `table_name` ADD INDEX index_name ( `column1` ,`column2` )
```
- **`主键索引`**
- 每一行数据都有唯一的主键
- 每个表只能有一个
- 不能为NULL
- ```MYSQL
ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
```
- **`唯一索引`**
- 索引中的值是唯一的 防止数据重复
- 可以为NULL
- ```mysql
ALTER TABLE `table_name` ADD UNIQUE (`column`)
```
- **`全文索引`** 写过了
- ```mysql
ALTER TABLE `table_name` ADD FULLTEXT index_name (`column`)
```
- **`空间索引`** 通常是R-树结构
- ```mysql
CREATE SPATIAL INDEX idx_location ON places(location);
```
### 补充
#### 关于倒排索引
我们一般搜索文章 都是通过 文章名字 ->找里面的单词 这种结构,但是如果我们想要通过文章的内容(一篇文章里面有苹果这个词)我们如何找到对应的文章???? 很简单就是**倒排索引** 这个索引将原先的 各位可以想象成Map的KV数值原先的存储方式是K文章V苹果 但是现在不一样了 我们改变了KV的内容 将一个K(苹果)V(包含苹果的文章1,2,3....)
冷知识 AI询问 正确与否代考就)未建索引时,搜索 “苹果” 需要逐篇扫描所有文章(全表扫描),时间复杂度是 **O (N)**N 为文档总数),数据量大时会非常慢;而倒排索引通过 “关键词→文档列表” 的直接映射,查询时只需定位到关键词对应的倒排列表,时间复杂度接近 ***O (1)(定位关键词)+ O (M)M 为匹配的文档数)***M 通常远小于 N因此速度会极大提升。
#### 为什么使用B树
B-树是一种**多路自平衡的搜索树** 它类似普通的平衡二叉树不同的一点是B-树允许每个节点有更多的子节点。B-Tree 相对于 AVLTree 缩减了节点个数,使每次磁盘 I/O 取到内存的数据都发挥了作用从而提高了查询效率。I/O渐进复杂度为O(h)
树非常矮,通常为 3~4 层,能存放千万到上亿的排序数据。树矮意味着访问效率高,从千万或上亿数据里查询一条数据,只用 3、4 次 I/O。
## Mysql的 存储引擎有哪些??
1. InnoDB
- 支持事务/行级锁&外键
- 提供包并发性能 适用于高附载的OLTP联机事务处理应用
- 数据以聚集索引存储 提高检索效率
2. MyISAM (早期的存储引擎 已逐渐被 InnoDB
- 不支持事物和外键 使用表级锁
- 适合读取多 更新少 ->>数据仓库
- 较高的读取性能 和 快的 表级锁定
3. MEMORY
- 存在内存中 快 但是重启丢失
- 适用于临时数据|快速缓存
4. NDB
- 高可用性和数据分布 适合大规模分布式应用
- 行级锁&自动分区
5. ARCHIVE
- 适用于存储大量历史数据 支持高效的插入&压缩
- 不支持索引 适合日志数据的存储
### 补充
#### 什么是聚集索引存储 为什么就提高了检索效率
就是聚簇索引
## 为什么用Mysql作为B+树的索引
1. 高效查找
- B+树是一个AVL树 每个叶子节点到根节点的长度相同 插入、查找、删除的时间复杂度为**O(logN)**
2. 树的高度增长不快使得磁盘的I/O次数减少
- B+树是一个多叉树,*** 非叶子节点仅仅保存主键或索引值和页面指针***,使得每一页都能容纳更多的记录 因此内存放的索引就多 更容易命中
3. 范围查询能力强
- B+树适合范围查询,因为叶子节点通过**链表链接**
### 扩展
1. B树的每个节点都存储的是完整的数据 而B+树则是存储的key&指针 完整的数据存储在叶子节点上因此B+树可以存放更多的索引页 减少磁盘查询次数。
2. B+树叶子组成了链表 而B树智能化每一层查找
3. B+树查找的平均时间更加稳定 B树因为非叶子节点就保存了数据因此 返回的快慢取决于节点到根的距离
4. 为什么不用二叉树?
- 可能会成为斜树 --> 一个链表 索引失效
5. 为什么不用红黑树?
- 红黑树毕竟还是二叉树一个结点点最多拥有两个直接子结点当我们插入大量数据的时候会导致的树的高度过高I/O渐进复杂度为O(h)。所以还是没有有效的减少查找过程中磁盘I/O的存取次数。
## 索引的最左前缀匹配原则是什么?
1. 索引的底层是一颗B+树那么联合索引的底层也就是一颗B+树只不过联合索引的B+树节点中存储的是键值。由于构建一棵B+树只能根据一个值来确定索引关系,所以数据库依赖联合索引最左的字段来构建。
2. 只有联合索引才能谈最左前缀匹配
3. 查询的顺序也是 按照从左到右的顺序查询 所以
- **原则**:联合索引必须从最左列开始匹配,且列之间连续。
- **范围条件的影响**:范围条件会阻断后续列的索引使用。
但是 如果
```mysql
ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(name,age,pos);
explain select *from staffs where name='z3'and age=22 and pos='manager';
explain select *from staffs where pos='manager' and name='z3'and age=22;
explain select *from staffs where age=22 and pos='manager' and name='z3';
```
这样**三个互换了顺序** ***也会进行联合索引*** 因为最主要是因为MySQL中有查询优化器***explain***所以sql语句中字段的顺序不需要和联合索引定义的字段顺序相同查询优化器会判断纠正这条SQL语句以什么样的顺序执行效率高最后才能生成真正的执行计划
**部分索引顺序**
如果缺胳膊少腿(有最左的 但是中间的可能少了),也是按照正常的索引
即使跳过了中间的索引,也是可以使用到索引去查询
但是如果只查最后的索引(没有大哥了)直接整个表的查询了
**模糊索引**
类似模糊索引就会使用到like的语句 如果复合最左前缀的话会使用到range或者是index的类型进行索引
**范围索引**
遇到范围索引就会停止
## 三层B+树结构
B+树的默认数据页的大小为**16KB**
- 每个节点页的大小为16KB
- 假设每个数据结构的主键和数据大小为1KB
- 每个内部节点 存储的是指向子节点的指针和索引键
- 第一层 根节点 指向1170个中间节点
- 第二层 假设每个指针是6字节 +8字节的索引键bigint=14字节 (一个节点的大小) 那么每个中间节点可以指向1170个叶子节点 **16KB/14Byte=1170**个
- 每个叶子节点可以存储16条数据结构
## 什么是回表
上文已经提到 由于在非聚簇索引中 索引存储的值 是索引字段的值和对应的主键的值 而并没有数据 所以当拿到这个索引的时候需要返回表中 重新去取叶子节点的数据 这就是**回表**
``回表会带来随机IO`` 因为这个索引可能是无序的 所以查起来不好查 只能随机查询![image-20251024143700377](F:\记录\八股文\image-20251024143700377.png)
## 索引失效
- 联合索引却不符合**最左前缀**
- 索引使用了计算
- 索引上函数
- like的随意使用
- or的随意使用
- 只有name有索引但是却使用了 select * from user where name="xxx" or id="xxx"
- 随意字段的使用
- 也就是要查询的字段和匹配的字段不是一个类型 那么myslq就会做转换 **隐式字符编码转换**
- 参数不同
- 表中两个不同的字段进行比较
- select * form user where id>name
- 使用了ORDER BY
- order by 后面如果不是**主键** 或者 不是**联合索引 **那么就不会走索引
神奇的地方就是 mysql查询都是按照索引查询的 但是问题就是这个索引是否生效 (有意思)
## 什么时候要建立索引
- 索引不是越多越好 毕竟建立索引需要空间 而且每次修改的时候还要额外去维护
- 有大量重复的地方 不建议上索引
- 长字段不要上索引 `but个人建议` 也是一个小tip 如果你做的是文章网站 那么好像 大概 可以给文章上倒排索引
- 当修改频率》》》查询 还是那句话**每次修改的时候还要额外去维护**
- 通过建立联合索引 减少单个索引的建立 毕竟管理一个表 总比管理一批表合理(笑)
## 索引越多越好?
空间角度
你每次建立一个索引 就会建立一个索引表 一般来说一个索引表的大小就是16KB
<img src="F:\记录\八股文\image-20251025125111136.png" alt="image-20251025125111136" style="zoom:33%;" />
时间角度
每次你写入一个数据 是不是需要在表中增加 这个插入要不要时间 你加完了 树的结构要改变 这个 为了维持B+树所作出的变形需不需要时间??
而且 你mysql使用索引的时候是不是需要通过评估器来评估哪个索引更好 是不是需要时间
## 如何使用explain语句进行分析
![image-20251025163127819](F:\记录\八股文\image-20251025163127819.png)
- `id` 就是查询的执行顺序的标识符 数字越大优先级越高
- *select_type* 查询的类型 有simple primary subquery
- table 查询的数据表
- type 访问的类型 比如ALL index range 等等 一般来说 性能从好到垃圾是**const > eq_ref>ref>range>index>ALL**
- possible_keys 可能用到的索引
*key* `实际用到的索引`
- key_len 索引长度
- ref 索引的哪一列被使用
- *rows* 估计要扫描的行数 数值越小越好
- filtered 希纳是查询条件过滤掉行的百分比
- *Extra* 额外信息 Using + index --》 使用覆盖索引 +where 条件过滤 +temporary 临时表 + filesort 额外的排序步骤
<img src="F:\记录\八股文\image-20251025163843482.png" alt="image-20251025163843482" style="zoom:50%;" />
## 如何进行mysql的优化
1合理设计索引利用联合索引进行覆盖索引的优化避免回表的发生减少一次查询和随机 I/O
2避免 SELECT *,只查询必要的字段
3避免在 SQL 中进行函数计算等操作,使得无法命中索引
4避免使用 % LIKE导致全表扫描
5注意联合索引需满足最左匹配原则
6不要对无索引字段进行排序操作
7连表查询需要注意不同字段的字符集是否一致否则也会导致全表扫描
除此之外,还可以利用缓存来优化,一些变化少或者访问频繁的数据设置到缓存中,减轻数据库的压力,提升查询的效率。
还可以通过业务来优化,例如少展示一些不必要的字段,减少多表查询的情况,将列表查询替换成分页分批查询等等。
补充的话就是三点: 命中索引 减少IO 减少回表
## B+树查询的全过程
先从根节点找起 然后根据数据键与节点中的**索引值(二分)**然后找分支
叶子节点存储的数据行记录 但是一页有**16KB**的大小 存储的数据行不止一行
叶子节点中的**数据行** 利用页目录结构 利用二分 找到对应的组
定位后 利用链表找到对应的行
###
## mysql中的count小知识
count(1)==count(0) 统计所有行数 **包括null**
count(字段名)--》返回字段的行数 **不包括null**
### char&varchar
char **定长** 当不满的时候会用空格填满
varchar **变长** 需要记录额外长度 1-2字节 支持的最大长度是65535 如果值为NULL 需要一个额外的1bit进行标记
双路排序 select字段多 放在StringBuffer里面 进行排序后 返回给客户端
单路排序 select字段少 放在StringBuffer里面
## Mysql是如何实现事务的
- 锁 主要服务于**隔离性**
- 利用`锁的机制` 使用数据并发修改的控制 来满足事物的`隔离性`
- Redo Lock 主要服务于**持久性**,同时辅助原子性
- 重做日志 主要就是`记录数据库的所有操作` 当mysql崩溃的时候 就重放一遍redolock 就可以恢复
- Undo Lock回滚日志 主要服务于**原子性**同时辅助隔离性MVCC
- `保存数据的历史版本` 用于事物的回滚 使得事务执行失败以后能够回到之前的样子 实现原子性
- MVCC 多版本并发控制 主要服务于**隔离性**
- 满足了非锁定读的需求 提高并发度 实现`读已提交`和`可重复读` 两中隔离级别 实现了事务的隔离性
`MySQL 事务的实现是**锁、Redo Log、Undo Log、MVCC 等机制协同作用的结果**`
- ### 什么是事务?
- 事务就是用户定义的一系列执行SQL语句的操作, 这些`操作要么完全地执行,要么完全地都不执行` 它是一个不可分割的工作执行单元。
**事物的四大特性**
- 原子性(Atomicity)
- 个事务中的所有操作要么全部提交成功,要么全部失败回滚
- 一致性(Consistency)
- 数据库总是从一个一致性的状态转换到另一个一致性的状态
- 隔离性(Isolation)
- 通常来说,一个事务所做的修改操作在提交事务之前,对于其他事务来说是不可见的。
- 持久性(Durability)
- 一旦事务提交,则其所做的修改会永久保存到数据库。
## 什么是MVCC 多版本并发控制
**是一种并发控制机制** 允许多个事务`同时读取`和`写入数据` 无需等待
### 这么做的?
1. 数据库为每一个事物创建一个数据快照。每当数据被修改的时候mysql不会立刻覆盖原有的数据而不是生成新版本的记录。每个记录都保留了对应的版本号和时间戳
2. 多个版本串联起来形成了一条版本链 这样可以不同时刻启动的事务可以`无锁`的获得不同版本的数据。
3. 写操作可以继续 无非就是创建新的数据版本
### 其实并不是存了很多个版本的数据
而是通过undolock的`保存数据的历史版本`来保留所有的版本 通过undolock来回溯到上面的版本
只是借助 undolog 记录每次写操作的反向操作
## Mysql的日志类型
### binlog 逻辑日志 二进制日志文件
- 用于记录mysql中的所有的DDL&DML
- 在事务提交后产生的 所以可以用来回复数据库
- 记录的是操作
- 因此他可以跨平台使用
### redolog 物理日志
- 用于恢复数据 保持数据的`一致性`和`持久性`
- 而且只是 保持数据的`一致性`和`持久性`
- 记录的是数据页的修改
### undolog 回滚操作
- 作用
- 事务回滚
- MVCC支持 :通过版本链实现多版本的并发控制
<img src="F:\记录\八股文\image-20251027164300265.png" alt="image-20251027164300265" style="zoom: 50%;" />
## Mysql的事务级别
### 读未提交
最低级的
可以读到其他事务为提交的数据 可能产生`脏读`
### 读已提交
可以读取其他事务已经提交的数据
但是可能会产生 `不可重读 `
### 可重复读
确保数据在多个查询下的返回是一样的
但是可能会产生`幻读`
mysql的默认的隔离级别
### 串行化
并发执行的SQL事务的操作 其效果与这些SQL事务按某种顺序串行执行的效果相同
串行执行sql事务在开启下一个事务之前要完成全部的操作
## Mysql中有哪些锁
1. **行级锁**
- 仅仅对特定的``加锁,允许其他事务**并发`访问`不同的行**,适用于高并发场景
2. **表级锁**
- 对整个表枷锁 其他事物**不能**对表进行任何**读写操作** 适用于保证完整性的小型表
3. 意向锁
- 表锁 表示某个事物对某行数据加锁的意图,分为意向共享锁和意向排他锁,主要用户行级锁的与表级锁的结合
4. **共享锁**
- 允许事物**并发的读取同一资源** **不能修改** 智能释放锁后 才能获得锁
5. **排他锁**
- 只允许**一个事务**对资源进行**读写** 其他只有拿到锁才可以
6. 元数据锁
- 用于保护数据对象的元数据 防止DDL操作时其他事务对这些对象进行修改
7. **间隙锁**
- 针对索引中的两个记录之间的间隙枷锁 防止其他事务在间隙中插入数据---》防止幻读
8. **临键锁**
- 行级锁+间隙锁 锁定具体的行和前面的间隙 确保在一个范围内不会出现幻读
9. 插入意向锁
- 等待间隙的锁 用于指示事务在某个间隙中插入记录 允许其他事务进行共享锁 但在插入时会组织其他锁
10. 自增锁
- 插入自增列时枷锁 保证自增的唯一性
##
## Mysql事务的二阶段提交的是什么
为了确保**redolock binlog**之间的一致性 使用的一种机制。
Mysql通过二阶段提交来保证在**crash recovery** 崩溃恢复 时 不会出现**数据丢失**和**数据不一致**的情况
### 二阶段的两个阶段
- `准备阶段` 在事务提交时Mysql的InnoDB引擎会先写入**redolog 将其状态标记为prepare**,表示事务已经准备提交但是还未真正完成 此时的redolog是预提交状态 还未标记为完成提交
- `提交阶段` redolog标记成prepare的时候 mysql server 会写入binlog 写入成功后 mysql会通知innodb 将redolog状态改为commit 完成整个事务的提交过程
## Mysql如果发生死锁会怎么办
#### 自动检索与回滚
MySQL自带死锁检测机制 ,当检测到死锁时,数据库会自动回滚到其中一个事务,以解除死锁。通常会回滚到事务持有的资源最少的那时候。
### 可以手动的kill发生死锁的语句
可以通过命令 来手动kill它
```mysql
SHOW ENGINE INNODB STATUS;//展示执行的事务和事务相关的锁
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;//获取事务id
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;//获取线程id
KILL <thread_id>;//手动中止事务
```
## Mysql是如何解决深度分页问题
什么是深度分页? 数据量很大的时候,按照分页访问后面的数据。
比如
```mysql
`SELECT * FROM mianshiya WHERE name = 'yupi' ORDER BY id LIMIT 99999990, 10;`
```
这样的一条查询语句,
可以优化成:
```mysql
sql SELECT * FROM mianshiya
WHERE name = 'yupi'
AND id >= (
SELECT id FROM mianshiya
WHERE name = 'yupi'
ORDER BY id
LIMIT 99999990, 1
)
ORDER BY id LIMIT 10;
```
name 有索引的情况下,这样的查询直接扫描 name 的二级索引,二级索引的数据量少,且在子查询中能直接得到 id 不需要回表。将子查询得到的 id 再去主键索引查询,速度很快,数据量也小。
**如果直接扫描主键索引的话,数据量就比较大**,因为**主键索引包含全部的数据**。
当然上面的 SQL 改成 Join 也行,本质上是一样的。
比如
```mysql
select * from mianshiya
inner join
(select id from mianshiya where name = 'yupi' order by id limit 9999999010)
as mianshiya1 on mianshiya.id = mianshiya1.id
```
其实本质上就是先查找在排序 而不是直接写成一句话 导致一边查找一边排序
方法二 记录id
每次分页返回当前最大的id 然后下次查询的时候 带上这个id 就可以利用id》maxid 过滤了
这种方式只适合 连续查询的时候 如果跳页就失效了
## 什么是mysql的主从机制是如何实现的
主从机制 就是mysql的一种数据复制技术 将主数据库上面的数据同步到一个或者多个从服务器中
主要是用binlog用于记录mysql中的所有的DDL&DML 把主数据库的binlog放到其他的数据库中 让其他的数据库重放就行了
### 具体流程
1. 线程创立 --> 服务器开始主从机制后 会创建i/o sql线程
2. 连接建立 -->服务器io线程与主服务器建立连接主服务器的binlog dump线程与之交互
3. 同步位置告知 --> 服务器的io线程 告诉主服务器的dump线程从何处开始接受binlog
4. 主服务器操作 --> 主服务器更新时 将更改的记录保存到binlog中
5. binlog传输 --> 主服务器的dump线程检测到binlog变化 从指定的位置读取 由从服务器io线程拉取 采用
6. 中继日志存储 --> 从服务器的io线程中 将接受的内存保存到relaylog中
7. 数据写入 --> 从服务器的sql线程读取relaylog内容 解析成具体操作后写入自身数据表
<img src="F:\记录\八股文\image-20251030165732479.png" alt="image-20251030165732479" style="zoom:50%;" />
### 主从复制的类型
- 异步复制 主库不需要从库中相应
- 同步复制 主库同步等待所有的库确认收到数据
- 半同步复制 主库等待至少一个库收到数据
## 主从同步延迟怎么处理
首先确定一点 这个延迟啊 无法避免只能尽力减少
- 二次查询:如果库查不到数据 那么就去主库查一遍 但是就把压力给到主库了 有点违法主从原则了
- 强制将写之后立马读的操作转移到主库上: 将代码写死了 比如一些写入之后立马查询的操作,绑定在一起,写死都走主库
- 关键业务读写都走主库
- 使用缓存: 主库写入后同步到缓存中 这样查询时就可以先查询缓存,避免了延迟的问题
- 提升从库配置:这没什么可解释的 金钱的力量
# Redis
## Redis常见的数据结构
- String 最大长度是512MB
- 缓存存session
- 计数器:统计访问量
- Hash 底层是哈希表
- 键值对,存储对象的属性。适合小规模数据
- 商品详情
- List 双向链表
- 消息队列
- 历史消息
- Set 无序且不重复 用哈希表实现
- 标签系统
- 唯一用户集合
- Sorted Set
- 有序集合 但是每个集合都有一个分数score用于排序 底层是跳表实现
## 高级的
- BItmap
- 用位为单位存储的数据的高效的方式
- 用户在线
- HyperLongLong
- 主要用于估算基数 适合估算网站的独立的用户数量
- GEO
- 存储地理信息的位置
- Stream
- 日志数据结构 支持高效的信息生产和消费模式
- 具有持久性和序列性化特性
- 消息队列
## Redis为什么这么快
数据存储在内存
优秀的线程模型&IO模型
- redis采用了IO多路复用技术
- redis采用的单线程来执行命令 无需线程的切换 避免了 上下文切换所带来的开销
高效的数据结构
## Redis为毛使用单线程6.0以后又引入多线程
单线程的原因是因为
1. Redis的操作是基于内存的 其大多数操作的性能瓶颈 不是CPU导致的
2. 使用单线程模式 减少线程上下文切换所带来的性能开销
3. redis在单线程的情况下使用IO多路分用模型 就可以提高redis的IO利用率
6.0使用多线程的原因是因为
随着数据规模的增多 请求量增多 Redis的瓶颈主要存在于网络IO引入多线处理可以提高网络IO处理速度
## Redis的跳表的底层原理
多路链表 底层保留所有的元素 每一层链表都是下一层的子集
<img src="F:\记录\八股文\image-20251106152729662.png" alt="image-20251106152729662" style="zoom:50%;" />
## Redis中的Hash
是一种键值对集合
可以将多个字段和值存储在一个键中
以便于管理一些数据
适合存一些小数据 支持快速的增删改查 非常适合存储对象的属性
## Redis Zset的实现原理
是一种`跳表`+`哈希表`
跳表 -->存储数据结构的排序和快速查找
哈希表 -->成员与分数的映射,提供快速查找
当Zset的元素过于少的时候 将使用``zip list`` 来节省内存
- 元素个数<128
- 元素成员名和分值长度<64字节
## 如何保证redis和Mysql的数据一致性
1. 先更新缓存 再跟新数据库
2. 先更新数据库 再更新缓存
3. 删缓存 更新数据库 等后续查询数据库把数据回种到缓存
以上都不建议
1. 先更新数据库 再删缓存 等后续查询数据库把数据回种到缓存
2. **缓存双删策略** 更新数据库之前先删一次缓存。更新换数据库以后 再延迟删缓存
3. 使用**binlgo异步更新**缓存监听到数据库的binlog的变化通过异步的方式更新Redis缓存
## 击穿 穿透 雪崩
### 击穿
热点数据失效 导致大量的请求直接访问数据库
**解决方案**
1. 热点数据永不过时 -->比如双十一的时候 就可以把一些热点的关键词 给设置一个不可能过期的时间
2. 热点数据预热 --> 预加载
3. 动态缓存 --> 使用互斥锁,确保一个时间只能由一个请求去查询数据库查询并且更新缓存
**穿透**:查询一个不存在的数据 然后每次都查询
1. 分布式锁
2. 即使是一个空的key 也存入redis中
**雪崩**:多个缓存数据在同一时间内失效 导致大量的请求同时访问数据库
1. 采用随机过期时间策略
2. 双缓存机制
## String的底层逻辑
SDS结构并结合int,embstr,raw等不同的编码实现的
根据长度不同编码的也不同 以44字节为节点 分embstr和raw
## 分布式锁
1. set en nx命令+Lua脚本
- 加锁&解锁
2. 集群模式下 使用redlock
## 实现分布式锁的问题
1. 业务未到期 ,锁失效
2. 单点故障
3. 主从不重复的问题
4. 网络分区的问题
5. 时钟漂移的问题
6. 锁的可重复入的问题
7. 误解放锁的问题
## Redis持久化机制
- RDB快照
通过某一时刻的数据快照来实现持久化,可以在特定时间间隔内保存数据的快照
适合灾难恢复和备份 能生成紧凑的二进制文件 但是可能在崩溃后丢失最后一次快照
- AOF日志
AOF通过将操作写道日志中 来实现持久化,支持将所有的写操作记下来以回复
文件更加准确 但是文件比较大 重写时会使用更多的资源
## 主从复制的实现原理
复制流程
- 开始同步
- 从节点通过主节点发送PSYNC命令发起同步
- 全量复制
- 如果第一次链接或者之前的连接失效,从节点请求全量复制,主节点会将当前的数据快照 发送给从节点
- 增量复制
- 全量复制完成后,主从之间回保持一个长连接,然后主节点会通过这个连接 将后续的读写操作传递给从节点
主从架构 就是 主操作写入 从节点来读取
## 数据过期后的删除策略是什么
- 定期删除
- 定期1000ms默认会随机检查一定数量的键如果发现过期键 就删除
- 主要应用在后台持续清理过期数据
- 惰性删除
- 在每次访问的键的时候redis检查键是否过期过期就删掉。这种策略保证了在使用过程中 只删除不要的数据,不访问就不删除。
## 如何解决热点key的问题
- 热点key的拆分 比如在key的前面加一些前缀 然后放到不同的实例上
- 多级缓存 CDN 本地缓存)
- 读写分离 通过主从复制 将请求分到其他的节点
- 降级和限流 在热点key过高的时候 应该采取限流的方法 减少对于redis的请求 必要时要使用降级的数据或者空值
## 集群的实现原理是什么
采用hash slot 机制来分配数据 将整个空间分为16384个槽 每个redis负责一定范围的哈希槽 数据的key经过哈希计算后 分配到对应的节点
## Big key问题
Redis 中的 "big Key" 是指一个内存空间占用比较大的键Key它的危害如下
- 内存分布不均。在集群模式下,不同 slot 分配到不同实例中,如果大 key 都映射到一个实例,则分布不均,查询效率也会受到影响。
- 由于 Redis 单线程执行命令,操作大 Key 时耗时较长,从而导致 Redis 出现其它命令**阻塞**的问题。
- 大 Key 对资源的占用巨大,在进行网络 I/O 传输时,会导致获取过程中产生的**网络流量较大**,进而产生网络传输时间延长甚至网络传输阻塞的现象,例如一个 key 2MB请求 1000 次就会产生 2000 MB 流量。
- 客户端超时。因为操作大 Key **时耗时较长**,可能导致客户端等待超时。
### 如何解决
开发方面:
- 对存储的数据进行压缩
- 将大的key分成一些小的对象
- 使用适合的数据结构
业务层面:
- 可以根据实际情况 减少存储的内容
- 优化业务逻辑
数据分布方面:
- 采用redis集群方式 进行redis的部署 然后将大的key拆分到不同的服务器上面
# 设计模式
## 单例模式哪几种实现方式 ?如何保证线程安全
- 饿汉式 --> 类加载的时候就加载
- ```java
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
```
- 懒汉式 --> 用到才创建
- ```java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
```
- 双重检查锁定 --> 懒汉式+ 只在第一次检查实例为空的时候加锁
- ```java
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
```
- 内部静态类 --> 利用类加载机制 实现懒加载和线程安全
- ```java
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
```
- 枚举单例 --> 通过枚举实现单例 简单防止反射和序列化攻击
```java
public enum Singleton {
INSTANCE;
public void bizMethod() {
// 一些业务逻辑方法
}
}
//使用
Singleton singleton = Singleton.INSTANCE;
singleton.bizMethod();
```
## 策略模式
一种行为设计模式,将每种算法封装起来,让他们可以相互替换,让算法独立于使用的客户端而变化
### 特点
- 算法封装
- 动态替换 在运行时选择和替换算法
- 遵循开闭原则
### 组成
- 策略接口:定义算法的通用接口
- 具体策略:实现算法的具体实现
- 上下文类:持有策略接口的使用,调用具体策略的方法
## 模板方式
抽象类定义了一个算法骨架,具体的步骤由子类提供
- 抽象类 定义模板
- 具体类 实现抽象方法
- 模板方法 调用一系列步骤方法
```java
// 抽象类
abstract class DataProcessor {
// 模板方法
public final void process() {
readData();
processData();
writeData();
}
protected abstract void readData(); // 读取数据
protected abstract void processData(); // 处理数据
protected void writeData() { // 写入数据
System.out.println("Writing data to output.");
}
}
// 具体实现类A
class CSVDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("Reading data from CSV file.");
}
@Override
protected void processData() {
System.out.println("Processing CSV data.");
}
}
// 具体实现类B
class JSONDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("Reading data from JSON file.");
}
@Override
protected void processData() {
System.out.println("Processing JSON data.");
}
}
// 客户端
public class Main {
public static void main(String[] args) {
DataProcessor csvProcessor = new CSVDataProcessor();
csvProcessor.process();
DataProcessor jsonProcessor = new JSONDataProcessor();
jsonProcessor.process();
}
}
```
## 常见的设计模式
1. 单例模式
- 保证系统中对象只有一个实例
- 连接池 全局配置
2. 简单工厂
- 获取不同对象的时候可以使用
- 将对象的创建逻辑抽离复用
3. 策略
- 封装一组算法让他们之间能够相互替代 能有效避免if sles
- 选择支付策略
4. 模板
- 提炼核心流程封装成一个模板方法
## 工厂模式&抽象工厂模式
工厂模式关注的是 **创建单一类型的对象**
定义一个抽象方法 由子类实现具体对象的实例化
抽象工厂模式 关注的是 **创建一族相关对象 **
提供一个接口来创建一组相关的或者相互依赖的对象 无需指定他们的具体类
# 计网
## 常见的Http状态码
- 1XX 消息响应
- 2XX 成功
- 3XX 重定向
- 4XX 客户端错误
- 403权限
- 5XX 服务器错误
## HTTP 请求包含哪些内容 请求头和请求体有哪些内容
组成部分
- 请求行
- GRT POST 之类的
- 请求路径
- HTTP版本
- 请求头
- 各种键值对
- 客户端环境
- 请求内容
- 认证信息
- 空行
- 分割体&头
- 请求体
- 要发送的数据
## GET和POST的区别
GET
- 请求数据
- 参数通过URL拼接 直接暴露于URL中不是很安全
- 有长度限制 2048字节
- 幂等性 重复请求不会改变服务器的状态
POST
- 提交数据
- 参数在请求体中
- 非幂等
## Http1.0
**无状态,无连接** 每次请求都要建立新的TCP
**队头阻塞** :每个请求都按照顺序解决
**不支持持久连接** 每次请求都要建立新的TCP 服务器消耗大
## Http2.0
**二进制分帧:**将http报文分割成更小的二进制提高了传输效率支持**多路分用**
**头部压缩**减少HTTP头部的大小降低网络的开销
**服务器推送** 服务器主动向 客户端发送请求 减少客户端的请求次数
**多路分用**在一个TCP连接上可以同时发送多个请求解决了HTTP1.1的对头阻塞问题
## Http3.0
基于QUIC协议使用UDP协议相当于TCP可靠性QUIC通过自身实现可靠传输减少了RTT
多路分用在一个QUIC连接上可以同时传输多个请求和相应并支持流优先级
更快的连接减少了TLS和三握手的时间
更少的延迟
<img src="F:\记录\八股文\image-20251109195036812.png" alt="image-20251109195036812" style="zoom:33%;" />
HTTP&HTTPS
**1.数据传输安全性**
- **HTTP**:数据以明文传输,容易被窃听、篡改。
- **HTTPS**:通过 SSL/TLS 协议对数据进行加密传输,提供数据机密性和完整性保障。
2. **端口号**
- **HTTP**:默认使用端口 80。
- **HTTPS**:默认使用端口 443。
3. **性能**
- **HTTP**:无加密过程,连接建立速度稍快。
- **HTTPS**:基于 HTTP 上又加了 SSL或 TLS协议来实现的加密传输加解密过程增加了计算开销握手时间较长但现代硬件和协议优化已使性能差距减小。
**4. SEO 影响**
- **HTTP**:搜索引擎一般会降低未加密站点的排名。
- **HTTPS**:搜索引擎更倾向于优先展示 HTTPS 网站。
## Tcp和Upd的区别
TCP
- 可靠连接
- 面向连接
- 提供流量控制和拥塞控制
- 保持数据顺序
- 头部较大
- 性能较低 延迟大
- 数据传输模式 -->以字节流传输模式
- 适用于 文件传输 web 邮件
UDP
- 不可靠
- 无连接
- 没有流量控制和拥塞控制
- 不保证数据顺序
- 头部节点较小8字节
- 性能高 延迟小
- 数据报传输模式
- 实时通讯 语音 视频 游戏
## 经典TCP三报文
客户端首先发送一个SYN(同步序列编号)消息给服务器服务器收到后回复一个SYN-ACK(同步序列编号-确认)消息最后客户端再发送一个ACK(确认)消息确认服务器已经收到SYN-ACK消息从而完成三次握手建立起一个可靠的TCP连接。
<img src="F:\记录\八股文\image-20251110202708473.png" alt="image-20251110202708473" style="zoom:33%;" />
### 为什么三握手
- 避免历史错误连接的建立 减少双方错误的不必要的资源的消耗
- 帮助双方同步初始化
## TCP是用来决解什么问题
可靠传输TCP确保数据包在网络传输过程中不丢失不重复
流量控制TCP通过滑动窗口机制调节发送方的数据发送速率 ,防止因接收方的处理能力而被数据流所淹没
拥塞控制TCP通过拥塞避免算法 (慢启动 拥塞避免 快速重传 快速回复) 来防止网络过载 确保网络资源的公平使用和稳定性
连接管理TCP是面向连接的协议 赛用三次握手 和四次挥手 机制来管理会话,确保通信的可靠性和状态的同步
解决了数据不可靠的IP网络上的问题
## 四挥手
四次挥手的作用就是 用于安全关闭一个已经建立的连接的过程 它确保双方都能完成数据传输并安全的释放连接资源
### 为什么挥手需要四次?
主要是为了确保数据完整性。
TCP 是一个全双工协议,也就是说双方都要关闭,每一方都向对方发送 FIN 和回应 ACK。客户端发起连接断开代表客户端没数据要发送的但是服务端可能还有数据没有返回给客户端。就像我对你说我数据发完了然后你回复好的你收到了。然后你对我说你数据发完了然后我向你回复我收到了。这样才能保证数据不会丢失。
所以一个 FIN + ACK 代表一方结束数据的传输,因此需要两对 FIN +ACK加起来就是四次通信。
## 粘包和拆包
粘包在Tcp传输中发送方的多个数据包在接收方被**合并成一个包接受**,导致多条数据粘在一起,接收方无法正确区分这些消息的边界
拆包:发送方的一个数据包在接受方被拆成了多个包接受,导致一个完整的消息被拆分成多个部分,接收方无法一次性接收到完整的数据
### why
粘包Tcp是面向字节流的协议 他不关心数据边界
拆包可能由于网络传输中的MUT最大传输单元限制或发送缓冲区大小的限制一个大包被拆分成了多个小包传输
### 解决:
使用特定长度
添加消息分隔符
使用消息头
## Tcp拥塞控制的步骤
### ![image-20251111214414297](F:\记录\八股文\image-20251111214414297.png)
## TCP/IP四层模型
- 应用层
- 通过各种协议提供网络应哟个程序的功能
- 传输层
- 在两个主机之间提供端口到端口的通信服务
- 网络层
- 通过Ip协议提供数据包的路由和转发
- 网络接口层
- 在计算机和网络硬件之间传输数据
## Cookie Session Token之间的区别
### Cookie
存储在用户浏览器端的一个小型数据文件,用于跟踪和保存用户的状态信息
用途: 保持用户登陆状态,跟踪用户行为,存储用户偏好
**存在客户端**
### Session
Session是服务器端保存用户状态的机制 每一个用户会话都会有已给唯一的SessionId
主要用于跟踪用户在服务器上的状态信息 -> 登陆状态和购物车内容
**存储在服务器端**
### Token
**加密字符串,用于身份认证和授权**,可以包含用户信息和权限,用于验证用户身份和授权访问资源
认证后后端服务会返回Token**存储在客户端** 后续可无端访问服务端需要带上这个Token
- Cookie-->客户端状态的简单存储和追踪
- Session--> 服务器端的复杂状态管理,需要存储大量会话数据
- Token--> 无状态的认证和授权,特别是在分布式和跨域环境下
## 输入一个网站发生了什么
1. 浏览器解析URL
2. DNS解析成最终的ip
3. 浏览器选择TCP/UDP
4. IP 在TCP数据包的基础上 在封装源地址IP和目标地址IP等信息
5. MAC 通过MAC确保子网内设备两点之间的通信寻址
6. 网卡 网卡将二进制数据转化为电信号
7. 交换机 工作在MAC层他会将数据包的MAC头找到另一个设备连接在交换机的哪个端口
8. 路由器 转发 三层网络设备 包含IP层
9. 层层验证
10. 服务器处理
11. 浏览器接受并且渲染
# 计算机组成原理
## 线程和进程的区别
进程:资源分配的基本单位 每一个进程都有一个内存空间。
线程是CPU的调度的基本单位 属于进程 一个进程可以包含多个线程 线程共享进程的测村空间和资源
## 进程之间的通信方式
- 管道 单向通信方式
- 命名管道 与匿名管道类似
- 信息队列 允许进程向另一个进程发送消息 消息顺序存储
- 共享内存
- 信息量
- 信号 异步通信方式
- Socket 在网络上不同主机上的进程通信
- 文件 通过读写文件俩来进行通信
### 调度算法
- 先来先服务
- 短作业有限
- 有限级调度
- 时间片轮转
- 最高相应比优先 计算影响比
- 多级反馈队列调度 结合多个调度策略