测试开发面试常见问题整理(语言基础,网络,数据库,Linux)

1、迭代器和生成器的区别
1.迭代器是一个更加抽象的概念,任何对象,如果它的类有next方法和iter方法返回自身。对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数,iter()是Python的内置函数。iter()会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数。在没有后续元素时,next()会抛出一个StopIterration的异常。
2.生成器(Generator)是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在返回数据的时候需要使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)
区别:生成器能做到迭代器能做的所有事,而且因为自动创建了__iter__()和next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保持程序状态的自动生成,当发生器终结时,还会自动跑出StopIterration异常。
2、简单谈下GIL
Global Interpreter Lock(全局解释器锁)
   Python代码的执行由python虚拟机(也叫解释器主循环,CPython版本)来控制,Python在设计之初就考虑到要在解释器的主循环中,同时只有一个线程在执行,即任意时刻,只有一个线程在解释器中运行。对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程环境中,Python虚拟机按以下方式执行:
1.设置GIL
2.切换到一个线程去运行
3.运行:
    a.指定数量的字节码指令,或者
    b.线程主动让出控制(可以调用time.sleep(0))
4.把线程设置为睡眠状态
5.解锁GIL
6.再次重复以上所有步骤
再调用外部代码(如C/C++扩展函数)的时候,GIL讲会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)。
3、find和grep
grep命令是一种强大的文本搜索工具,grep所有内容串可以是正则表达式,允许对文本文件进行模式查找。如果找到匹配模式,grep打印包含模式的所有行。
find通常用来在特定的目录下搜索符合条件的文件,也可以用来搜索特定用户属主的文件。
4、Python是如何进行内存管理的
一、垃圾回收:
python不像C++,Java等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对python语言来讲,对象的类型和内存都是在运行时确定的。这也是为什么我们称python语言为动态类型的原因(这里我们把动态类型语言可以简单的归结为对变量内存地址的分配是在运行时自动判断变量类型并对变量进行赋值)。
二、引用计数:
  python采用了类似windows内核对象一样的方式来对内存进行管理。每一个对象,都维护这一个对指向该对象的引用的计数。当变量被绑定在一个对象上的时候,该变量的引用计数就是1,(还有另外一些情况也会导致变量引用计数的增加),系统会自动维护这些标签,并定时扫描,当某标签的引用计数变为0的时候,该对象就会被回收。
三、内存池机制
  python的内存机制成金字塔形:
    第-1,-2层主要有操作系统进行操作;
    第0层是C中的malloc,free等内存分配和释放函数进行操作;
    第1层和第2层是内存池,有python的接口函数PyMem_Malloc函数实现,当对象小于256字节时由该层直接分配内存;
    第3层是最上层,也就是我们对python对象的直接操作;
在C中如果频繁的调用malloc与free时,是会产生性能问题的,在加上频繁的分配和释放小块的内存会产生内存碎片。
python在这里主要干的工作有:
如果请求分配的内存在1~256字节之间就使用自己的内存管理系统,否则直接使用malloc;这里还是会调用malloc分配内存,但每次回分配一块大小为256字节的大块内存。
经由内存池登记的内存到最后还是会回收到内存池,并不会调用C的free释放掉,以便下次使用。对于简单的python对象,例如数值、字符串、元组(tuple不允许被更改)采用的是复制的方式(深拷贝),也就是说当讲另一个变量B赋值给变量A时,虽然A和B的内存空间仍然相同,但是当A的值发生变化时,会重新给A分配空间,A和B的地址变得不再相同。
5.C++的内存管理
1.简介
在C++中,内存分为:栈、堆、自由存储区、全局/静态存储区、常量存储区。
栈,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束是这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率高,分配的内存容量有限。
堆,就是那些由malloc等分配的内存块,用free来释放内存。
自由存储区,那些由new分配的内存块,由应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。
2.堆和自由存储区的区别与联系
从技术上来说,堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就区别于堆了。我们所需要记住的就是:
堆是C语言和操作系统的术语、是操作系统维护的一块内存,而自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。
new所申请的内存区域在C++中称为自由存储区。藉由堆实现的自由存储,可以说new所申请的内存区域在堆上。
3.堆和栈的区别
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,默认的栈空间大小是1M(VS2017 项目-属性-链接器-系统可以修改)。
碎片问题:对于堆来讲,频繁的malloc/free势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。栈是先进后出的队列,以至于永远都不可能有一个内存块从栈中间弹出。
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
4.嵌入式系统–C++动态内存分配
在嵌入式系统中使用C++的一个常见问题是内存分配,即对new 和 delete 操作符的失控。具有讽刺意味的是,问题的根源却是C++对内存的管理非常的容易而且安全。具体地说,当一个对象被消除时,它的析构函数能够安全的释放所分配的内存。这当然是个好事情,但是这种使用的简单性使得程序员们过度使用new 和 delete,而不注意在嵌入式C++环境中的因果关系。并且,在嵌入式系统中,由于内存的限制,频繁的动态分配不定大小的内存会引起很大的问题以及堆破碎的风险。当你必须要使用new 和delete时,你不得不控制C++中的内存分配。你需要用一个全局的new 和delete来代替系统的内存分配符,并且一个类一个类的重载new 和delete。
5.为什么需要new/delete
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。最好不要混用。
6、python线程、进程、协程之间的区别
11.1概念:
(1)进程:
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动。进程是系统进行资源分配与调度的基本单元。每个进程都有自己独立的内存空间,由于进程占据独立的内存,所以上下文进程的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但是相对也比较安全。
(2)线程:
单个进程中执行中每个任务就是一个线程。线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中最小的单元,由线程ID、程序计数器、寄存器集合、和堆栈共同组成。它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
(3)协程:
协程,又被称为微线程,一个线程可以拥有多个协程,协程拥有自己的寄存器上下文和栈。协程在执行过程中,可以进行中断,然后转区执行别的子程序,在适当的时候在返回来接着执行,注意:这个过程是在一个子程序中中断,去执行其他的子程序,不是函数调用,有点类似于CPU的中断。
11.2进程与线程比较:
(1)定义区别:进程是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配和调度运行的基本单元。线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的基本单元。
(2)一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务。
(3)线程是一种轻量级的进程,与进程相比,线程给操作系统带来地创建、维护、和管理的负担要轻,意味着线程的代价或开销比较小。
(4)线程没有地址空间,线程包含在进程的地址空间中。进程拥有的所有资源都属于线程。所有的线程共享进程的内存和资源。但是每个线程拥有自己的栈段,寄存器的内容,栈段又叫运行时段,用来存放所有局部变量和临时变量。
(5)父和子进程使用进程间通信机制,同一进程的线程通过读取和写入数据到进程变量来通信(共享内存)。
(6)进程内的任何线程都处于相同的级别。子进程不对任何其他子进程施加控制,进程的线程可以对同一进程的其它线程施加控制。不管是哪个线程创建了哪一个线程,进程内的任何线程都可以销毁、挂起、恢复和更改其它线程的优先权。子进程不能对父进程施加控制,进程中所有线程都可以对主线程施加控制。进程中任何线程都可以通过销毁主线程来销毁进程。当进程退出时该进程所产生线程都会被强制退出并清除。
11.3线程与协程比较:
(1)一个线程可以拥有多个协程。
(2)线程进程都是同步机制,而协程是异步。一个线程的多个协程的运行是串行的
(3)对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。所以协程的切换比线程方便
(4)因为协程就是在一个线程内部,对于内存的操作不会出现冲突,向进程与线程那样对共享资源加锁,只需要程序在运行时判断状态即可。
7、http协议建立在TCP/IP协议之上
HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。默认HTTP的端口号为80,HTTPS的端口号为443。

8、HTTP协议
HTTP协议,超文本传输协议,是用于从万维网(WWW)服务器传输超文本到本地浏览器的传送协议。HTTP基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
HTTP的特点
(1)简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
(2)灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
(3)无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
(4)无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大HTTP1.1中增加了cookies来解决这一问题。另一方面,在服务器不需要先前信息时它的应答就较快。
(5)支持B/S及C/S模式。
HTTP请求报文和响应报文

