来源:https://www.nowcoder.com/discuss/551461694732570624
1、说说 TCP 的拥塞控制?
2、TCP 怎么保证可靠?
TCP 通过多种机制来保证可靠性,确保在数据传输过程中不丢失、不重复、不失序,并能正确处理网络中可能出现的错误。
保证可靠性的主要机制:
3、为什么握手只要三次而挥手要四次?
为什么握手是三次?
TCP 建立连接时之所以只需要"三次握手",是因为在第二次"握手"过程中,服务器端发送给客户端的 TCP 报文是以 SYN 与 ACK 作为标志位的。SYN 是请求连接标志,表示服务器端同意建立连接;ACK 是确认报文,表示告诉客户端,服务器端收到了它的请求报文。
即 SYN 建立连接报文与 ACK 确认接收报文是在同一次"握手"当中传输的,所以"三次握手"不多也不少,正好让双方明确彼此信息互通。
为什么挥手是四次?
TCP 释放连接时之所以需要“四次挥手”,是因为 FIN 释放连接报文与 ACK 确认接收报文是分别由第二次和第三次"挥手"传输的。
为何建立连接时一起传输,释放连接时却要分开传输?
4、HTTP 的发展历史?
HTTP 是一种用于传输超文本的协议,它在不同版本中不断演化和改进,以满足不断增长的网络需求。
HTTP 1.0:
HTTP 1.0 是最早的 HTTP 版本,定义于 1996 年。
它的特点如下:
HTTP 1.1:
HTTP 1.1 是 HTTP 1.0 的后续版本,于 1999 年发布。
它引入了以下改进:
长连接:HTTP 1.1 引入了持久连接,允许多个请求/响应共享一个 TCP 连接,减少了握手和释放的开销,提高了性能。
管道化:HTTP 1.1 支持请求/响应的管道化,允许客户端发送多个请求而不等待响应,进一步提高了性能。
Host 头部:引入了 Host 头部字段,允许在同一台服务器上托管多个域名,从而支持虚拟主机。
缓存控制:引入了更精细的缓存控制机制,使缓存更有效。
HTTP/2.0:
HTTP/2.0 是 HTTP 1.1 的进一步改进,于 2015 年发布。
它引入了以下特性:
HTTP/3.0:
HTTP/3.0 是最新的 HTTP 版本,于 2020 年发布。
它采用了一种名为 QUIC(Quick UDP Internet Connections)的新的传输协议,以进一步提高性能和安全性:
总的来说,HTTP 1.0 到 HTTP/3.0 的演化过程主要关注了性能、效率和安全性的提升。HTTP/2.0 和 HTTP/3.0 引入了多路复用和头部压缩等重要特性,以改进传输效率和降低延迟。HTTP/3.0 更进一步通过使用 QUIC 协议提供了更佳的性能和网络适应性。
5、POST 和 GET 本质区别 POST 几次 GET 最长 url?
GET 和 POST 的本质区别:
浏览器和服务器对 GET 请求的 URL 长度有一定的限制,具体限制因浏览器和服务器而异。一般来说,常见的浏览器如 Chrome、Firefox 等对 URL 长度的限制是几千个字符。而服务器端也可能对接收的 URL 长度进行限制。
6、ARP 怎么做的?
ARP(Address Resolution Protocol)是一种用于将网络层地址(如 IP 地址)映射到链路层地址(如 MAC 地址)的协议。主要用于在局域网中,通过已知 IP 地址获取相应的 MAC 地址。
ARP 的工作过程如下:
7、Map 为啥用红黑树不用其他二叉树?
主要是因为红黑树相对于普通的二叉搜索树(BST)和其他平衡二叉树结构具有一些优势。
以下是一些使用红黑树的原因:
8、MySQL 为啥用 b+做索引?
9、Mysql 解决不可重复读?
MySQL 通过实现事务隔离级别来解决不可重复读的问题。不可重复读指的是在一个事务中,如果一个事务在读取某一行数据后,另一个事务修改了这一行数据并提交了,那么在第一个事务中再次读取同一行数据时,会发现数据发生了变化,导致读到不一致的数据。
事务隔离级别包括:读未提交(Read Uncommitted)、读提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。这些隔离级别的选择允许用户在事务的一致性和性能之间进行权衡。
解决不可重复读的主要隔离级别是“可重复读”(Repeatable Read)。
在可重复读隔离级别下,一个事务执行期间,其他事务无法对该事务的数据进行修改,直到该事务结束。这样可以保证在同一个事务中多次读取同一行数据时,得到的结果是一致的。
在 MySQL 中,使用如下的 SQL 语句设置事务隔离级别:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
或者在开始事务时指定:
START TRANSACTION ISOLATION LEVEL REPEATABLE READ;
需要注意的是,默认情况下 MySQL 的事务隔离级别是“可重复读”,因此大多数情况下不需要额外设置。但如果需要显式设置或更改事务隔离级别,可以使用上面的 SQL 语句。
10、MySQL 存储过程?
MySQL 存储过程是一组经过预编译的 SQL 语句的集合,它们被存储在数据库中,可以像调用普通 SQL 语句一样被调用和执行。存储过程通常用于完成特定的数据库操作,并且可以包含控制结构、变量、条件和循环等编程元素。
CREATE PROCEDURE
语句可以创建存储过程。例如:DELIMITER //
CREATE PROCEDURE my_procedure()
BEGIN
-- 存储过程的 SQL 语句
SELECT \* FROM my_table;
END //
DELIMITER ;
创建了一个名为 my_procedure
的存储过程,其中包含了一个简单的 SQL 语句。
IN
(输入)、OUT
(输出)或 INOUT
(既输入又输出)类型。例如:DELIMITER //
CREATE PROCEDURE my_procedure_with_param(IN input_param INT, OUT output_param INT)
BEGIN
-- 存储过程的 SQL 语句
SELECT input_param \* 2 INTO output_param;
END //
DELIMITER ;
创建一个带有输入和输出参数的存储过程。
CALL
语句可以执行存储过程。例如:CALL my_procedure_with_param(5, @result);
SELECT @result;
调用带有参数的存储过程,并通过@result
变量获取了输出结果。
DROP PROCEDURE
语句可以删除存储过程。例如:DROP PROCEDURE IF EXISTS my_procedure;
删除名为 my_procedure
的存储过程。
存储过程的使用可以提高数据库的性能和可维护性,减少了重复编写 SQL
语句的工作。然而,在使用存储过程时需要注意合理的使用场景,避免滥用导致维护困难。
11、数据库与缓存一致?
数据库与缓存一致性是指在涉及数据读写的操作中,数据库和缓存之间的数据保持一致的状态。维持一致性对于系统的正确运行至关重要,否则可能导致数据不一致的问题。
12、设计模式?
单例模式(Singleton Pattern):
思想:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
在类加载的时候就创建实例,因此在使用时已经存在一个实例。
实现代码:
class SingletonEager {
private:
// 私有的构造函数,防止外部实例化
SingletonEager() {}
// 静态实例,在类加载时初始化
static SingletonEager instance;
public:
// 公共的访问点
static SingletonEager\* getInstance() {
return &instance;
}
};
// 初始化静态实例
SingletonEager SingletonEager::instance;
优点:
缺点:
在需要使用时才创建实例,避免了不必要的资源浪费。
实现代码:
class SingletonLazy {
private:
// 私有的构造函数,防止外部实例化
SingletonLazy() {}
// 静态实例,使用时初始化
static SingletonLazy* instance;
public:
// 公共的访问点,使用时创建实例
static SingletonLazy\* getInstance() {
if (!instance) {
instance = new SingletonLazy();
}
return instance;
}
};
// 初始化静态实例为 nullptr
SingletonLazy\* SingletonLazy::instance = nullptr;
优点:
缺点:
工厂模式(Factory Pattern):
代码示例:
// 抽象产品
class Product {
public:
virtual void create() = 0;
};
// 具体产品 A
class ConcreteProductA : public Product {
public:
void create() override {
std::cout << "Product A created.\n";
}
};
// 具体产品 B
class ConcreteProductB : public Product {
public:
void create() override {
std::cout << "Product B created.\n";
}
};
// 抽象工厂
class Factory {
public:
virtual Product* createProduct() = 0;
};
// 具体工厂 A
class ConcreteFactoryA : public Factory {
public:
Product* createProduct() override {
return new ConcreteProductA();
}
};
// 具体工厂 B
class ConcreteFactoryB : public Factory {
public:
Product* createProduct() override {
return new ConcreteProductB();
}
};
观察者模式(Observer Pattern):
// 抽象观察者
class Observer {
public:
virtual void update() = 0;
};
// 具体观察者 A
class ConcreteObserverA : public Observer {
public:
void update() override {
std::cout << "Observer A received update.\n";
}
};
// 具体观察者 B
class ConcreteObserverB : public Observer {
public:
void update() override {
std::cout << "Observer B received update.\n";
}
};
// 主题
class Subject {
private:
std::vector<Observer*> observers;
public:
void attach(Observer* observer) {
observers.push_back(observer);
}
void notify() {
for (auto observer : observers) {
observer->update();
}
}
};
13、修饰器模式与代理模式的区别?
修饰器模式(Decorator Pattern):
修饰器模式允许你通过将对象封装在装饰器类的对象中来动态地改变对象的行为。这种模式是对继承的一种有力补充,它提供了一种灵活的方式来扩展类的功能。
给个例子:
#include <iostream>
// Component 接口
class Coffee {
public:
virtual int cost() const = 0;
virtual ~Coffee() {}
};
// ConcreteComponent 具体组件
class SimpleCoffee : public Coffee {
public:
int cost() const override {
return 10;
}
};
// Decorator 装饰器
class CoffeeDecorator : public Coffee {
public:
CoffeeDecorator(Coffee* decorated_coffee) : decorated_coffee*(decorated_coffee) {}
int cost() const override {
return decorated_coffee_->cost();
}
private:
Coffee* decorated*coffee*;
};
// ConcreteDecorator 具体装饰器
class Milk : public CoffeeDecorator {
public:
Milk(Coffee* decorated_coffee) : CoffeeDecorator(decorated_coffee) {}
int cost() const override {
return CoffeeDecorator::cost() + 5;
}
};
// ConcreteDecorator 具体装饰器
class Sugar : public CoffeeDecorator {
public:
Sugar(Coffee* decorated_coffee) : CoffeeDecorator(decorated_coffee) {}
int cost() const override {
return CoffeeDecorator::cost() + 2;
}
};
// 使用
int main() {
Coffee* coffee = new SimpleCoffee();
std::cout << "Cost: $" << coffee->cost() << std::endl;
Coffee* milk_coffee = new Milk(coffee);
std::cout << "Cost: $" << milk_coffee->cost() << std::endl;
Coffee* sugar_milk_coffee = new Sugar(milk_coffee);
std::cout << "Cost: $" << sugar_milk_coffee->cost() << std::endl;
delete coffee;
delete milk_coffee;
delete sugar_milk_coffee;
return 0;
}
代理模式(Proxy Pattern):
代理模式通过引入一个代理类,控制对原始对象的访问,并且可以在调用前后执行一些额外的操作。
再给个例子:
#include <iostream>
// Subject 主题接口
class RealCoffee {
public:
virtual int cost() const = 0;
virtual ~RealCoffee() {}
};
// RealSubject 真实主题
class SimpleRealCoffee : public RealCoffee {
public:
int cost() const override {
return 10;
}
};
// Proxy 代理
class CoffeeProxy : public RealCoffee {
public:
CoffeeProxy(RealCoffee* real_coffee) : real_coffee_(real_coffee) {}
int cost() const override {
// 可以在调用前后执行一些额外的操作
std::cout << "Proxy: Preprocessing..." << std::endl;
int result = real_coffee_->cost();
std::cout << "Proxy: Postprocessing..." << std::endl;
return result;
}
private:
RealCoffee* real_coffee_;
};
// 使用
int main() {
RealCoffee* real_coffee = new SimpleRealCoffee();
std::cout << "Cost: $" << real_coffee->cost() << std::endl;
RealCoffee* coffee_proxy = new CoffeeProxy(real_coffee);
std::cout << "Cost: $" << coffee_proxy->cost() << std::endl;
delete real_coffee;
delete coffee_proxy;
return 0;
}
在修饰器模式中,Decorator
类和 ConcreteDecorator
类扩展了 Component
接口。而在代理模式中,Proxy
类继承了 Subject
接口,但在代理中引入了一个真实的主题对象,而 Decorator
直接包含另一个组件。这两者的差别在于它们的目的和关注点:修饰器模式关注动态地添加或覆盖对象的行为,而代理模式关注对对象的访问控制。
14、索引失效原因?
IO
操作。这种情况下,优化器可能会选择全表扫描而不是使用索引。WHERE UPPER(column_name) = 'VALUE'
,如果 column_name
上有索引,但是使用了 UPPER
函数,优化器可能无法使用索引。WHERE column_name <> 'VALUE'
,在某些情况下,数据库可能不会使用索引,而是选择全表扫描。WHERE column_name BETWEEN 'VALUE1' AND 'VALUE2'
,有些数据库可能无法有效使用索引。OR
操作符:当 OR
操作符连接多个条件时,优化器可能会选择不使用索引。例如,WHERE column_name = 'VALUE1' OR column_name = 'VALUE2'
,在某些情况下可能不会使用索引。15、数据区分度多大时用索引?
选择是否在某一列上创建索引通常取决于数据的区分度(selectivity)。数据的区分度是指该列中不同值的比例,通常用不同的值的数量与总行数的比率来表示。
高区分度:
低区分度:
在决定是否在某一列上创建索引时,需要考虑以下因素:
16 、Binlog 类型
Binary Log(Binlog)是 MySQL 用于记录数据库中发生的更改的一种日志。Binlog 记录了对 MySQL 数据库执行的所有更改操作,包括插入、更新和删除等。这对于数据备份、主从复制、故障恢复等方面都非常重要。
在 MySQL 中,有两种主要的 Binlog 类型:Statement-Based Logging(基于语句的日志)和 Row-Based Logging(基于行的日志),还有一种混合类型。
选择 Binlog 的类型通常取决于数据库的特定需求和使用场景。Statement-Based Logging 对于许多场景来说是足够的,而 Row-Based Logging 则提供了更详细和准确的信息,适用于需要精确数据更改记录的场景。混合模式则是一种折中的选择。
17、16kb 的页一行 1kb 可以存多少数据?
如果不思考直接回答 16 行那可能就是对数据库存储这块不怎么了解,所以一起看看阿 Q 怎么解的吧!
众所周知,MySQL 的数据都储存在磁盘中而不是内存,具体的数据是存在行中的,而行是存在页中的。
页是 InnoDB 存储引擎磁盘管理的最小单位,默认一页是 16k,那么可以存多少行呢?
学过计算机基础的都知道,1kb=1024b,那么 16kb=1024x16=16384b。
我们常用的 mysql 5.7 版本,UTF 编码,一个汉字等 3b,那么 16 kb = 16384 / 3 个汉字
看个图:
所以说一行 1kb,能存(16384-38-56-8)/1024 大概是 15 行数据。
18、场景题:在大文件中找关键字->长字符串中找子串
在大文件中找关键字:
在长字符串中找子串:
std::string::find
或 Python 的 str.find
。19、线程与进程?
进程:
线程:
共同点:
20、线程间通信?
本文来源微信公众:
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
添加我为好友,拉您入交流群!
请使用微信扫一扫!