c++面经总结

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • C++基础
    • 1)内存泄露
      • 定义
      • 内存泄露检测
    • 2)C++、Java的联系与区别,包括语言特性、垃圾回收、应用场景等(java的垃圾回收机制)
    • 3)C和C++的区别
    • 4)指针和引用的区别:
    • 5)堆和栈的区别
    • 6)堆、栈谁更快一点
      • 7)new和delete是如何实现的,new 与 malloc的异同处
    • 8)既然有了malloc/free,C++中为什么还需要new/delete呢?
    • 9)Struct和class的区别
    • 10)define 和const的联系与区别(编译阶段、安全性、内存占用等)
    • 虚函数的作用
    • 模板的作用,解决了什么问题
    • 内敛函数
    • 内存管理
    • 重载和重写的区别:
    • 介绍面向对象的三大特性,并举例说明
    • 多态的实现
    • 虚函数的实现原理
    • 纯虚函数和虚函数的区别
    • 析构函数设置成虚函数的作用
    • 哪些函数不能是虚函数?
    • 虚函数的代价
  • 计算机网络
    • 拥塞控制和流量控制的区别:
    • TCP拥塞控制4种核心算法
    • TCP和UDP的区别:
    • 三次握手:
      • 三次握手的作用
      • 初始化序号值是固定的吗?
      • 为啥只有三次握手才能确认双方的接受与发送能力是否正常,而两次却不可以:
    • 四次挥手
  • 数据库
    • 1、 索引的底层实现
    • 2、 为什么使用B+tree ?不用红黑树,B树?
    • 3、 B树和B+树的区别:
    • 4、 B+树的优点:
    • 5、 数据库中事务的ACID(四大特性都要能够举例说明,理解透彻,比如原子性和一致性的关联,隔离性不好会出现的问题)
    • 6、 数据库引擎介绍,innodb和myisam的特点与区别
    • 7、 什么是索引,索引的作用,优缺点,什么时候可以使用索引,什么时候不可以使用索引(重点)
    • 8、脏读、不可重复读、幻读
    • 9 、数据库的隔离级别,mysql和Oracle的隔离级别分别是什么(重点)
    • 10 、数据库范式
    • 数据的锁的种类,加锁的方式
    • 什么是共享锁和排他锁
    • 数据库高并发的解决方案
    • 乐观锁与悲观锁
    • 乐观锁与悲观锁是怎么实现的
    • Mysql的优化(高频,索引优化,性能优化)
    • 索引最左前缀/最左匹配
    • MySQL怎么建立索引,怎么建立主键索引,怎么删除索引
    • 数据库的索引类型
    • SQL优化步骤
  • 操作系统
    • 进程和线程的区别联系:
    • 进程间通信的方式:
    • 协程
    • Linux理论上最多可以创建多少个进程?一个进程可以创建多少线程,和什么有关


C++基础

1)内存泄露

定义

申请了一块内存空间,使用完毕后没有释放。
表现方式是程序运行时间越长,占用内存越多,最终将内存耗尽,系统崩溃。由程序申请的一块内存就,且没有任何一个指针指向它就,即代表此内存泄露

内存泄露检测

windows平台下可以采用CRT库来检测和识别内存泄露
原理:内存分配通过CRT运行时实现,在分配和释放内存时分别做好记录,程序结束时对比内存分配与释放的记录即可检测是否有内存泄露
	具体检测方式 https://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html

2)C++、Java的联系与区别,包括语言特性、垃圾回收、应用场景等(java的垃圾回收机制)

1、都是面向对象的语言,C++执行是编译成可执行文件就,java是编译后在虚拟机上运行,因此java具有良好的跨平台特性,但执行效率没C++高
2、java只有引用,没有指针
3、C++内存管理由程序员手动管理,java的内存管理由虚拟机完成,有垃圾回收机制
4、都有构造函数,但java没有析构函数

3)C和C++的区别

C是面向过程的语言,C++是面向对象的语言。C++有三大特性,继承、封装、多态
内存管理方式不一样就,C只有malloc/free,C++除此之外还有new/delete
C++中还有重载和引用等概念,C中没有

