今天分享的是训练营的朋友在字节跳动的面试,新鲜出炉的还热乎着呢。题目都挺简单的,但是他的面试体验不太好,因为面试官问了两个类似的问题,感觉有点不认真。
下面是面试的内容:
面经详解
- 简单介绍下你的项目
介绍下教育平台考试模块的业务.包括
- 题目的读写
- 试卷的生成
- 创建之后,试题如何存储
- 试卷如何分发给考生
- 考生如何提交试卷,你们如何收集试卷结果
- 最后怎么判题
以下是对每个问题的回答:
前端接口使用restful格式,post与get的区别是什么?
功能用途:
- GET:用于获取资源,通常是从服务器检索数据,比如获取文章列表、用户信息等,不会对服务器上的资源进行修改。
- POST:主要用于传输实体对象,常用于创建或更新资源,比如提交表单数据、上传文件、创建新用户等。
参数传递:
- GET:将参数拼加到URL上进行传递,形成查询字符串,如
http://example.com/api/user?id=123
。 - POST:把请求参数写入到请求正文中传递,参数不会显示在URL中。
- GET:将参数拼加到URL上进行传递,形成查询字符串,如
缓存情况:
- GET:一般会被缓存,浏览器和代理服务器可能会缓存GET请求的结果,以提高性能。
- POST:默认不进行缓存,每次请求都会发送到服务器进行处理。
历史记录与书签:
- GET:请求的参数会保存在历史记录中,其地址可被收藏为书签。
- POST:请求的参数不会保留到历史记录中,地址也不能被收藏为书签。
安全性与幂等性:
- GET:相对不安全,因为参数在URL中可见,且通常用于获取数据,应是幂等的,多次请求结果相同。
- POST:相对安全些,数据在请求体中,但仍需HTTPS加密敏感数据,不是幂等的,多次请求可能导致多次创建或更新资源。
HTTP网络返回的状态码有哪些?
- 1xx系列:表示请求已被接受,需要继续处理。如
100 Continue
,客户端应当继续发送请求;101 Switching Protocols
,服务器通知客户端采用不同的协议来完成请求。 - 2xx系列:代表请求已成功被服务器接收、理解、并接受。如
200 OK
,请求已成功;201 Created
,请求已经被实现,有一个新的资源已建立;202 Accepted
,服务器已接受请求,但尚未处理。 - 3xx系列:通常表示需要客户端采取进一步的操作才能完成请求,主要用于重定向。如
301 Moved Permanently
,被请求的资源已永久移动到新位置;302 Move Temporarily
,请求的资源临时从不同的uri响应请求;304 Not Modified
,客户端发送带条件的get请求且文档内容未改变。 - 4xx系列:一般表示客户端错误。如
401 Unauthorized
,用户需要提供身份验证凭据;403 Forbidden
,用户已被禁止访问该网站;404 Not Found
,未找到所请求的资源。 - 5xx系列:通常表示服务器内部错误。如
500 Internal Server Error
,网站出现严重错误;503 Service Unavailable
,服务器维护或暂时不可用。
go语言切片与数组的区别是什么?
定义方式:
- 数组:在Go语言中,数组是具有固定长度的同类型元素的序列,定义时需要指定长度和元素类型,如
var a [5]int
。 - 切片:切片是对数组的一个连续片段的引用,它本身不存储数据,只是对底层数组的一种抽象,定义方式如
var s []int
。
- 数组:在Go语言中,数组是具有固定长度的同类型元素的序列,定义时需要指定长度和元素类型,如
长度与容量:
- 数组:长度是固定的,在定义时就确定了,不能动态改变。
- 切片:长度可以动态变化,通过
append()
函数可以向切片中添加元素,自动扩容。切片还有容量的概念,容量通常大于或等于长度,表示切片可以容纳的元素数量。
传递与赋值:
- 数组:在Go语言中,数组是值类型,当把一个数组赋值给另一个数组或作为函数参数传递时,会进行值拷贝,产生一个新的数组。
- 切片:切片是引用类型,赋值和传递时只是复制了切片的引用,不会复制底层数组的数据,多个切片可以共享同一个底层数组。
内存分配与效率:
- 数组:在内存中是连续分配的一段空间,大小固定,访问效率高,但在需要动态变化长度时效率较低,因为需要重新分配内存并复制数据。
- 切片:切片的底层数组可能会根据需要动态扩容,扩容时可能会涉及到内存的重新分配和数据的复制,但在追加元素时一般比数组更灵活高效。
MySQL实现并发安全避免两个事务同时对一个记录写操作的手段有哪些?
悲观锁:
- 共享锁(S锁):允许事务对数据进行读取操作,但不允许其他事务对该数据进行修改操作,在执行
SELECT
语句时可以使用LOCK IN SHARE MODE
语句添加共享锁。 - 排他锁(X锁):事务获取排他锁后,其他事务既不能对该数据进行读取操作,也不能进行修改操作,在执行
UPDATE
、DELETE
等语句时会自动添加排他锁,也可以在SELECT
语句中使用FOR UPDATE
语句添加排他锁。
- 共享锁(S锁):允许事务对数据进行读取操作,但不允许其他事务对该数据进行修改操作,在执行
乐观锁:
- 版本号机制:在表中添加一个版本号字段,每次更新数据时,版本号都会递增。在更新数据时,先查询出当前记录的版本号,然后在更新语句中加入版本号的判断条件,如果版本号与查询出的一致,则更新成功,否则更新失败。
- 时间戳机制:类似于版本号机制,使用时间戳字段记录数据的最后更新时间,在更新数据时,先比较时间戳,只有当时间戳与查询出的一致时才允许更新。
事务隔离级别:
- 可重复读(REPEATABLE READ):在该隔离级别下,一个事务在执行过程中多次读取同一数据时,会看到相同的数据值,即使其他事务对该数据进行了修改并提交,也不会影响当前事务的读取结果,从而避免了脏读、不可重复读等问题。
- 串行化(SERIALIZABLE):最高的隔离级别,所有事务依次逐个执行,避免了并发冲突,但性能较差,一般在对数据一致性要求极高的场景下使用。
如何实现业务的幂等性(在golang代码中如何避免消息重复或处理已出现的消息重复)?
- 使用唯一标识:在业务中为每一个请求或消息生成一个唯一的标识,如UUID等。在处理请求或消息之前,先检查该标识是否已经存在,如果存在则说明是重复消息,直接返回之前的处理结果或进行相应的处理,不再重复执行业务逻辑。
- 数据库约束:利用数据库的唯一约束来保证数据的唯一性。例如,在插入数据时,如果违反了唯一约束,则说明该数据已经存在,此时可以根据具体情况进行相应的处理,如更新数据或直接返回错误信息等。
- 分布式锁:在处理消息或请求时,使用分布式锁来保证同一时间只有一个实例在处理该消息或请求。可以使用Redis等分布式锁来实现,在获取锁成功后再进行业务处理,处理完成后释放锁,避免多个实例同时处理导致消息重复。
- 消息队列的幂等性处理:如果使用消息队列来传递消息,可以在消息队列的消费者端进行幂等性处理。例如,在消息中添加一个唯一标识字段,消费者在处理消息时先检查该标识是否已经处理过,如果已经处理过则直接确认消息已消费,不再重复处理。
如何设置redis分布式锁?
- 使用SETNX命令:
SETNX key value
命令用于在指定的键不存在时,设置键的值。可以将锁的获取和释放操作封装成函数,在获取锁时,使用SETNX
命令设置一个特定的键值对,如果返回1表示获取锁成功,否则表示锁已被其他客户端获取。在释放锁时,使用DEL
命令删除该键。 - 设置过期时间:为了防止客户端在获取锁后出现异常情况导致锁无法释放,可以在获取锁时同时设置一个过期时间,使用
SET key value EX seconds
命令,这样即使客户端出现异常,锁也会在过期时间后自动释放,避免死锁的发生。 - 使用Redlock算法:Redlock算法是一种更复杂的分布式锁实现方式,它通过在多个Redis节点上同时获取锁,只有当大多数节点都获取锁成功时,才认为获取锁成功,从而提高了分布式锁的可靠性和安全性。
编写代码:元素不重复的整数数组,返回该数组第二大的元素。
package main
import "fmt"
func secondLargest(nums []int) int {
if len(nums) < 2 {
panic("数组元素个数不足")
}
first := nums[0]
second := nums[1]
if first < second {
first, second = second, first
}
for i := 2; i < len(nums); i++ {
if nums[i] > first {
second = first
first = nums[i]
} else if nums[i] > second && nums[i] < first {
second = nums[i]
}
}
return second
}
func main() {
nums := []int{5, 2, 8, 1, 9, 3}
fmt.Println(secondLargest(nums))
}
代码解释:
- 首先,检查数组长度是否小于 2,如果小于 2 则抛出异常,因为无法找到第二大元素。
- 初始化
first
和second
变量,将nums[0]
赋值给first
,nums[1]
赋值给second
。如果first
小于second
,交换它们的值,保证first
是最大的元素,second
是第二大的元素。 然后遍历数组中从索引 2 开始的元素:
- 如果当前元素比
first
大,将first
的值赋给second
,将当前元素的值赋给first
,因为找到了更大的元素。 - 如果当前元素比
second
大且比first
小,将当前元素的值赋给second
,因为找到了新的第二大元素。
- 如果当前元素比
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。