画船听雨眠

快手一面

6 min
⏳ 等待结果

实习太久了,很久没碰过八股和其余信息,感觉自己已经不适应面试了。

面试信息

  • 公司名称: 快手
  • 面试时间: 2025-08-06
  • 面试轮次: 一面

八股题

分布式锁以及实现分布式锁要关注的问题

比较简单的问题,现在较为常用的分布式锁就是Redis 的SetNX key_exp thread_id+count PX 30000

在这行命令里,已经设置了一个锁,就是key_exp,目前获得它的线程我们记为thread_id,和进入次数count拼接一下,就组成了value

为什么要这么设计呢,目的是为了锁的可重入性,也是就是同一个线程获取同一把锁的时候不会死锁

需要关注的问题

  1. 锁的可重入性
  2. 锁的自动续约机制
  3. 锁的过期自动释放

第一点之前已经说过了,在value里记录线程信息和进入次数,序列化为string即可
第二点,自动续约机制我们可以使用看门狗的方法,启动一个后台线程,每次sleep 10s,10s后修改redis里的过期时间
第三点,过期自动释放,PX已经做到了

索引

题目的具体描述是

select * from table where a=100 and b>10 and b<100 order by d asc limit 19

有这样一条sql,为了它的查询效率,该如何建索引。

select会用到索引这是无疑的,同时根据最左匹配原则,显然需要建立(a,b)的联合索引,这样可以快速找出符合的是数据。

但是需要将d加入联合索引吗,我觉得是两可的。对于确定的a,b对,则d是有序的,但是对于确定的a,多个b,在每个b内,d是有序的,如果d有序这样可以使用多路归并排序来快速排序d。

但是这样新增是有意义的吗?建立索引是需要额外的资源的,所以真的需要这么建立吗?需要按照场景来考虑。

如果是读多写少的场景,显然建立一个(a,b,d)的联合索引是值得的,

场景推荐索引理由
返回数据量小(< 1000 行),且系统写频繁(a, b)排序开销小,避免索引膨胀
返回数据量大,且 ORDER BY d 经常导致慢查询(a, b, d)消除 filesort,提升查询稳定性
d 是主排序字段,且有分页需求(如 LIMIT 10(a, b, d)可快速取前 N 条,无需全排序
存在大量类似查询,且 d 排序是刚需(a, b, d)长期收益大于索引成本

当时没有思考到这么多,所以说,答案应该是建立(a,b,d)的联合索引

事务的隔离级别

事务的四大特性:ACID,原子性、一致性、隔离性、持久性

A:事务是不可分割的,同时成功同时失败。
C:事务执行前后,必须保持完整性约束,确保数据从一个有效状态转换为另一个有效状态。
I:并发的事物之间互相隔离,操作互不干扰。
D:事务一旦提交,就不会丢失。

那么事务的隔离级别都有什么呢?

  1. 读未提交:
    存在问题:脏读、不可重复读、幻读
  2. 读已提交:
    存在问题:不可重复读、幻读
  3. 可重复读:
    存在问题:幻读
  4. 串行化

脏读
事务A未提交的变更能够被事物B读取到

不可重复读
事务A第一次读数据1和第二次读数据1的值不相同

幻读
事务A多次查询相同查询条件的数量不同

算法题

合并有序链表

简单题,没什么好说的。

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

type Node struct {
	Val  int
	Next *Node
}

func merge(list1, list2 *Node) *Node {
	fakeHead := &Node{}
	pre := fakeHead
	for list1 != nil && list2 != nil {
		if list1.Val < list2.Val {
			pre.Next = list1
			list1 = list1.Next
		} else {
			pre.Next = list2
			list2 = list2.Next
		}
		pre = pre.Next
	}
	if list1 != nil {
		pre.Next = list1
	} else {
		pre.Next = list2
	}
	return fakeHead.Next
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	line := strings.Split(scanner.Text(), ",")
	list1 := &Node{}
	pre := list1
	for _, cr := range line {
		node := &Node{}
		num, _ := strconv.Atoi(cr)
		node.Val = num
		pre.Next = node
		pre = node
	}
	scanner.Scan()
	line = strings.Split(scanner.Text(), ",")
	list2 := &Node{}
	pre = list2
	for _, cr := range line {
		node := &Node{}
		num, _ := strconv.Atoi(cr)
		node.Val = num
		pre.Next = node
		pre = node
	}
	list1 = list1.Next
	list2 = list2.Next
	list := merge(list1, list2)
	for list != nil {
		fmt.Println(list.Val)
		list = list.Next
	}
}

反问

业务、流程时间