哈喽~,大家好,我是千羽。
下面分享我认识的一位大佬华中科技大学985硕,腾讯暑期实习一面。
问的很常规,被吊打了。
- 1、自我介绍
- 2、项目中遇到的难点(redis操作,读写锁,分析业务场景)
- 3、之前的单体项目遇到什么问题?
- 4、你说要把抖音项目拓展为微服务,为什么要拓展为微服务呢?
- 5、乐观锁与悲观锁(忘得一干二净)
- 6、Go语言中指针逃逸的场景
- 7、在浏览器中输入一个网址,这个过程中发生了什么
- 8、Https中的s是什么(加密),知道它是怎么加密的吗
- 9、二分模板题
- 10、最后问题:对于为什么想来这边实习?
1、自我介绍
略
2、项目中遇到的难点(redis操作,读写锁,分析业务场景)
在项目中,可能会遇到与Redis操作、读写锁以及业务场景分析相关的难点。以下是一些可能的难点及解决方案:
- Redis操作难点:
- 数据一致性问题:在使用Redis作为缓存时,可能会遇到数据一致性问题,如缓存与数据库数据不一致。
- 解决方案:可以采用缓存更新策略,如先更新数据库,再删除缓存,确保下次读取时从数据库加载最新数据。
- Redis性能问题:大量数据读写可能导致Redis性能下降。
- 解决方案:对数据进行分片,使用多个Redis实例分担负载;监控Redis性能指标,及时优化配置。
- 读写锁难点:
- 死锁问题:不当使用读写锁可能导致死锁。
- 解决方案:设置锁的超时时间,确保锁在一定时间后被释放;检测到死锁时,主动放弃锁,让其他线程继续执行。
- 锁粒度问题:锁粒度太大可能导致并发性能下降,锁粒度太小可能增加锁竞争。
- 解决方案:根据业务场景调整锁粒度,找到合适的平衡点。
- 业务场景分析难点:
- 需求不明确:业务需求不清晰或频繁变更,导致难以分析和设计合适的解决方案。
- 解决方案:与业务方充分沟通,明确需求边界和优先级;采用敏捷开发方法,迭代交付,及时响应需求变更。
- 系统复杂性:业务场景涉及多个系统和组件,分析和解决问题需要考虑多方面因素。
- 解决方案:采用分而治之的策略,将复杂问题拆解成多个小问题逐一解决;与相关团队合作,共同推进解决方案。
- 高并发场景下的挑战:
- 在高并发场景下,Redis操作和读写锁可能会成为性能瓶颈。
- 解决方案:使用Redis集群或分布式缓存方案以提高缓存吞吐量;对于读写锁,可以考虑使用基于Redis的分布式锁实现高并发下的锁管理。
- 数据安全问题:
- Redis数据存储在内存中,可能存在数据泄露风险。
- 解决方案:启用Redis密码认证和防火墙规则,限制外部访问;对于敏感数据,考虑在客户端进行加密处理。
3、之前的单体项目遇到什么问题?
把抖音项目拓展为微服务的原因主要是为了解决单体项目面临的一系列问题。单体项目是指将所有的应用程序代码和组件集成在一个单独的部署单元中的项目。随着应用程序的不断增长和复杂化,单体项目可能会面临以下问题:
- 可扩展性差:单体项目的所有组件都紧密耦合在一起,当需要扩展某个组件时,必须对整个应用程序进行扩展,这会导致资源浪费和效率低下。
- 部署困难:由于单体项目的所有组件都集成在一起,每次更新或修复bug都需要重新部署整个应用程序,这增加了部署的复杂性和风险。
- 技术栈限制:单体项目通常使用相同的技术栈和框架,这限制了开发团队在选择技术和创新方面的灵活性。
4、你说要把抖音项目拓展为微服务,为什么要拓展为微服务呢?
而微服务架构可以解决这些问题:
- 可扩展性:微服务架构将应用程序拆分成多个独立的服务,每个服务都可以独立扩展,从而提高了整个系统的可扩展性。这意味着可以根据实际需求对特定的服务进行扩展,而无需对整个应用程序进行扩展。
- 独立性:每个微服务都是独立的、可独立部署和升级的。这意味着不同团队可以同时开发、测试和部署不同的服务,从而加速了开发速度和迭代周期。
- 技术多样性:微服务架构允许每个服务使用不同的技术栈和框架,这提高了开发团队在选择技术和创新方面的灵活性。不同的服务可以使用最适合的技术来解决特定的问题,从而提高了开发效率和代码质量。
- 容错性:由于微服务是独立运行的,如果其中一个服务出现故障,其他服务可以继续正常运行。这种隔离性提高了系统的可靠性和容错性。
因此,将抖音项目拓展为微服务可以解决单体项目面临的可扩展性差、部署困难和技术栈限制等问题,并提高系统的可扩展性、独立性、技术多样性和容错性。
5、乐观锁与悲观锁(忘得一干二净)
MySQL数据库中主要有两种锁的策略:悲观锁(Pessimistic Locking)和乐观锁(Optimistic Locking)。
- 悲观锁:假定会发生并发冲突,阻塞掉其他所有事务,直到该事务完成,其他事务才能继续执行。这种锁的策略在高并发读写,写操作非常频繁的场景中是非常有用的,因为它可以避免出现数据不一致的情况。但是,这种策略的缺点是会降低并发性能,因为一个事务可能需要等待其他事务完成后才能继续执行。
- 乐观锁:假定不会发生并发冲突,所以不会阻塞事务的执行。只是在更新数据时,会判断在此期间有没有其他事务也更新了这条数据,如果有,则操作失败。这种锁的策略适合读操作非常多而写操作极少的场景,因为它不会阻塞事务的执行,可以提高并发性能。但是,如果有很多写操作,可能会出现数据不一致的情况。
在MySQL中实现乐观锁通常会使用版本号或时间戳等方式,例如在更新一条记录时,会将记录的版本号加一,然后判断在更新期间有没有其他事务更新了这条记录,如果有,则操作失败。
6、Go语言中指针逃逸的场景
在Go语言中,指针逃逸(Pointer Escape)指的是一个局部变量或对象的指针被存储到堆上,导致该变量或对象无法在函数返回后被垃圾回收机制回收的情况。这通常发生在以下几种场景中:
- 将局部变量的指针存储到全局变量或外部可见的结构体中:如果将一个局部变量的指针赋值给全局变量或外部可见的结构体字段,那么该指针就会逃逸到堆上。这是因为全局变量和结构体字段的生命周期通常比局部变量的生命周期更长,所以垃圾回收器无法确定该指针指向的局部变量何时不再被使用。
var globalPtr *int
func foo() {
x := 10
globalPtr = &x // 指针逃逸
}
- 将局部变量的指针作为函数参数传递并存储:如果将局部变量的指针作为函数参数传递,并且在函数内部将该指针存储起来,那么该指针也会逃逸到堆上。这是因为函数参数的生命周期在函数调用结束后结束,但存储的指针可能仍然在其他地方引用。
func bar(ptr *int) {
// 存储指针
}
func foo() {
x := 10
bar(&x) // 指针逃逸
}
- 将局部变量的指针返回给调用者:如果将局部变量的指针作为函数的返回值返回给调用者,那么该指针也会逃逸到堆上。这是因为返回值的生命周期通常比局部变量的生命周期更长。
func foo() *int {
x := 10
return &x // 指针逃逸
}
需要注意的是,指针逃逸并不一定总是坏事。有时候,将某些数据存储在堆上是有意为之的,以便在函数之间共享数据或在不同的调用之间保持状态。但是,在编写代码时应该注意避免不必要的指针逃逸,以避免造成内存泄漏和性能问题。
7、在浏览器中输入一个网址,这个过程中发生了什么
当你在浏览器中输入一个网址时,以下步骤会发生:
- 地址栏输入:你输入网址的地址栏是浏览器界面的一部分,通常位于浏览器窗口的顶部。
- 浏览器解析URL:当你输入URL后,浏览器会解析这个URL,将其分解为不同的部分。例如,URL中的协议(如http或https)、主机名(如www.example.com)、路径(如/page.html)和其他参数(如?id=123)都会被分开并分别处理。
- DNS查询:浏览器会通过DNS(域名系统)查询将输入的主机名解析为对应的IP地址。这是一个网络查询过程,需要与互联网上的DNS服务器进行交互。一旦找到了对应的IP地址,浏览器就知道如何与服务器建立连接。
- 建立TCP连接:浏览器使用TCP(传输控制协议)建立一个与服务器的连接。TCP是一种可靠的、面向连接的协议,用于在网络上传输数据。
- HTTP请求:一旦建立了TCP连接,浏览器会使用HTTP(超文本传输协议)发送一个请求到服务器。HTTP请求通常包括请求方法(如GET或POST)、请求头和请求体。请求方法告诉服务器你想要做什么(例如,获取一个网页或提交一个表单)。请求头包含有关请求的附加信息,如请求的资源类型或发送内容的语言。请求体是可选的,用于发送数据给服务器。
- 服务器响应:服务器收到HTTP请求后,会进行处理并发送一个响应回来。响应包含HTTP状态码(如200表示成功)、响应头和响应体。状态码告诉浏览器请求是否成功。响应头包含有关响应的附加信息,如响应的内容类型或发送的数据长度。响应体是服务器发送的实际数据。
- 浏览器渲染页面:一旦收到服务器的响应,浏览器会将其解析并显示在屏幕上。这可能涉及解析HTML、CSS和JavaScript,以构建并样式化一个页面。如果页面包含动态内容或需要与用户交互,浏览器可能会进一步处理这些内容。
- 关闭连接:在浏览器与服务器之间的通信完成后,连接通常会被关闭。然而,对于一些需要长期保持连接的应用程序(如实时聊天或流媒体),连接可能会保持开启状态。
8、Https中的s是什么(加密),知道它是怎么加密的吗
HTTPS中的“S”代表“Secure”,意为安全,指的是在HTTP协议的基础上增加了SSL/TLS协议,从而实现了对传输的数据进行加密和保证数据传输的安全性。
HTTPS加密的过程:客户端和服务器之间建立连接后,服务器会向客户端发送一个数字证书,该证书包含了服务器的公钥和一些其他信息。
客户端在收到证书后会验证证书的有效性,如果证书有效,则客户端会使用服务器的公钥对要发送的数据进行加密,并将加密后的数据发送给服务器。服务器在收到加密数据后,会使用自己的私钥对数据进行解密,从而得到原始的数据。
由于私钥只有服务器拥有,因此可以确保数据在传输过程中不会被第三方窃取或篡改。这个过程被称为对称加密和非对称加密的结合。
9、二分模板题
二分查找(Binary Search)是一种在有序数组中查找特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是目标值,则搜索过程结束;如果目标值大于或小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且同样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
以下是一个简单的Java实现:
代码语言:javascript复制public class BinarySearch {
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left (right - left) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] > target) {
right = mid - 1;
} else {
left = mid 1;
}
}
return -1; // 如果目标元素不存在于数组中,则返回-1
}
public static void main(String[] args) {
int[] arr = {2, 3, 4, 10, 40};
int target = 10;
int result = binarySearch(arr, target);
if (result == -1) {
System.out.println("Element not present in array");
} else {
System.out.println("Element found at index: " result);
}
}
}
以上代码中,binarySearch
函数接受一个整数数组和一个目标值作为参数。它首先初始化左右两个指针来表示搜索范围的开始和结束。然后,它进入一个循环,每次循环都将目标值与数组中间元素进行比较。如果目标值等于中间元素,则返回中间元素的索引;如果目标值小于中间元素,则在左半部分数组中继续搜索;如果目标值大于中间元素,则在右半部分数组中继续搜索。如果在整个过程中没有找到目标值,则返回-1。
10、最后问题:对于为什么想来这边实习?
- 我对贵公司的实习职责和项目非常感兴趣。这些职责和项目与我的专业知识和兴趣相关,能够让1在实习期间充分展示自己的才能和发挥自己的潜力。
- 团队合作氛围:我听说贵公司的团队合作氛围非常好。我希望能够融入这样的团队,与优秀的同事们一起合作、互相学习、共同进步。
- 在腾讯这里实习会提供良好的发展机会和成长空间,自己想争取这个实习机会。
- 原文链接:https://github.com/warthecatalyst/What-to-in-Graduate-School/blob/main/秋招的面经/华科计科第二人的秋招报告.md