一、C/C++
-
C 和 C++的特点与区别?
C 语言特点:- 作为一种面向过程的结构化语言,易于调试和维护;
- 表现能力和处理能力极强,可以直接访问内存的物理地址;
- C 语言实现了对硬件的编程操作,也适合于应用软件的开发;
- C 语言还具有效率高,可移植性强等特点。
C++语言特点:
- 在 C 语言的基础上进行扩充和完善,使 C++兼容了 C 语言的面向过程特点,又成为了一种面向对象的程序设计语言;
- 可以使用 抽象数据类型进行基于对象的编程;可以使用多继承、多态进行面向对象的编程;
- 可以担负起以模版为特征的泛型化编程。
- 可以使用STL
-
volitale的作用:
它用来解决变量在共享环境下容易出现读取错误的问题。volitale 是拒绝编译器的优化,直接从内存读取数据的关键字。
使用实例:中断程序访问到的变量;多线程多任务共享的变量。 -
extern C 的作用:
利用extern “c”使c兼容c++,因为函数链接得到的符号不同。 -
引用和指针区别:
- 指针是一个实体,而引用仅是个别名;
- 引用使用时无需解引用(*),指针需要解引用;
- 引用只能在定义时被初始化一次,之后不可变;指针可变;
- 引用不能为空,指针可以为空;
-
关于静态内存分配和动态内存分配的区别及过程
- 静态内存分配是在编译时完成的,不占用CPU资源;动态分配内存运行时完成,分配与释放需要占用CPU资源;
- 静态内存分配是在栈上分配的,动态内存是堆上分配的,malloc函数;
-
static const等等的用法
- const的用法: 在定义的时候必须进行初始化;指针可以是const 指针,也可以是指向const对象的指针;定义为const的形参,即在函数内部是不能被修改的。
- static的用法:在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变;在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
-
预处理标识#error意义?
抛出错误,标识外部宏是否被定义 -
用struct关键字与class关键定义类以及继承的区别
struct 继承默认public,成员默认public。类成员默认private,默认private继承 -
虚函数与纯虚函数区别
- 虚函数在子类里面也可以不重载的;但纯虚必须在子类去实现
- 带纯虚函数的类叫虚基类也叫抽象类,这种基类不能直接生成对象,只能被继承,重写虚函数后才能使用,运行时动态动态绑定
-
深拷贝与浅拷贝
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。 -
多态实现机制?多态作用?两个必要条件?
C++中多态机制主要体现在两个方面,一个是函数的重载,一个是接口的重写。接口多态指的是“一个接口多种形态”。每一个对象内部都有一个虚表指针,该虚表指针被初始化为本类的虚表。所以在程序中,不管你的对象类型如何转换,但该对象内部的虚表指针是固定的,所以呢,才能实现动态的对象函数调用,这就是C++多态性实现的原理。
多态的基础是继承,需要虚函数的支持,简单的多态是很简单的。子类继承父类大部分的资源,不能继承的有构造函数,析构函数,拷贝构造函数,operator=函数,友元函数等等
作用:
隐藏实现细节,代码能够模块化;2. 接口重用:为了类在继承和派生的时候正确调用。
必要条件:
一个基类的指针或者引用指向派生类的对象;虚函数 -
多重继承有什么问题? 怎样消除多重继承中的二义性?
函数存在二义性,同名函数可能存在于多个基类。解决:用成员限定符和虚函数解决
可能多次继承一个间接基类。解决:虚继承 -
什么叫静态关联,什么叫动态关联
多态中,静态关联是程序在编译阶段就能确定实际执行动作,程序运行才能确定叫动态关联 -
什么叫智能指针?常用的智能指针有哪些?智能指针的实现?
智能指针是一个存储指向动态分配(堆)对象指针的类,构造函数传入普通指针,析构函数释放指针。栈上分配,函数或程序结束自动释放,防止内存泄露。使用引用计数器;
unique_ptr, 不支持复制和赋值,直接赋值会编译出错。
shared_ptr,基于引用计数的智能指针。可随意赋值,直到内存的引用计数为0的时候这个内存会被释放。 -
智能指针的循环引用怎么回事?如何解决?
两个指针相互指向对方,析构时由于引用计数不为0导致内存泄漏。
方法:采用弱引用的weak_ptr即可。弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。 -
智能指针是线程安全的吗?
shared_ptr 本身不是 100% 线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是 -
函数的重载
重载是在不同类型上作不同运算而又用同样的名字的函数。重载函数至少在参数个数或参数类型上有所不同。 -
面向对象的三个基本特征
- 封装:将客观事物抽象成类,每个类对自身的数据和方法实行
- 继承
- 多态:允许一个基类的指针或引用指向一个派生类对象
虚函数时,基类指针指向派生对象,得到的是派生类函数;override时则是基类函数。
-
纯虚函数如何定义?含有纯虚函数的类称为什么?为什么析构函数要定义成虚函数?
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。
含有纯虚函数的类称为抽象类在很多情况下,基类本身生成对象是不合情理的。
如果析构函数不是虚函数,那么释放内存时候,编译器会使用静态联编,认为p就是一个基类指针,调用基类析构函数,这样子类对象的内存没有释放,造成内存泄漏。定义成虚函数以后,就会动态联编,先调用子类析构函数,再基类 -
C++中哪些不能是虚函数?
- 普通函数只能重载,不能被重写,因此编译器会在编译时绑定函数。
- 构造函数是知道全部信息才能创建对象,然而虚函数允许只知道部分信息。
- 内联函数在编译时被展开,虚函数在运行时才能动态绑定函数。
- 友元函数 因为不可以被继承。
- 静态成员函数 只有一个实体,不能被继承。父类和子类共有。
-
内联函数与宏定义的区别?
内联函数是用来消除函数调用时的时间开销。频繁被调用的短小函数非常受益。
A. 宏定义不检查函数参数,返回值什么的,只是展开,相对来说,内联函数会检查参数类型,所以更安全。
B. 宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的 -
explicit是干什么用的 ?
阻止隐式类型转换。 -
new与malloc的区别,delete和free的区别?
- malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符
- new能够自动分配空间大小,malloc传入参数。
- new/delete能进行对对象进行构造和析构函数的调用进而对内存进行更加详细的工作,而malloc/free不能。
- C++允许对new重载,malloc不允许重载
-
既然new/delete的功能完全覆盖了malloc/free,为什么C++还保留malloc/free呢?
因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。 -
深入谈谈堆和栈
- 分配和管理方式不同 :
堆是动态分配的,其空间的分配和释放都由程序员控制。
栈由编译器自动管理。栈有两种分配方式:静态分配和动态分配。静态分配由编译器完成,比如局部变量的分配。动态分配由alloca()函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无须手工控制。 - 产生碎片不同
对堆来说,频繁的new/delete或者malloc/free势必会造成内存空间的不连续,造成大量的碎片,使程序效率降低。
对栈而言,则不存在碎片问题,因为栈是先进后出的队列,永远不可能有一个内存块从栈中间弹出。 - 生长方向不同
堆是向着内存地址增加的方向增长的,从内存的低地址向高地址方向增长。
栈是向着内存地址减小的方向增长,由内存的高地址向低地址方向增长。
- 分配和管理方式不同 :
-
sizeof
sizeof计算规则 -
析构函数可以抛出异常吗?为什么不能抛出异常?除了资源泄露,还有其他需考虑的因素吗?
C++标准指明析构函数不能、也不应该抛出异常。
如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。 -
拷贝构造函数作用及用途?什么时候需要自定义拷贝构造函数?
一般如果构造函数中存在动态内存分配,则必须定义拷贝构造函数。否则,可能会导致两个对象成员指向同一地址,出现“指针悬挂问题”。 -
动态链接库的两种使用方法及特点?
- 载入时动态链接,模块非常明确调用某个导出函数,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向系统提供了载入DLL时所需的信息及DLL函数定位。
- 运行时动态链接。
-
inline 函数
在编译时将调用函数的地方用函数的代码直接替换下来,这样在程序运行时就不用再提供额外的参数传递所消耗的时间与空间,于是提高了程序的执行效率。 -
vector/map迭代器失效问题
在插入或删除元素的时候会出现。 -
vector中erase()和remove()的区别?
erase函数可以用于删除vector容器中的一个或者一段元素。在进行单个元素删除后,传入的迭代器指向不变,仍然指向被删除元素的位置。
vector中remove的作用是将范围内为val的值都remove到后面,返回新的end()值(非val部分的end),但传入的原vector的end并没有发生改变,因此size也就没有变化。 -
模板特化与偏特化
特化是针对特殊类型单独处理。比如bool,指针等。
偏特化就是指需要根据模板的某些但不是全部的参数进行特化。 -
常见设计模式
- 创建型:工厂模式,工厂类下有多个工厂子类,不同子类下有多个产品。客户不需要了解实现细节,只需要到对应工厂提取对应产品。缺点是扩建麻烦。
- 结构型:桥接模式,分离抽象和实例。难度在于如何精确分离抽象和实例。
- 行为型:策略模式,环境 + 抽象策略 = 具体策略
-
函数压栈顺序
main在调用函数的时候,函数f中的参数是先压l后压g(从左往右),而函数f的形参则是 从右往左 压栈的。 -
STL的快速排序:
二、服务器编程
-
多线程和多进程的区别
- 进程数据是分开的:共享复杂,需要用IPC,同步简单;多线程共享进程数据:共享简单,同步复杂
- 进程创建销毁、切换复杂,速度慢 ;线程创建销毁、切换简单,速度快
- 进程占用内存多, CPU利用率低;线程占用内存少, CPU利用率高
- 进程间不会相互影响 ;进程一个线程挂掉将导致整个进程挂掉
线程所私有的:
线程id、寄存器的值、栈、线程的优先级和调度策略、线程的私有数据、信号屏蔽字、errno变量 -
公平锁和非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁在高并发的情况下,有可能会造成优先级反转或者饥饿现象 -
线程池了解吗?他是怎么实现的?
- 目的是减少线程创建和销毁的开销,以及减少线程创建的数目
- 提交一个任务到线程池中,线程池的处理流程如下:
- 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
- 线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
- 判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
-
网络编程设计模式,reactor/proactor/半同步半异步模式?
reactor模式:同步阻塞I/O模式,注册对应读写事件处理器,等待事件发生进而调用事件处理器处理事件。和epoll方式有关。 proactor模式:异步I/O模式。Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的,Reactor中需要应用程序自己读取或者写入数据,Proactor模式中,应用程序不需要进行实际读写过程。
半同步半异步模式:
上层的任务(如:数据库查询,文件传输)使用同步I/O模型,简化了编写并行程序的难度。而底层的任务(如网络控制器的中断处理)使用异步I/O模型,提供了执行效率。 -
connect可能会长时间阻塞,怎么解决?
- 使用定时器;(最常用也最有效的一种方法)
- 采用非阻塞模式:设置非阻塞,返回之后用select检测状态。
-
keepalive 是什么东西?如何使用?
keepalive,是在TCP中一个可以检测死连接的机制。- 如果主机可达,对方就会响应ACK应答,就认为是存活的。
- 如果可达,但应用程序退出,对方就发RST应答,发送TCP撤消连接。
- 如果可达,但应用程序崩溃,对方就发FIN消息。
- 如果对方主机不响应ack, rst,继续发送直到超时,就撤消连接。
-
socket什么情况下可读?
- 有数据可读
- 连接的读一半关闭,收到FIN信号,仍需要读剩下的数据
- socket收到了对方的connect请求已经完成的连接数为非0
-
socket什么情况可写
- 有空间能写
- 连接的写这一半关闭.主动发送FIN信号后仍需要写数据
-
udp调用connect有什么作用?
- 因为UDP可以是一对一,多对一,一对多,或者多对多的通信,所以每次调用sendto()/recvfrom()时都必须指定目标IP和端口号。通过调用connect()建立一个端到端的连接,就可以和TCP一样使用send()/recv()传递数据,而不需要每次都指定目标IP和端口号。但是它和TCP不同的是它没有三次握手的过程。
- 可以通过在已建立连接的UDP套接字上,调用connect()实现指定新的IP地址和端口号以及断开连接。
-
TCP与UDP的区别:
- 基于连接与无连接;
- 对系统资源的要求(TCP较多,UDP少);
- UDP程序结构较简单;
- 流模式与数据报模式 ;
- TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。TCP发送的数据包是按序号发送,有确认机制和丢失重传机制,而udp是不可靠的发送机制,发送的对应端口的数据包不是按顺序发送的。
三 Linux操作系统
- linux和windows区别
linux | windows | |
---|---|---|
文件扩展名 | 根据文件头部识别 | 根据扩展名识别 |
图形化界面 | 不强制,图形环境并未集成到linux中 | 强制使用 |
命令行 | 区分大小分 | 不区分大小写 |
-
共享内存段被映射进进程空间之后,存在于进程空间的什么位置?共享内存段最大限制是多少?
将一块内存映射到两个或者多个进程地址空间。通过指针访问该共享内存区。一般通过mmap将文件映射到进程地址共享区。存在于进程数据段,最大限制是0x2000000Byte -
动态链接和静态链接的区别?
动态链接是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在可执行文件运行时再装入;而静态链接是把所有的代码和数据都复制到本模块中,运行时就不再需要库了 -
说出你所知道的各类linux系统的各类同步机制(重点),什么是死锁?如何避免死锁(每个技术面试官必问)
- 原子操作
- 信号量(其实就是互斥锁也就是锁的机制)
- 读写信号量(就是读写锁)
- 自旋锁 、内核锁 、顺序锁
死锁就是几个进程申请资源,出现了循环等待的情况
避免死锁的方法:
资源是互斥的 ;不可抢占 ;占有且申请;动态检查系统安全性
-
系统如何将一个信号通知到进程?
内核给进程发送信号,是在进程所在的进程表项的信号域设置对应的信号的位。进程处理信号的时机就是从内核态即将返回用户态的时候。
执行用户自定义的信号处理函数的方法很巧妙。把该函数的地址放在用户栈栈顶,进程从内核返回到用户态的时候,先弹出信号处理函数地址,于是就去执行信号处理函数了,然后再弹出,才是返回进入内核时的状态。 -
在磁盘中打开文件往里面写数据操作系统都做了什么事
读文件- 进程调用库函数向内核发起读文件请求;
- 内核通过检查进程的文件描述符定位到虚拟文件系统的已打开文件列表表项;
- 调用该文件可用的系统调用函数read()
- read()函数通过文件表项链接到目录项模块,根据传入的文件路径,在目录项模块中检索,找到该文件的inode;
- 在inode中,通过文件内容偏移量计算出要读取的页;
- 通过inode找到文件对应的address_space;
- 在address_space中访问该文件的页缓存树,查找对应的页缓存结点:
- 如果页缓存命中,那么直接返回文件内容;
- 如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页;重新进行第6步查找页缓存;
- 文件内容读取成功。
写文件
9. 前6步和读文件一致,在address_space中查询对应页的页缓存是否存在:
1. 如果页缓存命中,直接把文件内容修改更新在页缓存的页中。写文件就结束了。这时候文件修改位于页缓存,并没有写回到磁盘文件中去。
2. 如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页。此时缓存页命中,进行第6步。
2. 一个页缓存中的页如果被修改,那么会被标记成脏页。脏页需要写回到磁盘中的文件块。有两种方式可以把脏页写回磁盘:
1. 手动调用sync()或者fsync()系统调用把脏页写回
2. pdflush进程会定时把脏页写回到磁盘同时注意,脏页不能被置换出内存,如果脏页正在被写回,那么会被设置写回标记,这时候该页就被上锁,其他写请求被阻塞直到锁释放。
-
内存泄漏
- 堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。 - 系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
- 堆内存泄漏(Heap leak)
-
linux打开的最大文件数。
ulimit -SHn 10000
其实ulimit 命令身是分软限制和硬限制,加-H就是硬限制,加-S就是软限制。默认显示的是软限制,如果运行ulimit 命令修改时没有加在这里插入代码片
上-H或-S,就是两个参数一起改变。
软限制和硬限制的区别?
硬限制就是实际的限制,而软限制是警告限制,它只会给出警告。
永久生效
要想ulimits 的数值永久生效,必须修改配置文件/etc/security/limits.conf
在该配置文件中添加
四 网络
- 打开网页输入网址一共经历哪些协议
应用层:
域名——IP DNS 查找是否有对应ip地址
发送HTTP协议
传输层
tcp—三次握手(http-用的是TCP传输)
网络层
ip ARP ICMP
链路层
PPP
物理层
透明比特传输 - DNS 流程
浏览器缓存,系统本机缓存,局域网LDNS - epoll哪些触发模式,有啥区别?(必须非常详尽的解释水平触发和边缘触发的区别,以及边缘触发在编程中要做哪些更多的确认)
epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式。LT模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作,而在ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值。
也就是说在LT模式的情况下一定要确认收发的数据包的buffer是不是足够大如果收发数据包大小大于buffer的大小的时候就可能会出现数据丢失的情况。 - epoll和select的区别?
- select在一个进程中打开的最大fd是有限制的,由FD_SETSIZE设置,默认值是2048。不过 epoll则没有这个限制,内存越大,fd上限越大,1G内存都能达到大约10w左右。
- select的轮询机制是系统会去查找每个fd是否数据已准备好,当fd很多的时候,效率当然就直线下降了,epoll采用基于事件的通知方式,一旦某个fd数据就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,高效。
- select还是epoll都需要内核把FD消息通知给用户空间,epoll是通过内核于用户空间mmap同一块内存实现的,而select则做了不必要的拷贝
- select底层是数组,poll是链表,epoll是红黑树
- epoll的工作模式
- LT(level triggered) 是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。
- ET (edge-triggered) 是高速工作方式,只支持no-block socket。 在这种模式下,当描述符从未就绪变为就绪时,内核就通过epoll告诉你,然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作而导致那个文件描述符不再是就绪状态。不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。
- TTL是什么?有什么用处,通常那些工具会用到它?ping? traceroute? ifconfig? netstat?
TTL是Time To Live,每经过一个路由就会被减去一,如果它变成0,包会被丢掉。它的主要目的是防止包在有回路的网络上死转,浪费网络资源。ping和traceroute用到它。 - 流量控制与拥塞控制的区别,节点计算机怎样感知网络拥塞了?
拥塞控制是网络能够承受现有的网络负荷,是一个全局变量;而流量控制往往只是指点对点之间对通信量的控制。
感知的手段应该不少,比如在TCP协议里,TCP报文的重传本身就可以作为拥塞的依据。依据这样的原理, 应该可以设计出很多手段。 - TCP拥塞控制
慢启动算法的思路:主机开发发送数据报时,如果立即将大量的数据注入到网络中,可能会出现网络的拥塞。慢启动算法就是在主机刚开始发送数据报的时候先探测一下网络的状况,如果网络状况良好,发送方每发送一次文段都能正确的接受确认报文段。那么就从小到大的增加拥塞窗口的大小 - 一致性哈希算法
一致性哈希算法是在哈希算法基础上提出的,在动态变化的分布式环境中,哈希算法应该满足的几个条件:平衡性、单调性和分散性。
①平衡性是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题。
②单调性是指在新增或者删减节点时,不影响系统正常运行。
③分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的数据 - 哈希冲突的产生原因
哈希是通过对数据进行再压缩,提高效率的一种解决方法。但由于通过哈希函数产生的哈希值是有限的,而数据可能比较多,导致经过哈希函数处理后仍然有不同的数据对应相同的哈希值。这时候就产生了哈希冲突。
解决方法:开放寻址,再次哈希,链式寻址。 - HTTP 和 HTTPS的区别
- HTTPS 需要SSL协议,更加安全,需要证书
- 端口不一样,HTTP是80端口,HTTPS是443端口
- HTTP工作于应用层,HTTPS工作于传输层
- HTTP方法
获取资源的get方法,传输实体的post方法,传输文件的put方法,获取报文首部的head方法,删除文件的DELETE方法等等
post 不是幂等的,put 幂等 - Hosts有什么作用?
作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。 - http无状态怎么解释 无状态怎么确认用户要想要什么
无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
为了支持客户端与服务器之间的交互,我们就需要通过不同的技术为交互存储状态,而这些不同的技术就是Cookie和Session了。 - Cookie的缺陷
- cookie会被附加在每个HTTP请求中,所以无形中增加了流量。
- 由于在HTTP请求中的cookie是明文传递的,所以安全性成问题。(除非用HTTPS)
- Cookie的大小限制在4KB左右。对于复杂的存储需求来说是不够用的。
- Cookie的存储
Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie。内存Cookie由浏览器维护,保存在内存中,浏览器关闭后就消失了,其存在时间是短暂的。硬盘Cookie保存在硬盘里,有一个过期时间,除非用户手工清理或到了过期时间,硬盘Cookie不会被删除,其存在时间是长期的。 - HTTP 1.0 和HTTP 1.1区别
- HTTP/1.0协议使用非持久连接,即在非持久连接下,一个道tcp连接只传输一个Web对象,;HTTP/1.1默认使用持久连接
五 数据库
- 三大范式:
- 每一项都是不可分割的原子项
- 非码属性必须完全依赖于候选码,如果不是需要分表
- 非主属性不可依赖于其他非主属性。(不可传递依赖主属性)
- b+树相比于b树的查询优势:
- b+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;
- b+树查询必须查找到叶子节点,b树只要匹配到即可不用管元素位置,因此b+树查找更稳定(并不慢);
- 对于范围查找来说,b+树只需遍历叶子节点链表即可,b树却需要重复地中序遍历
- 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:
- 原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。 - 一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。 - 隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。 - 持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
- 原子性(Atomicity)
- 数据库索引
数据库索引其实就是为了使查询数据效率快。
分类:- 聚集索引(主键索引):在数据库里面,所有行数都会按照主键索引进行排序。
- 非聚集索引:就是给普通字段加上索引。
- 联合索引:就是好几个字段组成的索引,称为联合索引。
聚集索引在叶节点储存了全部行数据
非聚集索引在叶节点只储存了聚集索引键值,然后需要通过聚集索引键值访问行数据。
联合索引遵循最左匹配原则。
- 事务的四个隔离等级和解决的问题。
读未提交(脏读)读已提交(不可重复读)可重复读(幻读)串行化
读提交,就是一个事务要等另一个事务提交后才能读取数据。
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。
串行化,是最高的事务隔离级别,在该级别下,事务串行化顺序执行。 - mvcc怎么是实现的。
MVCC(Multi-Version Concurrency Control | 多版本并发控制)
InnoDB通过为每一行记录添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。但是InnoDB并不存储这些事件发生时的实际时间,相反它只存储这些事件发生时的系统版本号(LSN)。这是一个随着事务的创建而不断增长的数字。每个事务在事务开始时会记录它自己的系统版本号。每个查询必须去检查每行数据的版本号与事务的版本号是否相同。 - 乐观锁和悲观锁
乐观锁:假设读的时候不会有别的用户来写,锁开销小。基于版本控制mvcc实现。JAVA中使用场景:Atomic类 CAS。是通过unsafe类执行的,操作系统层面保证原子性
悲观锁:假设每次读都会有别的用户来写,锁开销大 - Redis
- 为什么使用redis?
考虑性能和并发。
性能上:我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。Redis基于内存级别的缓存。
并发上:高并发下优先访问redis数据,避免直接访问数据库 - 支持的数据类型
string,hash(登录存储用户信息),list(消息队列),set,sorted set - redis存在哪些问题
缓存和数据库双写一致性:先更新数据库,再删缓存。如果项目要去强一致性,则不能使用缓存。
缓存雪崩:双缓存;缓存失效时间加上随机值,避免集体雪崩
缓存击穿:异步更新;互斥锁;过滤非法key
缓存并发竞争:锁;串行控制 - 单线程redis性能快的原因
纯内存操作
单线程操作,避免了频繁的上下文切换
采用了非阻塞I/O多路复用机制 - redis的过期策略以及内存淘汰机制
redis采用的是定期删除+惰性删除策略。-
为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key。 -
定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
-
采用定期删除+惰性删除就没其他问题了么?
如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。
-
- 为什么使用redis?
- 数据库设计通常分为6个阶段:
1需求分析:分析用户的需求,包括数据、功能和性能需求;2概念结构设计:主要采用E-R模型进行设计,包括画E-R图;3逻辑结构设计:通过将E-R图转换成表,实现从E-R模型到关系模型的转换;4数据库物理设计:主要是为所设计的数据库选择合适的存储结构和存取路径;5数据库的实施:包括编程、测试和试运行;6数据库运行与维护:系统的运行与数据库的日常维护。