面试心态
生活总是让我们遍体鳞伤,但到后来,那些受伤的地方一定会变成我们最强壮的地方。
TCP/IP
扩展阅读 硬不硬你说了算!近 40 张图解被问千百遍的 TCP 3 次握手和 4 次挥手面试题
握手:三次可有效排重
C->S: SYN; Seq_c
S->C: SYN+ACK; Seq_c+1; Seq_s
C->S: ACK; Seq_s+1 (可携带数据)
挥手:
C->S: FIN
S->C: FIN+ACK (服务端发送后进入 CLOSED_WAIT ) 等待自己关闭,下一步自己发FIN
S->C: FIN
C->S: FIN+ACK (客户端进入 TIME_WAIT)主动关闭的才会有这个状态,防止服务端没有收到ack
进程间通信
有以下6种方法
- 管道:特点单向堵塞
- 消息队列:带缓冲的管道,不适合传大消息
- 共享内存:2个进程把虚拟内存映射到相同的物理内存
- 信号量:共享内存升级版,带资源竞争管理
- socket:本地或网络
- 信号:通常为异常控制
队列MQ
队列好处:解耦/异步;可恢复性;缓冲池峰值处理
【自行扩展】kafuka 如何不丢失 ack机制 存储
Redis
数据类型
list;hash;string;set;SortedSet
为什么快:
- 基于内存,hashMap
- 高效的数据结构,大部分操作都是O(1)
- 单线程 不需要上下文切换。 多路IO复用
如何持久化
RDB: 定时对数据快照,速度快,会丢失数据
AOF:追加日志,占用硬盘大,需要定时rewrite
过期key删除
- 定期扫一部分过期的数据删除
- get的时候判断key是否过期
LRU实现 leetcode.com/problems/lr…
Hash + 双链表
Hash
扩容
- 一次性扩容:因子过大后,直接申请n倍当前容量的空间,将老数据挪过去。数据
- 穿插扩容:新老空间共存(查询顺序 先老后新),但每操作一次就从老空间挪点数据到新空间,久而久之老空间就没数据了
一致性哈希
- 设定哈希池为1~100w
- 假设现在有4个节点,对节点名取哈希,大致为1,25w,50w,75w
- 数据存放:比如数据hash是60w那么就存放到第一个比它哈希大的节点,也就是哈希值为75w的节点
- 扩容:新加一个节点计算它的节点哈希是70w,那么只需要把数据哈希是50w~70w的数据从节点75w挪到节点70w就行了
MySQL
B+Tree
1 矮胖树减少IO
2 非叶子节点不存数据 可存储更多的索引
3 叶子节点用有序链表关联 范围查询友好
其他树缺点
hash等值查询快 范围查询慢 消耗内存
二叉树 红黑树 层级高; 平衡二叉树多次旋转
事务隔离级别
RU - read uncommited
RC - read commited
RR - repeatable read,默认
S - Serializable
RR下 select为快照读 其余为当前读,
- begin 不生成快照 在select时生成,由MVCC实现 多版本并发控制 Multiversion concurrency control
主从复制原理【自行扩展】
索引
聚集索引:指主键
覆盖索引: 意思是不用回表的查询
主从延迟如何解决
- 强制读主
- 半同步复制,写完从库再返回,写性能下降
- 缓存key:更新cache,写主;读的话若命中cache读主,没命中读从
- 数据库中间件;自动判断写完之后,立刻读 那么就读主库
ACID 事务的4个特性
- Atomicity 原子性 不可分割的工作单位 要么全成功 要么全失败 ; Undo log,如果是insert 那么回滚日志就记录delete
- Isolation 隔离性 MVCC
- Durability 持久性 一旦提交 就是永久改变 不受其他故障影响 比如断电; Redo log 事务提交时 调用fsync对redo log刷盘,顺序写硬盘,redo log用于 crash recovery, binlog用于point-in-time recovery
- Consistency 一致性 事务执行前后 数据的完整性不会被破坏。 用以上3个特性实现
时序数据库
- 时间即主键
- 高吞吐量(硬盘顺序读写快)LSM树
- 很少更新, 写效率大于读
- 过期数据自动丢失
算法
秒写算法:
二分查找
快速排序
归并排序
计数排序
全排列
Heap 代码实现 golang.org/src/contain…
通常解决TopK问题
-
i节点:parent (i-1) / 2 left child 2i +1 ; right child 2i+2
-
pop:堆顶与最后一个元素交换,对堆顶元素down操作
-
down(i): 若儿子比i小,与i的最小儿子交换, i等于小儿子。继续循环。break条件1是超出边界。2是儿子都比i小
-
push:简单的append,然后up最后一个元素
-
up:无脑与自己parent比较 若比parent还小 就交换
~
init: n := h.Len()
for i := n/2 - 1; i >= 0; i-- {
down(h, i, n)
}
被问到或印象深刻的算法列表
- 爬楼梯 leetcode.com/problems/cl…
- 接雨水 leetcode.com/problems/co…
- 打家劫舍 leetcode.com/problems/ho…
- 第K大 leetcode.com/problems/kt…
- 最大矩形 leetcode.com/problems/la…
- leetcode.com/problems/lo…
- 最大子列表 leetcode.com/problems/ma…
- 删除重复元素 leetcode.com/problems/re…
- 删除K个数字使其最小 leetcode.com/problems/re…
- 复合接雨水 leetcode.com/problems/tr…
- 数组种超过一半的数字 leetcode-cn.com/problems/sh…
程序设计
程序设计特别重要,需要自行扩展
需重度阅读 www.w3cschool.cn/architectro…
限流算法
Token桶
权限设计
用户;角色;权限
热点Key计分,单key变更并发极大,单台redis扛不住。
- 借鉴LSM树
- 用10个redis,比如10w分,每个redis初始1w,这样单key压力是原来的1/10
微服务
单体问题
- 代码耦合上线影响面大
- 数据库耦合
- 扩展性差,技术升级苦难。牵一发而动全身
- 开发效率低 每个成员都有完整的代码,多线开发版本冲突
- 不利于安全管理 微服务好处
- 小版本迭代快
- 分布式高性能
- 削峰填谷(性能均衡) 易伸缩
- 限流、降级、容错 灰度发布,天下武功唯快不破
拆分的代价
- 调用流复杂
- 调用失败处理
- 运维复杂度up
- 监控&问题排查难度up 根据什么拆:
- !团队人员架构,其实是根据服务划分规划人员分组
- 最大化复用,最小化变更
- 业务边界清晰
- 动静分离
- 业务&基础服务 拆分Tips ?
- 禁止双向依赖
- 避免分布式事务
- 插入抽象层