PHP Golang高级工程师面试题 · 后端学习路线 · 看云
## 准备
1. 自我介绍:说上家公司负责的项目或者浓缩简历。
2. 简历(最优.pdf格式)项目经验层次【qǐ chéng zhuǎn hé】
3. 具体展开到某个项目(需要结合自身岗位定位,用[STAR](https://link.segmentfault.com/?enc=6VBKlZCPjpkPD2%2FcaykxKg%3D%3D.zpG1l%2ByinKvvJNB%2B4hjDrzpiQqG26PFw51O9%2BdSdF0HS9qzZKzMDr2zxqhjm8uU%2BMFQA9E2WS5gAhgsMxGYKKw%3D%3D)法则)
* 项目背景
* 难点在哪
* 遇到什么问题及解决方案
* 工作内容(利用什么技术,实现了哪些功能)
* 面向(对象oop,切面aop)
* 设计模式(代理、工厂、单例、门面、观察者)
* xx模块(socket,pdo,curl)
* 中间件(codis,Mycat,mq,kafka)
* 并发,锁,协程
* 容器及编排
* 涉及底层(e.g.:网络模型,内核调用,多进程/线程、算法)
* 工作结果(尽量量化,比如减少了多少成本)
* 项目周期及你的职责、角色
4. 应用型的问题:要记得站高看远、架构分层
5. 涉及管理经验及自身对相项目管理的理解
6. 如果问你还有什么要问的问题,可以参考以下几个点【注意顺序】
* 部门的技术栈?以及对用人的要求和考核?
* 接下来的面试流程是什么,还有几轮(有没有机会)?
* 我是来维护代码(填坑)的还是来针对新业务(挖坑)的?
## 知识点列表
### 一、Redis
1. redis(这块说的太乱了,回头我好好整理下~)
* 应用场景
* string:cache,incr(此时redisObject encoding为int)
* hash :key为key,value为Hashmap(少时用redisObject encoding ziplist OR 增大时为ht)
```[sql]
```[sql]
* set:去重(中奖只一次sismember),交,并,差(如微博社交关系),内部实现为value为null的hashmap
```
```
* zset: 或者叫sorted set。既去重又能保证按照score排序,比如按照帖子的关注个数排序,value为帖子id,个数为score。面试中常问其实现机制(跳跃列表(skiplist):用多个指针分层串起来+hashmap(本身hashmap无法进行二分查找,所以必须加持skiplist机制)),另外还有一个应用可以是消息优先级。
* list:(阻塞rpop)消息队列、列表旋转(常用于监控,rpoplpush)
* HyperLogLog:大量统计(非精确)
* bitmaps
* [内部数据结构](https://link.segmentfault.com/?enc=wL4Kk%2F0jlw2Ye5LbToUGiw%3D%3D.9btasfl4ocsZEtvo%2BZziwL6WfRmCZJwEYXy54vqp1JA%3D)
* * 一图先胜个千言↓
* 
* dict:底层数据库和hash结构都是key\[ redis string obj\] AND value \[redis obj\]组成的,其中 string是redis通过内部封装的SDS结构体实现,而value五种object对应redis中常见的五种数据类型。了解了dict是掌握redis数据结构的前提,见下图↓:
* 
* Hash:dictht->dickEntry,dict是对dictht结构再次封装。装拉链法(单向链表)解决冲突(同php),采用头插的方法;装载因子(哈希表已保存节点数量 / 哈希表大小)超过预定值自动扩充内, 引发(增量式|渐进式)rehash
* 根据ht\[0\]创建一个比原来size大一倍(也可能减少)的hashtable,
* 重新计算hash和index值,根据rehashindex逐步操作
* 到达一定阈值(ht\[0\]为空)操作停止
* 期间响应客户端
* 新增写操作:新增的写到新的ht\[1\]
* 删除、查找、更新操作:先读ht\[0\],再读ht\[1\]
* sds:simple dynamic string
* string的底层实现为sds,但是string存储数字的时候,执行incr decr的时候,内部存储就不是sds了。
* 二进制安全binary safe(5种类型的header+falgs得到具体类型,进而匹配len和alloc)
* robj:redis object,为多种数据类型提供一种统一的表示方式,同时允许同一类型的数据采用不同的内部表示,支持对象共享和引用计数。
* sds
* string
* long
* ziplist
* quicklist
* skiplist
* ~~~c
typedef struct redisObject {
unsigned type:4;//OBJ_STRING, OBJ_LIST, OBJ_SET, OBJ_ZSET, OBJ_HASH
unsigned encoding:4/*上述type的OBJ-ENCODING _XXX常量,四个位说明同一个type可能是不同的encoding,或者说同一个数据类型,可能不同的内部表示*/;
unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr//真正指向的数据;
} robj
~~~
2. OBJ\_ENCODING\_
* string
* OBJ\_ENCODING\_RAW,代表sds ,原生string类型
* OBJ\_ENCODING\_INT,long类型
* OBJ\_ENCODING\_EMBSTR ,嵌入
* OBJ\_HASH
* OBJ\_ENCODING\_HT,表示成dict
* OBJ\_ENCODING\_ZIPLIST,hash用ziplist表示
* OBJ\_SET
* OBJ\_ENCODING\_INTSET,表示成intest
* config
* set-max-intset-entries 512【是整型而且数据元素较少时,set使用intset;否则使用dict】
* OBJ\_ZSET
* OBJ\_ENCODING\_[SKIPLIST](https://link.segmentfault.com/?enc=YRjpOzx02N3zwibkuIimxw%3D%3D.WaDvPlCtuHp3loFMSf1ypeRGMsj9ajysAuCRfRhGkz35rdlUISEjtrBYKyze5%2F%2Fsr5m5Kk%2FZ%2FKyTKNsjSSN%2F0g%3D%3D),表示成skiplist
* 思想:多层链表,指针来回查找,插入和更新采取随机层数的方法来规避
* config
* zset-max-ziplist-entries 128
* zset-max-ziplist-value 64
* OBJ\_LIST
* OBJ\_ENCODING\_QUICKLIST
* config
* list-max-ziplist-size -2
* list-compress-depth 0
3. 内部结构实现(针对上面的再‘翻译一下’)
* string
* hash(两种encoding,根据下面的config)
* ziplist
* dict
* config
* hash-max-ziplist-entries 512【注意单位是“对儿”】
* hash-max-ziplist-value 64【单个value超过64】
* list
* quicklist
* 定义:是一个ziplist型的双向链表
* 压缩算法:LZF
4. 扩展问题:
* zset如何根据两个属性排序?比如根据id和age
* 可以用位操作,把两个属性合成一个double
* 用zunionstore合并存储为新的key,再zrange
* redis是如何保证原子性操作的?
* 因为他是tm单线程的!(ps:mysql是多线程)
* 在并发脚本中的get set等不是原子的~
* 在并发中的原子命令incr setnx等是原子的
* 事务是保证批量操作的原子性
* 主从复制过程:
1. 从服务器向主服务器发送sync
2. 主服务器收到sync命令执行BGSAVE,且在这期间新执行的命令保存到一个缓冲区
3. 主执行(BGSAVE)完毕后,将.rdb文件发送给从服务器,从服务器将文件载入内存
4. BGSAVE期间到缓冲区的命令会以redis命令协议的方式,将内容发送给从服务器。
5. 特性:
* 单线程,自实现(event driver库,见下面四个io多路复用函数)
* 在/src/ae.c中:宏定义的方式
* ~~~c
/* Include the best multiplexing layer supported by this system.
* The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
~~~
* io多路复用,最常用调用函数:select(epoll,kquene,avport等),同时监控多个文件描述符的可读可写
* reactor方式实现文件处理器(每一个网络连接对应一个文件描述符),同时监听多个fd的accept,read(from client),write(to client),close文件事件。
6. 备份与持久化
* rdb(fork 进程dump到file,但是注意触发节点的覆盖问题,导致数据不完整)
* 手动: save (阻塞) & bgsave(fork 子进程),但是这两个不会同时进行(避免竟态条件)
* 自动触发:`conf:save 900 1 save 300 10 save 60 10000 dbfilename dump.rdb`
* rdb优点:对服务进程影响小,记录原数据文件方式便于管理还原
* rdb缺点:可能数据不完整
* rdb为非纯文本文件,可以用od -c dump.rdb分析
* aof(类似binlog)
* 三种写入同步方式
* appendfsync no OS自主决定何时flush
* appendfsync everysec(每个事件循环写入缓冲区,但是每隔一秒同步到磁盘文件)
* appendfsync always (每执行一个命令,每个事件循环都会执行写入aof 缓冲区并同步到磁盘文件,效率最慢,但是最安全)
* aof优点:数据最完整,可以通过`auto-aof-rewrite-percentage``auto-aof-rewrite-min-size`来fork新的进程来重写内存中的数据(),存储内容为redis的纯文本协议
* aof缺点:文件相对rdb更大,导入速度比rdb慢
* 一般有了aof就不rdb,因为aof更新频率更高
7. 过期策略:
* 定时过期:时间到了立即删除,cpu不友好,内存友好。
* 惰性过期:访问时判断是否过期:cpu友好,内存不友好
* 定期过期:expires dict中scan,清除已过期的key。cpu和内存最优解
8. 内存淘汰机制
* ~~~apache
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
~~~
* noeviction:新写入时回报错
* allkeys-lru:移除最近最少使用的key
* allkeys-random:随机移除某些key
* volatile-lru:设置了过期时间的key中,移除最近最少使用
* volatile-random:不解释
* volatile-ttl:设置类过期时间的键中,有更早过期时间的key优先移除
9. redis队列特殊关注(可能坑)之处
1. 队列可能丢东西
* 比如redis挂了,producer没有停止,但是队列数据无法写入(除非同步落地到mysql)
2. 队列的consumer 需要手动处理commit协议
* 如果consumer处理完,表示真正完成
* 如果没有处理完?放回队列?直接丢弃?
3. 事件重放机制不支持
* 比如consumer消费错了,那能不能将队列回放呢再次处理呢?
4. 队列最大长度及过期时间
* 如果producer远大于consumer,撑爆了怎么办
* 如果comsumer 一直没有处理,producer的数据如何处理
5. exactly once
6. 单机锁`sennx`或者基于`set`众多参数没问题,集群下可利用tag机制
7. 如何保证业务执行时间超过锁的过期时间,而引起误删除操作。答案是可以加一个唯一标识
10. [Redis vs Memcache](https://link.segmentfault.com/?enc=UFBpXQ3qkHSN%2BI%2BLu%2B%2FyTQ%3D%3D.mpk68Z6eUx7%2BzdOiJU%2BZqn3NsfR5KLOrGtCD%2BBN0f%2F5uN8OWuCtefnPGpgUKgl0elFKgajdQt87l%2FEqw6WE%2F3Q%3D%3D)
* memcached
* 优势
* 多线程(listen & woker),利用多核
* round robin
* cas(check and set,compare and swap)
* 劣势
* cache coherency、锁
* key大小有限制(1M)
* 特点
* 内存预分配:slab+trunk
* redis
* 优势:
* 自己封装了一个AEEvent(epoll+select+kqueue),io多路复用
* 丰富的数据结构(对内+对外)
* 良好的持久化策略(rdb +aof)
* 单机可部署多实例,利用多核
* 劣势:
* 排序、聚合cpu密集操作会等影响吞吐量
* key 大小最大为1g
11. more | other
13. * redis ziplist与普通双向链表的区别:普通的链表每一项都占用独立的一块内存,各项之间用地址指针(引用)连接起来,这样会导致大量碎片。而ziplist是将表中每项放在前后连续地址空间内,而且对值存储采取变长编码。
* redis msetnx对应的del,可以采取lua脚本保证get del的原子性
* redis 单线程如何实现阻塞队列?
* 阻塞是阻塞client,又不是阻塞server,server不发数据,client不就阻塞住了,当client想要阻塞在某个key上,server会把这个client放到一个block list里,等key发生变化,就send数据给client。
* redis 阻塞队列的时间设置实现?
* blocklist里只存了列表,这个timeout存在连接上,靠serverCron来遍历检测,每次遍历5个,
* 高性能的方案是小堆或者红黑树或者时间轮实现的定时器结构,epoll wait那块timeout参数就设置成下次超时时间
* 每次poll loop里除了处理io事件,再把定时器的数据结构里处理下,堆和红黑只要检测到一个未超时就可以break了,时间轮这是当前槽都触发了就行
* 每次检测5个这种比较折中,因为他场景不是大量并发的服务器,rds cli的连接数量毕竟使用者内部可控,而且不需要精确打击,只要保障相对能及时清理就行,redis的网络部分相对比较简单,业务场景可控,足够了
* redis集群情况下如何做到两个key必hash到一个节点?用{}
* redis 事务机制
* 基于乐观锁的multi exec|discard watch key unwatch
* redis call lua脚本(比如get+del一起)
* 2.6.12后set命令支持(setnx+expire就不需要写lua script了)
* redis 下的分布式锁,当主从不同步或者主重新被选举需要多想想,主从情况下一般采用从节点的大多数(es也是这样)
* redis 主从哨兵配置,copy三份redis.conf文件,以下设置一主二从一哨兵
~~~smali
vim redis01.conf
port 63791
vim redis02.conf
port 63792
slaveof 127.0.0.1 63791
vim redis03.conf
port 63793
slaveof 127.0.0.1 63791
vim sentinel.conf
daemonize yes
port 26379
sentinel monitor mymaster 127.0.0.1 63791 1
// mymaster为自定义命名,127.0.0.1 63791为master,1为选举主节点的时候投票数目的同意个数,1代表有一个哨兵同意就行。
~~~
* redis cluster集群
* 集群会将数据自动按照算法分割在不同节点负责的槽上(data sharding)
* [redis cluster官方文档简洁明了啦](https://link.segmentfault.com/?enc=GS8ReK2kWXyPCC9nIyS%2FNw%3D%3D.Qrif6tZkG2oU5Ffqu1gFF45gyhsyYZQOyzfcyH4VbJyRnotPRzYk%2BPuoG4Ssq8Sh)
* 具体展开问题
* redis的过期删除策略有哪些?
* Redis是单线程的?为什么还要加锁呢?
* redis的缓存穿透和缓存击穿怎能解决?
* 缓存穿透是指一杆到底,(即cache和db中都没有,比如请求一个id=-1的数据)穿了cache和db。可以加业务前台一层过滤器以及完善NULL empty等情况缓存。
* 缓存击穿不可避免(比如秒杀情况)。这样可以通过增加互斥锁回写缓存|缓存过期时间适当延长
* 缓存雪崩 cache崩完db崩,db崩完系统崩
* 原因:大量key同一时间失效|cache宕机,导致并发连锁崩
* 解决:[分级缓存](https://link.segmentfault.com/?enc=Hfd8N5ptB7nwiBWWTAP1FA%3D%3D.prDJMyCMrPsqeJhL8Ti6fnHLhx4SGQ%2B5KAzJWHiT2rk%3D)
* 客户端分级缓存:需要结合业务场景自动探测发现+cache部署与一致性更新+命中与miss统计等核心点。
* 服务端分布式缓存:类似redis cluster
* * *
### 二、MySql
1. mysql
* 索引
* 物理存储
* 聚簇索引
* 非聚簇索引
* 数据结构
* B+树
* hash
* fulltext
* R-tree
* 逻辑角度
* 唯一索引 unique
* 普通索引index
* 主键索引 primary key
* 全文索引 full index(myisam)
* 复合索引 (最左前缀原则)
* 类似 where a and b and c a b c 问题
* 联合索引(a,b,c) 能够正确使用索引的有(a=1), (a=1 and b=1),(a=1 and b=1 and c=1)(b=1 and c =1
* 引擎类型
* myisam
* innodb
* 区别:
1. myisam采用非聚集索引,innodb采用聚集索引
2. myisam索引myi与数据myd文件分离,索引文件仅保存数据记录指针地址。
3. myisam的主索引与辅助索引在结构上没区别,而innodb不一样:innodb的所有辅助索引都引用主索引作为data域。
4. innodb支持事务,行级锁。myisam不行。
5. innodb必须有主键,而myisam可以没有。
* 相同点:
* 都是b+tree 索引
* 存储
* innodb
* 数据被逻辑的存在tablespace,extend(区)中有多个page,page(默认16kb)里放row(每一行,大概每个page放2-200行记录)
* .frm:table's format
* .ibd:table data & associated index data
* ubunut的frm文件和ibd文件可在目录`root@udev:/var/lib/mysql# ls`中查看,下图为`innodb`行格式,由下到上,均向上兼容
* 
* Antelope 羚羊 对存放bolb或者大varchar存储极长的数据时,将行数据的前768字节存储在数据页中,后面通过偏移量指向溢出页。
* compact 紧凑的
* redundant 多余的
* Barracuda 梭鱼
* antelope对存放blob的数据完全溢出,在数据页中只存在20个字节的指针,实际数据放在bolb page
* compressed
* dynamic
* [more](https://link.segmentfault.com/?enc=TwPymCRXah3aoeos7gw6%2Fw%3D%3D.RJUGi%2BJV%2FSlksNHv749cFe4xYCsEOla0BZxY4T8bAD7zdrZWTVuoyV7B7BBzd1yoQzAlaNkzIYUTPwiI3sERCk2RDgFuyEIDa83g8xQ47lU%3D)
* myisam
* frm(与innodb通用),在磁盘的datadir文件
* myi
* myd
* 事务
* 原子性atomicity
* 一致性consistency
* 隔离性lsolation
* 持久性durability
* 分表数量级
* 单表在500w左右,性能最佳。BTREE索引树 在3-5之间
* 隔离级别
* 事务的隔离性是数据库处理数据的基础之一,隔离级别是提供给用户在性能和可靠性做除选择和权衡的配置项目,以下四种情况都有一个前提
* 1. 
* 1.read\_uncommited:脏读,未提交读。不加任何锁,能读取到其他会话中未提交事务修改的数据,主流数据库几乎没有这么用。的见下图
* 
* 2.read\_commit:RC,不可重复读,已提交读。注意这个与1的区别是,读取不加锁,但是数据的写入、修改和删除是需要加锁的,而1是啥锁都不加。啥🔒都不加。只对记录加记录锁,而不会在记录间加间隙锁。所以允许新的记录插入到被锁定记录的附近,所以多次使用查询语句时,可能得到不同的结果,non\_repeatable\_read,见下图,第二个事务修改了id=1的数据后,第一个事务再次读取的时候结果不一样。这就是不可重复读,即两次结果不一样。
* 
* 3.repeatable\_read【默认级别】:RR,可重读,幻读,返回第一次的查询的快照(不会返回不同数据)。虽然事务2提交了更改,但是事务1仍然读到的是事务2未修改的状态。见下图
* 
* 4.serialize:解决了幻读,可串行化。幻读和可重复度读的区别在于导致幻读的是其他事务的insert。这样会引起第一个事务莫名其妙的多出来一条数据。幻读主要是依靠读锁写锁相互独立(互斥),但是会极大的降低并发能力。
* 一句话总结:
* 读未提交:B可以读到A未提交的事务,i.e A中未提交前所做的改变可以让其他任何事务看到。
* 读已经提交:B只能读到A提交之后的事务,i.e A中的更改只有在提交之后才能让其他事务看到。
* 可重复读:B执行过程中,其他数据想对来说都是静态的。也就是说看到A的数据是一致的(不管期间A提交了没提交)
* 串行化:B和A读写都依次分别上锁
* 索引机制(算法)
* * hash
* b+tree(m阶b+tree)
* 
* 所有数据保存在叶子节点,有k个子树的中间节点包含有k个元素
* 所有叶子节点包含了全部的元素信息,及指向这些元素记录的指针,且叶子节点本身依关键字的大小自小而大顺序链接
* 所有的中间节点元素都同时存在于子节点,在子节点元素中都是最大(或最小)元素(或者说:每一个父节点的元素都出现在子节点中,是子节点的最大或者最小元素)
* 插入的元素,要始终保持最大元素在根节点中,再次说:所有的叶子节点包含了全量元素信息。每个叶子节点都带有指向下个节点的指针,形成了有序链表
* 
* b-tree【不要念成b减tree,-只是个符号】
* 
* 内存操作(单一节点数量很多时,注意并不比二叉查找树数量少,只是速度比硬盘要快)
* 自平衡
* 左旋、右旋
* mongoDB用的是balance tree
* 特点(m阶的B树)
* 根节点至少有两个子女
* 每个中间节点都包括k-1个元素和k个孩子(m/2<=k<=m)
* 每个叶子节点都包括k-1个元素,(m/2<=k<=m)
* 所有的叶子节点位于同一层
* 每个节点中的元素从小到大排序,节点当中k-1个元素正好是k个孩子包含元素的值域划分
* b+与b-tree区别(为啥mysql中用Bplus,而mongo用B-tree?)
* 说简单点就是业务场景,mysql一般设计where,group,order,join等操作,mongo更多是一把梭(也可以order range,但是你这么用容易造成姿势不对而导致的性能差后果)
* 他俩都是m叉平衡查找树。B+中间节点没有数据(存储多个子节点指针),值在叶子节点存储。而b-Tree有具体数据(可以理解为key+data的二维数组,没有叶子节点的概念),所以前者同样大小可以容纳更多的节点元素。这样又导致了B+比b更“矮胖”,更进一步减少了io查询次数。很好的解释了下面这句话:在cluster index(聚集索引中),叶子节点直接包含卫星数据;在非聚集索引中nonclustered index中,叶子节点带有指向卫星数据的指针。
* b-只要查找到匹配元素,直接返回。而b+查询必须查找到叶子节,相对于b,b+无需返回上层节点重复遍历查找工作,所以得出b查找并不稳定,而b+是稳定的,b+树通过双向链表将叶子节点串联起来,基于空间换时间。目的就是支持倒叙和顺序查询。
* 针对范围和顺序查询,b需要n次中序遍历,而b+只需要通过叶子节点的双向链表指针遍历即可。
* b+更符合磁盘的局部预读原理,b只需要简历索引拿到想要的data就可以(mogno就是如此,范围、顺序、多索引查找等不适合用mongo)
2. 锁
* 种类
* optimistic lock乐观锁(并非真正的锁,是基于版本号基础的先尝试再更改,loop and try,可以参考状态机的状态)
* 特点:不会真死锁,一定条件下有较高的冲突频率和重试成本,但是相对悲观可以有更好的并发量
* pessimistic lock悲观锁
* 为了保证事务的隔离性,就需要一致性锁定读。读的时候要加锁,防止其他事务再次更改,修改的时候也要加锁,其他事务无法读取。主要就是依靠数据库的锁机制来实现,同时缺点很明显,就是会带来性能的开销,并发的减少。
* innodb的MVCC(Multi-Version Concurrency Control)
* 翻译成中文叫~心理医生~,多版本并发控制,适用于行锁的、事务性的数据库模型。
* 适用于innodb的rc和rr级别,因为可串行化涉及到锁表
* 实现思想是在每行增加一个create\_verison和delete\_version字段
* update 是插入一个新行,先保存当前版本号到旧行的delete\_version,且新建行的new\_create\_version也就是delete\_version
* delete操作就是直接标记delete\_version
* insert的时候,就是保存至create\_version
* selete的时候可以这样
* 读delete\_version为空的
* 大于当前事务版本号的
* 创建版本号<=当前事务版本号的
* 粒度划分
* 实例锁
* 使用场景:全局数据备份
* 支持事务的(e.g. innodb),可以使用mysqldump –single-transaction,会自动设置隔离级别为repeated read,期间的dml的update和ddl将会被锁住。
* 不支持事务的(e.g. myisam,memory)
* flush tables with read lock
* lock tables
* 行锁
* 表锁
* 意向锁 intention lock(表级锁)
* 场景:A对表中一行进行修改,B对整个表修改。如果没有以下的两个锁,B将对全表扫描是否被锁定。反之,A可以对某行添加意向互斥锁(表级),然后再添加互斥锁(行级),然后B只需要等待意向互斥锁释放)
* 意向共享锁
* 意向互斥锁
* 共享锁shard lock 读锁(行锁)
* 排它锁exclusive lock 写锁(行锁)
* 关于innodb必须要知道的
* 可以通过`SELECT \* FROM products WHERE id='3' FOR UPDATE`进行锁,但是必须在事务中
* 上述语句必须是命中索引才会行锁,否则是table lock
* 两阶段锁(注意区分分布式事务的2pc)
* 锁的算法
* record lock:加到索引记录上的锁,如果通过where条件上锁,而不知道具体哪行,这样会锁定整个表
* gap lock:间隙锁某个区间的锁定,对索引记录中的一段连续区域的锁。
* next-key lock:行锁和GAP(间隙锁)的合并,next-key锁是解决RR级别中 幻读问题的主要方案。可以搜索关键字 快照读(snapshot read)和当前读(current read)去了解~
* 死锁:

* 注意区分 deadlock VS lock wait timeout
3. 分库分表
* 方式:垂直、水平。按范围、按业务属性、取模
* 注意点:增加连表复杂性,某一个库宕机后导致数据查询不准确,分布式事务的问题
4. 主从复制的简单过程 及策略:
* master log \[binlog event\] dump thread->slave IO thread\[Slave\_IO\_Running\] \[flush binlog event =>relay log\]->slave SQL thread\[Slave\_SQL\_Running\]->exec relay log
* 策略:
~~~crmsh
- 异步Asynchronous:不需要ack,通过
- 全同步:从库完全同步才可以,需要slave的ack发给slave。
- Semisynchronous Replication 半同步 复制:从库有部分ok就算ok,同样需要slave的ack发给slave
~~~
5. mysql主从同步为什么会延迟,有什么解决办法?
* 硬件资源瓶颈导致:网络、cpu,硬盘
* 业务量太大:分库分表
* 参考同步原理并优化
* 原因1:从库上单线程Slave\_SQL\_Running可能有ddl语句和查询造成lock,导致延迟。(主库ddl时为多线程)
* 优化1:【5.7后可修改`set global slave_parallel_workers=10`】,用多个线程并行重放relay log【基于GTID中的commit\_id&sequence\_number等字段,可以保证多个从库和多线程最终的一致性】
* 原因2:内存flush到硬盘策略(redis的aof也是如此)
* 优化2:
* 考虑禁用salve端binlog
* bin\_log落盘机制sync\_binlog=0的优化
6. ACID
7. 覆盖索引(复合索引)
* 定义:包含两个或多个属性列的索引称为复合索引。如果查询字段是普通索引,或者是联合索引的最左原则字段,查询结果是联合索引的字段或者是主键。这种就不必通过主键(聚集索引再次查询)
* 目的:减少磁盘io,**不用回表**
* b+树索引
8. 聚集索引cluster index 一般为primary key
* 定义:按照每张表主键构建一棵B+TREE,叶子节点放的整张表的行记录数据
* 与之相对应的是辅助索引(secondary index)
* innodb存储引擎支持覆盖索引,即从辅助索引中可以查到查询记录,而不需要查询聚集索引中的记录。
* b平衡树+树索引

* 上图对应的表结构:
* ~~~sql
CREATE TABLE users(
id INT NOT NULL,
first_name VARCHAR(20) NOT NULL,
last_name VARCHAR(20) NOT NULL,
age INT NOT NULL,
PRIMARY KEY(id),
KEY(last_name, first_name, age)
KEY(first_name)
~~~
* 一张表一定包含一个聚集索引构成的b+树以及若干辅助索引构成的b+树
* 每次给字段建一个索引,字段中的数据就会被复制一份出来。用于生成索引,(考虑磁盘空间)。不管何种方式查表,最终都会利用主键通过聚集索引来定位到数据。聚集索引(主键)是通往真实数据的唯一出路。
9. 辅助索引:非聚集索引都可以被称作辅助索引,其叶子节点不包含行记录的全部数据,仅包含索引中的所有键及一个用于查找对应行记录的【书签(即主键或者说聚集索引)】,下面两个图为辅助索引(first\_name,age)以及通过主键再次查找的过程
* 
* 
10. 联合索引:与覆盖索引没有区别,或者理解为覆盖索引是联合索引的最优解(无需通过主键回表)。
11. explain
* extra
* using index :condition(用了索引,但是回表了)
* using where :uning index(查询的字段在索引中就能查到,无需回表)
* using index condition:using filesort(重点优化:表明查到数据后需要再进行排序,尽量利用索引的有序性。)
* using where:using index
* type(连接类型:join type),以下逐步增大
* system 系统表,磁盘io忽略不计(有些数据就已经在内存中)
* const 常量连接(加了where条件限制,命中主键pk或者唯一unique索引)
* eq\_ref 主键索引或者非空唯一索引、等值连接;如果把唯一索引改为普通索引+等值匹配,可能type只为ref,因为可能一对多
* range 区间范围 between and;where in,gt lt;(注意必须是索引)
* index 索引树扫描,即需要扫描索引上的全部数据,比如innodb的count
* all 全表扫描
* select \* 与索引(看主要命中条数与总条数,如果相近,用不到索引,就全回表了,如果是一定范围,那就是range use indexcondition)
* rows(粗略统计,不是精确)
12. 其他:
1. varchar为啥为65535?compact行记录的第一个字段为变长字段长度列表,为2个字节16位。[参考](https://link.segmentfault.com/?enc=8HFh%2BJLkHUsuLw0xSTK1lg%3D%3D.Se3e9F0zSSLIuqs7MjsB%2FdIkDH6%2BB%2B2GOYFVEjT3zl4bGeynIBlTKqWUPR5m5UmRYsEKRLo6uIlF0tbmrltF4Q%3D%3D)
2. 一个表最多多少行?1023,具体也是看行格式的数据结构即可,参考上文的参考链接。
3. 为什么建议给表加主键?主键的作用是把数据格式转为索引(平衡树)
4. 联合索引在b+树中如何存储?
5. 为什么索引不直接用二叉查找树,要用b树,b+树?主要考虑减少磁盘io(考虑磁盘物理原理及局部性与磁盘预读的特性:)
6. myisam和innodb必须有主键吗?innodb必须有,数据文件需要按照主键聚集,如果没有innodb会自动生成。
7. mysql 最大连接数默认多少(max\_connections)?答案是151。(这里可以引申到php-fpm的pconnect相关问题)
8. mvcc是怎么实现的?
* mvcc 利用read view结构(包含rw\_trx\_ids、min\_trx\_id 、max\_trx\_id、 curr\_trx\_id,根据这四个字段的大小判断当前的事务执行是否可显示)和undo log实现。且只能在rr 和rc两个级别下生效【因为ru和serializ前者只读最新值,而后者会加读写锁,更新会直接阻塞,也不会存在多值的情况】
* 通过undo log记录,通过每行的三个隐藏字段 (隐藏的rowID+隐藏的事务ID+隐藏的回滚指针roll\_ptr)指向updo log表空间。
9. redo undo binlog是做什么用的?谈谈两阶段提交、两阶段锁的理解?
* 两阶段锁一般用在innodb的行锁中,需要的时候加锁,commit|rollback之后释放【知识点是一个事务里的操作的先后顺序:把时间最长的放最后】。
* redo log事务(只有innodb存储引擎支持)重放日志,一般针对insert 和update时buffer pool的数据页与磁盘中不一致(没有flush|fsync到磁盘,脏页),而这时候crash。执行记录就被append到redo log中,但是redo log空间有限,会定期
* undo log 记录被insert和update之前的值,相当于ctrl+z(回撤,回滚)。
* 如果uodo insert,那么对应起回滚操作就是delete
* 如果为undo delete ,那么对应回滚操作就是insert
* 如果 是update,那么对应回滚操作就是逆向的update
* binlog 是server层面的log,是类似append file的形式。
* 两阶段提交:指的是redo log【prepare+commit】和 binlog的同步措施
* 先写binlog,然后宕机,导致redolog没有写。这样binlog同步时会导致数据差异
* 先redo log commit后,宕机导致binlog写入失败。binlog同步时仍会导致数据差异
* 通过有prepare和binlog的场景,系统恢复后自动commit
* 如果没有binlog,就直接回滚
10. 你还知道哪些数据库,和mysql相比有哪些具体的使用场景和优势?
11. 聚簇索引的结构是怎样的?
12. acid,四种隔离级别是如何实现的?
13. mysql binlog的几种同步方式?
* 异步Asynchronous
* 全同步
* Semisynchronous Replication
14. 一张表可以建立多少个索引?
15. 谈谈你对gtid的理解?
16. 索引下推IndexConditionCountDown和回表有什么区别和联系?
* * *
### 三、算法&数据结构
* 总览(见下图):
* 
* 查找算法【适用场景及其优缺点】
* 顺序查找
* 二分查找
* hash查找
* 平衡树
* 二叉搜索树
* B树
* B+
* 索引查找
* bfs&dfs
* 红黑树
* 排序算法
* 应用问题
~~~markdown
- 如何实现一个LRU功能?【双向链表】
- 如何实现浏览器前进后退功能?【两个栈】
- 上台阶问题【递归斐波那契】
~~~
* * *
### 四、设计模式
1. 设计模式
* 单例模式 (static ,consturct)
~~~php
static private $instance;
private $config;
private funciton __construct($config){
$this->config=$config;
}
private funciton __clone(){
}
static public function instance($config){
if(!self::$instance instanceof self){
self::$instance=new self($config);
}
return self::$instance;
}
}
~~~
* 简单工厂(switch case include new return )
~~~php
{
public function makeModule($moduleName, $options)
{
switch ($moduleName) {
case 'Fight':
return new Fight($options[0], $options[1]);
case 'Force':
return new Force($options[0]);
case 'Shot':
return new Shot($options[0], $options[1], $options[2]);
}
}
}
# 使用工厂方式 001
class Superman
{
protected $power;
public function __construct()
{
// 初始化工厂
$factory = new SuperModuleFactory;
// 通过工厂提供的方法制造需要的模块
$this->power = $factory->makeModule('Fight', [9, 100]);
// $this->power = $factory->makeModule('Force', [45]);
// $this->power = $factory->makeModule('Shot', [99, 50, 2]);
/*
$this->power = array(
$factory->makeModule('Force', [45]),
$factory->makeModule('Shot', [99, 50, 2])
);
*/
}
}
# 使用工厂方式 002
class Superman
{
protected $power;
public function __construct(array $modules)
{
// 初始化工厂
$factory = new SuperModuleFactory;
// 通过工厂提供的方法制造需要的模块
foreach ($modules as $moduleName => $moduleOptions) {
$this->power[] = $factory->makeModule($moduleName, $moduleOptions);
}
}
}
// 创建超人
$superman = new Superman([
'Fight' => [9, 100],
'Shot' => [99, 50, 2]
~~~
* 门面模式
* 对客户屏蔽子系统组件,减少子系统与客户之间的松耦合关系
* 设计原则
* 依赖注入(DI、AOP)
* 开闭原则(open for extension, but closed for modification)
* * *
### 五、正则表达式
1. 正则表达式
* 应用场景
* 范匹配
* 模版引擎
* 词法分析器(lex)
* 常见正则
* * *
### 六、PHP
1. php
* 代码解释过程(大多的非编译语言)
* lexical词法分析,输入为源代码,输出为token
* 语法分析 工具为文法(LALR),输出为表达式,7.0为AST,涉及:
~~~asciidoc
- 注释
- 分号 & 分隔符
- 变量
- 常量
- 操作数
~~~
* 类型检查、关键字处理、导入,输出为中间代码。工具为选定的的编译器优化工具
~~~asciidoc
- 中间代码生成(Opcodes)
- 机器码生成(编译语言)
~~~
* session共享配置
* phpunit用法
* cookie购物车和session购物车的实现
* 弱类型实现
\-zval(不仅是变量名) & zend\_val变量值
* 代码规范
* 自动化:sonarquebe+jenkins
* 单元测试
* php进程间如何通信
* 信号量(消息同步|互斥)(pv操作) sem\_\*
* 信号(信号触发事件)(pcntl\_signal, pcntl\_wait\*)
* 消息队列 msg\_\*
* 管道(pipe)
* socket |unix \_\*.sock
* 共享内存(shm\_*,shmop\_*)
* php并发模型
* php执行流程
* 
* 变量底层存储结构
* zval结构了解多少
* 常用的数组函数(列出10个)
* array\_combine(前面数组作为其键,后面数组做为其值)
* array\_merage(合并两个数组,后面覆盖前面,但数字索引会重新索引,不会覆盖)
* array\_multisort
* php垃圾回收机制(gc)
* zend.enable\_gc`php.ini`
* gc\_enable()`funciton`
* 引入计数(zval指向zend\_value个数为0时)+写时拷贝(copy on write)
* 循环引用问题(array、object引用自身成员),垃圾回收器将其收集至于一个buffer(\_zend\_gc\_global->gc\_root\_buffer)后启动垃圾鉴定程序
* 把session放入redis里面还会触发类似文件的state session
* session.gc\_probability (default 1)
* session.gc\_divisor (default 100)
* session.gc\_maxlifetime(单位秒)
* session.cookie\_lifetime(单位秒,0表示直到关闭浏览器)
* session.save\_path,如果你真的想实现不同应用目录分层,一定去找下面这个文件(大概率不会有这种需求)
* 
* session\_write\_close (显示关闭,后期使用需要显示开启)
* 说明:因为php的语言特性,针对session的gc只能是session模块启动的时候去执行。默认的概率为1%,另外默认所有的站点都是在一个目录下,如果多站点在同一目录设置了不同的session时间,gc不会区分站点。只按照当前时间-文件创建|更新时间是否大于gc\_maxlifetime来判断。当然现在一般都放到redis实现了sessionhander接口,就不用gc了。(Not necessary, Redis takes care of that.
* 内存模型
* 整型、浮点、bool、NULL、内部字符串、不可变数组都是通过zval直接保存,不会用到引用计数
* string、array都会使用引入计数(支持复制cow),object、resource本身可以理解为引用
* fpm三种配置及场景
* dynamic
~~~diff
- pm.start_servers
- pm.max_children
- pm.max_spare_servers
- pm.min_spare_servers
~~~
* static
~~~asciidoc
- pm.max_children
~~~
* ondaemon
~~~asciidoc
- pm.process_idle_timeout
~~~
* 数组底层
* 如何保证有序:又加了一层映射表(与bucket大小相同)
* 如何解决hash冲突:拉链法(头插)
* 扩容:逻辑删除(考虑unset内存情况、是否需要重建索引)
* fpm 的mater worker模型与nginx的master模型有什么区别?
* nginx的子进程基于epoll,是非阻塞的。可以同时监听多个请求,只处理活跃的fd
* fpm 基于 fcntl封装的FCGI\_LOCK|UNLOCK函数,只能响应一个请求,是阻塞在当前进程的
* fpm 的master和worker之间如何通信?答:信号 signal,比如检测worker的超时时间
* fpm状态查看
~~~apache
curl localhost/php_fpm-status
pool: www
process manager: dynamic
start time: 13/May/2017:09:50:43 +0800
start since: 986
accepted conn: 2
listen queue: 0
max listen queue: 0
listen queue len: 0
idle processes: 1
active processes: 1
total processes: 2
max active processes: 1
max children reached: 0
slow requests: 0
~~~
* * *
### 七、操作系统
1. 操作系统
* 线程,进程,协程。并发,并行
* socket和管道的区别
* 进程间通信手段
* 共享内存
* rpc
* 管道
* 线程间通信手段
* 读写进程数据段
* 说一下同步阻塞,异步非阻塞的关系、IO多路复用和异步IO了解多少?
* * *
### 八、网络协议
1. 网络协议
* http
* 构成:起始行(GET =>200),首部头 (ACCEPT=>CONTENT-TYPE),主体 name =》tongbo
* 版本:
* 1.0
* 1.1
* 2.0 :多路复用、流量控制
* WebSocket
* 可以用wireshark抓包看下具体upgrade的流程
* 常见面试题
* ws server安全验证怎么做?(不能让谁都连吧)
* 思路:handleshake,key
* ws server的 Cluster怎么做?
* 多节点的ws server 如何群发消息( 可以配合redis pubsub 、mq、kafka实现)
* 长连接
* 在一个连接上发送多个数据包
* 心跳、如何发送心跳
* httpdns
* 定义:用http协议代替原始的udp dns协议,可以绕过运营商的local dns
* 解决问题:避免local dns造成的域名劫持问题和调度不精确问题(更多是在移动客户端)
* 其他解决方案
* 客户端dns缓存
* 热点域名解析
* 懒更新策略(ttl过期后再同步)
* post请求分割head 和body
* get vs post:
* get(
* 安全幂等,请求实体资源
* 参数只能url编码,且参数长度有限制
* 浏览器会自动加cache
* post
* 附加请求实体于服务器
* 产生两个tcp数据包
* 数据支持多种编码格式
* resultful
* get:获取资源
* post:新建资源
* put:更新完整资源
* delete:删除资源
* patch:更新部分资源
* RPC
* 看张图↓
* 
* RPC框架涉及基本组件服务
* 客户端、服务端自动代码生成、多语言支持
* 消息序列化、反序列化
* 连接池、负载、故障、队列、超时、异步
* 以上可以引申出问题:RPC和http各自的优缺点?
* 常见协议
* soap(http |jsonrpc)
* GRPC:protocol buffer,(HTTP2 AND Tcp)
* thrift(tcp)
* [http vs rpc的缠绵关系](https://link.segmentfault.com/?enc=BoEuU2f9hvAanB0vDFGFmg%3D%3D.VL77t7TQ%2BTjdO9CSYwIgumfGYAL72paZcXaV9XciFfkVRllccRnS5I%2BdH0qPhXy5Zr9zkR0gJ4Qxq%2BHOkX0PbA%3D%3D)
* [TCP](https://link.segmentfault.com/?enc=cOBwk2ABHpDcEXrzyhJwXw%3D%3D.HyHqz3UDXFfVe2VCpvEzoqn5K31YoAsaIGlNjiJas6P7MwXQhNw9tdnBnH6VGsmc)
* * [三次握手](https://link.segmentfault.com/?enc=1DdpZG1PmVk6%2FmivuG8oEw%3D%3D.9zR24B8WJDhj80D3yiqQOfpouPMuuy4vMYEGnK0fNKWSUx%2F8Am%2Ft7AYa6p4VjIF2r0n3FFSbIL%2BuG3haMhE6rA%3D%3D)
* 
* 四次挥手
* 
* 挥手过程:
* 客户端:FIN\_WAIT 1,停止发送数据给服务端。等待服务端确认
* 服务端:ack ,进入CLOSE\_WAIT(关闭等待),此时如果服务端有数据要发送,客户端还可以接收。
* 客户端收到服务端确认后,进入FIN\_WAIT 2,等待服务器发出连接释放报文段。
* 此时如果服务端没有数据要发送,发送上步骤客户端等待的释放报文段,然后服务端进入LAST\_ACK
* 客户端收到服务端的last\_ack后,发出确认,进入TIME\_WAIT,经过2MSL后,客户端关闭
* 服务端收到客户端报文段后,进入CLOSE
* 以下这张图明确的指出了syn、ack的交互及对应的转换状态,值得收藏!!
* 
* tcp特点
* 面向连接,先建立(握手),然后释放(挥手确认拜拜)
* 只能点对点
* 可靠交付(相对来说),全双工,接收和发送端都设有发送和接收cache
* 面向字节流(流:一连串,无结构的的信息流,流入到进程或从进程流出的字节序列,而一个报文段多少字节是根据窗口值和网络拥塞程度动态变化的)
* 关于两个重要的队列:半连接队列syns-quene & 全连接队列accept quene,如下图
* 
* 关于**TIME\_WAIT**:
* time\_wait是一种TCP状态,等待2msl可以保证客户端最后一个报文段能够到达服务器,如果未到达,服务器则会超时重传连接释放报文段。使得客户端、服务器都可以正常进入到CLOSE状态。
* 关于'粘包'
* 分包:在一个消息体或一帧数据时,通过一定的处理,让接收方能从字节流中识别并截取(还原)出一个个消息体。
* 短连接tcp分包:发送方关闭连接,接收方read 0,就知道消息尾了
* 长连接TCP分包:
* 消息长度固定or消息头中加长度字段
* 固定消息边界,比如http:rn
* 利用消息本身格式,如`xml,json`
* 关于syn flood
* 基本思想:恶意像某个服务器端口发送大量SYN包,服务器会分配一个transmission control block,并返回ack。然后服务端转为syn-recv状态。系统保持这个资源
* 常见防攻击方法
* 入门级:系统监视半连开连接和不活动的连接。一视同仁,超过一个阈值后全部rst这些连接。
* 延缓tcp分配:当三次握手后再分配tcb,这样可以有效的减轻对服务器资源消耗(常见方法是使用syn cache,syn cookie)
* syn cache
* 保存对应的序列号和报文(这里特指半连接)到hash表中,直到收到正确的ack报文再分配tcp
* syn cookie
* 使用特殊算法生成sequence number,主要涉及到对方无法了解到的己方固定的一些信息(比如己方mss 时间等)。这样如果对方真的发送过来了ack报文,对其ack-1.如果相等,那么就分配这个tcb。没有或者不相等,那就不分配。
* syn proxy防火墙:我简单理解为是一层代理,中间有验证,或者涉及到序列号的修改。具体的不了解。。
2. 特性协议
* 拥塞控制(涉及4个核心算法,全局控制)
* 1.慢开始
* 2.拥塞避免
* 3.快速重传
* 触发点:收到了3个相同的ack NO,会在定时器过期之前,重传这个丢失的NO.
* 3.1选择重传select-ack
* 是对快速重传的优化:需要在tcp '头部选项'中加sack。`/proc/sys/net/ipv4# cat tcp_sack >1`,这个参数默认是打开的。说白了就是接收端在发送ack给发送端的时候告诉他我已经收到了哪批数据,你只需要选择我没有的就好了。
* 3.2选择重传 duplicate select-sack
* 主要是针对接收方丢失ack的情况,告知发送端是我的问题。这个段儿内的不要发了。
* 4.快速恢复
* 滑动窗口(点对点控制)
* 断线重连
* 停等
* 超时重传
* 超时重传的间隔(Retransmission Timeout)是怎么计算的?答:有个计算公式,涉及【α = 0.125,β = 0.25, μ = 1,∂ = 4】,别问我怎么来的了。
3. udp
* 无连接、best effort、面向报文(不合并、不拆分,保留边界)
* 无拥塞控制、流量控制、首部开销小(8个字节,而tcp有20个首部)
* 支持一对一,一对多,多对一
4. 自定义协议
5. rpc
* * *
### 九、大前端
* js
* 百度统计的实现
* 基于cookie,引入js脚本及baidu个人账户id,读取当前信息,适当节点发送请求给百度服务器
* 前后端分离
* jwt
* IOS/Android/React/vue
* Flutter
* 弹性布局
* * *
### 十、中间件
1. 中间件
* **RabbitMQ**
* 消息可靠性保证,靠分别保证以下四点及基本实现:
1. 保证生产者发送消息mq 正常
* 事务机制:publish 自身控制
* 发送方确认机制【包括发送失败及没有找到对应的exchange|queue并设置mandatory参数(是自动删除还是返回给publish失败)等都会触发mq回调,可以根据具体fail原因来分别处理】: mq 发送ack给publish`publisher-confirm-type: correlated`,根据一个CorrelationData(uuid),并设置一个setConfirmCallback回调函数来判断。
~~~markdown
2.保证mq->exchange && - 3 exchange->queue正常 -3.5
- exchange 和queue的 durable 参数设置持久化
- 发送的消息默认为持久化
- 有必要的话引入分布式镜像队列,保证exchange queue msg的持久化。
3. 保证 consumer消费消息正常
- 保证消费者消费消息确认[acknowledge-mode: manual]
- 注意可能出现重复消费,需要(分布式)幂等处理
4. EXTRA:可以先做一个基础数据落地+mq(publish+consumer)+定时任务做兜底,做到数据的100%处理。
~~~
* * 核心组件:broker(virtual host(exchange(queue(message) bind)) (connection(channel)))
* 消息如何限流?
自带QOS策略,在非自动确认消息`acknowledge-mode: manual`下,如果一定量的消息还未被消费,则不进行新消息的消费,`prefetch`参数
* 延迟消费怎么实现?
支持消息和queue的过期设置+DLE(比如下单后30分钟取消订单,取消动作由消费者对DLE中的队列消费【跳过已经付款状态的即可】)
* 死信队列怎么理解?
* 特殊的exchange 为Dead Line EXchange
* 如果拒绝消费、消费超时、ttl过期、队列达到max length等,可设置自动转发到DLX
* 重复消费怎么办
* 顺序消费怎么保证
* 高可用性怎么保证
* exchange 交换机的分发模式
* * fanout:将publish的消息发送到之绑定的所有queue
* direct :将publish的消息发送到之绑定的所有queue && routeing key matching
* topic :将publish的消息发送到之绑定的所有queue && routeing key fuzzy matching
* **kafka**
~~~pgsql
- 源自linkedin,用于处理活动流和运营数据。主要是同一消息为多个应用程序消费
- f分布式流处理,持久化,leader负责读写,follower负责数据同步,leader不可用后通过in-sync-replica来选择一个leader
- broker(服务器),其中producer发消息到brokder,consumer向brokder读取
- topic:逻辑上消息类别
- partition:物理分区,一个topic可能存在多个partition上,一个分区中设置一个偏移量。具体数据存在partition的segment中,包括数据文件和索引
- consumer group:每个consumer可以指定特定group
- 消费端消费确认(commit),支持换group的方式数据回放。默认保存7天,可以 frpm beginining again
- [需要自己扩展的](https://www.cnblogs.com/qiaoyihang/p/9229854.html):丢数据(生产方or消费方)、数据重复(at-least-once)的场景
~~~
* **Redis 队列**
~~~markdown
- 支持优先级队列brpop
- 延迟队列:sortSet+timestatmp+cron轮询(ZRANGEBYSCORE)
- 不支持自动commit offset
~~~
* 近实时解决方案
* cancel->kafka-streamSet->kudu->hue|Davinci
* * *
### 十一、php框架
1. php框架
* ci
* ci大对象
* hyperf
* 注解()
* ioc&aop
* co协程相关
* yii
* laravel
* 核心关键字:
* Container
* ServerProvider
* facade &contraces
* binding & make &reflection
* AppServiceProvider register:服务提供者注册
* IocContainer:(工厂模式的升华:ioc容器)
* 控制反转(inversion of control)可以降低计算机代码之间的耦合,其中最常见的方式叫做依赖注入。(Dependence Injection),还有一种方式为依赖查找。可以理解简单的工厂可以实现依赖的转移(简单理解不需要在A类中依次new ABCDE,只需要传递ABCDE这个参数(必要的时候循环就可以了),然后调用工厂的makeObj方法就可以)
* 实现方式
* 基于接口:实现特定接口以供外部容器注入所依赖类型的对象。
* 基于set方法:就是一个属性
* 基于构造函数:实现特定参数(比如一个已经new出来的Object)的构造函数
* 管理类依赖
* 执行(依赖注入DI):通过构造函数或者某些情况下通过setter方法将类依赖注入到类中,容器并不需要被告知如何构建对象,因为他会使用php的反射服务自动解析出具体的对象。
* 中间件middare(装饰器模式在) |pipe实现
* 队列中涉及的redis lua脚本
* 另外可以参考spring的相关思想。
* swoole
* * *
### 十二 、运维
1. 运维&架构
* 服务器cpu99%如何分析
* mysql 占cpu如何分析
* php占cpu较高如何分析
* sso实现方法
* mysql优化方法
* 如何提高监测数据的准确性
* docker 原理及引用及编排管理
* docker 工具
* rancher
* harbor
* * *
### 十三、golang
* golang vs php
* golang的协程和swoole的协程的区别是什么
* swoole的协程和php自带的yield的场景有哪些?
* 引用和指针的区别和具体应用场景?
* golang 核心特点
* 语言层面实现协程调度器scheduler【[gmp模型](https://link.segmentfault.com/?enc=kqLWmCn1WB1AV1MjvSsTBQ%3D%3D.QBIs2s%2B7yVU68iJKyTyFN0lAik%2BxS8nmftzIGBwRMx2nRZSglW3LqQKh1dKfynO3Lmaq5ZsUVM5oouIqKTWowg%3D%3D)】
* 
* goroute和线程之间的数量关系
* 线程最大数默认和cpu和核数相等,goroutine跑在不同的cpu核对应的线程上。
* runtime 用户态协作式调度,channel通信
* 列举golang调度的触发时机
* I/O操作
* select|channel
* 锁
* runtime.Gosched()
* 显示的停止一个goroutine有哪些方法
* channel(类似发一个signal)
* Context(withCancel、withDeadLine)
* WaitGroup(Add、Done、Wait)
* 调试工具
* go tool trace 【支持web】
* pprof【支持web】
* 微服务实现 go kit | go micro
* 语法层面
* interface【多态】
* eface
* iface
* 具体问题
* golang的内存管理模型
* 基于ThreadCachingMalloc
* golang的调度模型
* M:内核级线程(相对用用户态线程),相当于内核线程在go进程中的映射,与操作内核线程1:1对应
* P:更多的是M执行g时候所需要的上下文环境。M和p可以是m:n的关系,p由GOMAXPROCS变量决定
* g:即go语言层面实现的用户态线程,拥有独立的栈、状态和代码片段
* 简单说一下变量逃逸 variable escape
* 变量逃逸和具体语法没关系,和编程效率有一定关系
* 一般来说 局部变量会分配到栈上
* 但是有局部变量被引用(return &),或者局部变量很大,大概率分配到heap上。
* 可以通过`go run -gcflags '-m -l' main.go`显示查看
* goloang的chanel实现原理
* golang的context有哪些?分别的应用场景是那哪些?
* withcancle
* withdeadline
* withtimeout
* withvalue
* goroutie如何管理
* [channel通信的基本过程及原理](https://link.segmentfault.com/?enc=6mLfMCru32L5WinhyRyskA%3D%3D.35rlPlgV90I8Jaw5b5R3sr6bZR0XAWoA9QJDWXTe0sSibg5rihZdUUdTlVGcZJ24dxnTYGQQuLuWMychxpb4ULD6ERJry%2FwR0811BzppSmU%3D)
* buf(循环链表)+recx|sendx边界(保证FIFO)+lock(保证读写原子性即发送方要复制&写入到channel,接收方要从channel复制&写入到recGo&channel删除)
* 注意发送和接收阻塞的情况以及直接不通过buf(buf为0)发送、接收数据的情况
* 说一下go中的浅拷贝和深拷贝
* * *
### 十四、 Linux
1. linux 常用命令
* netstat 查看tcp udp unixsock网络
* 查看负载cat /proc/loadavg | w | top
* df
* lstat:strace的时候常常可见他
* top shift+M
* top -Hp Pid
* top -c Q
* free
* lsof 查看当前进程id,进程名等占用的文件描述符
* ipstat
* strace
* grep \[-A ,-B, -C\]'HTTP/1.1" 200' access.log |wc -l
* socket和管道(pipe)的区别:socket全双工,pipe半双工\*2
* awk & sed
* awk '{print $1}' access.log |sort |uniq |wc -l
* * *
### 十五、nginx
1. nginx
* 负责均衡实现方式
* fair 响应时间短的优先分配
* least\_conn 分配给连接数较少的后端机器
* hash(:ip\_hash)ip与hash绑定,可以解决session共享问题,url\_hash,更亲和与url缓存类场景
* consistent hash 一致性hash
* robin 轮询(:upstream) 按照时间顺序,逐一分配(如果down,能自动剔除)。nginx默认方式
* 权重 (: upstream+weight) 和weight的值成正比,常用于后端每台机器资源侧重点或者硬件性能不一致
* 服务模型
* * *
### 十六、分布式 | 微服务
1. 分布式
* 理论基础
* 权衡
* acid
* base
* cap
* 解决多个节点数据一致性的方案其实就是共识算法
* 分布式协议
* Paxos:Proposer, Acceptor, Learner
* ZAB:Follower, Leader, Observer
* raft:leader ,follower,candidate
* 
* 数据处理
* 数据分片:HASH|REGION
* 数据冗余:主从复制|一致性协议(raft,paxos)|去中心化
* 分布式工具【注意区分工具实现的cp还是ap】
* zookeeper:zab(base paxos)protocol,
* [etcd](https://link.segmentfault.com/?enc=s0MK8wV80mk9deDTwnGJJA%3D%3D.huVzDnhxVFTFyXxTFoVQ8m5nfwDsvdCV9Cr3oMXggcV3D0RDG7AyR09d8uSSR0SN):
* raft protocol(mini PAXOS),k-v database
* WAL(Write Ahead Log)
* 具体
* 两个mysql的实例,一个存用户钱包,一个存用户积分。怎么实现分布式事务?
* 如何安全的使用redis锁?[分布式锁问题](https://link.segmentfault.com/?enc=wsSQzqDf59BgG%2BqVcFTvPw%3D%3D.zXO1sPbUsvc1PIQed%2BXyOt3eC521gP3t3P8vmDJSamfnuTi6xFjWO11bS1brjSjEruHp3tWC7oAxA%2F6jNBqHbQ%3D%3D)
* 现有设计参考
* HDFS||GFS(分布式文件系统)
* Kafka ||Pulsr(分布式消息队列)
* Redis Cluster || Codis(分布式缓存),属于AP模型,单机为CP^\_^
* MySQL (分库分表)
* MongoDB || ElasticSearch ReplicaiSet&Sharding
* zookeeper|etced|consul
2. 微服务
* 优点:
* 相对于单体服务更简单,注重单一功能
* 每个服务可以独立开发打包测试部署,且语言环境无关
* 可以水平、高效扩展
* 缺点:(或者难点)
* 运维成本,服务发现、治理、降级、熔断
* 网络消息传输、安全、延迟
* 服务调用排查追踪
* 最佳原则(Domain dirven design)
* 高内聚:修改一个功能,只需要改一个服务
* 低耦合:修改了一个地方,不需要改其他的地方(下游消费者不受影响)
* 业务内原则:
* 新服务用新的微服务,确定无误后保留推进,否则调整
* 老的保留,直到新服务稳定再切换
* 必需的的监控与日志|生产-订阅—消费模型
* 尝试对外不可见的服务先做试点,错误邮件、日志、系统内调用、api内部分成熟接口
* 分层参考
* ~~~pgsql
业务层 【车源、零售、交易、物流、售后等】
应用(子)层【mail、sms、login、call、bi】& 数据架构层【数据模型】
技术架构层【网络拓扑、分布式】
~~~
* 服务发现及相关工具
* 
* 服务发现的三种模式
1. 侵入式代理(正向代理),配合服务注册中心(服务端注册,客户端发现)
2. 集中式代理 nginx |f5,由代理解析并分发
3. 独立代理进,单机可复用代理,配合服务注册中心使用ServcieMesh
* 考虑问题
* 部署 编排 持续集成|部署 自动化测试 服务注册发现 监控 调用链追踪 (基于OpenTracing协议的JeagerORZipkin)分布式事务 等开源工具应用
* 容器部署
1. 用户交互发起部署请求
2. 调度-打包-拉取-集成
3. 根据配置、选择对应runtime平台
4. 打包成docker iamge(放到镜像仓库harbor)
5. 生成、部署容器
* 考虑服务之间的依赖(迁一发而动全身.雪崩)及调用幂等、系统维护等成本
* 三板斧限流、熔断、降级的意义及如何实现?
* 熔断:就是保险丝烧断了,但是这个保险丝可以自恢复。调用方控制,在出现异常及阈值(超时或者retry次数)后取兜底(mock值)或者去别的端口去取的逻辑。(closed->open->half open)
* 限流:发起方、接收方都可以限制。发起方层面限制可以避免打垮上游服务,接收方可以避免自己被压垮。可以结合qps和qos来参考。
* 降级:丢卒保車,丢車保帅。人比人该死,货比货该扔。服务也是类似道理
* 与soa区别?soa配合enterprise service bus,相对微服务更重。
* 服务调用统一过程(IDL语言)
* * *
### 十七、 DevOps
1. 简单串个图

### 具体
1. 如何对一个大文件排序(装不进内存的)
* 思路:
* map reduce
* 分割成小文件(临时文件)
* 去重
* awk grep end for sort
* 输入输出缓冲区
2. 快速排序代码
3. 冒泡排序代码
* 外层循环 0到n-1 //控制比较轮数 n 表示元素的个数
* 内层循环 0到n-i-1 //控制每一轮比较次数
* 两两比较做交换
* 外层循环开始声明 is\_switch flag为false,内层循环有交换为true,外层循环结束时判断无switch break
4. 归并排序代码
5. 二分查找binary search【主要是注意边界和细节】
* 无重复找其一
* 有重复找左第一次|右第一次
6. lru应用场景及实现
7. php
* 两个不同长度数组(假设每个数组内有顺序),合并成一个数组并保持顺序
* php与nginx交互流程,php内部执行流程
8. mysql
* 对订单表中按每天统计下单量(group by+date\_format+count)
* 对订单表中按每天统计下单量,取出最早下单的两个(group\_cat)
* `select date\_format(create\_time, '%Y-%m-%d'), group\_concat(id order by create\_time asc), substring\_index(group\_concat(id order by create\_time asc),',',2)
from order group by date\_format(create\_time, '%Y-%m-%d') having count(id)>0;`
~~~markdown
- 双写怎么保证近实时数据一致性和原子性?
- 答案:引入MQ或者Kafka可以保证最终一致性;消费端ack||挂掉的话自己记录offset,again即可(解决原子性问题);引入Canel事件的中间件(解决近实时问题)。
~~~
## 面试完成后
1. 自身定位与公司给定技术评级
2. 没拿到offer不要慌,也许就是给HR刷kpi,面试就是照照自己。
3. 复盘,复盘,针对性的调整简历
4. 多说一句,作为一个程序员,数据结构+算法+设计模式才是基础内功。
## 其他参考
1. [面个php要求怎么要求这么高?](https://link.segmentfault.com/?enc=A0QNoYwZVNm9PMrd7L1UlA%3D%3D.kjJbTSIT9lgUwZIvi7PMrlBL0HSZliDITSXVCsGeR8ICr7bK4Ve0wKDX2aI3QQHgPzZKvom8V2kw254DnMioUA%3D%3D)
2. [\[GO知识图谱](https://link.segmentfault.com/?enc=gxXrMCTQD0DruYoeFU1iCw%3D%3D.meAgY5kbuG36mj7ZoRkkHyuTJQSjjPziRRHeoFjMpUeR50apCIL03sieeigExCo2AKq0iKRz%2Bk1nz8PbLTAuqt8PiBx0m9sNdVIOxPhG1%2FQ%3D)\]