ITPub博客

首页 > 数据库 > 数据库开发技术 > 美团二面,我是没想到还是AI

美团二面,我是没想到还是AI

原创 数据库开发技术 作者:皇子谈技术 时间:2024-08-17 17:53:49 0 删除 编辑

AI:是时候展示真正的技术了...动态题目、回答追问、智能评估。。。

大家好,我是皇子。

美团的骑手“进门难”的问题近期备受社会关注,不过在秋招阶段很多技术同学也反馈入团难的问题。

因为前面文章中我也有介绍过,美团秋招会第一轮就直接使用AI面试官了。

这个问题不知道是否跟使用的AI面试有关系呢?

关于AI技术面试有关的题型和应对方式方法,请阅读前面的文章(文末附直接访问)。


有美团后端秋招的同学反馈,以为开团用AI技术面试后,后面就直接是真人面试了,没想到二面还是AI!

不过同学反馈问的问题倒是挺专业的,而且全程无废话,无刁难,难度也适中。

这里我也稍微整理下涉及到的面试技术点,希望能帮到大家。

网络

OSI参考模型包含那些内容

  • 应用层:为特定应用程序提供数据传输服务,例如 HTTP、DNS 等协议。数据单位为报文。

  • 传输层(运输层):为进程提供通用数据传输服务。由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。

  • 网络层:为主机提供数据传输服务。而传输层协议是为主机中的进程提供数据传输服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。

  • 数据链路层:网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的主机提供数据传输服务。数据链路层把网络层传下来的分组封装成帧。

  • 物理层:考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。

其中表示层和会话层用途如下:

  • 表示层 :数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。

  • 会话层 :建立及管理会话。

五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理。

数据在各层之间的传递过程

在向下的过程中,需要添加下层协议所需要的首部和尾部,而在向上的过程中不断拆开首部和尾部。

路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要传输层和应用层。

操作系统

查看系统的启动日志

1、使用 dmesg 命令

这个命令用来查看内核启动时产生的日志信息,通常包含硬件检测和加载模块等信息。

dmesg

如果想要查看更早的日志或者保存的输出,可以用 -T 参数来显示时间戳,或者用 -c 清除现有的日志。

dmesg -T

2、使用 journalctl 命令

journalctl 是 Systemd 系统和服务管理器的一部分,用来查看由 Systemd 产生的日志。它非常灵活,可以查看从启动到现在的所有日志。

journalctl -b

小贴士

如果你想查看启动时某个特定服务的日志,可以使用 journalctl -u <service>.service。

3、/var/log/messages

有些系统可能会将日志记录到 /var/log/messages 文件中,这个文件类似于 syslog,也是记录系统运行期间的各种日志。

less /var/log/messages

Golang

Channel 缓冲有什么特点?

无缓冲的 channel 是同步的,而有缓冲的 channel 是异步的。

Channel 使用时,需要注意什么?

空(nil)channel:

  • 发送和接收数据都会发生永久阻塞

已经关闭的 channel:

  • 发送会引起panic
  • 通道没有值时,value, ok := <-stop接收到的value会返回一个零值,并且的ok等于false。

注意:

有一个向通道发送数据的 goroutine 和一个从通道接收数据的 goroutine,如果发送方在关闭通道之前已经发送了一些数据,但接收方还没有接收这些数据,那么在通道关闭后,接收方仍然可以从通道中接收到这些数据,每次接收操作都会返回 true 的 ok 值,直到所有数据都被接收完毕,才会返回返回 false 的 ok 值。

MySQL

MySQL中有哪几种锁,列举一下?

表锁、行锁的区别

如果按锁粒度划分,有以下3种,包含了他们的区别

  • 表锁:开销小,加锁快;锁定力度大,发生锁冲突概率高,并发度最低;不会出现死锁。

  • 行锁:开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高。

  • 页锁:开销和加锁速度介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般。

建立索引的原则?

选择性原则:对于经常用于筛选(特别是作为 WHERE 子句的一部分)的列创建索引。选择性高的列更适合作为索引的依据,即那些具有高唯一值比例的列。

频繁查询原则:为经常出现在 SELECT 查询中的列创建索引。特别是那些被频繁用来做 JOIN 或者 GROUP BY 的列。

排序和分组原则:如果一个列经常被用来进行 ORDER BY 或 GROUP BY 操作,则考虑为该列创建索引。