4)指针和引用的区别:

1、指针是一个新的变量,指向的是另一个变量的地址,我们可以访问这个地址来修改这个变量,而引用是变量的别名,对引用操作就是对变量本身操作
2、指针可以有多级,引用只有一级
3、传参的时候指针需要解引用才能对参数进行修改,而使用引用可以直接对参数进行修改
4、指针大小一般为4字节,引用的大小取决于引用对象的大小
5、指针可以为空,引用不行

5)堆和栈的区别

从定义上,堆是由new和malloc开辟的一块内存,有程序员手动管理,栈是编译器自动管理的内存,存放函数的参数和局部变量
堆空间因为有频繁的分配释放操作,会产生内存碎片
堆生长空间向上,地址会越来越大,栈的生长空间向下,地址越来越小

6)堆、栈谁更快一点

	栈会更快一点,因为操作系统会对底层栈提供支持,会分配专门的寄存器存放栈的地址,出入栈也比较简单,同时有专门的指令执行,所以会更快一点。而堆是由C++函数库提供的,在分配内存的时候需要一定的算法寻找合适大小的内存,并且获取堆的内容要经过两次访问,一次访问指针,第二次蜂聚指针保存的地址访问内存,因而堆会更慢一点

7)new和delete是如何实现的,new 与 malloc的异同处

在new一个对象的时候,会调用malloc为对象分配内存,然后调用对象的构造函数,delete会调用对象的析构函数,然
后调用free回收内存
new和malloc都会分配空间,但是new还会调用对象的构造函数进行初始化,malloc需要给定空间
大小,而new只需要对象名

8)既然有了malloc/free,C++中为什么还需要new/delete呢?

malloc/free 和 new/delete都是用来申请和释放内存的
对于非内部数据对象(eg:类对象),只用malloc/free无法满足动态对象的要求。这是因为对象在创建的同时需要自
动执行构造函数,对象在消亡之前要自动执行析构函数,而由于malloc/free是库函数而不是运算符,不在编译器的
控制权限内,也就不能自动执行构造函数和析构函数。因此,不能将执行构造函数和析构函数的任务强加给
malloc/free。所以,在c++中需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理和释放内
存工作的运算符delete。

9)Struct和class的区别

默认访问权限不同:Struct默认访问权限为public,而Class的默认访问权限是private(针对成员变量)

继承权限也不一致,Struct的默认继承是public,而class的默认继承是private

class 可以用作模板,而Struct不能

10)define 和const的联系与区别(编译阶段、安全性、内存占用等)

联系:它们都是定义常量的一种方法。

区别:

define定义的常量没有类型,只是进行了简单的替换,可能会有多个拷贝,占用的内存空间大,const定义
的常量是有类型的,存放在静态存储区,只有一个拷贝,占用的内存空间小。

define定义的常量是在预处理阶段进行替换,而const在编译阶段确定它的值。

define不会进行类型安全检查,而const会进行类型安全检查,安全性更高。

const可以定义函数而define不可以。

虚函数的作用

允许派生类重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数

模板的作用,解决了什么问题

可以编写与类型无关的代码,比如两个整数相加减和两个浮点数相加减,这时我们可以用模板来忽略类型
的不同,只需编写一套代码即可

内敛函数

内敛函数:需要在函数名前放inline ,在类中定义的函数都是内敛函数
(引入内敛函数的目的主要是解决程序中函数调用的效率问题,程序在编译的时候会讲程序中出现的内敛函数表达式用内敛函数的函数体进行替换)

内存管理

c++内存主要分为5个区:栈区、堆区、自由存储区、全局/静态存储区、常量存储区

a) 栈:内存由编译器在需要时自动分配和释放。通常用来存储局部变量和函数参数。

b) 堆:内存使用new进行分配使用delete或delete[]释放。如果未能对内存进行正确的释放,会造成内存泄漏。但在程序结束时,会由操作系统自动回收。

c) 自由存储区:使用malloc进行分配,使用free进行回收。和堆类似。

d) 全局/静态存储区:存储全局变量和静态变量