HTTP之状态码
状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别:
(1)1xx:信息性状态码–表示服务器已接收了客户端请求,客户端可继续发送请求;
(2)2xx:成功–表示已被成功接收请求并处理;
(3)3xx:重定向–要完成请求必须进行更进一步的操作
(4)4xx:客户端错误–请求有语法错误或请求无法实现
(5)5xx:服务器端错误–服务器未能实现合法的请求
常见状态码:
200 //客户端请求成功
204 // No Content 成功,但不返回任何实体的主体部分
205 // Partial Content 成功执行了一个范围(Range)请求
301 //永久性重定向,响应报文的Location首部给出该资源的新URL
302 //临时性重定向,响应报文的Location首部给出URL来临时定位资源
303 //请求的资源存在着另一个URI,客户端应使用GET方法定向获取请求的资源
304 //服务器内容没有更新,可以直接读取浏览器缓存
307 //临时重定向。与302 Found含义一样。302禁止POST变换为GET,但实际使用时并不一定,307则更多浏览器可能会遵循这一标准,但也依赖于浏览器具体实现
400 //客户端请求有语法错误,不能被服务器所理解
401 //请求未经授权,这个状态代码必须和WWW-Authenticate响应头一
起使用
403 //服务器收到请求,但是拒绝提供服务
404 //请求资源不存在,eg:输入了错误的URL
500 //服务器发生不可预期的错误
503 //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
301和302的区别:
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。
他们的不同在于:301永久性重定向,表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302临时性重定向,表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。
HTTP请求方法
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
(1)GET:请求指定的页面信息,并返回实体主体。一般用来获取/查询资源信息。
(2)HEAD:类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头。
(3)POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。一般用于更改资源信息。
(4)PUT:从客户端向服务器传送的数据取代指定的文档的内容。
(5)DELETE:请求服务器删除指定的页面。
(6)CONNECT: HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
(7)OPTIONS:允许客户端查看服务器的性能。
(8)TRACE:回显服务器收到的请求,主要用于测试或诊断。
GET和POST的区别
(1)GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连;POST方法是把提交的数据放在HTTP请求包的请求体中.
(2)GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
(3)安全性。GET方式提交数据,会带来安全问题,比如一个登录页面,通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码。
(4)安全的和幂等的。所谓安全的意味着该操作用于获取信息而非修改信息。幂等的意味着对同一URL的多个请求应该返回同样的结果。换句话说,GET 请求一般不应产生副作用。POST 表示可能改变服务器上的资源的请求。
9、cookie 和session
Cookie和Session都为了用来保存状态信息,都是保存客户端状态的机制,它们都是为了解决HTTP无状态的问题而所做的努力。Session可以用Cookie来实现,也可以用URL回写的机制来实现。用Cookie来实现的Session可以认为是对Cookie更高级的应用。
服务器在响应消息中用Set-Cookie头将Cookie的内容回送给客户端,客户端在新的请求中将相同的内容携带在Cookie头中发送给服务器。从而实现会话的保持。

Session机制
Session机制是一种服务器端的机制。当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识——称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。
Session失效的情况:
(1)Session超时:Session在指定时间内失效,例如30分钟,若在30分钟内没有操作,则Session会失效,例如在web.xml中进行了如下设置:

    30 //单位:分钟
   
(2)使用session.invalidate()明确的去掉Session。
cookie 和session的比较:
(1)Cookie将状态保存在客户端,Session将状态保存在服务器端;
(2)Cookies是服务器在本地机器上存储的小段文本,并随每一个请求发送至同一个服务器。服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将向同一服务器发送的任何请求缚上这些cookies。Session并没有在HTTP的协议中定义;
(3)Session是针对每一个用户的,用一个sessionID来区分是哪个用户session变量,变量的值保存在服务器上,这个值在浏览器访问服务器时返回给服务器。
(4)就安全性来说:当你访问一个使用session 的站点,同时在自己机子上建立一个cookie,建立在服务器端的SESSION机制更安全些。因为它不会任意读取客户存储的信息。
10、Web缓存
WEB缓存(cache)位于Web服务器和客户端之间。
缓存会根据请求保存输出内容的副本,例如html页面,图片,文件,当下一个请求来到的时候:如果是相同的URL,缓存直接使用副本响应访问请求,而不是再次向源服务器发送请求。HTTP协议定义了相关的消息头来使WEB缓存尽可能好的工作。
缓存的优点:
(1)减少相应延迟:因为请求从缓存服务器(离客户端更近)而不是源服务器被相应,这个过程耗时更少,让web服务器看上去相应更快。
(2)减少网络带宽消耗:当副本被重用时会减低客户端的带宽消耗;客户可以节省带宽费用,控制带宽的需求的增长并更易于管理。
客户端缓存生效的流程
服务器收到请求时,会在200 OK中回送该资源的Last-Modified和ETag头,客户端将该资源保存在缓存中,并记录这两个属性。当客户端需要发送相同的请求时,会在请求中携带If-Modified-Since和If-None-Match两个头。两个头的值分别是响应中Last-Modified和ETag头的值。服务器通过这两个头判断本地资源未发生变化,客户端不需要重新下载,返回304响应。

11、HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer)
HTTPS,安全超文本传输协议,在HTTP上建立安全套接字层(SSL)进行信息交换,并对传输数据进行加密,是HTTP协议的安全版,是使用TLS/SSL加密的HTTP协议。HTTP协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议TLS/SSL具有身份验证、信息加密和完整性校验的功能,可以避免此类问题发生。HTTPS所用的端口号是443。TLS/SSL全称安全传输层协议Transport Layer Security, 是介于TCP和HTTP之间的一层安全协议,不影响原有的TCP协议和HTTP协议,所以使用HTTPS基本上不需要对HTTP页面进行太多的改造。

HTTPS通信过程:

HTTPS的作用:
(1)对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全;
(2)对网站服务器进行真实身份认证。
HTTPS通信的优点:
1)客户端产生的密钥只有客户端和服务器端能得到;
2)加密的数据只有客户端和服务器端才能得到明文;
3)客户端到服务端的通信是安全的。
HTTPS和HTTP的区别是什么
1、HTTPS是加密传输协议,HTTP是明文传输协议;
2、HTTPS需要用到SSL证书,而HTTP不用;
3、HTTPS比HTTP更加安全
4、HTTPS标准端口443,HTTP标准端口80;
5、HTTPS基于传输层,HTTP基于应用层;
6、HTTPS在浏览器显示绿色安全锁,HTTP没有显示;
12、HTTP中的重定向和请求转发的区别  
转发是服务器行为,重定向是客户端行为。
转发过程:客户浏览器发送http请求----》web服务器接受此请求–》调用内部的一个方法在容器内部完成请求处理和转发动作----》将目标资源发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。
重定向过程:客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器–》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址----》服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。
13、三次握手

TCP三次握手的过程
第一次握手:客户端A将标志位SYN置为1,随机产生一个值为seq=x(x的取值范围为=1234567)的数据包到服务器,客户端A进入SYN_SENT状态,等待服务端B确认;
第二次握手:服务端B收到数据包后由标志位SYN=1知道客户端A请求建立连接,服务端B将标志位SYN和ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给客户端A以确认连接请求,服务端B进入SYN_RCVD状态。
第三次握手:客户端A收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并将该数据包发送给服务端B,服务端B检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,客户端A和服务端B进入ESTABLISHED状态,完成三次握手,随后客户端A与服务端B之间可以开始传输数据了。
为什么要三次握手?
目的:为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。 client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。
假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”。主要目的防止server端一直等待,浪费资源。
14、四次挥手
四次挥手的过程

