字节社招二面,怀疑是来套我方案的。。。

校招八股文学习网站:https://interviewguide.cn
这是阿秀的第「372」篇原创

小伙伴们大家好,我是阿秀。

校招和社招面经断断续续都分享过不少了,尤其是校招相关的内容:

今天再分享一份字节Data部门的社招二面面经,这位读者目前在滴滴从事网约车业务,22 届毕业的,不得不说现在换工作都很快了,这才一年半就有润的打算了

不过面试过程不太友好,这位读者怀疑字节存在套方案嫌疑。。。。下面是面经:

字节电商二面面经

defer是用来做什么的?有哪些应用场景?

defer是Go语言中的一个关键字,它用于注册延迟调用。这些调用直到return前才被执行。

因此,可以用来做资源清理,如果多个defer语句,按先进后出的方式执行。

应用场景

1.并发同步控制:在并发编程中,可以使用defer来确保某个函数在所有子协程都退出后再执行。2.锁场景:在锁场景下,可以使用defer来确保锁释放,防止死锁等问题。

map是无序的,每次迭代map的顺序可能不同,如果需要按特定顺序遍历map,应该怎么做呢?

如果需要按特定顺序遍历map,可以采用以下步骤:

  • 创建一个切片来保存map的键。
  • 遍历map,将键存储到切片中。
  • 对切片进行排序。
  • 根据排序后的键顺序,遍历map并访问对应的值。

示例代码:以下是一个示例代码,展示如何按键的升序遍历map:

package main

import (
 "fmt"
 "sort"
)

func main() {
 m := map[string]int{
  "b": 2,
  "a": 1,
  "c": 3,
 }
 keys := make([]string, 0, len(m))
 for k := range m {
  keys = append(keys, k)
 }
 sort.Strings(keys)
 for _, k := range keys {
  fmt.Println(k, m[k])
 }
}

主要就是使用sort.Strings函数对切片进行升序排序。通过以上步骤,我们可以按照特定顺序遍历map,并访问对应的键值对。

请注意,这里使用的是升序排序,如果需要降序排序,可以使用sort.Sort(sort.Reverse(sort.StringSlice(keys)))进行排序。

Gorm中怎么进行事务管理?哪几个关键字?

gorm的事务管理使用 Begin、Commit 和 Rollback 方法实现。

实例

tx := DB.Begin()
defer func() {
    if r := recover(); r != nil {
        tx.Rollback()
    }
}()
// 在事务中执行一系列操作
if err := tx.Create(&user).Error; err != nil {
    tx.Rollback()
    return err
}
if err := tx.Create(&address).Error; err != nil {
    tx.Rollback()
    return err
}
// 提交事务
tx.Commit()

gRPC支持哪些数据类型的序列化?

二进制(使用Protocol Buffers)和JSON。

其中,二进制序列化在效率和性能方面比JSON序列化更优秀。但是,JSON序列化在可读性方面更好,可以方便地进行调试和测试。

虚拟内存是什么?

虚拟内存是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存而存在的。

为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。

这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。

从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。

例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。

介绍一下一些常用的网络协议吧

应用层:HTTP、HTTPS

传输层:TCP、UDP

网络层:RIP、IP、ICMP

补充

域名解析知道吧?为什么用UDP协议而不用TCP?

因为UDP快!这就是最根本的原因。

UDP的DNS协议只要一个请求、一个应答就好了;而使用基于TCP的DNS协议要三次握手、发送数据以及应答、四次挥手。

还有就是 UDP 协议传输内容长度有限自,不能超过 512 字节。不过客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,所以用UDP传输即可。

悲观锁和乐观锁的原理?应用场景有哪些?

悲观锁

先获取锁,再进行业务操作,一般就是利用类似 SELECT … FOR UPDATE 这样的语句,对数据加锁,避免其他事务意外修改数据。当数据库执行SELECT … FOR UPDATE时会获取被select中的数据行的行锁,select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。

乐观锁

先进行业务操作,只在最后实际更新数据时进行检查数据是否被更新过。Java 并发包中的 AtomicFieldUpdater 类似,也是利用 CAS 机制,并不会对数据加锁,而是通过对比数据的时间戳或者版本号,来实现乐观锁需要的版本判断。

数据库的持久性是什么?

持久性

根据定义,持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

如果无法保证持久性会怎么样?

在MySQL中,为了解决CPU和磁盘速度不一致问题,MySQL是将磁盘上的数据加载到内存,对内存进行操作,然后再回写磁盘。好,假设此时宕机了,在内存中修改的数据全部丢失了,持久性就无法保证。

设想一下,系统提示你转账成功。但是你发现金额没有发生任何改变,此时数据出现了不合法的数据状态,我们将这种状态认为是数据不一致的情形。

MVCC机制了解吗?

MVCC就是多版本并发控制,实现了读写的并发控制,在mysql通过readview 隐藏字段和undolog实现了,比如在可重复读里面,比如开启了一个事务,就生成了一个readview,然后记录现在active的事务,判断查询的数据在这个事务可不可读。