e) 常量存储区:存储常量,不允许被修改。

重载和重写的区别:

重载是指函数名相同,参数列表不同的函数实现方法
重写是指函数名相同,参数列表也相同,只是函数实现方法不同,一般用于子类继承父类方法时对父类方法的重写

介绍面向对象的三大特性,并举例说明

封装、继承和多态
封装隐藏了类的实现细节和成员数据,实现了代码模块化比如类里面的public和private
继承使得子类可以复用父类的成员和方法,实现了代码重用
多态则是“一个接口,多个实现”,通过父类调用子类成员,实现了接口重用,比如父类指针指向子类的对象

多态的实现

C++多态包括编译时多态和运行时多态,编译时体现在函数重载和模板上,运行时体现在虚函数上
虚函数:在基类的函数前加virtual关键字,在派生类中重写该函数,运行时根据对象的实际类型来调用相应的函数(如果对象是基类,就调用基类函数,如果是派生类,就调用派生类函数)

虚函数的实现原理

C++虚函数是实现多态的机制。虚函数通过虚函数表实现,虚函数表中会存放每个虚函数地址,类的实例在调用函数时会在虚函数表中寻找函数地址调用,如果子类覆盖了父类的函数,则子类的虚函数表会指向子类实现的函数地址,否则指向父类函数的地址,一个类中所用的实例都共享同一种虚函数表

纯虚函数和虚函数的区别

纯虚函数是在基类中声明的虚函数,在基类中没有定义,但要求在派生类都有定义自己的实现方法
在基类中实现纯虚函数的方法是在函数原型后加“=0”
纯虚函数只有定义,没有实现,虚函数既有定义,也有实现
函数纯虚函数的类不能定义对象,含有虚函数的类可以定义对象

析构函数设置成虚函数的作用

如果不设置成虚函数,容易造成内存泄露

哪些函数不能是虚函数?

构造函数:原因暂时还不懂
内联函数:内联函数可以在编译阶段进行函数体的替换,而虚函数需要在运行期间才能确定
静态函数:静态函数不属于对象而属于类,因为静态成员没有this指针,所以无法访问对象的虚表指针,也就无法访问虚函数表,将静态函数设成虚函数没有意义
友元函数:不属于类,不能被继承,没有继承就没有虚函数的说法
类外的普通函数:不具备继承特性,也没有虚函数的说法

虚函数的代价

带有虚函数的每个类会产生一个虚函数表,用来存储虚成员的指针
带有虚函数的没有类都会有一个指向虚函数表的指针

计算机网络

拥塞控制和流量控制的区别:

流量控制解决的是发送方和接收方速率不匹配的问题;拥塞控制解决的是避免网络资源被耗尽的问题。流量控制是通过滑动窗口来实现的;拥塞控制是通过拥塞窗口来实现的。(拥塞控制是全局性的过程,流量控制是点对点的过程)

TCP拥塞控制4种核心算法

慢开始、拥塞避免、快速重传、快速恢复
c++面经总结_第1张图片
1、慢开始:慢开始算法的主要原理就是,一开始先不发送大量的数据,先探测一下网络的拥塞程度,也就是从小到大(指数级)逐渐增加拥塞窗口(cwnd)的大小。为防止拥塞窗口的增长引起网络拥塞,需要设置一个慢开始门限(ssthresh),当cwnd>ssthresh时,改用拥塞避免算法。
2、拥塞避免算法:主要作用是让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1(按线性规律增长),而不是加倍。当网络发送拥塞时,ssthresh值会更新为拥塞前ssthresh值的一半,cwnd重新设置为1(再指数增长,直到cwnd = ssthress时,开始执行拥塞避免算法,cwnd按线性规律增长)。
3、快重传算法:当发送端连续收到三个重复的ack时,表示该数据段已经丢失,需要重发。此时慢启动阈值ssth变为原来一半,拥塞窗口cwnd变为ssth+3,然后+1+1的发(每一轮rtt+1)。
4、快恢复算法:当超过设定的时间没有收到某个报文段的ack时,表示网络拥塞,慢启动阈值ssth变为原来一半,拥塞窗口cwnd=1,进入慢启动阶段。