第一次挥手:Client发送释放连接请求报文,其中FIN(终止位)=1,seq(序列号)=u,Client进入FIN_WAIT_1(终止等待1)状态。此时,client不再发数据,但是还可以接收数据。
第二次挥手:Server收到FIN后,发送一个确认报文给Client,其中ACK=1,seq=v,ack(确认号) = u +1,Server进入CLOSE_WAIT(关闭等待)状态。Client收到这个确认后就进入FIN-WAIT-2(终止等待2)状态,等待B发出连接释放的请求。
第三次挥手:当Server已经没有要发送的数据时,Server就会给Client发送一个释放连接报文,其中FIN=1,ACK=1,seq=w,ack=u+1,Server发送完之后,进入LAST-ACK(最后确认)状态。
第四次挥手:Client收到释放连接请求后,对此发出确认。其中ACK=1,seq=u+1,ack=w+1;Client在发送完毕后,进入到TIME-WAIT (时间等待)状态。Server在收到确认之后,进入到CLOSED(关闭)状态。在经过时间等待计时器设置的时间之后,Client才会进入CLOSED状态。
为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
TIME_WAIT状态
首先,MSL即Maximum Segment Lifetime,就是最大报文生存时间,是任何报文在网络上的存在的最长时间,超过这个时间报文将被丢弃。客户端在发送完最后一次确认之后,还要等待2MSL的时间。原因: 为了让Server能够按照正常步骤进入CLOSED状态; 为了防止已经失效的请求连接报文出现在下次连接中。
解释:1)由于客户端最后一个ACK可能会丢失,这样B就无法正常进入CLOSED状态。于是B会重传请求释放的报文,而此时A如果已经关闭了,那就收不到B的重传请求,就会导致B不能正常释放。而如果A还在等待时间内,就会收到B的重传,然后进行应答,这样B就可以进入CLOSED状态了。2)在这2MSL等待时间里面,本次连接的所有的报文都已经从网络中消失,从而不会出现在下次连接中。
15、TCP如何保障顺序
(1)主机每次发送数据时,TCP就给每个数据包分配一个序列号并且在一个特定的时间内等待接收主机对分配的这个序列号进行确认。
(2)如果发送主机在一个特定时间内没有收到接收主机的确认,则发送主机会重传此数据包。
(3) 接收主机利用序列号对接收的数据进行确认,以便检测对方发送的数据是否有丢失或者乱序等,
(4)接收主机一旦收到已经顺序化的数据,它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。
具体步骤如下:
(1)为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区;
(2)并为每个已发送的数据包启动一个超时定时器;
(3)如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区;
(4)否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。
(5)接收方收到数据包后,先进行CRC校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可方在数据包中捎带过去。
16、TCP如何保证可靠性
(1)校验和
目的:检测数据在传输过程中有没有变化。
计算方式:在数据传输的过程中,将发送的数据段都当做一个16位的整数。将这些整数加起来。并且前面的进位不能丢弃,补在后面,最后取反,得到校验和。
发送方:在发送数据之前计算检验和,并进行校验和的填充。
接收方:收到数据后,对数据以同样的方式进行计算,求出校验和,与发送方的进行比对。如果收到段的检验和有差错,TCP将丢弃这个报文段且不进行确认,发送端超时会重发数据)

注意:如果接收方比对校验和与发送方不一致,那么数据一定传输有误。但是如果接收方比对校验和与发送方一致,数据不一定传输成功。
(2)序列号
序列号:TCP传输时将每个字节的数据都进行了编号。序列号不仅仅起到确认应答的作用,有了序列号能够将失序的数据根据序列号排序,并且去掉重复序列号的数据。

(3)确认应答
TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答,也就是发送ACK报文。这个ACK报文中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。这个确认不是立即发送,通常将推迟几分之一秒,之所以推迟,可能是要对包做完整校验。
(4)超时重传
发送方没有接收到响应的ACK报文原因: 数据在传输过程中由于网络原因等直接全体丢包,接收方根本没有接收到。 接收方接收到了响应的数据,但是发送的ACK报文响应却由于网络原因丢包了。
TCP在解决这个问题的时候引入了超时重传机制。当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。如果是第一个原因,接收方收到二次重发的数据后,便进行ACK应答。如果是第二个原因,接收方发现接收的数据已存在(判断存在的根据就是序列号,所以上面说序列号还有去除重复数据的作用),那么直接丢弃,仍旧发送ACK应答。
(5)连接管理
三次握手与四次挥手的过程
(6)流量控制
流量控制所要做的就是控制发送端发送速率,以便使接收端来得及接受。接收端在接收到数据后,对其进行处理。连接的每一方都有固定大小的缓冲空间,如果发送端的发送速度太快,导致接收端的结束缓冲区很快的填充满了。此时如果发送端仍旧发送数据,那么接下来发送的数据都会丢包,继而导致超时重传。而TCP根据接收端对数据的处理能力,决定发送端的发送速度,这个机制就是流量控制。
在TCP协议的报头信息当中,滑动窗口大小实际上是接收端接收数据缓冲区的剩余大小。这个数字越大,证明接收端接收缓冲区的剩余空间越大,网络的吞吐量越大。接收端会在确认应答发送ACK报文时,将自己的即时窗口大小填入,并跟随ACK报文一起发送过去。而发送方根据ACK报文里的窗口大小的值的改变进而改变自己的发送速度。如果接收到窗口大小的值为0,那么发送方将停止发送数据。并定期的向接收端发送窗口探测数据段,让接收端把窗口大小告诉发送端。

(7)拥塞控制
拥塞控制就是防止过多的数据注入到网络中,这样可以使网路中的路由器或链路不致于过载。TCP传输的过程中,发送端开始发送数据的时候,如果刚开始就发送大量的数据,那么就可能造成一些问题。网络可能在开始的时候就很拥堵,如果给网络中在扔出大量数据,那么这个拥堵就会加剧。拥堵的加剧就会产生大量的丢包,就对大量的超时重传,严重影响传输。
所以TCP引入了慢启动的机制,在开始发送数据时,先发送少量的数据探路。探清当前的网络状态如何,再决定多大的速度进行传输。这时候就引入拥塞窗口,发送刚开始定义拥塞窗口为 1,每次收到ACK应答,拥塞窗口加 1。当窗口值逐渐增大时,为了防止cwind的增长导致网络拥塞,还需要一个变量–慢开始门限ssthresh在发送数据之前,首先将拥塞窗口与接收端反馈的窗口大小比对,取较小的值作为实际发送的窗口。
四种拥塞控制算法,即慢开始,拥塞避免、快重传、快恢复。

  1. List和Set和Map的实现方式以及存储方式
    16、访问一个URL经历了哪些过程?

客户端获取URL - > DNS解析 - > TCP连接 - >发送HTTP请求 - >服务器处理HTTP请求 - >返回报文 - >浏览器解析渲染页面 - > TCP断开连接。
DNS是一个域名系统,是万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。
DNS工作原理:
第一步:客户机提出域名解析请求,并将该请求发送给本地的域名服务器。第二步:当本地的域名服务器收到请求后,就先查询本地的缓存,如果有该纪录项,则本地的域名服务器就直接把查询的结果返回。第三步:如果本地的缓存中没有该纪录,则本地域名服务器就直接把请求发给根域名服务器,然后根域名服务器再返回给本地域名服务器一个所查询域(根的子域) 的主域名服务器的地址。第四步:本地服务器再向上一步返回的域名服务器发送请求,然后接受请求的服务器查询自己的缓存,如果没有该纪录,则返回相关的下级的域名服务器的地址。第五步:重复第四步,直到找到正确的纪录。第六步:本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时还将结果返回给客户机。
17、TCP和UDP区别?

18、http和https区别?
1、https协议需要到CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。(原来网易官网是http,而网易邮箱是https。)
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的。Https协议是由SSL+Http协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。)
19、Python中is和的区别
is判断的是两个量(变量,常量)的内存地址是否相等,而
判断的是两个量(变量,常量)的内存单元的内容是否相等,也就是说,当两个量的指针相等的话,他们的值也必定相等;但是他们的值相等,指针不一定相等

20、数组和链表的区别
(1)链表是链式的存储结构,通过指针来连接元素;数组是顺序的存储结构,把所有元素按次序依次存储。
(2)数组大小固定,不适合动态存储、动态添加,增删操作较慢。内存地址连续,可随机访问,查询较快。
(3)链表大小可变,扩展性强,增删操作较快。内存地址不连续,只能顺着指针的方向查询,速度较慢。
21、计算机网络模型

22、TCP的滑动窗口机制
滑动窗口实现了TCP流量控制。首先明确滑动窗口的范畴:TCP是双工的协议,会话的双方都可以同时接收和发送数据。TCP会话的双方都各自维护一个发送窗口和一个接收窗口。各自的接收窗口大小取决于应用、系统、硬件的限制(TCP传输速率不能大于应用的数据处理速率)。各自的发送窗口则要求取决于对端通告的接收窗口,要求相同。滑动窗口解决的是流量控制的的问题,就是如果接收端和发送端对数据包的处理速度不同,如何让双方达成一致。
滑动机制:
(1)发送窗口只有收到发送窗口内字节的ACK确认,才会移动发送窗口的左边界。
(2)接收窗口只有在前面所有的段都确认的情况下才会移动左边界。当在前面还有字节未接收但收到后面字节的情况下,窗口不会移动,并不对后续字节确认。以此确保对端会对这些数据重传。
(3)遵循快速重传、累计确认、选择确认等规则。

