今天分享粉丝最新投稿的北京贝壳外包面经,我把项目相关的都去掉了,其他的问题整理下来感觉不是很难,你觉得呢?换做是你的话你答得出来吗?
面经整理如下图:
北京贝壳外包
- 自我介绍
- 项目拷打
Redis 跟 MySQL 以及 Kafka 之间是什么关系?
Redis 是内存数据结构存储系统,用于快速读写和高并发访问。MySQL 是关系型数据库,用于持久化存储结构化数据。Kafka 是分布式消息队列系统,用于处理大规模消息流和数据分发。
在应用中,MySQL 存主要数据,Redis 加速访问,Kafka 传递消息和解耦组件。例如电商系统中,用户信息在 MySQL,购物车数据在 Redis,订单处理消息通过 Kafka 传递。
Kafka partion的概念
在 Kafka 里,Partion 就是把一个主题的数据分成了好几个部分。 每个 Partion 都是按顺序存消息的,而且这些消息改不了。同一主题的不同 Partion 能放在不同的地方,这样就能让数据处理得更快更平衡。
Partion 有这些用处: 一是存数据,消息按顺序存进去,还给每个消息弄个编号来标明位置。 二是能同时处理,多个 Partion 能让不同的人或者组一起处理,效率高。 三是不容易出错,如果有地方坏了,也就影响那一部分,整个主题的数据不会丢。
Kafka 偏移量
在 Kafka 里边,偏移量就是个数字,专门用来表明消息在分区里处于啥位置。
打个比方,每个消息在分区里都有个独一份的编号,这就是偏移量。消费者读消息的时候,会记住自己读到哪个编号了。下次再读,就知道从哪儿接着来,不会重复读,也不会把消息给漏了。
比如说,偏移量是 5 ,那就说明这是分区里的第 6 条消息(因为偏移量从 0 开始算)。偏移量能保证 Kafka 里消息处理得有顺序,而且不会丢。
比如说有你在线上发现一条慢SQL,你怎么分析它?
使用 explain ,说明:
首先关注“type”这一项。若显示为“ALL”,意味着进行了全表扫描,这通常不是理想情况,需要寻求改进。若显示为“index”或“range”等,则相对较好。
其次留意“possible_keys”和“key”这两项。“possible_keys”表示可能能够使用的索引,而“key”表示实际使用的索引。倘若“key”这一项为空,意味着未使用索引,此时需要探究原因,例如索引创建是否正确。
再者,“rows”这一项也颇为重要,它代表预计扫描的行数。该数值越大,表明需要处理的数据量越多,可能导致查询速度变慢。
另外,“extra”这一项也值得关注。如果出现“Using filesort”或者“Using temporary”等内容,往往意味着存在问题,需要进行优化。
索引的最左前缀原则是什么意思?a > 1 and b = 1 and c = 1, 你看这个能命中这个索引吗?
索引的最左前缀原则就是在创建联合索引时,查询语句用索引的条件得从索引的最左边开始,而且得连续匹配。
对于“a > 1 and b = 1 and c = 1”这个条件,能不能命中索引得看创建的索引是啥样。
要是创建的索引是 (a, b, c) ,那这查询就能命中索引,因为是从最左边的“a”开始匹配的。 可要是创建的索引是 (b, c, a) 或者其他不是以“a”开头的组合,那这查询就不能命中索引。
Redis 常见的数据结构?他们的应用场景?
- 字符串(String):可以存储字符串、整数或浮点数。常用于缓存用户信息、计数器、分布式锁等。
- 哈希(Hash):适合存储对象,例如存储用户的详细信息。
- 列表(List):可以实现队列、栈等数据结构。常用于消息队列、最新文章列表等。
- 集合(Set):用于存储不重复的元素,可用于社交关系中的共同好友、抽奖等场景。
- 有序集合(Sorted Set):每个元素都有一个分数,可根据分数排序。适用于排行榜、限时活动等。 例如,在电商网站中,字符串可以存储商品库存数量,哈希存储商品详情,列表用于存储用户浏览记录,集合用于存储用户收藏的商品,有序集合用于商品销量排行。
zset 你一般在什么场景下会用?底层的数据结构是什么?
ZSet(有序集合)通常在以下场景中使用:
- 排行榜:例如游戏得分排行榜、商品销量排行榜等,按照分数或销量等进行排序。
- 优先级队列:可以根据元素的权重或优先级来处理任务。
- 时间序列数据:比如按照时间戳排序的事件记录。
Redis中ZSet底层实际有两种数据结构:
一、压缩列表(ziplist)
使用条件(以下两个条件需同时满足) 12:
- 存储的元素数量较少(通常规定是小于配置参数
zset-max-ziplist-entries
,一般默认是128 ,但可以修改配置)。- 所有元素长度都比较小(规定是小于配置参数
zset-max-ziplist-value
,一般默认是64字节,但可以修改配置)。结构特点:
- 是一种为节省内存而设计的特殊编码结构,将所有的元素和分数紧凑地存储在一起。
- 元素和它的分数在压缩列表中是交替存储,即第一个元素是成员,第二个元素是分数,第三个元素是成员,第四个元素是分数,以此类推1。
- 在redis的源代码中,压缩列表(ziplist)的结构并没有直接定义为一个C结构体,而是通过一系列的宏和函数来操作一段连续的内存1。其结构大致包含以下部分 1: -
zlbytes
:一个4字节的整数,表示整个压缩列表占用的字节数量,包括它自身的大小。-zltail
:一个4字节的整数,表示压缩列表中最后一个元素的偏移量(相对于整个压缩列表的起始地址)。-zllen
:一个2字节的整数,表示压缩列表中的元素数量。如果元素数量超过65535,那么这个值就会被设定为65535,需要遍历整个压缩列表才能获取到实际的元素数量。-entry
:压缩列表中的元素,每个元素都由一个或多个字节组成。每个元素的第一个字节(又称为 “entry header”)用于表示这个元素的长度以及编码方式。-zlend
:一个字节,值为255,表示压缩列表的结束。二、跳跃表(skiplist)
使用条件(只要有一个不满足上述ziplist条件就会切换到跳跃表)12 : 当ZSet存储的元素数量较多,或者元素的字符串长度较长时。
结构特点:
- 在Redis的源代码中,跳跃表的结构定义如下2: - 有一个
zskiplist
结构体表示整个跳跃表: -header
:指向跳跃表的表头节点,通过这个指针程序定位表头节点的时间复杂度是O(1)。-tail
:指向跳跃表的表尾节点,通过这个指针程序定位表尾节点的时间复杂度为O(1)。-length
:记录跳跃表的长度,就是跳跃表目前包含节点的数量(表头节点不算在内)。-level
:记录在目前的跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)。- 跳跃表节点zskiplistnode
: -obj
:成员对象。-score
:分值(用于排序)。-backward
:后退指针,指向位于当前节点的前一个节点(程序从表尾向表头遍历时使用,每个节点只有一个后退指针 )。-level
:是一个可变长数组结构(C99标准引入),里面包含: -forward
:前进指针,用于访问位于表尾方向的其他节点。-span
:这个层跨越的节点数量(记录前进指针所指向节点和当前节点的距离跨度越大、距离越远 )。跳跃表通过维护多级索引来实现快速查找和高效的插入、删除、更新元素等操作2 。它的时间复杂度平均为O(log n) ,性能比较高。
在Redis中ZSet元素的存储和访问等操作过程中:
- 当使用跳跃表时,元素的成员和分值等信息存储在跳跃表节点中,同时还有一个哈希表(在整个ZSet结构层面,不是跳跃表结构内部)用于快速通过成员查找其分值等操作2 。
- 当使用压缩列表时,直接在压缩列表中操作元素和分值等数据。
JWT 是什么东西?
JWT(JSON Web Token)是一种用于在网络应用环境中进行安全信息传递的开放标准。
它由三部分组成:
头部(Header),通常包含令牌的类型和使用的加密算法;
载荷(Payload),包含声明,比如用户身份信息、权限等;
签名(Signature),用于验证消息的完整性和真实性,防止被篡改。
JWT 的主要优点是无状态,服务器不需要保存会话信息,减轻了服务器的存储负担。它常用于身份验证和授权,比如在单点登录、微服务架构等场景中。用户在登录成功后获取 JWT 令牌,后续的请求携带该令牌,服务器通过验证令牌的有效性来确认用户身份和权限。
协程了解吗?和线程对比
协程和线程是在编程中常见的概念,它们有一些明显的区别。
线程是操作系统层面的概念,由操作系统进行调度和管理。多个线程可以在同一进程中并发执行,它们共享进程的资源。线程的切换通常由操作系统控制,开销相对较大。 协程则是在用户态实现的轻量级线程。协程的切换由程序自身控制,不需要操作系统的干预,因此协程切换的开销通常比线程小得多。协程更适用于协作式的任务调度,多个协程可以在同一个线程中运行,通过主动让出控制权来实现任务的切换。
在资源消耗方面,线程由于需要操作系统进行管理和调度,会占用较多的系统资源,如内存等。而协程相对来说资源消耗较少。
在编程模型上,线程的并发模型相对较为复杂,需要处理线程安全、同步等问题。协程的编程模型通常更简单直观,更容易理解和实现复杂的逻辑控制。
一道算法题
找到具有最大和的连续子数组,并返回该子数组
编程题
用两个协程、两个channel循环打印1, 2
package main
import (
"fmt"
"sync"
)
func Print1(ch1 chan bool, ch2 chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
<-ch1
fmt.Print("1")
ch2 <- true
}
}
func Print2(ch1 chan bool, ch2 chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
<-ch2
fmt.Print("2")
ch1 <- true
}
}
func main() {
var wg sync.WaitGroup
ch1 := make(chan bool)
ch2 := make(chan bool)
wg.Add(2)
go Print1(ch1, ch2, &wg)
go Print2(ch1, ch2, &wg)
ch1 <- true
wg.Wait()
}
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang2024,备注:csdn面试群。
版权归原作者 王中阳Go 所有, 如有侵权,请联系我们删除。