字节跳动国际电商后端,难度有点大呀
1. tcp的客户端如果异常断开连接了服务端如何处理?
在 TCP 网络协议中,当客户端异常断开连接时,服务器需要采取一些措施来正确处理这种情况:
-
检测断开连接:
- TCP Keepalive:服务器可以使用 TCP Keepalive 选项来检测连接的状态。TCP Keepalive 会定期发送探测包,如果在一定时间内没有收到客户端的响应,服务器会认为连接已经断开。
- 错误处理:当服务器尝试读写数据时,如果发现连接断开,通常会收到一个错误,例如
ECONNRESET
或EPIPE
,服务器可以通过捕获这些错误来检测断开连接。
-
资源清理:
- 关闭套接字:一旦检测到客户端断开连接,服务器需要关闭相应的套接字。调用
close()
函数来释放资源。 - 释放内存和资源:确保释放与该连接相关的所有资源,包括内存、文件描述符等。
- 关闭套接字:一旦检测到客户端断开连接,服务器需要关闭相应的套接字。调用
-
处理半关闭连接:
- 在 TCP 协议中,连接的一端可以执行半关闭操作,即关闭连接的写方向而保持读方向打开。服务器需要处理这种情况,通常可以通过
shutdown()
函数来关闭连接的读方向或写方向。
- 在 TCP 协议中,连接的一端可以执行半关闭操作,即关闭连接的写方向而保持读方向打开。服务器需要处理这种情况,通常可以通过
-
使用超时机制:
- 服务器可以设置读写操作的超时时间,通过
setsockopt()
函数设置SO_RCVTIMEO
和SO_SNDTIMEO
选项。如果在超时时间内没有完成操作,服务器可以认为连接已经断开并采取相应的措施。
- 服务器可以设置读写操作的超时时间,通过
-
检测 FIN 和 RST 包:
- 当客户端正常关闭连接时,会发送一个 FIN 包,服务器收到后需要回复一个 ACK 包。当服务器关闭连接时,也会发送一个 FIN 包,客户端回复 ACK 包。如果客户端异常断开连接,服务器可能会收到一个 RST 包,这意味着连接被强制重置。服务器需要处理这些 TCP 包以正确管理连接状态。
2. https的ssl加密是如何做的?
HTTPS 使用 SSL/TLS 来实现加密和安全通信。
1. SSL/TLS 握手过程
SSL/TLS 握手是建立加密连接的过程,包括以下几个步骤:
a. 客户端问候 (Client Hello)
客户端向服务器发送一个“客户端问候”消息,内容包括:
- 支持的 SSL/TLS 版本。
- 支持的加密算法 (Cipher Suites)。
- 一个随机数 (Client Random)。
b. 服务器问候 (Server Hello)
服务器响应客户端,发送一个“服务器问候”消息,内容包括:
- 选择的 SSL/TLS 版本。
- 选择的加密算法。
- 一个随机数 (Server Random)。
- 服务器的数字证书 (包含服务器的公钥)。
- (可选)请求客户端证书。
c. 服务器证书验证
客户端验证服务器的数字证书,确保证书由可信任的证书颁发机构 (CA) 签发,且证书未过期。如果验证失败,握手过程会终止。
d. 客户端密钥交换 (Client Key Exchange)
客户端生成一个称为 Pre-Master Secret 的随机数,并用服务器的公钥加密,然后发送给服务器。
e. 会话密钥生成
服务器使用自己的私钥解密 Pre-Master Secret,客户端和服务器根据之前交换的随机数和 Pre-Master Secret 共同生成会话密钥。
f. 客户端完成消息 (Client Finished)
客户端使用会话密钥发送一条“完成”消息,这条消息是客户端到服务器的第一个加密消息,用于确认握手过程的完整性。
g. 服务器完成消息 (Server Finished)
服务器也使用会话密钥发送一条“完成”消息,确认握手过程结束。
2. 加密通信
在握手过程完成后,客户端和服务器之间的通信将使用会话密钥进行对称加密。对称加密算法如 AES、DES、ChaCha20 等可以高效地加密和解密数据。
3. 连接终止
当通信完成后,客户端或服务器可以发起连接终止过程:
- 发送一个“关闭通知”消息,表示即将关闭加密连接。
- 对方接收到“关闭通知”后,也会发送一个“关闭通知”消息,然后关闭连接。
3. 说一说数据库索引失效的场景?
-
不满足最左匹配原则:
- 在联合索引中,查询条件没有使用到最左边的索引列,导致索引失效。例如,假设存在联合索引(sex, age, name),但查询条件只包含age和name,而不包含sex,此时索引将不会被使用。
-
索引列上进行计算或函数操作:
- 当在索引列上应用计算或函数时,索引通常会失效。例如,
SELECT * FROM table WHERE YEAR(date_column) = 2023;
这样的查询中,由于使用了YEAR函数,date_column上的索引将不会被使用。
- 当在索引列上应用计算或函数时,索引通常会失效。例如,
-
数据类型不匹配:
- 当查询中使用的数据类型与索引列的数据类型不匹配时,索引可能会失效。例如,将字符串类型的列与数值类型进行比较时,索引可能不会被使用。
-
LIKE查询以通配符开头:
- 在使用LIKE查询时,如果通配符(%)位于搜索模式的开头,索引可能会失效。例如,
SELECT * FROM table WHERE name LIKE '%John';
这样的查询通常不会使用name列上的索引。
- 在使用LIKE查询时,如果通配符(%)位于搜索模式的开头,索引可能会失效。例如,
-
使用OR关键字连接的条件:
- 当查询条件使用OR连接时,如果OR连接的两个条件分别涉及到不同的索引列,或者两边分别使用了范围查询(如>和<),则索引可能会失效。
-
使用NOT IN或NOT EXISTS:
- 在某些情况下,使用NOT IN或NOT EXISTS的查询可能会导致索引失效,尤其是当它们涉及到大量数据时。
-
使用不等于(<> 或 !=):
- 当查询条件中使用了不等于操作符时,如果查询的结果集较大,索引可能会失效。
-
IS NOT NULL:
- 虽然IS NULL通常可以使用索引,但IS NOT NULL在某些情况下可能无法使用索引。
-
索引列的顺序不正确:
- 在复合索引中,如果查询条件中列的顺序与复合索引的列顺序不一致,索引可能不会被使用。
-
数据分布不均
- 如果索引列上的数据分布非常不均匀,有些值出现频率特别高,查询这些高频值时,索引可能无法显著提高性能。数据库优化器可能会选择全表扫描而不是使用索引。
-
前缀索引使用不当:
- 如果使用了前缀索引,但前缀长度设置得不够长,导致查询结果不准确,索引可能会失效。
-
查询条件中包含非索引列:
- 如果查询条件中同时包含了索引列和非索引列,并且查询结果需要非索引列的数据,那么即使索引列上的索引有效,也可能需要进行额外的磁盘I/O操作来检索非索引列的数据,从而影响查询性能。
3. 联合索引中如果建立的顺序是(A,B,C,D),使用时有哪些注意事项?
当联合索引的建立顺序为(A,B,C,D)时,使用时需要注意以下事项:
-
最左前缀原则:
- 查询条件必须包含索引的最左列,即A列。如果查询条件中不包含A列,那么索引可能不会被使用。
- 查询可以仅使用A列,或者使用A和B列,或者A、B和C列,或者完整的A、B、C、D列,这些查询都可以利用该联合索引。
-
列顺序:
- 在创建联合索引时,列的顺序应该根据查询的需求和数据的选择性来决定。通常,将选择性最高的列放在最左侧可以提高查询性能。
- 例如,如果查询经常根据A列和B列进行,并且B列的选择性较高,那么将B列放在A列之后可能不是最佳选择。
-
索引宽度:
- 联合索引的宽度是所有索引列的宽度之和。过宽的联合索引可能会导致索引占用更多的磁盘空间,并且在内存中加载索引时可能会增加额外的开销。
- 在设计联合索引时,需要权衡列的选择和索引的宽度,避免创建过宽的索引。
-
查询覆盖:
- 如果查询只需要访问索引中的信息,而不需要访问表中的数据行,那么这个查询就被称为“覆盖索引”查询。这样的查询通常非常快。
- 在设计联合索引时,可以考虑将查询所需的列都包含在索引中,以实现覆盖查询,提高查询性能。
-
冗余索引:
- 当表已经有了一个联合索引时,不需要再创建包含该联合索引的子集的单列索引。因为联合索引已经可以支持包含该子集的查询,创建冗余索引只会增加索引维护的开销,浪费存储空间。
-
更新代价:
- 联合索引在插入、更新和删除操作时需要维护索引。如果表的更新操作频繁,联合索引可能会增加更新的代价。因此,在设计联合索引时,要考虑到表的更新模式和频率。
-
查询选择性:
- 联合索引的选择性是指索引列中不同值的数量与总行数的比率。选择性越高,索引的效果越好。因此,在选择联合索引的列时,应考虑到查询选择性,选择具有高选择性的列作为联合索引的一部分。
-
索引列的使用:
- 索引列不能参与计算,保持列“干净”。例如,对索引列使用函数或进行计算可能导致索引失效。
- 在查询时,尽量按照联合索引的顺序来编写查询条件,以充分利用索引。
-
优化器选择:
- MySQL的查询优化器会根据查询语句的成本来选择使用哪个索引。因此,即使存在多个可用的索引,也不一定能保证联合索引总是被使用。
4. 数据库的主键为什么要用自增的?
数据库的主键使用自增有多个原因:
-
唯一性: 主键的主要目的是确保表中每一行数据的唯一性。自增字段可以确保每次插入新行时都会生成一个新的、唯一的值。
-
简化开发: 在插入新记录时,开发人员不需要手动为每一行生成一个唯一的值。数据库系统会自动处理自增值的生成,从而简化了开发过程。
-
减少冲突: 如果使用其他值(如 UUID 或自定义的唯一字符串)作为主键,可能会存在与其他表中的值冲突的风险。自增值通常是整数,并且在每次插入时递增,因此不太可能与其他表中的值冲突。
-
性能: 整数类型的主键通常比长字符串或UUID等更复杂的数据类型具有更好的性能。整数在存储、索引和查询时通常更快。
-
易于排序和分页: 由于自增值是顺序递增的,因此它们很容易用于排序和分页操作。例如,你可以很容易地获取表中的前10个记录,或者跳过前100个记录并获取接下来的10个记录。
-
扩展性: 虽然整数类型的主键有范围限制,但在大多数情况下,这个范围对于单个表来说已经足够大了。如果需要存储更多的数据,可以考虑使用BIGINT类型的主键,它可以支持更大的范围。
-
避免暴露业务逻辑: 如果主键是基于业务逻辑生成的,那么当业务逻辑发生变化时,可能需要更改主键的生成逻辑。这可能会导致数据迁移、应用程序修改等复杂问题。而使用自增主键可以避免这种情况,因为主键的生成完全由数据库系统控制。
-
易于备份和恢复: 如果数据库需要备份和恢复操作,使用自增主键可以简化这些过程。因为每次恢复数据库时,自增字段都会从上次的最大值开始递增,所以不需要担心重复的主键值问题。
5. 数据库的四种事物隔离级别以及它们的细节?
数据库的四种事务隔离级别,按照从低到高的顺序,分别是:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。以下是这四种隔离级别的详细解释:
-
读未提交(Read Uncommitted)
- 简述:最低的隔离级别,一个事务可以读取另一个事务尚未提交的数据修改。
- 特性:
- 允许脏读:事务A可以读取事务B尚未提交的数据修改。
- 不保证一致性:由于允许脏读,无法保证事务的一致性。
- 低并发性:需要较少的并发控制和锁定操作,但增加了数据不一致的风险。
- 应用场景:通常用于对数据一致性要求不高、并发性要求较高的场景,如临时数据分析任务。
-
读已提交(Read Committed)
- 简述:一个事务只能读取到已经提交的数据修改。
- 特性:
- 已提交数据可见:其他事务尚未提交的数据修改对当前事务是不可见的。
- 一致性保证:保证事务读取到的数据是一致的。
- 不可重复读:同一个事务在不同的时间点多次读取同一行数据,可能会得到不同的结果。
- 无法解决幻读问题:无法解决由其他事务插入或删除数据导致的查询结果不一致问题。
- 应用场景:提供了一定程度的数据隔离,避免了脏读问题,并保证了读取到的数据是一致的。
-
可重复读(Repeatable Read)
- 简述:一个事务在执行过程中多次读取同一行数据,可以得到一致的结果。
- 特性:
- 保证一致性:在事务执行过程中,多次读取同一行数据得到的结果始终保持一致。
- 防止不可重复读:解决了读已提交隔离级别中的不可重复读问题。
- 防止幻读(部分):一定程度上防止了幻读问题,但不完全保证查询结果的完全一致性。
- 应用场景:提供了较高的隔离性,保证了事务内部多次读取同一行数据的一致性。
-
串行化(Serializable)
- 简述:最高的隔离级别,事务串行化顺序执行。
- 特性:
- 避免所有并发问题:完全避免了脏读、不可重复读和幻读等所有并发问题。
- 性能影响:由于事务需要串行执行,对系统的性能产生较大影响。
- 死锁风险:可能导致死锁的风险增加。
- 应用场景:提供了最高的隔离性,可以完全避免所有的并发问题。但由于其严格的执行顺序要求,可能会对系统的性能产生较大的影响。
6. 操作系统进程调度算法以及它们的优缺点?
操作系统中的进程调度算法是决定哪个进程将获得CPU资源以执行其任务的重要机制。以下是几种常见的进程调度算法及其优缺点的详细解释:
-
先来先服务(FCFS,First Come First Serve)
- 优点:
- 易于理解和实现,只需要一个队列即可。
- 公平,按照进程到达的先后顺序提供服务。
- 缺点:
- 不利于短作业,可能导致短作业长时间等待。
- 如果有长作业先到达,可能导致后续短作业长时间等待,即出现饥饿现象。
- 优点:
-
时间片轮转(RR,Round Robin)
- 优点:
- 适用于分时系统,保证每个用户都能获得一定的CPU时间。
- 平均响应时间短,用户交互性好。
- 缺点:
- 不利于处理紧急作业,因为即使作业紧急也需要等待当前时间片用完。
- 时间片大小的选择对系统性能有重要影响,过大可能退化为FCFS,过小则导致频繁切换,增加系统开销。
- 优点:
-
短作业优先(SJF,Shortest Job First)
- 优点:
- 减少平均等待时间,提高系统吞吐量。
- 优先照顾短作业,降低平均周转时间。
- 缺点:
- 对长作业不利,可能导致长作业长时间得不到执行,出现饥饿现象。
- 需要准确估计作业的执行时间,估计不准确会影响调度性能。
- 优点:
-
高响应比优先(HRRN,Highest Response Ratio Next)
- 优点:
- 综合考虑了作业的等待时间和执行时间,避免了SJF中的饥饿现象。
- 响应比的计算使得即使长作业等待时间较长,也有机会获得CPU资源。
- 缺点:
- 计算响应比需要消耗系统资源。
- 可能导致较长的平均等待时间。
- 优点:
-
优先级调度(Priority Scheduling)
- 优点:
- 根据作业的紧迫程度进行调度,满足不同需求。
- 既可以用于作业调度,也可以用于进程调度。
- 缺点:
- 可能导致低优先级作业长时间得不到执行,出现饥饿现象。
- 优先级的设定和修改需要额外的管理和开销。
- 优点:
-
多级反馈队列调度(Multilevel Feedback Queue Scheduling)
- 优点:
- 兼顾长短作业,有较好的响应时间和吞吐量。
- 灵活性强,可以根据系统情况动态调整队列级别和时间片大小。
- 缺点:
- 实现相对复杂,需要维护多个队列和时间片。
- 可能导致进程在队列间频繁迁移,增加系统开销。
- 优点:
7. 介绍一下IO多路复用?
IO多路复用是一种高效的IO处理机制,它允许单个线程或进程同时监听多个输入/输出通道(如网络套接字、文件描述符等),并在有数据可读或可写时进行相应的处理,而不需要为每个通道创建一个独立的线程或进程。这种机制可以显著提高系统的性能和资源利用率,减少不必要的线程切换和上下文切换开销。
-
基本概念:
- IO多路复用通过一个线程或进程来管理多个IO通道,避免了为每个通道创建独立线程或进程的开销。
- 常见的IO多路复用机制包括select、poll和epoll(Linux特有)。
-
实现方式:
- select:
- 阻塞住监视三类文件描述符(写、读、异常),当有数据可读、可写、出异常或超时时返回。
- 遍历fdset数组来找到就绪的描述符,然后进行IO操作。
- 在所有平台上支持,但性能随文件描述符数量增多而下降,且每次调用都需要将fd集合从用户态拷贝到内核态。
- poll:
- 原理与select一致,也是轮询+遍历,但没有最大文件描述符限制(使用链表方式存储fd)。
- 同样存在性能随文件描述符数量增多而下降的问题。
- epoll:
- Linux特有的IO多路复用机制,使用内核事件表来管理和监听多个IO事件的就绪状态。
- 通过epoll_ctl注册fd,一旦fd就绪就会通过callback回调机制来激活对应fd,进行IO操作。
- 高效,使用回调通知而不是轮询的方式,不会随着FD数目的增加效率下降。
- 支持大量文件描述符,且没有fd限制,是构建高性能网络应用的首选。
- select:
-
优点:
- 减少系统开销:不必创建过多的进程/线程,也不必维护这些进程/线程。
- 提高性能和可伸缩性:可以有效地处理大量并发请求,提高了性能和可伸缩性。
- 节省资源:减少内存和系统资源的消耗。
- 支持非阻塞I/O:允许程序在等待数据准备好时继续执行其他任务。
- 简化程序结构:不需要处理复杂的线程管理和同步问题。
-
应用场景:
- 在许多网络应用中,如Web服务器、聊天应用、实时游戏等,需要同时处理大量的连接请求或数据传输,IO多路复用技术可以显著提高这些应用的性能。
- 在高性能服务器应用中,如Redis、Nginx等,IO多路复用技术被广泛使用,以支持更多的并发连接请求。
8. leetcode31.下一个排列
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int i = nums.size() - 2;
while (i >= 0 && nums[i] >= nums[i + 1]) {
i--;
}
if (i >= 0) {
int j = nums.size() - 1;
while (j >= 0 && nums[i] >= nums[j]) {
j--;
}
swap(nums[i], nums[j]);
}
reverse(nums.begin() + i + 1, nums.end());
}
};
#校招过来人的经验分享#收录各个网友分享的各个公司的面经,并给出答案。