发送方的发送缓存内的数据都可以被分为4类:
已发送,已收到ACK
已发送,未收到ACK
未发送,但允许发送
未发送,但不允许发送
其中类型2和3都属于发送窗口。
接收方的缓存数据分为3类:
已接收
未接收但准备接收
未接收而且不准备接收
其中类型2属于接收窗口。
窗口大小代表了设备一次能从对端处理多少数据,之后再传给应用层。缓存传给应用层的数据不能是乱序的,窗口机制保证了这一点。
滑动窗口机制的意义
(1)可靠性,滑动窗口只有在队列前部的被确认之后,才会往后移动,保证数据包被接收方确认并接收。
(2)传输效率,信道利用率,TCP是发送报文段为单位的,假如每发一个报文就要等ACK,那么对于大数据包,等待时间就太长了。只要发送的报文在滑动窗口里面,不用等每个ACK回来就可以向右滑动(累计确认)。
(3)稳定性,TCP的滑动窗口大小,是整个复杂网络商榷的结果,会进行动态调整,可以尽量地避免网络拥塞,更加稳定。
23、(1) 一个 TCP 连接可以对应几个 HTTP 请求?(提示,这在问你HTTP1.0和1.1的区别)
在 HTTP/1.0 中,一个服务器在发送完一个 HTTP 响应后,会断开 TCP 链接。但是这样每次请求都会重新建立和断开 TCP 连接,代价过大。HTTP/1.1默认开启持久连接,除非请求中写明请求头 Connection: close,那么浏览器和服务器之间会维持一段时间的 TCP 连接,不会一个请求结束就断掉。
答案:默认情况下建立 TCP 连接不会断开,此时一个TCP连接可以对应多个HTTP请求;只有在请求报头中声明 Connection: close 才会在请求完成后关闭连接,此时一个TCP请求对应一个HTTP请求。
(2)一个 TCP 连接中 HTTP 请求发送可以一起发送么(比如一起发三个请求,再三个响应一起接收)?(提示,这就是在问你HTTP2.0和HTTP1.1协议的区别)
在 HTTP/1.1 存在 Pipelining 技术可以完成这个多个请求同时发送,但是由于浏览器默认关闭,所以可以认为这是不可行的。在 HTTP2.0 中由于 Multiplexing 多路传输特性的存在,多个 HTTP 请求可以在同一个 TCP 连接中同时进行。
那么在 HTTP/1.1 时代,浏览器是如何提高页面加载效率的呢?主要有下面两点:.维持和服务器已经建立的 TCP 连接,在同一连接上顺序处理多个请求。和服务器建立多个 TCP 连接。
(3)浏览器对同一Host建立TCP连接的数量有没有限制?
假设我们还处在HTTP/1.1 时代,那个时候没有多路传输,当浏览器拿到一个有几十张图片的网页该怎么办呢?肯定不能只开一个 TCP 连接顺序下载,那样用户肯定等的很难受,但是如果每个图片都开一个 TCP 连接发 HTTP 请求,那电脑或者服务器都可能受不了。所以浏览器对同一Host建立TCP连接的数量有限制。Chrome 最多允许对同一个 Host 建立六个 TCP 连接,不同的浏览器有一些区别。
如果图片都是 HTTPS 连接并且在同一个域名下,那么浏览器在 SSL 握手之后会和服务器商量能不能用 HTTP2,如果能的话就使用 Multiplexing 功能在这个连接上进行多路传输。如果发现用不了 HTTP2或者用不了 HTTPS(现实中的 HTTP2 都是在 HTTPS 上实现的,所以只能使用 HTTP/1.1),那浏览器就会在一个 HOST 上建立多个 TCP 连接,连接数量的最大限制取决于浏览器设置,这些连接会在空闲的时候被浏览器用来发送新的请求,如果所有的连接都正在发送请求,那其他的请求就只能等待。
24、TCP报文

字段介绍:
1、源端口(16bit):一个端口所属一个进程,可以通过源端口定位到具体的进程;
2、目的端口(16bit):通过目的端口和Ip报文的目的ip地址可以唯一定位到一个进程;
3、序号(32bit):当前发送数据的起始序号,每一个字节都和一个序号对应,通过序号可以保证数据的有序性;
4、确认序号(32bit):只有ACK为1时确认序号才有效,表示期望下一次希望收到的数据的起始序号;
5、数据偏移(4bit):报文中包含数据部分,因为报文头的大小不是固定的(由于选项和填充的存在),所以通过这个数据偏移定位到数据相对于整个TPC报文的起始位置。
6、保留(6bit):保留,以作之后其他用途;
7、控制位:
(1)URG:紧急指针标志:1:紧急指针有效、0:紧急指针无效;
(2)ACK:确认序号标志:1:确认号有效、0:确认号无效;
(3)PSH:push标志,1:带有push标志的报文,表示接收方在收到报文后应尽快交给应用程序,而不是放到缓存区;
(4)RST:重置连接标志:用于重置由于主机崩溃或者其他错误导致的错误连接。或者用于拒绝非法的报文段或者连接请求。
(5)SYN:同步序号标志,用于建立连接过程,SYN=1、ACK=0表示没有使用捎带的确认域;SYN=1、ACK=1表示连接应答捎带一个确认域。
(6)FIN:结束标志,表示没有数据发送了,结束本次连接请求。
8、窗口(16bit):滑动窗口大小,表示告诉发送端接收端的缓存大小,从而进行流量控制;
9、校验和(16bit):根据整个TCP报文生成的一个校验码,在发送端生成,在接收端校验;
10、紧急指针(16bit):只有URG等于1时才有效,紧急指针是一个正的偏移量,和序列号相加生成一个紧急数据最后一个序列号,对于这部分数据会以一个紧急的方式发送。
11、选项和填充(不固定大小):加上一个额外的信息,比如MSS等。
12、数据(不固定大小):数据部分可有可无。