Redis跳跃表了解吗?

先看这样一张图:

如上图,我们要查找一个元素,就需要从头节点开始遍历,直到找到对应的节点或者是第一个大于要查找的元素的节点(没找到)。时间复杂度为O(N)。

这个查找效率是比较低的,但如果我们把列表的某些节点拔高一层,如下图,例如把每两个节点中有一个节点变成两层。那么第二层的节点只有第一层的一半,查找效率也就会提高。

查找的步骤是从头节点的顶层开始,查到第一个大于指定元素的节点时,退回上一节点,在下一层继续查找。

比如我们要查找16:

  1. 从头节点的最顶层开始,先到节点7。
  2. 7的下一个节点是39,大于16,因此我们退回到7
  3. 从7开始,在下一层继续查找,就可以找到16。

这个例子中遍历的节点不比一维列表少,但是当节点更多,查找的数字更大时,这种做法的优势就体现出来了。还是上面的例子,如果我们要查找的是39,那么只需要访问两个节点(7、39)就可以找到了,这比一维列表要减少一半的数量。

为了避免插入操作的时间复杂度是O(N),skiplist每层的数量不会严格按照2:1的比例,而是对每个要插入的元素随机一个层数。

随机层数的计算过程如下:

  1. 每个节点都有第一层
  2. 那么它有第二层的概率是p,有第三层的概率是p*p
  3. 不能超过最大层数

Redis持久化机制知道吗?

Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。

以下有两种持久化机制

1、快照(snapshotting)持久化(RDB持久化)

Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行 备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性 能),还可以将快照留在原地以便重启服务器的时候使用。

快照持久化是Redis默认采用的持久化方式,在Redis.conf配置文件中默认有此下配置:

save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令
创建快照。

save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

2、AOF(append-only file)持久化

与快照持久化相比,AOF持久化的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启 AOF(append only file)方式的持久化,可以通过appendonly参数开启:appendonly yes

开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的 保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。

在Redis的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:

appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec  #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no  #让操作系统决定何时进行同步

为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec选项 ,让Redis每秒同步一次AOF文件,Redis性能 几乎没受到任何影响,而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。

当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

目前在做Web业务?主要做什么的?

是的,目前主要在做网约车的XXX分析,主要是做滴滴与其余竞品平台的一些数据对比,找出可优化项和服务点,毕竟市场就那么大,谁都想吃更大的蛋糕。

具体对比什么呢?

读者:从这个问题开始我就有点怀疑是来套我方案的了,很少有问的这么细的,我去面百度的时候最多也就是问上面那个问题。

额,这个也不能透露太多,主要是在路径规划上以及其余的一些目前我们滴滴可以提升的点,比如乘客综合反馈比较多的不等人和最后100米环节。

这些就可以?还有其他的吗?

读者:这个问题一出,基本可以确定是来套我方案的,社畜应该都知道,现司业务不要说在职,哪怕是离职了也不能透露过细,否则要吃官司的。

不好意思,这个不能透露,能说的都已经说了

然后重点来了,一直在旁敲侧击的问我具体业务,僵持了差不多三五分钟。。。

你认为字节还有市场可以做网约车业务吗?

有,蛋糕永远都有,也许滴滴、高德打车、曹操出行、滴答这些入场较早,但市场还在,用户永远都有,永远都有入场的机会。

应该怎么入场?随便说说,就当是闲聊

个人认为完全可以采用前期给司机和客户双方发补贴,等用户产生足够多粘性后再提价的方式进行入场,用了用户就不怕司机不来。

字节资本够大,资金链充足,广告营收高的吓人,直接打价格战就可以。

上一个打价格战还历历在目吧?pdd直接打价格战,一番重拳出击,直接乱拳打死老师傅,两个老师傅都被打的够呛,年纪大点的那个老师傅,用户的反馈永远是最真实的

你还有什么想问的吗?

问了下部门业务、自己如果入职会做什么?

最后问了个灵魂问题:加班吗?累不累?

你好,我是阿秀,普通学校毕业,校招时拿到字节跳动SP、百度、华为、农业银行等6个互联网中大厂offer,毕业后先于抖音部门担任全栈开发工程师,目前在上海某外企带领团队继续从事全栈开发,负责对印项目。

去年阿秀从字节离职,跳槽换工作期间开发了一个互联网大厂面试真题记录网站,支持按照行业公司岗位科目考察时间等查看面试真题,有意者欢迎体验,已经有不少小伙伴遇到原题了,具体可以看下链接:2023年7月字节跳动后端研发岗面试考察题目Top10局部性原理还真有用!

网址:https://top.interviewguide.cn/

也欢迎25、26届的小伙伴加入阿秀的学习圈,目前这个学习圈已经运营超过 2 年时间了,已经累计服务过 3400+ 人,近期正在分享一些开箱即用的项目,花1-2天时间就可以拿去面试,昨天就分享了一个高性能通讯服务器脚手架项目,点此可了解更多

请使用浏览器的分享功能分享到微信等