近期面试中遇到的一些问题的总结
规划
近一年有1/3的时间中老家开荒种地,其他时间有用来学会和中年危机自处,虽然是失败了,还在调整中;有一部分时间用来思考和规划自己的未来,前期很盲目,后期在某音上看到某网红的3+1理论,大意是择业标准:趋势(主) + (爱好,能力,资源)的一个抉择,个人觉得很有道理。
自己选了 golang 作为后续的主要发展目标,完全摒弃了前端开发。当然,自己业余还是可以做一点的。
在网站上刷新状态,被动式找工作。
然后由于没有 golang 的实际操作经验,导致自己的面试的机会和面试的表现都很差劲。
所以今天决定静下心来学习和沉淀一些东西以认真的态度对待面试,以及面对自己的人生。
虽然前路迷茫,但是还得走一步是一步。
问题汇总
golang的垃圾回收
golang 的垃圾回收机制和我之前所了解的 as 所使用的引用技术法有根本性的不同。
golang 使用的是 标记清除法,即 mark-sweeping。
他主要是分为两步:
- 标记:三色标记法,通过将所有对象区分为白,灰,黑三色,配合以写屏障 write barrier
- 清理:清理所有的白色对象
需要注意的是,标记清理期间需要暂停整个程序 stw:stop the world
变量逃逸
一般代指局部变量本来应当分配到栈上的,但是这个局部变量在本作用域,即本函数外需要使用到,那么这个局部变量会被存储到堆上去。
字符串拼接
这个主要考察的是否你是否会直接使用 ‘+’ 来拼接字符串,其实跟 java 类似,java 有类似 StringBuffer 之类的东西来处理字符串拼接,golang 里面用的是 string.Buffer 来处理字符串拼接。
当然,我面试的时候回答用 ‘[]byte’ 来处理拼接也是可以的。但是,这里是需要指定长度的。不用 ‘+’ 进行操作也是这个运原理,一个string 其实代表的是一个 rune数组。
内存分配的问题
之前被问到 slice的内存分配是什么样子的。我的回答是当前 内存x2。面试官接着问是否一直都是 x2 的申请呢?答案肯定不是。在 2048 byte 之前,内存都是 x2 的去申请,大于 2048 byte 的话每次申请 640 byte。
处理channel超时
goroutine 超时可以通过 Context.withtimeout 和 timer.after 来处理。
goroutine 是不能杀死的。
for和range
不懂区别的最好用 for,因为无论在何种情况下for的性能永远是大于等于range的,range在循环复杂对象的时候性能会远小于 for ,当然,传入复杂对象指针除外。
想起面试过一个问题,map 的循环是引用还是值,其实,这个问题是值得商榷的,他循环是其实是值的复制。
另外,数组的基本值,不是引用类型。
读写锁和互斥锁
互斥锁就是 mutex.Lock, 读写锁就是 mutex.RWLock 。两者的区别是把读和写分开来,读写锁可以同时读,也就是只锁定写。效率在读多写少的情况下肯定是互斥锁强得多。
常用缓存失效算法
- FIFO: first in first out
- LFU: Least Frequenctly Used
- LRU: Least Recently Used
分布式缓存相关
分布式缓存节点存值问题
分布式缓存并不是每个节点上存的都一样,他是根据每个key进行hash操作然后确定这个缓存会存储到哪个节点上去。
这样节省了缓存空间,也提高了缓存效率。
缓存雪崩
节点变化,例如 10 个变成了 9 个。那么h hash 取值就从 hash(key)%10
变成了 hash(key)%9
。这样一来,几乎所有的缓存都失效了,节点接受到请求之后都需要重新去获取,就造成了缓存雪崩。
缓存雪崩:缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。常因为缓存服务器宕机,或缓存设置了相同的过期时间引起。
解决缓存雪崩,需要用到一致性 hash 。
一致性hash算法
一致性hash算法原理:
- 一致性hash算法将key映射到2^32的空间中,这是一个首尾相连的环
- 计算节点的hash值,将他投放到环上,hash值通常以机器码,ip,编号等来计算
- 计算key的hash值,顺时针寻找到的第一个计算节点就是他存取的节点
这样一来,新增/删除节点只需要重新定位这个节点附近的节点就可以了。
一致性hash可能会导致的数据倾斜问题
但是这样的话,由于机器hash分布不均匀,有可能导致数据倾斜。
为了解决这个问题,引入了虚拟节点,一个真实节点对应多个虚拟节点,例如:节点1包含节点1-1,节点1-2….即诶单1-n
代价是只需要维护一个map来存放虚拟节点的映射关系。
缓存击穿和缓存穿透
缓存击穿:一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到 DB ,造成瞬时DB请求量大、压力骤增。
缓存穿透:查询一个不存在的数据,因为不存在则不会写到缓存中,所以每次都会去请求 DB,如果瞬间流量过大,穿透到 DB,导致宕机。
这两个都是出现在并发请求过高的时候,优化的手段是同时收到的请求合并处理就可以了。
Redis缓存淘汰策略
缓存设置:
- 在 redis 中,允许用户设置最大使用内存大小maxmemory,默认为0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一定要设置。
- redis 内存数据集大小上升到一定大小的时候,就会实行数据淘汰策略。
- redis淘汰策略配置:maxmemory-policy voltile-lru,支持热配置
缓存淘汰策略:
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据