25、ip与tcp校验和方式的不同
TCP校验和覆盖TCP首部和TCP数据,而IP首部中的校验和只覆盖IP的首部,不覆盖IP数据报中的任何数据。
(1)IP报文检验和:
发送端:
把校验和字段置为0;
需要校验的数据看成以16位为单位的数字组成,对IP首部中的每16bit进行二进制求和;
如果和的高16bit不为0(不足16位左侧以0补充),则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;
将该16bit的值取反,存入校验和字段。
接收端:计算数据包的检验和步骤同上:对IP首部(包括检验和)的每16bit进行二进制求和,然后取反,检查校验和结果是否为0;如果等于0,说明被整除,校验和正确。否则,校验和就是错误的,协议栈要抛弃这个数据包。
(2)TCP检验和:
发送端:
把TCP报头中的校验和字段置为0。
将ip伪首部、tcp报头、tcp数据分为16位的字,然后进行累加(如果总长度为奇数个字节,则在最后增添一个位都为0的字节 )。
最后对累加的和进行按位取反即可,写入检验和。Ip伪首部包括源ip地址(4字节)、目的ip地址(4字节)、协议号(两字节)、tcp包长(2字节) ,共14字节。 伪首部是为了增加TCP校验和的检错能力:通过伪首部的目的IP地址来检查TCP报文是否收错了、通过伪首部的传输层协议号来检查传输层协议是否选对了。
接收端:计算数据包的检验和步骤同上:将ip伪首部、tcp报头、tcp数据(包括检验和)每16bit进行求和,然后取反,检查校验和的结果是否为0;如果等于0,说明被整除,校验和正确。否则,校验和就是错误的,协议栈要抛弃这个数据包。
26、有哪些常用的加密算法
HTTP协议传输的数据都是未加密的,也就是明文的,不安全。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。HTTP 协议本身没有加密机制,但是可以使用 SSL(Secure Socket Layer)/TLS(Transport Layer Security)协议解决 HTTP 的安全性问题。SSL 处于应用层和传输层之间,不仅提供加密解密处理,还提供了一种证书机制,用于对客户端或服务器进行身份认证。证书由值得信任的第三方机构颁发,用来证明客户端和服务端是真实存在的。
1)共享(对称)秘钥加密
所谓共享秘钥加密(Common key crypto system)就是加密和解密都使用相同的秘钥,因此也被称为对称秘钥加密。相对于非对称加密具有更高的加密解密速度,但使用共享秘钥加密时必须将秘钥也发送给对方,秘钥在传输过程中可能会被窃取,因此安全性相对于非对称加密就比较低;
2)公开(非对称)秘钥加密
公开秘钥加密使用一对非对称的秘钥,一把叫做私有秘钥(private key),另一把叫做公开秘钥(private key)。公开秘钥任何人都可以获得,但是私有秘钥是私有的。通过这种方式,可以很好的解决共享秘钥加密的安全性问题。
接收方在发送消息之前,会提前生成公钥和私钥,把公钥发送给发送方。发送方收到公钥之后,将带发送的数据用共钥加密发送给接收方。接收到接收到数据之后,用私钥对其进行解密。在这个过程中,公钥负责加密,私钥负责解密,数据在传输过程中即使被拦截,攻击者也没有私钥,因此无法破解。它的加解密速度低于对称加密算法,但是安全性更高。
3)HTTPS 加密方式
HTTPS 采用共享密钥加密和公开密钥加密两者并用的混合加密机制。 使用公开密钥加密方式安全地交换在稍后使用的共享秘钥中的秘钥;确保秘钥是安全的前提下,使用共享秘钥加密方式进行通信。
如何保证公开密钥加密方式交换的秘钥是安全的呢? 将公钥放在数字证书中,数字证书由双方都可依赖的第三方机构颁发,因此只要证书是可信的,公钥就是可信的。
27、数据库索引
索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。
索引的作用(优点)
创建索引可以大大提高系统的性能。
第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四,在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
索引的缺点
第一,创建索引和维护索引要耗费时间,这种时间随着数据 量的增加而增加。
第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
哪些列上应该建立索引
(1)在经常需要搜索的列上,可以加快搜索的速度;
(2)在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
(3)在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
(4)在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
(5)在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
(6)在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
不该建立索引的情况
(1)对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
(2)对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
(3)对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
(4)当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。
索引类型
(1)按物理存储角度分:
聚集索引:表中存储的数据按照索引的顺序存储,所以查询效率快,只要找到第一个索引值记录,其余连续性的记录在物理上一样连续存放.聚集索引的缺点就是修改慢,因为为了使表记录和索引的排列顺序一致,在插入记录的时候,会对数据页重新排序
非聚集索引:非聚集索引表示数据存储在一个地方,索引存储在另一个地方,索引带有指针指向数据的存储位置,非聚集索引检索效率比聚集索引低,但对数据更新影响较小。
(2)按逻辑角度分:
普通索引:最基本的索引,它没有任何限制。它有以下几种创建方式:
a.创建索引
CREATE INDEX indexName ON mytable(username(length)); 
如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length,下同。
b.修改表结构
ALTER mytable ADD INDEX [indexName] ON (username(length)) 
c.创建表的时候直接指定
CREATE TABLE mytable(   
ID INT NOT NULL,    
username VARCHAR(16) NOT NULL,  
INDEX [indexName] (username(length))   
);  
删除索引的语法:DROP INDEX [indexName] ON mytable;
唯一索引
与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
a.创建索引
CREATE UNIQUE INDEX indexName ON mytable(username(length))
b.修改表结构
ALTER mytable ADD UNIQUE [indexName] ON (username(length))
c.创建表的时候直接指定
CREATE TABLE mytable(   
ID INT NOT NULL,    
username VARCHAR(16) NOT NULL,  
UNIQUE [indexName] (username(length))  
);
主键索引
它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引:
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
PRIMARY KEY(ID)
);
当然也可以用 ALTER 命令。记住:一个表只能有一个主键。
组合索引
多个字段上建立的索引,提高复合条件查询的速度
ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age); 
建立这样的组合索引,其实是相当于分别建立了下面三组组合索引:
usernname,city,age
usernname,city
usernname
为什么没有 city,age这样的组合索引呢?
这是因为MySQL组合索引“最左前缀”的结果。简单的理解就是只从最左面的开始组合。并不是只要包含这三列的查询都会用到该组合索引,下面的几个SQL就会用到这个组合索引:
SELECT * FROM mytable WHREE username=“admin” AND city=“郑州”
SELECT * FROM mytable WHREE username=“admin”
而下面几个则不会用到:
SELECT * FROM mytable WHREE age=20 AND city=“郑州”
SELECT * FROM mytable WHREE city=“郑州”
索引失效的情况
(1)条件中用or(这就是为什么少用or的原因)。使用or,又想索引生效,只能将条件中的每个列都加上索引。
(2)对于多列(复合、联合)索引,不是使用的第一部分,则不会使用索引。(最左匹配原则或者叫做最左前缀原则)。 比如:Index_SoftWareDetail索引包含(a,b,c) 三列,但是查询条件里面,没有a,b 列,只有c 列,那么 Index_SoftWareDetail索引也不起作用。例如:bc c acb bac 都是不行的
(3)like的模糊查询以%开头,索引失效
(4)如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不会使用索引
(5)如果MySQL预计使用全表扫描要比使用索引快,则不使用索引
(6)判断索引列是否不等于某个值时。‘!=’操作符。比如:select * from SoftWareDetailInfo where SoftUseLine != 0
(7)对索引列进行运算。这里运算包括±*/等运算。也包括使用函数。比如:select * from SoftWareDetailInfo where SoftUseLine +0= 0;此时索引不起作用。select * from SoftWareDetailInfo where count(SoftUseLine) = 0;此时索引也不起作用。也就是说如果不是直接判断索引字段列,而是判断运算或其它函数处理后的索引列索引均不起作用。
(8)索引字段进行判空查询时。也就是对索引字段判断是否为NULL时。语句为is null 或is not null。比如:select * from SoftWareDetailInfo where CreateTime is null 此时就不检索time字段上的索引表了。也就是索引在这条语句执行时失效了。
(9)范围列可以用到索引(联合索引必须是最左前缀),但是范围列后面的列无法用到索引
数据库索引底层原理
一般的查找算法有顺序查找、折半查找、快速查找等,但是每种查找算法都只能应用于特定的数据结构之上,例如顺序查找依赖于顺序结构,折半查找通过二叉查找树或红黑树实现二分搜索。这样的索引数据结构还是会对数据库的数据结构有要求,而且对磁盘IO的操作依旧很频繁。因此采用了B树和B+ 树.
(1)B+树是一种平衡多路查找树,且具有以下特点:
内节点不存储data,只存储key和指针;
叶子节点不存储指针,存储key和data。
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B树算法减少定位记录时所经历的中间过程,从而加快存取速度。
B+树查找过程:

如上图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
(2)带有顺序访问指针的B+树(更有利于区间查询)
MyISAM索引

这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:

同样也是一颗B+Tree,叶节点的data域存放的是数据记录的地址,MyISAM的索引方式也叫做“非聚集”的。MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。 
InnoDB索引

与MyISAM索引的不同: MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键(因此必须为表指定主键,如果没有指定,系统会默认指定一个或者自动生成主键),因此InnoDB表数据文件本身就是主索引。 这种索引叫做聚集索引。 InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。例如,下图为定义在Col3上的一个辅助索引:

这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
(3)MySQL为什么要采用 B+ 树存储索引结构呢而不是 B树?
B树的每一个节点,存储的是关键字和对应的数据地址,而B+树的非叶子节点只存关键字,不存数据地址。因此B+树的每一个非叶子节点存储的关键字是远远多于B树的,B+树的叶子节点存放关键字和数据,从树的高度上来说,B+树的高度要小于B树,使用的磁盘I/O次数少,因此查询会更快一些。(相同数量的节点数,B+树存放的数据多,相同数量的数据,B+树节点数就少 -> 树的高度可能就小 -> 查询的效率就会高)
B树由于每个节点都存储关键字和数据,因此离根节点近的数据,查询的就快,离根节点远的数据,查询的就慢,耗时不均匀;B+树所有的数据都存在叶子节点上,因此在B+树上搜索关键字,找到对应数据的时间是比较平均的,没有快慢之分。
在B树上如果做区间查找,遍历的节点是非常多的;B+树所有叶子节点被连接成了有序链表结构,因此做整表遍历和区间查找是非常容易的。
数据库中主键和索引的区别。
(1)主键是能够唯一标识表中某一行的属性或属性组,索引可以提高查询的速度。 主键可以保证记录的唯一和主键域非空,数据库管理系统对于主键自动生成唯一索引,所以主键也是一个特殊的索引。
(2)主键一定是唯一性索引,唯一性索引并不一定就是主键。
(3)一个表中可以有多个唯一性索引,但只能有一个主键。
(4)主键列不允许空值,而唯一性索引列允许空值。
28、数据库的事务ACID
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
(1)事务的四个特性:
原子性:操作要么全部发生,否则全部不发生。
一致性:在事务开始前和结束后,数据库的完整性约束没有被破坏
隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。
持久性(事务结束后,对数据库的更改不会回滚):事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
(2)事务的实现原理:
隔离性:通过锁来实现
原子性和持久性:通过redo log 来实现
一致性:通过undo来实现
a.Undo原理:(备份旧数据)
在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为Undo Log)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。
b.Redo原理:(保存最新数据)
和Undo Log相反,Redo Log记录的是新数据的备份。在事务提交前,只要将Redo Log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是Redo Log已经持久化。系统可以根据Redo Log的内容,将所有数据恢复到最新的状态。
29、四种隔离级别
隔离级别规定了:在一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。低级别的隔离可以执行更高级别的并发,性能好,但是会出现脏读和幻读的现象。如果不考虑隔离性,事务存在3种并发访问问题:
脏读(dirty read):两个事务,一个事务读取到了另一个事务未提交的数据,这便是脏读。
不可重复读:一个事务中两次读取的数据的内容不一致
幻读(phantom read):两个事务,事务A与事务B,事务A在自己执行的过程中,执行了两次相同查询,第一次查询事务B未提交,第二次查询事务B已提交,从而造成两次查询结果不一样,这个其实被称为不可重复读;如果事务B是一个会影响查询结果的insert操作,则好像新多出来的行像幻觉一样,因此被称为幻读。其他事务的提交会影响在同一个事务中的重复查询结果。(一个事务中两次读取的数据的数量不一致,因为另一个事务是insert操作)

