小红书冲上热搜,大家都听说了吗?

由于一些特殊原因,小红书上突然涌现出一大批外国用户,他们自称是“某k的难民”,开始在小红书上分享内容。

不过,小红书目前还没有自带的翻译功能,不会中文的外国用户只能使用英文或使用第三方翻译软件与中国用户进行交流。同时,多种语言来袭,也给小红书平台带来了内容审核的压力

据经济观察网报道,一位接近小红书的人士表示,小红书内部团队从1月13日当天就开始加班了,正在针对外国用户做功能优化,他们希望努力承接这波流量。

不知道有没有在小红书上班的朋友,在评论区分享一下你的故事~

由于这个事情,我也特意整理了一些小红书的golang面经,感兴趣的朋友可以接着往下看:

小红书冲上热搜,大家都听说了吗?_第1张图片

以下是详细答案:

channel关闭之前和关闭之后的读取情况如何?

答案:

当通道被关闭后,对其发送操作会引发panic,因为通道已经关闭,任何进一步的写入都将失败。对于读取操作,如果通道关闭前有未读的数据,那么这些数据可以被正确读取。如果通道关闭前没有未读的数据,那么读取操作将立即返回通道元素的零值(对于非阻塞读取)或者返回false(对于阻塞读取)。在通道关闭后,默认情况下读取操作会返回通道元素的零值和true,除非在关闭前最后一个接收操作之后还有其他接收操作。

请阐述Golang中map的哈希桶迁移机制。

答案:

Go语言中的map底层是一个哈希表,当map中的元素数量不断增加,装载因子超过阈值(通常是6.5)或哈希冲突过多时会触发扩容。扩容时会创建一个新的更大的哈希表,新的哈希表的大小通常是旧哈希表的两倍。然后将旧哈希表中的每个元素重新计算哈希值,并根据新的哈希表大小将其放入新的桶中,这个过程就是哈希桶迁移。Go语言使用渐进式扩容技术,不会一次性完成所有元素的迁移,而是将迁移过程分散到后续的插入和查找操作中。

sync.map和普通map

答案:

  • 并发安全性:普通map在没有外部同步的情况下,不是并发安全的,在多goroutine访问时,如果没有适当的锁或其他同步机制保护,可能会导致数据竞争和未定义行为。而sync.Map是并发安全的,它内部实现了必要的同步机制,允许多个goroutine同时读写而不会引发数据竞争问题。
  • 性能:普通map对于单线程或同步控制下的访问,性能通常优于sync.Map,因为它避免了额外的同步开销。sync.Map由于其内部的读写锁和复杂的逻辑,在并发访问下虽然保证了安全,但可能比直接操作普通map慢一些,不过在高并发且读多写少的场景下,它的性能损失相对较小。
  • 使用场景:普通map适用于单线程环境或在有明确同步控制的多goroutine环境中。sync.Map适用于无须显式锁控制的多goroutine共享数据场景,比如作为缓存、计数器等,特别是在读远多于写的场景下表现更佳。

阐述对context的理解(使用及原理)

答案:

  • 使用:在Go语言中,context主要用于在多个goroutine之间传递上下文信息,如请求的截止时间、取消信号、共享数据等。比如在一个HTTP服务器中,每个请求都可以有自己的context,用于在处理请求的各个阶段传递和共享相关信息,当请求超时或被取消时,可以通过context及时通知各个处理函数停止工作。
  • 原理:context的核心是一个接口类型,包含了获取截止时间、取消信号、获取键值对等方法。它通过树状结构来管理和传递上下文信息,根节点通常是由程序的主函数或框架初始化创建,子节点可以通过父节点衍生而来,当父节点的上下文发生变化,如被取消或超时,子节点会继承这些变化并及时做出响应。

对比Golang的协程与进程、线程,并说明CPU如何运行进程。

答案:

  • 协程与进程:进程是操作系统资源分配的基本单位,拥有独立的内存空间和系统资源,进程之间相互隔离。而协程是一种轻量级的用户态线程,由程序自身管理和调度,多个协程可以在同一个进程内共享内存和其他资源,协程的创建和销毁成本比进程低很多,切换开销也远小于进程切换。
  • 协程与线程:线程是操作系统调度的基本单位,线程之间共享进程的内存空间,但线程的创建和切换开销相对协程较大。协程的调度更加灵活,可以由程序根据自身的业务逻辑进行调度,而线程的调度由操作系统内核决定。
  • CPU运行进程:CPU通过时间片轮转的方式来运行进程,操作系统会为每个进程分配一定的时间片,当进程获得CPU时间片时,CPU会从进程的指令指针所指向的位置开始执行指令,执行完一个时间片后,如果进程未结束,操作系统会保存进程的上下文信息,然后将CPU分配给其他进程,当再次轮到该进程时,再恢复其上下文信息继续执行。

Redis的数据类型

答案:

基础数据类型

  • 字符串(String):是最基本的数据类型,可以存储任何形式的字符串,包括整数、浮点数等,常用于缓存、计数器、分布式锁等场景。
  • 列表(List):是一个字符串列表,按照插入顺序排序,可以在列表的两端进行插入和删除操作,常用于消息队列、任务队列等场景。
  • 哈希(Hash):是一个键值对集合,类似于Java中的Map,常用于存储对象的属性、用户信息等。
  • 集合(Set):是一个无序的字符串集合,不允许重复元素,常用于存储标签、用户关系等。
  • 有序集合(Sorted Set):是一个有序的字符串集合,每个元素都有一个分数,可以根据分数进行排序,常用于排行榜、优先级队列等场景。

高级数据类型

  • 地理空间(Geospatial)

    • 功能:用于存储和操作地理空间位置信息。
    • 应用场景:适用于地图应用、位置服务、附近的人等功能的实现。
  • Hyperloglogs

    • 功能:用于统计基数数量,即不重复元素的数量,与Set类似,但只记录存入的元素数量,不记录存入元素的具体值。
    • 应用场景:常用于大数据场景下的去重计数,如统计网站的独立访客数量等,相比Set可以节省大量内存空间。
  • Bitmaps

    • 功能:实际上也是一种key-value,Bitmaps的value只能是0或1,Bitmaps的key代表偏移量是“数位”的位置,可以用于记录状态、统计信息等。
    • 应用场景:例如记录用户的登录状态、每周的打卡情况等,通过位运算可以高效地进行统计和查询。

Dockerfile中add和copy的区别是什么?

答案:

  • 功能上:add命令除了可以将本地文件或目录复制到镜像中,还可以自动解压一些压缩文件,如tar、gz等格式的文件。而copy命令只能单纯地将本地文件或目录复制到镜像中,不会进行解压操作。
  • 使用场景上:如果只是简单地复制文件或目录,建议使用copy命令,因为它的功能更单一,行为更明确。如果需要在复制的同时进行解压操作,或者需要从远程URL获取文件并解压到镜像中,则可以使用add命令。
  • 安全性上:由于add命令会自动解压文件,可能会存在安全风险,如意外解压恶意文件等。因此,在使用add命令时需要特别小心,确保复制的文件来源可靠。

请谈谈服务发现和服务注册、服务治理相关内容。

答案:

  • 服务发现:是指在分布式系统中,服务消费者能够自动发现服务提供者的地址和端口等信息的过程。通常采用的方式有基于DNS的服务发现、基于注册中心的服务发现等。例如,在微服务架构中,当一个服务需要调用另一个服务时,它可以通过服务发现机制获取到目标服务的实例列表,然后根据负载均衡策略选择一个实例进行调用。
  • 服务注册:是服务提供者将自身的服务信息注册到注册中心的过程,服务信息包括服务名称、地址、端口、协议等。服务提供者在启动时会向注册中心发送注册请求,注册中心会保存这些信息。当服务提供者的信息发生变化时,如地址或端口变更,也需要及时更新到注册中心。
  • 服务治理:是对分布式系统中的服务进行管理和协调的一系列活动,包括服务的监控、流量控制、熔断降级、配置管理等。通过服务治理,可以确保系统的稳定性、可靠性和性能。例如,当某个服务出现故障或性能下降时,可以通过熔断机制暂时切断对该服务的调用,避免故障扩散;当流量过大时,可以通过流量控制机制限制对某些服务的访问频率,保护系统的稳定性。

如何在Linux中查看CPU利用率以及查看指定进程的情况?

答案:

  • 查看CPU利用率:可以使用top命令,它会实时显示系统中各个进程的CPU使用率、内存使用率等信息,在top命令的输出中,第一行显示的是系统的总体CPU利用率,包括用户态、内核态、空闲等各个状态的占比。也可以使用mpstat命令,它可以更详细地查看每个CPU核心的利用率情况。
  • 查看指定进程的情况:可以使用ps命令,如ps -ef可以列出系统中所有的进程信息,包括进程的PID、PPID、CPU使用率、内存使用率等。还可以使用top -p 命令,其中是指定进程的PID,这样可以只查看指定进程的详细信息,包括实时的CPU使用率、内存使用率、线程信息等。另外,也可以使用pmap命令查看指定进程的内存映射情况。

编写代码实现:使用两个协程交叉打印单数和双数。

答案:

package main

import (
    "fmt"
    "sync"
)

func printOdd(wg *sync.WaitGroup, ch chan struct{}) {
    defer wg.Done()
    for i := 1; i <= 10; i += 2 {
        <-ch
        fmt.Println(i)
        ch <- struct{}{}
    }
}

func printEven(wg *sync.WaitGroup, ch chan struct{}) {
    defer wg.Done()
    for i := 2; i <= 10; i += 2 {
        <-ch
        fmt.Println(i)
        ch <- struct{}{}
    }
}

func main() {
    var wg sync.WaitGroup
    ch := make(chan struct{}, 1)
    ch <- struct{}{}

    wg.Add(2)
    go printOdd(&wg, ch)
    go printEven(&wg, ch)

    wg.Wait()
    close(ch)
}

通过创建一个容量为1的channel来控制两个协程的执行顺序,实现交叉打印单数和双数。

欢迎关注 ❤

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。

没准能让你能刷到自己意向公司的最新面试题呢。

感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:sf面试群。

你可能感兴趣的:(go后端面试问题小红书程序员)