最左前缀原则:在复合索引中,查询使用的索引字段顺序应该与索引定义的顺序一致,并且从最左边开始。例如,如果索引是 (A, B, C),那么 WHERE A = ? 和 WHERE A = ? AND B = ? 都可以使用这个索引,但 WHERE B = ? 则不能。

避免更新频繁的列:尽量不要为经常更新的列创建索引,因为每次数据更新时索引也需要更新,这会增加写操作的时间开销。

覆盖索引原则:创建包含所有查询需要字段的索引,这样数据库可以直接从索引中获取数据,而不需要额外访问表数据(这被称为“覆盖索引”或“索引覆盖”)。

限制索引数量:每个表上的索引数量应该有限制,过多的索引会降低写操作的性能。考虑到每个索引都会占用存储空间,因此要平衡索引带来的读取性能提升和额外的存储成本。

使用合适的数据类型:索引列应尽量使用适合的数据类型,以减少索引的大小并提高性能。比如主键索引,大多数情况都会使用整型,二叔字符串。

开放题

设计一个电子邮件发送系统

简单的可以从如下方面进行介绍

在实现技术细节中,我们加上并发控制的代码示例。

package main

import (
"context"
"fmt"
"net/smtp"
"sync"
"time"

"gopkg.in/gomail.v2"
)

// EmailTask 结构体表示一个待发送的邮件任务
type EmailTask struct {
To string
Subject string
Body string
}

// sendEmail 发送单个邮件
func sendEmail(task EmailTask) error {
m := gomail.NewMessage()
m.SetHeader("From", "your-email@example.com")
m.SetHeader("To", task.To)
m.SetHeader("Subject", task.Subject)
m.SetBody("text/html", task.Body)

dialer := gomail.NewDialer("smtp.example.com", 587, "your-email@example.com", "your-password")

if err := dialer.DialAndSend(m); err != nil {
return fmt.Errorf("failed to send email: %w", err)
}

return nil
}

// worker 是一个 goroutine 函数,它不断地从队列中取出任务并执行
func worker(id int, tasks chan EmailTask, wg *sync.WaitGroup) {
defer wg.Done()

for task := range tasks {
fmt.Printf("Worker %d processing email to %s\n", id, task.To)
if err := sendEmail(task); err != nil {
fmt.Printf("Error sending email to %s: %v\n", task.To, err)
} else {
fmt.Printf("Email sent to %s\n", task.To)
}
}
}

func main() {
// 初始化邮件队列
tasks := make(chan EmailTask, 10)

// 创建 WaitGroup 以等待所有 workers 完成
var wg sync.WaitGroup

// 启动 5 个 workers
const numWorkers = 5
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(i+1, tasks, &wg)
}

// 添加一些任务到队列中
go func() {
tasks <- EmailTask{"alice@example.com", "Welcome!", "Welcome to our service!"}
tasks <- EmailTask{"bob@example.com", "Reset Password", "Please reset your password."}
tasks <- EmailTask{"charlie@example.com", "Important Update", "We have important updates for you."}
close(tasks)
}()

// 等待所有 workers 完成
wg.Wait()

fmt.Println("All emails have been processed.")
}

解读main.go

  • 初始化一个 tasks 队列。
  • 创建一个 WaitGroup 用于同步。
  • 启动多个 workers 处理队列中的任务。
  • 向队列中添加几个示例任务。
  • 关闭队列,并等待所有 workers 完成。

注意事项

  • 在实际应用中,可能还需要考虑错误处理、重试机制以及监控等功能。
  • 根据具体需求,我们还需要调整 worker 的数量以及队列的大小已达到较好的吞吐量。
  • 如果邮件发送量非常大,我们还可以考虑使用第三方服务或队列服务(如 RabbitMQ、Kafka)来处理邮件队列。

以上,点亮【赞与在看】让我们心中充满力量、披荆斩棘、不惧未来!

【推荐阅读】

美团面试的新方式,涨见识了

字节跳动申请出战,校招4000+,根本面不完

美团再招2000人,一个村的人都想黄袍加身

京东秋招16000个兄弟,强哥批量收小弟


关于作者:一位热爱技术,并在职场与自媒体间探索的实践者,希望通过分享个人经验和见解,帮助更多人实现自我成长和价值。

如果您对我的公众号内容感兴趣,欢迎关注我。

也可以在下方添加我微信和一支烟花AI社区技术群,一起交流技术、职场心得。

原文作者:皇子谈技术;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2023-12-21

  • 博文量
    113
  • 访问量
    56514