(1)READ UNCOMMITTED (未提交读) :隔离级别:0;哪个问题都不能解决
原理:事务A和事务B,事务B可以读取事务A未提交的记录。会出现脏读,因为事务A可能会回滚操作,导致数据发生变化。
(2)READ COMMITTED (提交读) :隔离级别:1;可以解决脏读 。
原理:事务中只能看到已提交的修改,提交读这种隔离级别保证了读到的任何数据都是提交的数据,避免了脏读,但是不保证事务重新读的时候能读到相同的数据,因为在每次数据读完之后其他事务可以修改刚才读到的数据。
(3)REPEATABLE READ (可重复读) :隔离级别:2. 可以解决脏读和不可重复读,实现不幻读,需要加锁。
原理:在InnoDB中是这样的:RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),因此不存在幻读现象。但是标准的RR只能保证在同一事务中多次读取同样记录的结果是一致的,而无法解决幻读问题。Mysql默认的隔离级别是RR。
InnoDB的幻读是依靠MVCC的实现机制来解决的(增加系统版本号,每次事务操作,会比较系统版本号):InnoDB为每行记录添加了一个版本号(系统版本号),每当修改数据时,版本号加一。在读取事务开始时,系统会给事务一个当前版本号,事务会读取版本号<=当前版本号的数据,这时就算另一个事务插入一个数据,并立马提交,新插入这条数据的版本号会比读取事务的版本号高,因此读取事务读的数据还是不会变。
例如:此时books表中有5条数据,版本号为1
事务A,系统版本号2:select * from books;因为1<=2所以此时会读取5条数据。
事务B,系统版本号3:insert into books …,插入一条数据,新插入的数据版本号为3,而其他的数据的版本号仍然是2,插入完成之后commit,事务结束。
事务A,系统版本号2:再次select * from books;只能读取<=2的数据,事务B新插入的那条数据版本号为3,因此读不出来,解决了幻读的问题。
(4)SERIALIZABLE (可串行化):隔离级别:3.
原理:该隔离级别会在读取的每一行数据上都加上锁,退化为基于锁的并发控制,即LBCC。可以解决脏读不可重复读和幻读—相当于锁表
需要注意的是,MVCC只在RC和RR两个隔离级别下工作,其他两个隔离级别都和MVCC不兼容。
30、数据库删除表数据操作
drop、truncate、delete
(1)drop table 表名称
drop (删除表):删除内容和定义,释放空间。简单来说就是把整个表去掉.以后要新增数据是不可能的,除非新增一个表。drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger),索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid。
(2)truncate table 表名称
truncate (清空表中的数据):删除内容、释放空间但不删除定义(保留表的数据结构)。与drop不同的是,只是清空表数据而已。
注意:truncate 不能删除行数据,要删就要把表清空。
(3)delete from 表名称 where 列名称 = 值
delete (删除表中的数据):delete 语句用于删除表中的行。delete语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存,以便进行进行回滚操作。
drop、truncate、delete的区别
(1)truncate与不带where的delete :只删除数据,而不删除表的结构
(2)truncate table 删除表中的所有行,但表结构及其列、约束、索引等保持不变。如果想保留标识计数值,请改用delete。
Delete:Id列标识列,因之前delete过行数据,所以会出现标识列不连续(体现了delete删除是不释放空间的)

Truncate:同样Id是标识列,发现插入数据的时候,标识列连续了(体现了truncate删除是释放空间)

(3)如果要删除表定义及其数据,请使用 drop table 语句。
(4)执行速度,一般来说: drop> truncate > delete。
(5)delete语句是数据库操作语言(dml),这个操作会放到 rollback segement中,事务提交之后才生效;如果有相应的 trigger,执行的时候将被触发。
(6)truncate、drop 是数据库定义语言(ddl),操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发 trigger。
31、上网的时候界面有时候会弹出广告,是什么原因?
因为 HTTP 是明文传输的,在传输过程中,在一些路由节点中很容易被别人捕获,识别你的内容,再在传输的数据中添加一些广告代码,最后数据传到你的手机里就显示成这样了,很不爽。而 HTTPS 能很有效的解决这个现象,我们在数据传输之前,双方商量一个加密的密码,然后俩个人之间的通讯都用这个密码进行加密,即便在路由节点被第三方捕获,它没有密码,看到的也是一堆乱码,没有办法在合适的位置插入广告代码。
32、数据库都有什么锁?
数据库锁的目的:处理并发问题。
并发控制的主要技术手段:乐观锁、悲观锁和时间戳。
数据库锁可以分为两类:悲观锁和乐观锁。悲观锁一般就是我们通常说的数据库锁机制,乐观锁一般是指用户自己实现的一种锁机制。
(一)悲观锁:对于数据被外界修改持保守态度,认为数据随时会修改,所以整个数据处理中需要将数据加锁。悲观锁一般都是依靠关系数据库提供的锁机制,事实上关系数据库中的行锁,表锁,读锁,写锁都是悲观锁。
(1)悲观锁按照使用性质划分:
共享锁(Share locks简记为S锁):也称读锁,事务A对对象T加S锁,其他事务也只能对T加S,多个事务可以同时读,但不能有写操作,直到A释放S锁。
排它锁(Exclusivelocks简记为X锁):也称写锁,事务A对对象T加X锁以后,其他事务不能对T加任何锁,只有事务A可以读写对象T直到A释放X锁。
更新锁(简记为U锁):用来预定要对此对象施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的对象将要被更新时,则升级为X锁,主要是用来防止死锁的。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排它锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个对象申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就可以避免死锁。
(2)悲观锁按照作用范围划分:
行锁:锁的作用范围是行级别,数据库能够确定哪些行需要锁的情况下使用行锁,如果不知道会影响哪些行的时候就会使用表锁。
表锁:锁的作用范围是整张表。
(二)乐观锁:每次操作数据的时候认为没有人会来修改它,所以不去加锁,但是在更新的时候会去判断在此期间数据有没有被修改,需要用户自己去实现。既然都有数据库提供的悲观锁可以方便使用为什么要使用乐观锁呢?对于读操作远多于写操作的时候,大多数都是读取,这时候一个更新操作加锁会阻塞所有读取,降低了吞吐量。最后还要释放锁,锁是需要一些开销的,我们只要想办法解决极少量的更新操作的同步问题。换句话说,如果是读写比例差距不是非常大或者你的系统没有响应不及时,吞吐量瓶颈问题,那就不要去使用乐观锁,它增加了复杂度,也带来了额外的风险。
(1)乐观锁实现方式:
版本号(记为version):就是给数据增加一个版本标识,在数据库上就是表中增加一个version字段,每次更新把这个字段加1,读取数据的时候把version读出来,更新的时候比较version,如果还是开始读取的version就可以更新了,如果现在的version比老的version大,说明有其他事务更新了该数据,并增加了版本号,这时候得到一个无法更新的通知,用户自行根据这个通知来决定怎么处理,比如重新开始一遍。这里的关键是判断version和更新两个动作需要作为一个原子单元执行,否则在你判断可以更新以后正式更新之前有别的事务修改了version,这个时候你再去更新就可能会覆盖前一个事务做的更新,造成第二类丢失更新,所以你可以使用update … where … and version=”old version”这样的语句,根据返回结果是0还是非0来得到通知,如果是0说明更新没有成功,因为version被改了,如果返回非0说明更新成功。
时间戳(timestamp):和版本号基本一样,只是通过时间戳来判断而已,注意时间戳要使用数据库服务器的时间戳不能是业务系统的时间。
待更新字段:和版本号方式相似,只是不增加额外字段,直接使用有效数据字段做版本控制信息,因为有时候我们可能无法改变旧系统的数据库表结构。假设有个待更新字段叫count,先去读取这个count,更新的时候去比较数据库中count的值是不是我期望的值(即开始读的值),如果是就把我修改的count的值更新到该字段,否则更新失败。
所有字段:和待更新字段类似,只是使用所有字段做版本控制信息,只有所有字段都没变化才会执行更新。
(2)乐观锁几种方式的区别:
新系统设计可以使用version方式和timestamp方式,需要增加字段,应用范围是整条数据,不论那哪个字段修改都会更新version,也就是说两个事务更新同一条记录的两个不相关字段也是互斥的,不能同步进行。旧系统不能修改数据库表结构的时候使用数据字段作为版本控制信息,不需要新增字段,待更新字段方式只要其他事务修改的字段和当前事务修改的字段没有重叠就可以同步进行,并发性更高。
33、线程的sleep和wait的区别
sleep():
属于Thread类,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态
sleep方法没有释放锁
sleep必须捕获异常
sleep可以在任何地方使用
wait():
属于Object,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法或等待超时唤醒该进程
wait方法释放了锁
wait不需要捕获异常
wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用
sleep(1000)和wait(1000)的区别:
Thread.Sleep(1000) 意思是在未来的1000毫秒内本线程不参与CPU竞争,1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。
wait(1000)表示将锁释放1000毫秒,到时间后如果锁没有被其他线程占用,则再次得到锁,然后wait方法结束,执行后面的代码,如果锁被其他线程占用,则等待其他线程释放锁。注意,设置了超时时间的wait方法一旦过了超时时间,并不需要其他线程执行notify也能自动解除阻塞,但是如果没设置超时时间的wait方法必须等待其他线程执行notify。
34、死锁
概念
多个并发进程因争夺系统资源而产生相互等待的现象。
原理
当一组进程中的每个进程都在等待某个事件发生,而只有这组进程中的其他进程才能触发该事件,这就称这组进程发生了死锁。
本质原因
系统资源有限;进程推进顺序不合理。
死锁产生的4个必要条件
(1)互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
(2)占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
(3)不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
(4)循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。
避免死锁的方法
(1)死锁预防 ----- 确保系统永远不会进入死锁状态
由于互斥条件是非共享资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产生死锁的其他三个条件。
1.破坏“占有且等待”条件
方法1:所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源。
优点:简单易实施且安全。
缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成资源浪费。使进程经常发生饥饿现象。
方法2:该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。
2.破坏“不可抢占”条件
当一个已经持有了一些资源的进程在提出新的资源请求没有得到满足时,它必须释放已经保持的所有资源,待以后需要使用的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。
该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作实效等,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。
3.破坏“循环等待”条件
可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当一个进程占有编号为i的资源时,那么它下一次申请资源只能申请编号大于i的资源。如图所示:

这样虽然避免了循环等待,但是这种方法是比较低效的,资源的执行速度回变慢,并且可能在没有必要的情况下拒绝资源的访问,比如说,进程c想要申请资源1,如果资源1并没有被其他进程占有,此时将它分配个进程c是没有问题的,但是为了避免产生循环等待,该申请会被拒绝,这样就降低了资源的利用率。
(2)避免死锁 ----- 在使用前进行判断,只允许不会产生死锁的进程申请资源的死锁避免是利用额外的检验信息,在分配资源时判断是否会出现死锁,只在不会出现死锁的情况下才分配资源。两种避免办法:
1、如果一个进程的请求会导致死锁,则不启动该进程
2、如果一个进程的增加资源请求会导致死锁 ,则拒绝该申请。
死锁避免的优点:不需要死锁预防中的抢占和重新运行进程,并且比死锁预防的限制要少。
死锁避免的限制:
必须事先声明每个进程请求的最大资源量
考虑的进程必须无关的,也就是说,它们执行的顺序必须没有任何同步要求的限制。
分配的资源数目必须是固定的。
在占有资源时,进程不能退出。
(3)死锁检测与解除 ----- 在检测到运行系统进入死锁,进行恢复。
常用的解除死锁的方法:
抢占资源:从一个或多个进程中抢占足够数量的资源分配给死锁进程,以解除死锁状态。
终止(或撤销)进程:终止或撤销系统中的一个或多个死锁进程,直至打破死锁状态。
a、终止所有的死锁进程。这种方式简单粗暴,但是代价很大,很有可能会导致一些已经运行了很久的进程前功尽弃。
b、逐个终止进程,直至死锁状态解除。该方法的代价也很大,因为每终止一个进程就需要使用死锁检测来检测系统当前是否处于死锁状态。另外,每次终止进程的时候终止那个进程呢?每次都应该采用最优策略来选择一个“代价最小”的进程来解除死锁状态。
一般根据如下几个方面来决定终止哪个进程:
进程的优先级;
进程已运行时间以及运行完成还需要的时间;
进程已占用系统资源;
进程运行完成还需要的资源;
终止进程数目;
进程是交互还是批处理。
35、地址解析协议ARP
ARP简介
地址解析协议,其基本功能为透过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。它是IPv4中网络层必不可少的协议,不过在IPv6中已不再适用,并被邻居发现协议(NDP)所替代。
ARP工作流程
假设主机A和B在同一个网段,主机A要向主机B发送信息,具体的地址解析过程如下:
(1) 主机A首先查看自己的ARP表,确定其中是否包含有主机B对应的ARP表项。如果找到了对应的MAC地址,则主机A直接利用ARP表中的MAC地址,对IP数据包进行帧封装,并将数据包发送给主机B。
(2) 如果主机A在ARP表中找不到对应的MAC地址,则将缓存该数据报文,然后以广播方式发送一个ARP请求报文。ARP请求报文中的发送端IP地址和发送端MAC地址为主机A的IP地址和MAC地址,目标IP地址和目标MAC地址为主机B的IP地址和全0的MAC地址。由于ARP请求报文以广播方式发送,该网段上的所有主机都可以接收到该请求,但只有被请求的主机(即主机B)会对该请求进行处理。
(3) 主机B比较自己的IP地址和ARP请求报文中的目标IP地址,当两者相同时进行如下处理:将ARP请求报文中的发送端(即主机A)的IP地址和MAC地址存入自己的ARP表中。之后以单播方式发送ARP响应报文给主机A,其中包含了自己的MAC地址。
(4) 主机A收到ARP响应报文后,将主机B的MAC地址加入到自己的ARP表中以用于后续报文的转发,同时将IP数据包进行封装后发送出去。
ARP报文格式