TCP和UDP的区别:

1、TCP面向连接,UDP无连接
2、TCP提供可靠传输,UDP尽最大努力交付,即不保证可靠交付
3、TCP面向字节流(把数据看成一连串无结构的字节流),UDP面向报文(没有拥塞控制,会出现丢包)
4、TCP对资源的需求高于UDP,速度慢于UDP
5、TCP点对点传输、UDP可以一对多
6、TCP首部开销为20字节,高于UDP的8字节

三次握手:

c++面经总结_第2张图片
第一次握手:客户端发给服务端SYN报文(SYN=1),并指明客户端自己的初始化序号(seq = x),此时客户端处于SYN_SENT状态
第二次握手:服务端收到客户端的SYN报文,会以自己的报文作为应答(SYN = 1),并且指定自己的初始化序号(seq =y),同时会把客户端的seq值加1作为ACK的值,表明收到了客户端的SYN,此时,服务器端处于SYN_RCVD
第三次握手:客户端收到SYN报文后,会发送一个ACK报文,也是把服务器端的seq+1作为ack的值,表明已经 收到服务器端的SYN报文,此时客户端处于established状态。当服务器端也收到ACK报文时,状态变为established状态,双方建立连接

三次握手的作用

1、确认双方的收发状态是否正常
2、指定双方的初始化序号,为后面的可靠传输做准备

初始化序号值是固定的吗?

不是,动态生成的,如果ISN值固定容易遭受攻击

为啥只有三次握手才能确认双方的接受与发送能力是否正常,而两次却不可以:

为了防止已失效的请求报文段突然又传送到了TCP服务器进程因而导致错误,两次只能保证单向传输可靠,至少三次才可以保证双向传输可靠
当客户端向服务器端发送一个连接请求时,由于某种原因长时间驻留在网络节点中,无法到达服务器端,由于TCP的超时重传机制,当客户端在特定的时间内没有收到服务器端的的确认应答时,就会重新向服务器端发送连接请求,该请求到达服务器端并建立连接,进行数据传输,当数据传输完成时,释放了TCP连接。
但如果这时拥塞解除,第一次的连接请求报文段延迟了一段时间后到达了服务器端,本来这是一个很早到达的失效的报文段,但是服务器端收到了该链接请求后误以为是客户端重新又发起了一次连接请求,于是服务器端发出确认应答报文段,并表示同意建立连接。如果没有第三次握手,由于服务器端发送了确认应答信息,则表示新的连接建立成功,但是客户端并没有向服务器端发送任何建立请求,客户端将忽略服务器端的确认报文,更不会发送任何请求或数据。而服务器端认为建立成功了,并一直在等待建立连接,直到超出计数器的设定值,则认为服务器端出现了异常,并关闭此链接。这个等待的过程中,浪费了服务器端的资源。如果是三次握手将不会出现不该建立的连接。

四次挥手

1、第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态。
2、第二次握手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT状态。
3、第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
4、第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态。服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
c++面经总结_第3张图片
TIME_WAIT:为什么客户端发送 ACK 之后不直接关闭,而是要等一阵子才关闭?
要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端,客户端再次收到 ACK 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。TIME_WAIT 持续的时间至少是一个报文的来回时间。一般会设置一个计时,如果过了这个计时没有再次收到 FIN 报文,则代表对方成功就是 ACK 报文,此时处于 CLOSED 状态。

数据库

1、 索引的底层实现

索引的底层实现使用B+tree 索引结构

2、 为什么使用B+tree ?不用红黑树,B树?

首先红黑树本质上是二叉树,相对于二叉树而言,B+树层级更少,搜索效率会更高
对于B树,无论叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针会跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低
c++面经总结_第4张图片

3、 B树和B+树的区别:

叶子节点连接方式:B+树中所有叶子节点都是通过指针连接在一起,而B树不会
分支结点的构造不同:B+树的分支结点仅仅存储着关键字信息和儿子的指针(这里的指针指的是磁盘块的偏移量),也就是说内部结点仅仅包含着索引信息。
查询不同:B树在找到具体的数值以后,则结束,而B+树则需要通过索引找到叶子结点中的数据才结束,也就是说B+树的搜索过程中走了一条从根结点到叶子结点的路径。
数据存储位置不同:对于B+树,数据只存储在叶子节点,而B树数据不仅存储在叶子节点,还会存储在内部节点

4、 B+树的优点:

非叶子节点上不会带有指向记录的指针,这样,一个块中可以容纳更多的索引项,可以降低树的高度
叶子节点通过指针连接,范围扫描将十分简单,这对于B树是无法实现的

5、 数据库中事务的ACID(四大特性都要能够举例说明,理解透彻,比如原子性和一致性的关联,隔离性不好会出现的问题)

原子性:(Atomicity):事务是不可分割的最小单元,要么全部成功,要么全部失败
一致性:(consistency):事务完成时,必须使所有的数据保持一致状态
隔离性:(Isolation): 数据库系统提供的隔离机制,保证事务不受外部并发操作影响下运行
持久性:(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的

6、 数据库引擎介绍,innodb和myisam的特点与区别

InnoDB : InnoDB是mysql的默认引擎,支持事务和外键,支持容灾恢复。适合更新频繁和多并发的表 行级锁
MyISAM : 插入和查询速度比较高,支持大文件,但是不支持事务,适合在web和数据仓库场景下使用 表级锁
MEMORY : memory将表中的数据保存在内存里,适合数据比较小而且频繁访问的场景

7、 什么是索引,索引的作用,优缺点,什么时候可以使用索引,什么时候不可以使用索引(重点)

索引:是对数据库表中的一列或者多列进行排列的一种结构,使用索引可以快速访问数据库中特定的信息(类似于书的目录,可以提供你需要查询信息的页数)
优点:1、加快数据检索速度 2、通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性
缺点: 1、创建和维护索引需要耗费时间,随着数据量的增加而增加 2、索引需要占用物理空间,数据量越大,占用空间越多3、会降低表的增删改效率,每次增删改索引,都需要动态维护
什么时候需要创建索引
1、主键自动建立唯一索引
2、频繁作为查询条件的字段应该建立索引
3、经常需要排序的列或者经常需要范围查找的列
什么时候不需要创建索引
1、频繁更新的字段不适合创建索引(需要一直更新记录)
2、where条件里用不到的字段
3、表记录太少或者经常增删改的表

8、脏读、不可重复读、幻读

脏读是指一个事务在处理过程中读取了另一个还没提交的事务的数据。
比如A向B转账100,A的账户减少了100,而B的账户还没来得及修改,此时一个并发的事务访问到了B的账户,就是脏读
不可重复读:不可重复读是对于数据库中的某一个字段,一个事务多次查询却返回了不同的值,这是由于在查询的间隔中,该字段被另一个事务修改并提交了。
比如A第一次查询自己的账户有1000元,此时另一个事务给A的账户增加了1000元,所以A再次读取他的账户得到了2000的结果,跟第一次读取的不一样。
不可重复读与脏读的不同之处在于,脏读是读取了另一个事务没有提交的脏数据,不可重复读是读取了已经提交的数据,实际上并不是一个异常现象。
幻读:事务多次读取同一个范围的时候,查询结果的记录数不一样,这是由于在查询的间隔中,另一个事务新增或删除了数据。
比如A公司一共有100个人,第一次查询总人数得到100条记录,此时另一个事务新增了一个人,所以下一次查询得到101条记录。
不可重复度和幻读的不同之处在于,幻读是多次读取的结果行数不同,不可重复度是读取结果的值不同。

避免不可重复读需要锁行,避免幻读则需要锁表
脏读,不可重复读和幻读都是数据库的读一致性问题,是在并行的过程中出现的问题,必须采用一定的隔离级别解决

9 、数据库的隔离级别,mysql和Oracle的隔离级别分别是什么(重点)

读未提交 Read Uncommitted 最低级别的隔离,不能解决以上问题
读已提交 Read committed: 可以避免脏读的发生
可重复读 Reapeatable read 可以避免脏读和不可重复读。 通过锁行来实现
串行化 Serializaion 最严格的事务隔离机制,要求所有事务被串行执行,可以避免以上所有问题。 通过锁表来实现

10 、数据库范式

第一范式:属于第一范式关系的所有属性都不可再分,即数据项不可(第一范式强调数据表的原子性,是其他范式的基础)
第二范式:(满足第一范式)(确保表中的每列都和主键相关)(例如学生表中的学号与姓名,年龄等,但学号与班主任、教室等不够成第二范式)
第三范式:(确保非主键的列没有传递依赖) (比如一张学生信息表,主键是(学号)列包括 姓名,班级,班主任 就不符合第三范式,因为非主键的列中 班主任 依赖于 班级)
BCNF范式(确保主键之间没有传递依赖)(当主键是复合主键时,任意主键直接不存在依赖关系,谁也不能决定谁)

数据的锁的种类,加锁的方式

按照类型来分有乐观锁和悲观锁
根据粒度来分有行级锁,页级锁,表级锁(粒度一个比一个大)
根据作用来分有共享锁(读锁)和排他锁(写锁)

什么是共享锁和排他锁

共享锁是读操作的时候创建的锁,一个事务对数据加上共享锁之后,其他事务只能对数据再加共享锁,不能进行写操作直到释放所有共享锁。
排他锁是写操作时创建的锁,事务对数据加上排他锁之后其他任何事务都不能对数据加任何的锁(即其他事务不能再访问该数据)

数据库高并发的解决方案

1、在web服务框架中加入缓存。在服务器与数据库层之间加入缓存层,将高频访问的数据存入缓存中,减少数据库的读取负担。
2、增加数据库索引。提高查询速度。
3、主从读写分离,让主服务器负责写,从服务器负责读
4、将数据库进行拆分,使得数据库的表尽可能小,提高查询的速度
5、使用分布式架构,分散计算压力

乐观锁与悲观锁

一般的数据库都会支持并发操作,在并发操作中为了避免数据冲突,所以需要对数据上锁,乐观锁和悲观锁就是两种不同的上锁方式。
悲观锁假设数据在并发操作中一定会发生冲突,所以在数据开始读取的时候就把数据锁住。而乐观锁则假设数据一般情况下不会发生冲突,所以在数据提交更新的时候,才会检测数据是否有冲突。

乐观锁与悲观锁是怎么实现的

悲观锁有行级锁和页级锁两种形式。行级锁对正在使用的单条数据进行锁定,事务完成后释放该行数据,而页级锁则对整张表进行锁定,事务正在对该表进行访问的时候不允许其他事务并行访问。

悲观锁要求在整个过程中一直与数据库有一条连接,因为上一个事务完成后才能让下一个事务执行,这个过程是串行的。

乐观锁有三种常用的实现形式:

	一种是在执行事务时把整个数据都拷贝到应用中,在数据更新提交的时候比较数据库中的数据与新数据,如果两个数据一摸一样则表示没有冲突可以直接提	交,如果有冲突就要交给业务逻辑去解决。
	一种是使用版本戳来对数据进行标记,数据每发生一次修改,版本号就增加1。某条数据在提交的时候,如果数据库中的版本号与自己的一致,就说明数据没有发生修改,否则就认为是过期数据需要处理。
	最后一种采用时间戳对数据最后修改的时间进行标记。与上一种类似

Mysql的优化(高频,索引优化,性能优化)

高频访问:
分表分库:将数据库表进行水平拆分,减少表的长度
增加缓存: 在web和DB之间加上一层缓存层
增加数据库的索引:在合适的字段加上索引,解决高频访问的问题
并发优化:
主从读写分离:只在主服务器上写,从服务器上读
负载均衡集群:通过集群或者分布式的方式解决并发压力

索引最左前缀/最左匹配

顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边

MySQL怎么建立索引,怎么建立主键索引,怎么删除索引

MySQL建立索引有两种方式:用alter table或者create index
Mysql删除索引同样也有两种方式:alter table 和 drop index

数据库的索引类型

数据库的索引类型分为逻辑分类和物理分类

逻辑分类:

主键索引: 当关系表中定义主键时会自动创建主键索引。每张表中的主键索引只能有一个,要求主键中的每个值都唯一,即不可重复,也不能有空值。
唯一索引: 数据列不能有重复,可以有空值。一张表可以有多个唯一索引,但是每个唯一索引只能有一列。如身份证,卡号等。
普通索引: 一张表可以有多个普通索引,可以重复可以为空值
全文索引: 可以加快模糊查询,不常用
物理分类:

聚集索引:(聚簇索引) 数据在物理存储中的顺序跟索引中数据的逻辑顺序相同,比如以ID建立聚集索引,数据库中id从小到大排列,那么物理存储中该数据的内存地址值也按照从小到大存储。一般是表中的主键索引,如果没有主键索引就会以第一个非空的唯一索引作为聚集索引。一张表只能有一个聚集索引。
非聚集索引: 数据在物理存储中的顺序跟索引中数据的逻辑顺序不同。非聚集索引因为无法定位数据所在的行,所以需要扫描两遍索引树。第一遍扫描非聚集索引的索引树,确定该数据的主键ID,然后到主键索引(聚集索引)中寻找相应的数据。

SQL优化步骤

1、通过慢查日志等定位那些执行效率较低的SQL语句
2、explain 分析SQL的执行计划
3、show profile 分析
4、trace
5、确定问题并采用相应的措施

操作系统

进程和线程的区别联系:

资源分配:进程是对运行程序的封装,是资源分配的最小单位;线程是进程的子任务,线程不分配资源,但可以使用进程的资源
系统开销:在创建和撤销进程时,都有为其分配和回收资源,导致系统的开销要大于线程
并发性:进程之间互不影响,但线程的崩溃会导致其他线程也崩溃

进程间通信的方式:

管道:管道分为匿名管道和有名管道,通信方式为半双工通信,匿名管道只能用于父子进程通信(具有亲缘关系的进程间通信)
有名管道则运行无亲缘关系进程间的通信
消息队列:用于同一台机器上的进程间通信,和管道很类似,是在一个系统内核中用来保存消息的队列(以消息链表的形式出现)
可以有选择的接收消息
共享内存(进程间通信最快的方式):多个进程访问同一块内存空间来进行通信,不同进程之间可以看到对方进程中对内存中数据的更新
信号量:主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段,可以用来控制多个进程对共享资源的访问
信号:是Linux系统中用于进程之间通信或操作的一种机制,信号可以在任何时候发送给某一进程,而无须知道该进程的状态。如果该进程并未处于执行状态,则该信号就由内核保存起来,知道该进程恢复执行并传递给他为止。
套接字:也是一种进程间通信机制,可用于不同设备及其间的进程通信

协程

协程是一种比线程更轻量级的存在。一个线程可以拥有多个协程。协程就是子程序在执行时中断并转去执行别的子程序,在适当的时候又返回来执行。这种子程序间的跳转不是函数调用,也不是多线程执行,所以省去了线程切换的开销,效率很高,并且不需要多线程间的锁机制。

Linux理论上最多可以创建多少个进程?一个进程可以创建多少线程,和什么有关

  1. 因为进程的pid是用pid_t来表示的,pid_t的最大值是32768.所以理论上最多有32768个进程
    至于线程。进程最多可以创建的线程数是根据分配给调用栈的大小,以及操作系统(32位和64位不同)共同决定的。Linux32位下是300多个
    (我们知道了创建一个线程会占用多少内存,这取决于分配给线程的调用栈大小,可以用ulimit -s命令来查看大小,显示的单位是KB(一般常见的有10M或者是8M,也可以临时修改)。我们还知道,一个进程的虚拟内存是4G,在Linux32位平台下,内核分走了1G,留给用户用的只有3G,于是我们可以想到,创建一个线程占有了10M内存,总共有3G内存可以使用。于是可想而知,最多可以创建差不多300个左右的线程)
    https://blog.csdn.net/lyl194458/article/details/88768526

你可能感兴趣的:(c++,开发语言,后端)