个人面试题的一些总结
大部分来自于网络上各路大佬的摘抄。
MYSQL 隔离级别
- Read uncommitted 读取未提交
事务能读取到其他事务未提交的内容 - Read committed 读取已提交
事务只能读取到已经提交的内容 - Repeatable read 可重复读
保证一个事务内,多次重复读到的内容一致 - serializable 串行化
给每个事务上锁,保证事务执行的顺序
> - 脏读
读到了未提交的内容 - 不可重复读
读到了其他事务update的内容 - 幻读
读到了其他事务insert的内容
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读取未提交 | √ | √ | √ |
读取已提交 | × | √ | √ |
可重复读 | × | × | √ |
串行化 | × | × | × |
MYSQL 引擎
MyISAM | InnoDB | |
---|---|---|
介绍 | 提供高速存储和检索,以及全文搜索能力。 | 具有提交、回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎 |
主外键 | × | √ |
事务 | × | √ |
行表锁 | 表锁 | 表锁、行锁 |
表空间 | 小 | 大 |
缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引,还缓存数据 |
关注点 | 性能 | 事务 |
MYSQL 索引
索引:排好序的快速查找的数据结构,索引往往以索引文件的形式存储在磁盘上
- 普通索引 INDEX
默认索引 - 唯一索引 UNIQUE INDEX
索引列的值必须唯一,但允许有空值。 - 主键索引 PRIMARY KEY
是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。 - 组合索引
指多个字段上创建的索引,使用组合索引时遵循最左前缀集合。从前往后依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用 - 全文索引 FULLTEXT
主要用来查找文本中的关键字,而不是直接与索引中的值相比较。
>
- B树与B+树
- B树中每个节点(包括叶节点和非叶节点)都存储真实的数据,B+树中只有叶子节点存储真实的数据,非叶节点只存储键。
- B树中一条记录只会出现一次,不会重复出现,而B+树的键则可能重复出现。
- B+树的叶节点之间通过双向链表链接。
- B树中的非叶节点,记录数比子节点个数少1;而B+树中记录数与子节点个数相同。
- B+树优点
- 更少的IO次数:B+树的非叶节点只包含键,而不包含真实数据,因此每个节点存储的记录个数比B树多很多,因此B+树的高度更低,访问时所需要的IO次数更少。此外,由于每个节点存储的记录数更多,所以对访问局部性原理的利用更好,缓存命中率更高。
- 更适于范围查询:在B树中进行范围查询时,首先找到要查找的下限,然后对B树进行中序遍历,直到找到查找的上限;而B+树的范围查询,只需要对链表进行遍历即可。
- 更稳定的查询效率:B树的查询时间复杂度在1到树高之间(分别对应记录在根节点和叶节点),而B+树的查询复杂度则稳定为树高,因为所有数据都在叶节点。
MYSQL MVCC
- MVCC多版本并发控制:提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读
- 实现原理主要是依赖记录中:3个隐式字段,undo日志 ,Read View读视图
MYSQL COUNT
- COUNT(*)和COUNT(1)不会忽略NULL行
- COUNT(列名)会忽略NULL的行
- 在InnoDB中COUNT(*)和COUNT(1)实现上没有区别,而且效率一样,更推荐COUNT(*),但是COUNT(字段)需要进行字段的非NULL判断,所以效率会低一些。
用户态 内核态
- 内核态(Kernel Mode):运行操作系统程序,操作硬件
- 用户态(User Mode):运行用户程序
- 用户态切换到内核态:系统调用、异常、外围设备中断
分段 分页
- 分段机制就是把虚拟地址空间中的虚拟内存组织成一些长度可变的称为段的内存块单元。
- 分页机制在段机制之后进行的,它进一步将线性地址转换为物理地址。
- 分段机制和分页机制的区别
- 分页机制会使用大小固定的内存块,而分段管理则使用了大小可变的块来管理内存。
- 分页使用固定大小的块更为适合管理物理内存,分段机制使用大小可变的块更适合处理复杂系统的逻辑分区。
- 段表存储在线性地址空间,而页表则保存在物理地址空间。
mmap
- mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
memcpy strcpy
- 复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意类型的内容。strcpy只用于字符串复制,并且还会复制字符串的结束符。memcpy对于复制的内容没有限制,用途更广。
- 复制的方法不同。strcpy不需要指定长度,遇到结束符’\0’才会结束,所以容易溢出。memcpy则是根据第三个参数决定复制的长度
- 用途不同。通常在复制字符串时用strcpy,在复制其他类型数据时一般用memcpy。
物理内存 虚拟内存
- 物理内存:就是指计算机的安装内存“通俗的讲就是内存条的大小”
- 虚拟内存:是把硬盘中的一部分空间用来当做内存使用。
- 当每个进程创建的时候,内核会为进程分配虚拟内存,实际上并不立即就把虚拟内存对应位置的程序数据和代码拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好。这个时候数据和代码还是在磁盘上的。当运行到对应的程序时,进程去寻找页表,发现页表中地址没有存放在物理内存上,而是在磁盘上,于是发生缺页异常,于是将磁盘上的数据拷贝到物理内存中。
TCP SYN Flooding
- 该攻击以多个随机的源主机地址向目的路由器发送SYN包,而在收到目的路由器的SYN ACK后并不回应,这样,目的路由器就为这些源主机建立了大量的连接队列,而且由于没有收到ACK一直维护着这些队列,造成了资源的大量消耗而不能向正常请求提供服务,甚至导致路由器崩溃.服务器要等待超时(Time Out)才能断开已分配的资源。
中间人攻击 MITM攻击
- 通过各种技术手段将受入侵者控制的一台计算机虚拟放置在网络连接中的两台通信计算机之间,这台计算机就称为“中间人”。
- 信息篡改:当主机A、和主机B通信时,都由主机C来为其“转发”* 信息窃取:当A、B通信时,C不主动去为其“转发”,只是把他们的传输的数据备份,以获取用户网络的活动,包括账户、密码等敏感信息,
TCP粘包
- 发送端为了将多个发往接收端的包,更加高效的的发给接收端,于是采用了优化算法(Nagle算法),将多次间隔较小、数据量较小的数据,合并成一个数据量大的数据块,然后进行封包。那么这样一来,接收端就必须使用高效科学的拆包机制来分辨这些数据。
- 原因
- 发送方:TCP使用Nagle算法、数据包过大
- 接收方:TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包,即数据之间没有分界
从输入URL到页面加载全过程
- 应用层进行DNS解析,将域名解析成IP地址
- 浏览器构建HTTP Request请求
- 网络传输
- 服务器构建HTTP Response 响应
- 网络传输
- 浏览器渲染页面
模板特化
- 模板特化不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化,以实现特定类型下非通用行为。
特化为绝对类型(全特化)
在模板类里,所有的类型都是模板(template<class T>)
,而一旦我们将所有的模板类型T都明确化,并且写了一个类名与主模板类名相同的类,那么这个类就叫做全特化类。1
2
3
4
5
6template <typename T>
class Class_Name
{};
template <>
class Class_Name<Type_Name>
{};半特化、偏特化
上面对主版本模板类和全特化类进行了定义,那么偏特化就是介于二者之间的模板类,它的类名与主版本模板类相同,但是它的模板类型中,有被明确化的部分和没有被明确化的部分。1
2
3
4
5
6template <typename T1,typename T2>
class Class_Name
{};
template <>
class Class_Name<Type_Name,T2>
{};复杂点的偏特化
①.绝对类型特化
②.引用/指针类型特化
③.转化为另外一个类模板
HTTPS 原理
- 对称加密:
- 双方使用同一个 加密算法 对数据加密解密
- hack:加密算法被获取,则双方通信都直接被解密
- 非对称加密:
- 每个主机都有自己的 公钥、私钥 ,公钥加密的数据可以用私钥解密,私钥加密的数据可以用公钥解密,公钥可以随意给,私钥只能自己有
- 客户端获取服务器 公钥 ,通过服务器公钥加密数据后发送给服务器端
- 服务器端通过 私钥 解密客户端发来的数据
- 服务器端通过 私钥 对数据加密后发送给客户端
- 客户端通过服务器 公钥 解密服务器端发来的数据
- hack:黑客也能获取服务器公钥,可以用公钥解密服务器发来的数据
- 非对称+对称加密:
- 客户端先获取服务器端 公钥 ,后用 公钥 加密一个 对称秘钥 发送给服务器端
- 服务器端通过 私钥 ,解密后获得 对称秘钥,返回ok
- 之后双方都通过 对称秘钥 进行加密解密传输数据
- hack:中间人攻击,黑客一开始在客户端获取服务器的公钥时就介入,伪造一个假的公钥返回给客户端
- 单向验证
- 客户端发送请求:客户端支持的SSL/TLS版本、支持的对称加密算法、客户端生成随机数A
- 服务器端回应:通过CA的私钥加密的数字证书:SSL/TLS版本、对称加密算法、服务器公钥、服务器端生成的随机数B
- 客户端回应:通过浏览器写死的CA公钥,检查证书是否过期可信有效,并生成随机数C,用服务器的公钥加密后发送
- 服务器端:用服务器私钥解密数随机数C
- 至此,客户端服务器端都获得了随机数A、B、C,通过三个随机数生成一个 对称加密的密钥,作为后续数据传输时加密秘钥
- 双向验证
- 单向验证的基本流程+验证客户端信息
- 第二步服务器端回应添加:要求客户端提供客户端的证书
- 第三步客户端回应添加:客户端证书,客户端证书验证信息:客户端之前所有接收到的信息经过一个hash得到一个hash值,并通过客户端私钥进行加密得到数字签名
- 第四步服务器端:验证客户端证书,且用客户端证书中的公钥去验证数字签名
- https知乎讲解
- httpsB站讲解
ARP攻击
- ARP:通过IP地址以广播形式找到对应的MAC地址
- ARP泛洪攻击:发送大量ARP报文导致网络瘫痪
- ARP欺骗:伪造ARP响应,使得ARP缓存表中得到错误结果,进而导致后面数据传输都会发送到错误的主机
NAT原理
- NAT就是在局域网内部网络中使用内部地址,而当内部节点要与外部网络进行通讯时,就在网关处,将内部地址替换成公用地址,从而在外部公网上正常使用,
多态
- 编译时多态:函数重载、模板、
- 运行时多态:虚函数
类型转换
- static_cast:编译时检查
- dynamic_cast:运行时检查,用于在类的继承层次之间进行类型转换,它既允许向上转型(Upcasting),也允许向下转型(Downcasting)。向上转型是无条件的,不会进行任何检测,所以都能成功;向下转型的前提必须是安全的,只能用于含有虚函数的类,要借助 RTTI 进行检测,所有只有一部分能成功。
- const_cast:将const变量转为非const变量
MYSQL日志
- 错误日志:记录出错信息,也记录一些警告信息或者正确的信息。
- 查询日志:记录所有对数据库请求的信息,不论这些请求是否得到了正确的执行。
- 慢查询日志:设置一个阈值,将运行时间超过该值的所有SQL语句都记录到慢查询的日志文件中。
- 二进制日志:记录对数据库执行更改的所有操作。binlog
- 中继日志:从库去同步主库的一个中间文件
- 事务日志:redolog+undolog
>
- redo log是重做日志,undo log是回滚日志。
- undo log不是redo log的逆向过程,其实它们都算是用来恢复的日志:
- redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。
- undo用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录。
迭代器失效
- vector:insert和erase会使后面所有元素迭代器失效,如果发生了扩容,则会导致所有元素迭代器失效
deque:
- 在deque容器首部或者尾部插入元素不会使得任何迭代器失效。
- 在其首部或尾部删除元素则只会使指向被删除元素的迭代器失效。
- 在deque容器的任何其他位置的插入和删除操作将使指向该容器元素的所有迭代器失效。
链表型数据结构(list):对于list型的数据结构,使用了不连续分配的内存,删除运算使指向删除位置的迭代器失效,但是不会失效其他迭代器.解决办法两种,erase(*iter)会返回下一个有效迭代器的值,或者erase(iter++).
关联式数据结构(map, set,multimap,multiset):有序关联容器使用红黑树来存储数据,无序关联容器使用哈希表,插入不会使得任何迭代器失效;删除运算使指向删除位置的迭代器失效,但是不会失效其他迭代器.erase迭代器只是被删元素的迭代器失效,但是返回值为void,所以要采用erase(iter++)的方式删除迭代器。
进程三大状态
- 就绪(Ready)状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。 - 执行(Running)状态
当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。 - 阻塞(Blocked)状态
正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
- 进程三种状态间的转换
一个进程在运行期间,不断地从一种状态转换到另一种状态,它可以多次处于就绪状态和执行状态,也可以多次处于阻塞状态。
- 就绪→执行
处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。 - 执行→就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。 - 执行→阻塞
正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。 - 阻塞→就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
http状态码
- 1xx 继续
- 100 Continue 客户端应继续发送请求
- 2xx 成功
- 200 OK 请求成功
- 204 No Content 成功,但响应不包括任何实体
- 3xx 重定向
- 301 Moved Permanently 永久重定向
- 302 Found 临时重定向
- 304 Not Modified 资源没有更新,可以直接从缓存中获取
- 303 See Other 临时重定向,明确要求客户端应该采用GET方法获取资源。
- 307 Temporary Redirect 临时重定向,要求浏览器不会把重定向请求的POST方法改成GET方法。
- 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。
- 4xx 客户端错误
- 400 Bad Request 通用客户请求错误:语法有错误、缺少参数
- 401 Unauthorized 当前请求需要用户验证
- 403 Forbidden 请求被拒绝
- 404 Not Found 资源没有找到
- 407 Proxy Authentication Required 客户端需要先获得代理服务器的认证
- 5xx 服务器错误
- 500 Internal Server Error 通用服务器错误
- 502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应
- 503 Service Unavailable 由于临时的服务器维护或者过载,服务器当前无法处理请求,代表临时错误
- 504 Gateway Timeout 当服务器充当网关并且无法及时获得响应时,将给出此错误响应
- 505 HTTP Version Not Supported 服务器不支持,或者拒绝支持在请求中使用的HTTP版本。
CSRF攻击
- 跨站请求伪造:是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
Reactor模式、Proactor模式
- Reactor:要求主线程只负责监听文件描述符上是否有事件发生,有的话立即将该事件通知工作线程。返回就绪事件。
- Proactor:将所有I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。返回完成事件。
SQL中不会用到索引情况
- 索引列在表达式或函数中
- 联合索引中,非最左前缀
- 联合索引中,最左前缀,但是中间有范围查询,那么范围查询后面的列都用不到索引
- in 查询语句中多个值的数据类型不一致的情况
- 在无索引的列上使用了 or 那么有索引的列也用不上
- 查询的列中采用了!=
- 存在索引列的数据类型隐形转换
Redis的BGSAVE
- fork新建立一个进程,子进程对数据库生成rdb文件
- redis的fork采用了特殊的写时复制:按内存页表级别复制
- Redis创建子进程以后,根本不进行数据的copy,主进程与子线程是共享数据的。主进程继续对外提供读写服务。
- 虽然不copy数据,但是kernel会把主进程中的所有内存页的权限都设为read-only,主进程和子进程访问数据的指针都指向同一内存地址。
- 主进程发生写操作时,因为权限已经设置为read-only了,所以会触发页异常中断(page-fault)。在中断处理中,需要被写入的内存页面会复制一份,复制出来的旧数据交给子进程使用,然后主进程该干啥就干啥。
MYSQL的乐观锁
- 乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测
- 使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现 式。即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
- 在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp),和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
缓存穿透、击穿、雪崩
- 缓存穿透
- 缓存和数据库中都没有的数据,而用户不断发送请求
- 解决:布隆过滤器、缓存空对象(下次不会查缓存)
- 缓存击穿
- 缓存中没有但数据库中有的数据,发生于热点数据缓存过期时
- 解决:设置热点数据永不过期、使用互斥排队
- 缓存雪崩
- 缓存在同一时间内大量过期,接下来请求瞬间都落在了数据库中
- 解决:缓存随机时间过期、使用互斥排队、双层缓存:主缓存+备份缓存
- 缓存预热
- 系统上线后,主动将热点数据加入到缓存中
HTTP请求方法
- GET 申请获取资源,不会对服务器产生任何其他影响
- HEAD 和GET方法类似,不过仅要求服务器返回头部信息,而不需要任何传输任何实际内容
- POST 客户端向服务器提交数据的方法。服务器可能根据收到的数据动态创建新的资源,也可能更新原有资源
- PUT 上传某个资源
- DELETE 删除某个资源
- TRACE 要求目标服务器返回原始HTTP请求的内容
- OPTIONS
- CONNECT
- PATCH
I/O复用的水平触发LT和边沿触发ET
- epoll支持LT、ET,select和poll只支持LT
- 水平触发LT:只要事件是处于就绪态,就能被检测出来
- 边沿触发ET:只有事件从未就绪态切换到就绪态这个过程,才能被检测出来
select、poll、epoll对比
系统调用 | select | poll | epoll |
---|---|---|---|
事件集合 | 用户通过3个参数分别传入感兴趣的读、写、异常事件,内核通过对这些参数的在线修改来反馈其中的就绪事件,这使得用户每次调用select都要重置这3个参数 | 统一处理所有事件类型,因此只需一个事件集参数。用户通过pollfd.events传入感兴趣的时事件,内核通过修改pollfd.revents反馈其中就绪的事件 | 内核通过一个事件表直接管理用户感兴趣的所有事件。因此每次调用epoll_wait时,无需反复传入用户感兴趣的事件。epoll_wait系统调用的参数events仅用来反馈就绪的事件 |
应用程序索引就绪文件描述符的时间复杂度 | O(n) | O(n) | O(1) |
最大支持文件描述符 | 一般有最大值限制 | 理论上无最大值,实际局限于内存等 | 理论上无最大值,实际局限于内存等 |
工作模式 | LT | LT | LT、ET |
内核实现和工作效率 | 采用轮询方式来检测就绪事件,O(n) | 采用轮询方式来检测就绪事件,O(n) | 内核维护红黑树+链表,采用回调方式来检测就绪事件,O(1) |
cpp编译到可执行
- 预处理阶段:对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,生成预编译文件.i
- 编译阶段:将经过预处理后的预编译文件转换成特定汇编代码,生成汇编文件.s
- 汇编阶段:将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件.o
- 链接阶段:将多个目标文件及所需要的库连接成最终的可执行目标文件
inode
- inode
- 文件的字节数
- 文件拥有者的User ID
- 文件的Group ID
- 文件的读、写、执行权限
- 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
- 链接数,即有多少文件名指向这个inode
- 文件数据block的位置
- 用stat查看某个文件的inode信息
- inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。
- 每个inode都有一个号码,操作系统用inode号码来识别不同的文件。
swap分区
- Swap分区在系统的物理内存不够用的时候,把硬盘内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap分区中,等到那些程序要运行时,再从Swap分区中恢复保存的数据到内存中。