先要清楚,一般说以太网地址就是指MAC地址。
字段1是ARP请求的目的以太网地址,全1时代表广播地址。
字段2是发送ARP请求的以太网地址。
字段3以太网帧类型表示的是后面的数据类型,ARP请求和ARP应答这个值为0x0806。
字段4表示硬件地址的类型,硬件地址不只以太网一种,是以太网类型时此值为1。
字段5表示要映射的协议地址的类型,要对IPv4地址进行映射,此值为0x0800。
字段6和7表示硬件地址长度和协议地址长度,MAC地址占6字节,IP地址占4字节。
字段8是操作类型字段,值为1,表示进行ARP请求;值为2,表示进行ARP应答;值为3,表示进行RARP请求;值为4,表示进行RARP应答。
字段9是发送端ARP请求或应答的硬件地址,这里是以太网地址,和字段2相同。
字段10是发送ARP请求或应答的IP地址。
字段11和12是目的端的硬件地址和协议地址。
免费ARP
免费ARP指主机发送ARP查找自己的IP地址,通常发生在系统引导期间进行接口配置时。与标准ARP的区别就是免费ARP分组的目的IP地址字段封装的是自己的IP地址,即向所在网络请求自己的MAC地址。
免费ARP的作用有:

  1. 一个主机可以通过它来确定另一个主机是否设置了相同的 IP地址。
    正常情况下发送免费ARP请求不会收到ARP应答,如果收到了一个ARP应答,则说明网络中存在与本机相同的IP地址的主机,发生了地址冲突。
    2)更新其他主机高速缓存中旧的硬件地址信息。
    如果发送免费ARP的主机正好改变了硬件地址,如更换了接口卡。
    其他主机接收到这个ARP请求的时候,发现自己的ARP高速缓存表中存在对应的IP地址,但是MAC地址不匹配,那么就需要利用接收的ARP请求来更新本地的ARP高速缓存表表项。
    3)网关利用免费ARP防止ARP攻击
    有些网关设备在一定的时间间隔内向网络主动发送免费ARP报文,让网络内的其他主机更新ARP表项中的网关MAC地址信息,以达到防止或缓解ARP攻击的效果。
    4)利用免费ARP进行ARP攻击
    ARP协议并不只在发送了ARP请求才接收ARP应答,计算机只要接收到ARP应答数据包,就会使用应答中的IP和MAC地址对本地的ARP缓存进行更新。
    主机可以构造虚假的免费ARP应答,将ARP的源MAC地址设为错误的MAC地址,并把这个虚假的免费ARP应答发送到网络中,那么所有接收到这个免费ARP应答的主机都会更新本地ARP表项中相应IP地址对应的MAC地址。更新成功后,这些主机的数据报文就会被转发到错误的MAC地址,从而实现了ARP欺骗的攻击。
    ARP攻击
    (1)ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的进行。
    A的地址为:IP:192.168.10.1 MAC: AA-AA-AA-AA-AA-AA
    B的地址为:IP:192.168.10.2 MAC: BB-BB-BB-BB-BB-BB
    C的地址为:IP:192.168.10.3 MAC: CC-CC-CC-CC-CC-CC
    A和C之间进行通讯.但是此时B向A发送一个自己伪造的ARP应答,而这个应答中的数据为发送方IP地址是192.168.10.3(C的IP地址),MAC地址是BB-BB-BB-BB-BB-BB(C的MAC地址本来应该是CC-CC-CC-CC-CC-CC,这里被伪造了)。当A接收到B伪造的ARP应答,就会更新本地的ARP缓存(A被欺骗了),这时B就伪装成C了。同时,B同样向C发送一个ARP应答,应答包中发送方IP地址四192.168.10.1(A的IP地址),MAC地址是BB-BB-BB-BB-BB-BB(A的MAC地址本来应该是AA-AA-AA-AA-AA-AA),当C收到B伪造的ARP应答,也会更新本地ARP缓存(C也被欺骗了),这时B就伪装成了A。这样主机A和C都被主机B欺骗,A和C之间通讯的数据都经过了B。主机B完全可以知道他们之间说的什么。这就是典型的ARP欺骗过程。
    (2)双向欺骗与单向欺骗的区别
    单向欺骗: A要跟C正常通讯。B给A说我才是C,那么A就把数据就给C了,此时A就把原本给C的数据给了B了,A修改了本地的缓存表,但是C跟A的通讯还是正常的。只是A跟C的通讯不正常。
    双向欺骗: A要跟C正常通讯。B对A说我是C,B对C说我是A,那么这样的情况下A跟C的ARP缓存表全部修改了,发送的数据全部发送到B那里去了。

36、数据库三大范式
(1)第一范式(1NF):确保每列的原子性,即列不能够再分成其他几列)。
(2)第二范式(2NF):满足第一范式的基础上,确保非主键列都不存在部分函数依赖(都和主键相关,而不能只与主键的某一部分相关)。
(3)第三范式(3NF):满足第二范式的基础上,确保非主键列直接依赖于主键,不能存在传递依赖。
37、指针和数组的区别
(1)空间分配:数组是静态分配,且分配的空间是连续的;指针是动态分配,分配的空间不一定是连续的。
(2)安全性:使用数组可能会造成数组越界;指针使用时可能会造成内存泄漏。
(3)访问效率:数组直接访问数据;指针是间接访问数据。
(4)函数形参:一维数组用元素指针来接;指针用指针的指针来接。
(5)处理数据:指针偏向于地址的处理;数组偏向于数据的处理。
38、递归与动态规划的区别
两个方法都是通过把一个大的问题化小,小到可以一步解决。归是采用从顶层向下层层递归,直到递归出口之后得到结果。它调用自身(参数的值更小),具有终止条件,可以直接计算其结果。而动态规划是从本来需要递归的最底层开始层层往上计算出结果。我们可以按照从最小开始的顺序计算所有函数值来求任何类似函数的值,在每一步使用先前已经计算出的值来计算当前值,我们称这项技术为自底向上的动态规划。
39、线性表和单链表的区别
(1)线性表
从栈中开辟一片连续的存储单元对数据进行存储,存储结构和数据逻辑相同;
需要预分配存储空间,大了浪费,小了上溢。空间大小固定,自由度小;
通过下标获取元素,支持随机访问;
查找元素快,平均时间复杂度为O(1);
增删元素慢,增删的平均时间复杂度为O(n);
(2)链表
从堆中动态开辟出的不连续存储单元,通过指针在计算机存储单元上对数据逻辑进行链接
不需要预先分配存储空间,空间大小动态变化,自由度大,扩展性好;
通过结构体next数据项中存储的指针寻找并获取元素,不支持随机访问;
查找元素慢,查找平均时间复杂度为O(n);
增删元素快,增删的平均时间复杂度为O(1);
综上,在查找操作频繁时候考虑采用顺序表,在增删操作频繁时候考虑采用链表。
40、Linux命令
常用基本命令:
ls   显示文件或目录

 -l           列出文件详细信息l(list)

 -a          列出当前目录下所有文件及目录,包括隐藏的a(all)

mkdir 创建目录

 -p           创建目录,若无父目录,则创建p(parent)

cd 切换目录

touch 创建空文件

echo 创建带有内容的文件。

cat 查看文件内容

cp 拷贝

mv 移动或重命名

rm 删除文件

 -r            递归删除,可删除子目录及文件

 -f            强制删除

find 在文件系统中搜索某文件

wc 统计文本中行数、字数、字符数

grep 在文本文件中查找某个字符串

rmdir 删除空目录

tree 树形结构显示目录,需要安装tree包

pwd 显示当前目录

ln 创建链接文件

more、less 分页显示文本文件内容

head、tail 显示文件头、尾内容

ctrl+alt+F1 命令行全屏模式
系统管理命令:
stat 显示指定文件的详细信息,比ls更详细

who 显示在线登陆用户

whoami 显示当前操作用户

hostname 显示主机名

uname 显示系统信息

top 动态显示当前耗费资源最多进程信息

ps 显示瞬间进程状态 ps -aux

du 查看目录大小 du -h /home带有单位显示目录信息

df 查看磁盘大小 df -h 带有单位显示磁盘信息

ifconfig 查看网络情况

ping 测试网络连通

netstat 显示网络状态信息

man 命令不会用了,找男人 如:man ls

clear 清屏

alias 对命令重命名 如:alias showmeit=“ps -aux” ,另外解除使用unaliax showmeit

kill 杀死进程,可以先用ps 或 top命令查看进程的id,然后再用kill命令杀死进程。
打包压缩命令:
gzip:

bzip2:

tar: 打包压缩

 -c              归档文件

 -x              压缩文件

 -z              gzip压缩文件

 -j              bzip2压缩文件

 -v              显示压缩或解压缩过程 v(view)

 -f              使用档名

例:

tar -cvf /home/abc.tar /home/abc 只打包,不压缩

tar -zcvf /home/abc.tar.gz /home/abc 打包,并用gzip压缩

tar -jcvf /home/abc.tar.bz2 /home/abc 打包,并用bzip2压缩

当然,如果想解压缩,就直接替换上面的命令 tar -cvf / tar -zcvf / tar -jcvf 中的“c” 换成“x” 就可以了。

关机重启命令:

Linux管道:
将一个命令的标准输出作为另一个命令的标准输入。也就是把几个命令组合起来使用,后一个命令除以前一个命令的结果。
例:grep -r “close” /home/* | more 在home目录下所有文件中查找,包括close的文件,并分页输出。
Vim使用:
vim三种模式:命令模式、插入模式、编辑模式。使用ESC或i或:来切换模式。
命令模式下:
:q 退出
:q! 强制退出
:wq 保存并退出
:set number 显示行号
:set nonumber 隐藏行号
/apache 在文档中查找apache 按n跳到下一个,shift+n上一个
yyp 复制光标所在行,并粘贴
h(左移一个字符←)、j(下一行↓)、k(上一行↑)、l(右移一个字符→)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值