测试开发面试(八)——进程与线程、python数据结构、数据库

一、对公司有何了解?

星网锐捷是位于福州国内领先的企业级网络通讯终端设备视频应用产品及系统解决方案供应商,秉承的是“融合科技创新,构建智慧未来”的经营理念,公司旗下有多个子公司:星网物联、锐捷网络等。是中国数据通信解决方案领导品牌

二、进程与线程的区别?

多任务:同一时间做多个事情,进程和线程就是为了实现多任务同时进行,实现更高的效率
一个程序是静止的,操作系统想要程序运行起来的话,那么操作系统是要把资源分配给进程,然后用进程去启动我们的程序,运行我们的程序。进程中有这个程序运行所需要的所有资源,所以程序要想运行起来的话必须要依赖进程。
进程一个程序执行起来,代码+用到的资源称之为进程,他是操作系统分配资源的基本单元,每个进程中必须有一个主线程,可以实现多任务。
线程是进程中的一个实体,是cpu调度和分派的基本单元,线程必须依赖进程存在,他是比进程更小的独立运行的基本单元组,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器、寄存器和栈)但是与同属于一个进程的其他线程共享进程所拥有的全部资源。

三、进程与线程的区别?

1、进程之间不共享资源、而线程之间是共享同一个进程的所有资源,这里会有一个问题就是资源竞争的问题,存在安全问题,解决的方法就是互斥锁,当一个线程要用这块资源的时候给加锁,用完之后释放给下一个进程使用;
2、创造进程的开销要比创建线程的资源开销要大;
3、进程是操作系统分配的基本单位,线程是cpu调度的基本单位;
4、线程是不能独立运行的,必须依存在进程中;
5、多进程开发比单进程多线程开发要强,因为多线程之间是共享资源的,共享资源的话就会有资源竞争,存在线程安全性的问题,所以说不稳定。

四、多进程与多线程,进程之间是如何进行通信的?

进程与进程之间是独立存在的,每个进程都会维护一份资源,进程之间是不共享资源的
多线程:多线程实现多任务,一个进程中创建了多个线程,多线程之间是共享进程中的所有资源,线程消耗的资源不是很多。

进程之间的通信方式?

管道:将一个进程的输出作为另一个进程的输入,实现进程之间的通信
消息队列:我们的内核给我们创建一个消息队列,操作系统中的多个进程都可以操作这个消息队列,可以往里面发送消息也可以从里面接收消息。
共享内存+信号量线程之间可以共享进程的资源,进程之间不可以共享资源,开辟一篇连续的存储空间来让所有的进程都可以访问到这篇存储空间,就像线程之间的访问一样,速度非常快,减少了不必要的拷贝、传输等。但是多进程同时访问的话就会出现进程不安全的问题,所以就在共享内存的基础上引入了信号量,说明这个共享内存在同时间内只能被一个进程访问。
信号:当我们系统出现故障的时候,信号立即通知到我们,类似于终端,就是说让内核放下手头的事情,去处理现在出现的问题提,也即是说一个进程可以向另一个进程发送一个消息
socket:更适用于网络之间的传输,通过进程端口来建立http的套接字来实现,网络之间的传输也是进程之间的传输。

多线程死锁问题?

原因
死锁产生的四个必要条件
互斥条件:即当资源被一个线程使用(占有)时,别的线程不能使用
不可抢占:资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
请求和保持条件:即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
循环等待条件:即存在一个等待队列:T1占有T2的资源,T2占有T3的资源,T3占有T1的资源。这样就形成了一个等待环路。
线程1 首先已经占有对象1,接着试图占有对象2
线程2 首先已经占有对象2,接着试图占有对象1
线程1 等待线程2释放对象2
线程2等待线程1释放对象1
这样线程1和线程2就会一直相互等待下去,(死锁)

如何避免?

1 避免一个线程同时获取多个锁
2、避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
3、尝试使用定时锁,使用lock.tryLock来代替使用内置锁。
4 如果项目是分布式也要考虑

并发与并行?

并发是指任务数多于cpu核数,通过操作系统各种调度算法,是西安多个任务的一起执行,实际上总有一些任务不在执行,因为切换速度相当快,看上去在一起同时执行,单核cpu就是限制性一个任务,完成后切换到下一个任务执行,速度非常快。
并行:指的是任务数小于等于cpu核数,也就是多任务一起同时执行

osi七层模型

从下到上的话依次是物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
物理层:二进制的比特流进行传输的。
数据链路层:将数据封装成帧传输的,功能有差错控制、MAC寻址、流量控制
网络层:IP寻址、路由选择。数据是包的形式。主要有IP、ARP、RARP、ICMP(是IP主机、路由器之间传递和控制消息的协议)这些协议
传输层:是一个端到端的传输,这里的话数据会封装成段的形式。TCP和UDP两大协议。TCP的话提供了可靠传输的作用(流量控制、差错控制这些作用)
会话层:就是说不同机器上的用户之间建立会话的这么一个关系,
表示层:数据的表现形式,特定的功能的实现,比如说数据的加密。
应用层:提供了应用的接口,主要有FTP(文件传输协议)、Telnet(远程控制协议)、HTTP、SMTP和pop3(是邮件传送协议)

tcp/IP协议族?

TCP/IP协议族按照层次由上到下,层层包装。应用层,这里面有http,ftp 等等我们熟悉的协议。而第二层则是传输层,著名的TCP和UDP协议就在这个层次。第三层是网络层,IP协议就在这里,它负责对数据加上IP地址和其他的数据以确定传输的目标。第四层是数据链路层,这个层次为待传送的数据加入一个以太网协议头,并进行CRC编码,为最后的数据传输做准备。

五、ARP地址解析协议是什么?

1、首先、每个主机都会在自己的ARP缓冲区中建立一个ARP列表,表示的是IP地址和MAC地址之间的对应关系。
2、当源主机要发送数据时,会先检查ARP列表中是否含有对应IP地址的目的主机的MAC地址,如果有的话,直接发送数据。如果没有,就向本网段的所有主机广播发送ARP数据包,这个数据包包括:源主机IP地址、源主机MAC、目的主机IP地址。
3、当所有主机收到这个ARP包后,会先检查数据包中的IP地址是否是自己的IP地址,如果不是则直接忽略掉。如果是则先从数据包中取出源主机IP和MAC写入到自己的ARP列表中去,如果已经存在则覆盖掉,然后将自己的MAC地址写入到响应包中,告诉源主机自己就是它要找的MAC地址。
4、当源主机收到响应包后,将目的主机的IP和MAC地址写入到ARP列表中,并利用这个信息来发送数据。(如果源主机已一直没有收到ARP响应包,则表示ARP查询失败)然后广播发送ARP请求,单播发送ARP响应,是这么一个过程。

六、ping一个网站的过程是什么?

当我们ping一个网站的时候:1、首先当我们ping一个域名的时候,通过DNS把我们输入的域名解析成IP地址,得到对应的IP地址

4、http和https的区别?

http不安全,信息是铭文传输的https安全的,是具有安全性的ssl传输协议
http和https的连接方式不一样,http是连接简单,是无状态的(意思就是在数据包的发送、传输和接收都是相互独立的,也就是通信双方都不长久的维持对方的任何信息)。
http标准端口是80,https标准端口是443
主要区别是ssl证书:从安全性上来讲,https相较于http是更加安全的,因为https是在http协议的基础上新增一层加密层,当进行数据交互的时候,他会基于一个更为安全的信息通道,来实现到这个数据交互。
ssl加密证书,在通信的时候,首先是我们需要有这个加密证书的:过程就是当客户端和服务端建立连接以后,服务端会及那个CA证书返回给客户端,证书里有公钥,客户端验证CA证书的合法性,生成随机的对称加密密钥,对称的加密密钥通过公钥加密,发送给服务端,双方通过对称密钥加密来进行通信的。

https用到的加密算法有哪些?

单向加密、对称加密算法和非对称加密算法。
使用对称加密时,加密和解密用的都是同一个密钥;
而非对称加密,则是两个密钥,公钥加密则需要私钥解密,私钥加密则需要公钥解密。不能私钥加密,私钥解密
在osi网络模型中,https的加密是在传输层完成的,因为ssl是位于传输层的。

python的数据结构的区别(数组、列表、字典、集合set等)

1、从存储空间的角度来说:列表存储相同的元素或者是相同的数据的时候列表消耗的空间要比元组的空间要大。(原因:(预分配)列表一旦创建出来就会分配一定的空间,即使是空列表也会分配。元组一旦创建,它就会直接将这一个元组直接写死在内存里面),列表和元组本质上是存储的是指针,也就是内存地址。
2、从性能上来说:列表是动态的,长度是不固定的,而元组空间是固定的
3、增删改的效率上说:列表可以直接进行append添加元素,这个列表的内存地址是不变的。元组的话,想要在元组上进行添加元素,并不是直接修改这个对象,而是现在内存中重新开辟一个内存空间,把原有的值复制过去,同时,再执行一个添加元素的操作,也就意味着进行元组当中元素的增删改,它需要先进行内存空间的申请。
两者的对比:要进行增删改的话,列表的效率要高,元组的效率要低
列表跟元组它的底层数据结构都是数组

7、python中is和==的区别?

is比较的是两者的内存地址是否相等
==比较的是两者的值是否相等

8、内存溢出和内存泄漏的区别、产生原因、以及解决方案?

内存溢出:是指程序在申请内存的时候,没有足够的内存空间供其使用。出现out of memory
内存泄漏:是指程序在申请内存后,无法释放已经申请的内存空间,一次内存泄漏影响不大可以忽略,但是多次堆积后会很严重,不管多少内存都会被耗尽。
内存泄漏会导致内存溢出
内存溢出的原因
1、内存中加载数据量过大,一次性从数据库中取过多的数据量。
2、代码中存在死循环或者在循环体内产生了过多的重复的对象
3、循环引用导致对象的引用无法清空,则不会被当作垃圾进行回收
解决方案:
1、直接增加内存
2、检查错误日志,定位问题出现的位置,检查在出现out of memory 之前有没有其他错误。
3、对代码进行分析,找出可能发生内存溢出的位置。
如何排查内存泄漏?

7、python的装饰器、迭代器和生成器是什么?

装饰器:python装饰器本质上是一个python函数,她可以让其他函数在不需要做任何代码变动的前提下添加额外功能。返回值也是一个函数对象。简单的说装饰器就是一个用来返回函数的函数。
迭代器和生成器有什么区别?他们有什么作用?
迭代器:任何实现了__iter__()和__next__()都是迭代器。 iter() 返回迭代器自身,next() 返回容器下一个值。任何迭代器对象都是可迭代对象。迭代器是一个懒加载模式,用的时候才生成列表更倾向于是一个容器类型,所有元素都在内存里,全部生成好了,但是非常耗费内存。
生成器:生成器是一种特殊的迭代器,本质是一个函数,它记住了上一次返回时在函数体中的位置。对生成器函数的第二次调用,跳转到函数上一次挂起的位置,而且记录了程序执行的上下文,生成器不仅仅记住了它的数据状态,也记住了它执行的位置。

8、堆和栈的区别是什么?

首先堆和栈都是在内存上的堆的话是一个动态的概念栈是一个静态的概念栈是在编译的时候确定的,堆是在运行的时候确定的,栈的大小是在编译的时候就确定好的,堆的话有可能是个动态变化的概念,取决于程序运行计算到那一刻的情况,从访问效率来看,因为堆是动态,栈是静态的堆的速度要慢。在访问权限方面,也就是不同函数之间的栈数据不能共享,同样也适用于多线程,不同线程的栈数据之间是不能访问的。对于堆来说,堆是在进程上的堆,只要是这个进程内的所有线程都可以访问堆数据

9、什么时候使用堆?什么时候使用栈

当数据size不确定的时候,使用堆,当确定size大小的数据的时候,使用栈。可以极大提高运行速度。

python的内存管理和垃圾回收机制

引用计数器为主,标记清除和分代回收为辅 +缓存机制
1、引用计数器:
在python中,创建的任何对象都会放在双向链表中,当创建一个对象的时候(上一个对象、下一个对象、类型、引用个数),当有另外一个引用的时候,计数器+1。每个对象都有计数器,默认值为1,当有其他变量引用这个对象时候,引用计数器会加1,当删除变量的时候,引用计数器会减少1。当引用计数器为0的时候,意味着这个对象就是垃圾了,就要进行垃圾回收。(1、对象从refchain链表移除,2、将对象进行销毁,内存归还。)
注意:会出现循环引用的问题:由于在内存中,对象没有被及时地销毁,也就是当一个对象没有被变量引用了,计数器应该变为0,但是实际上计数器不为0,所以会一直存储在内存中,也不会被当作垃圾进行回收,当这样的对象有很多的时候,就会消耗内存。导致内存泄漏。当我们关机、开机后就会发现程序正常了。
2、标记清除:
为了解决引用计数器循环引用的问题而产生的。如何来实现的呢?
在python的底层再去维护一个链表,专门存放那些可能存在循环引用的对象(列表、元组、字典、集合可能会存在循环引用问题)。
维护两个链表:一个链表存放所有的对象,另外一个链表存放可能发生循环引用的对象。在python内部某种情况下触发会去扫描可能存在循环引用的链表中的每个元素,检查是否有循环引用,如果有则让双方的引用计数器都减1,
如果是0,则垃圾回收,如果不是0则说明还有其他变量用着呢,不回收。
会有问题:1、什么时候扫描?
2、可能存在循环引用的链表扫描的代价大。(因为每次扫描耗时久)3、分代回收:解决了上面两个问题
就是将可能存在循环引用的对象维护成3个链表:
0代:0代中对象个数达到700个扫描一次
1代:0代扫描10次,1代扫面一次
2代:1代扫面10次,2代扫面1次
面试官问道内存管理和垃圾回收机制怎么回答: 首先,在python中,有维护了一个refchain的双向环状链表,这个链表中存储程序创建的所有对象,每种类型的对象中都有一个引用计数器的值,引用的个数+1、或者-1,最后当引用计数器变为0时,则会进行垃圾回收(对象从refchain中销毁)。但是在python中,对于那些可以有多个元素组成的对象可能会存在循环引用的问题。为了解决这个问题又引入了标记清除分代回收机制。在他的内部维护4个链表,分别是
1、refchain 维护所有的对象
2、2代
3、1代
4、0代
在源码的内部当达到各自的阈值时,就会触发扫描链表进行标记清除的动作(如果有循环引用则各自-1,)这样的话不仅解决了什么时候扫描的问题又解决了因为扫描对象比较多,我们给加上了0代、1代、2代的这么一个优先级。

5、确认测试是什么?回归测试是什么?

确认测试也称再测试,缺陷修复以后,验证缺陷是否真的修复。
回归测试:缺陷修复后,确保对程序的修改没有给软件其他功能未改变的部分带来新的缺陷。
6、简述测试的基本过程
(1)测试人员进行测试需求分析。
(2)测试负责人编写测试计划。
(3)测试人员根据测试需求分析设计和编写测试用例。
(4)测试人员搭建测试环境、创建测试数据、执行测试用例、提交缺陷报告并进行跟 踪、记录测试事件。
(5)进行测试评估和总结。 每一分步工作完成后都进行评审。

7、长连接和短链接的区别

长连接意味着进行一次数据传输之后,不关闭连接,长期保持连通状态,如果双方还有新的交互,新的数据传输的话,则直接复用这个连接,无需再建立一个新的连接。
短链接意味着每一次的数据传输都需要建立一个新的连接,用完马上关闭,下次再进行交互的话再进行建立连接,就是这样反复进行的过程。
优点和缺点:
长连接的优点:在于多次通信中可以省去建立连接和关闭连接的开销,总体上看,进行多次数据传输的总耗时更少。
长连接的缺点:需要花费额外的精力来保持这一个连接一直是可用的。
短链接的优点:在于每次使用的连接都是新建立起来的,可以基本上保证只要能建立起连接,数据就大概率能送达对方。还有就是哪怕这次数据传输失败,下次也会建立新的连接。总体上影响比较小。
短链接的缺点:就是每次连接都要经过三次握手和四次挥手,耗时大大增加。

8、post、get的区别?常用的请求方式有哪些?

get请求会向数据库发索取数据的请求,从而来获取信息,就比如数据库中的select查询一样,不会修改、增加数据,不会影响资源的内容。而且参数通过url传递,
post放在request body中,get请求在url中传递的参数是长度有限的,而post是没有
put请求是向服务端发送数据的,从而改变信息,用来修改数据的内容,但是不会增加数据的种类,也就是说无论进行多少次put操作,其结果并没有不同的地方。
post请求是向服务端发送数据的,但是该请求会改变数据的种类等资源,就像数据库的insert一样,会创建新的内容,几乎目前所有的提交操作都是用post请求的、
delete请求就是用来删除某个资源的。
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
我们可以这样理解:
1、post /url 创建
2、delete /url/xxx 删除
3、put /url/xxx 更新
4、get /url/xxx 查看
比如说我们:创建主题就用post修改主题就用put、获取主题内容就用get、删除的话就用delete

session和cookie的区别

cookie是本地缓存,session是临时会话,cookie的缓存是缓存在本地的,它是针对每个不同的网站需要缓存的信息会将这些缓存的内容或者说资源会缓存到本地
也就是客户端这一块儿。session是系统与用户进行交互的一种形式,session叫临时会话机制,它针对于每一个用户都会有一个临时会话,当我们用户跟服务器发生连接的时候,它除去按照http建立一个连接的tcp通道以外,如果用户需要进行交互的话,我们为了解决http这样一个无状态的这么一个形态,就是服务器和用户进行沟通的这么一个窗口,这个session不会存放于用户的本地,而是在服务器端进行管理的,这样的话,服务器会产哼非常多的session临时会话,每个session会给他一个sessionID,服务器通过这些sessionID来管理这些session,而sessionID会通过服务端返回给用户,让用户端将sessionID保存在cookie里面,所以用户通过sessionID来跟服务器进行交互。来进行数据的通信。

tcp和udp的区别

1、TCP提供面向对象的连接,通信前要进行三次握手机制的连接UDP提供无连接的传输,传输前不用建立连接
2、tcp提供****可靠的、有序的、不会丢失数据的传输,UDP提供不可靠的传输
3、tcp提供面向字节流的传输,它将信息分割成组,并在接收端将其重组,UDP提供面向数据报的传输,没有分组开销的过程。
4、tcp提供拥塞控制,流量控制机制,udp没有的。

TCP为什么可靠?

建立连接的时候三次握手机制是确认重传流量控制的基础,在传输过程中如果发生丢包或者延时的话,则发送端会进行重传,
流量控制:tcp窗口会指明双方能够发送接收的最大数据量。
tcp建立连接时,各端分配一个缓冲区来存储接收的数据,并将缓冲区的尺寸发送给另一端,接收方发送的确认消息中包含了自己剩余的缓冲区的尺寸,剩余
缓冲区的大小叫做窗口,就是所谓的滑动窗口,也就是接收端可以根据自己的状况通告窗口的大小,从而控制发送端的接收,进行流量控制。

三次握手和四次挥手的过程和目的

三次握手其实就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后续的可靠性传送做准备。实质上就是连接服务器指定端口,建立tcp连接,并且同步连接双方的序列号和确认号,交换tcp窗口大小信息。
1、刚开始客户端处于closed状态,服务端处于listen状态
进行三次握手:(1)第一次握手:客户端给服务端发送一个SYN置为1的报文,并指明客户端的初始化序列号seq=x。此时客户端处于SYN_SENT状态。
第二次握手:服务器收到客户端发送的SYN报文后,会以自己的SYN=1报文作为应答,也指定自己的初始化序列号seq=y,会把客户端的序列号 +1作为ACK的值,表示已经收到了客户端的SYN,此时服务器处于SYN_RCVD状态。
第三次握手:客户端收到SYN报文之后,会发送一个ACK确认报文,把服务器的序列号+1作为ack的值,表示已经收到了服务端的报文SYN
此时,客户端处于established状态,服务端收到了ACK之后,也处于established状态,此时,双方建立了连接。

为什么要进行三次握手?两次不行吗?

首先第一次握手是为了证明客户端的发送能力和服务器的接受能力是正常的。
第二次握手时为了证明客户端的接收能力和服务器的发送能力是正常的
第三次握手是为了证明客户端的发送能力和接受能力 服务器的发送能力和接受能力是正常的。

四次挥手

第一次挥手:A数据传输完毕需要断开连接,A的应用进程向其TCP发出连接释放报文段(FIN = 1,序号seq = u),并停止再发送数据,主动关闭TCP连接, 进入FIN-WAIT-1状态,等待B的确认。
第二次挥手:B收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),B进入CLOSE-WAIT关闭等待状态,此时的TCP处于半关闭状态, A到B的连接释放。而A收到B的确认后,进入FIN-WAIT-2状态,等待B发出的连接释放报文段。
第三次挥手:当B数据传输完毕后,B发出连接释放报文段(FIN = 1,ACK = 1,序号seq = w,确认号ack=u+1),B进入LAST-ACK(最后确认)状态,等待A 的最后确认。
第四次挥手:A收到B的连接释放报文段后,对此发出确认报文段(ACK = 1,seq=u+1,ack=w+1),A进入TIME-WAIT(时间等待)状态。
此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,A才进入CLOSE状态。

7、数据库:

show databases;显示所有的数据库
create database name;创建数据库
drop database name; 删除数据库
表的操作: create table name(
字段1,数据类型,字段属性
字段2,数据类型,字段属性
) 创建一张表
主键约束:primary key()
唯一约束:unique key()
外键约束 :foreign key()(用于两个表之间建立关系,需要指定引用主表的哪一个字段)
drop table name 删除数据表
insert into name (字段1,字段2,字段3…)values (字段1对应的值,字段2对应的值,字段3对应的值…)
修改表名:alter table 旧表名 rename 新表名;
添加字段:alter table 表名 add 字段名,数据类型;字段名,数据类型
修改字段:alter table 表名 change 原字段名,新字段名,数据类型
删除字段:alter table 表名 drop 字段名
在创建完表后添加主键约束:
alter table 表名 add constraint 主键名 primary key 表名(主键字段)
在创建完表后添加外键约束:
alter table 表名 add constraint 外键名 foreign key(外键字段) references 关联表名 (关联字段);
更新数据:update 表名 set 列名=更新至值 where 更新条件;
修改newstudent表中id=1001的数据名字为tom;
update newstudent set ‘name’ = ‘tom’ where id =1001;
删除数据:
1、delete from 表名 where 删除条件;delete删除的是整条数据,不会只删除单个列。数据可以进行回滚恢复
2、truncate table 表名 where 删除条件;truncate删除的是表中所有的行,但是表的结构、约束、索引等不会变,删除数据不能恢复
3、drop table 表名;删除整个表
创建索引:create index 索引名 on 表名(创建索引的列)
删除索引:drop index 索引名
查看索引:show index from 表名

数据库事务是什么?

数据库的事务是程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作过程中所做的所有更改都会被撤销
四个特性:
1、原子性:事务中包含的所有操作,要么全部成功,要么全部失败。
2、一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。当事务只包含成功事务提交的结果时,就说数据库处于一个一致性状态。如果数据库系统运行时发生故障,有些事务未完成就被迫中断,这些未完成的事务对数据库做的修改一部分已经写入物理数据库,这时候数据库就处于一种不一致性状态。
3、隔离性:一个事务的执行不能被其他事务干扰,也就是一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发的各个事务之间是不能互相干扰的。
4、持久性:指一个事务一旦提交,它对数据库中的数据的改变就是永久性的,接下来的其他操作或者故障不应该对其执行结果有任何影响。

9、MySQL的四种隔离级别:

1、read uncommitted(读未提交):也就是一个事务能够读取到另一个未提交事务的数据,也就是脏读现象
2、read committed (读已提交):也就是一个事务要等另一个事务提交之后才可以读取数据(读已提交解决的是脏读现象)
3、不可重复读:也就是如果有事务对数据库进行更新update的操作时候,读操作事务要等更新操作完成之后才能读取数据
4、重复读:也就是在开始读取数据(开启事务)的时候,不再允许修改操作。(重复读可以解决不可重复读的问题)
5、幻读:幻读对应的是insert操作,
怎么解决幻读?办法是序列化,是最高的隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读,这种隔离级别效率低 比较消耗数据库性能,一般不使用。

数据库里有的锁

数据库里面的锁是基于索引实现的
有很多种,为了方面理解,所以我根据其相关性"人为"的对锁进行了一个分类,分别如下:
基于锁的属性分类:
共享锁:当一个事务对数据加上读锁之后,其他事务只能对该数据加读锁,而无法对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加写锁。 加了共享锁之后,无法再加排它锁,这也就可以避免读取数据的时候会被其它事务修改,从而导致重复读问题
排他锁:当一个事务对数据加上写锁之后,其他事务将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。加了排他锁之后,其它事务就无法再对数进行读取和修改,所以也就避免了脏写和脏读的问题
基于锁的粒度分类:
表锁:表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;
行锁(记录锁、间隙锁、临键锁):对所有行级别锁的一个统称,比如下面说的记录锁、间隙锁、临键锁都是属于行锁, 行锁是指加锁的时候锁住的是表的某一行或多行记录,多个事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问;
基于锁的状态分类
意向锁:当一个事务试图对整个表进行加锁(共享锁或排它锁)之前,首先需要获得对应类型的意向锁(意向共享锁或意向共享锁)
意向共享锁:当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁
意向排它锁:当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁。

乐观锁和悲观锁解释下?

悲观锁:是一种悲观的思想,它总认为最坏的情况可能会出现,认为数据很可能会被其他人所修改,所以悲观锁会在持有数据的时候总会把数据或者资源锁住,这样其他线程想要请求这个数据的时候就会阻塞,直到等悲观锁把资源释放为止。比如行锁、表锁、读锁、写锁等都是在做操作之前先上锁。
mysql实现悲观锁:select … from test for update ,当一个事务对某资源使用了这个语句,其他所有调用该资源的事务只有等待,直到第一个事务释放。
悲观锁的问题:当for update 的字段为 索引或者主键的时候,只锁住索引或者主键对应的行,否则锁住整个表。
乐观锁:是一种乐观的思想,它总认为资源和数据不会被别人所修改,所以读取的时候不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过。
实现方案有两种:版本号机制和CAS实现,乐观锁适合用于多读的应用类型,这样可以提高吞吐量。
mysql乐观锁实现:版本机制version。更新数据库,增加version字段(alter table add version int not null)

如果并发量高,用乐观锁还是悲观锁?

实际生产环境里,如果数据量不大,完全可以使用悲观锁的方法,用起来非常简单和方便。但是如果系统的并发非常大的话,悲观锁会带来性能问题,要选择乐观锁。

B+树和B树的区别?

1、B树的每个节点都存储索引和data元素(也就是索引所在行的磁盘文件地址),而B+树没有存储data元素,只有所有的叶子节点才有data
2、B+树的叶子节点拥有整张表的所有的索引元素叶子节点的元素是大于等于上一层非叶子节点的索引元素的
3、B+树的所有元素从左到右是依次递增的,排好序的
4、存储同样个数数据的话,B树的高度要远远大于B+树的,查询效率不高,因为B树的非叶子节点既要存储索引元素也要存储数据所以每个节点存储索引的数量越小
5、B树叶子节点没有指针,也不支持范围查询的,所以B树没有B+树的效率高

数据库索引有哪些?

数据库中的表里的数据、索引都是存储在磁盘上,数据库文件下有myd文件(存放数据的)、myI文件(存放数据索引的,主键索引就是放在这里的)
mysam数据库的一条查询语句的底层过程:比如说select * from t where col=30 先看这个条件字段是否是索引字段,如果是索引字段,先去到索引文件里面快速定位索引元素,把索引元素对应的data元素(也就是索引元素对应行的磁盘文件地址),然后拿着这个地址去到myd文件中快速定位数据表中的数据记录。

二叉树、B树、B+树

B+树(B树的变种)的特点:
1、非叶子节点不存储data,只存储索引,可以放更多的索引
2、叶子节点包含所有的索引字段
3、叶子节点双向指针连接,提高范围查询的性能
4、非叶子节点上的元素在叶子节点上都冗余了,也就是叶子节点中存储了 所有的元素,并且是排好序的。
5、每个节点存放大量的索引元素,这样树的高度就不会很高
mysql索引用的是B+树,因为索引是用来加快查询的,而B+树通过对数据进行排序所以是可以提高查询速度的,然后通过一个节点中可以存储多个元素,从而使得B+树的高度不会太高,并且叶子节点之间有指针,可以很好的支持全表扫描,范围查询等。

innodb索引采用B+树实现的

1、表数据文件本身就是按照B+树组织的一个索引结构文件
2、聚集索引-叶节点包含了完整的数据记录

为什么innodb表必须有主键?并且推荐使用整型的自增的主键?

因为方便维护整张表的数据结构,因为mysql表必须要用一颗B+树数据结构来组织。如果没有建主键索引的话,后台就会自动帮助我们维护一列自增的唯一索引便于维护整张表当我们去找某一个元素的时候,在这个过程中要不断的比较某个索引的大小,这个比较的次数操作如果是很多的话就会效率低,查找慢整型的自增效率会很高。还有就是主要涌向数据的写入性能,如果不是自增的话,写入数据的话树要进行排序,分支,这样会使得写入性能大大降低

为什么非主键索引结构叶子节点存储的是主键值

(一致性和节省存储空间)

什么是线程死锁?如何产生的?如何避免线程死锁?

由于两个或多个线程互相持有对方所需要的资源而导致这些线程处于等待状态无法继续工作,这就是死锁。
死锁产生的条件:
1、互斥:也就是一个资资源只能被一个进程锁占用。
2、请求与保持:一个进程因为请求资源而阻塞时,对已经获得的资源保持不释放。
3、不剥夺:进程已经获得的资源,在没有使用完之前,其他进程不能强行剥夺。
4、循环等待:多个进程之间形成一种互向循环等待资源的关系。
解决死锁的方法:就是破坏以上死锁的条件就可以。也就是从根本上解决了死锁。
当然还有非常具体的解决办法:
1、按顺序加锁
2、增加超时检测:当一个进程在规定的时间内没有获得想要的锁,那么就释放掉现在持有的锁
3、死锁检测:也就是在产生死锁之前,检测程序是否会有可能发生死锁,如果监测会发生死锁则直接释放掉目前的锁、有一个著名的算法叫银行家算法。

扫码测试:

1、二维码
  二维码是否可扫描,扫描弹出页面是否正确
  扫描弹出是固定金额还是需要手动输入金额
  二维码时长有效性
  过期二维码扫描
  模糊二维码扫描
  保存图片后扫描
2.扫码
  多用户对同一个订单进行扫描,只能被付款一次
  扫码过程中断验证,有来电等
  扫码后切回其他程序,切回后页面状态
3.支付
  对接不同三方平台支付
  同一笔订单重复支付
  金额验证
    输入单笔金额的最大、最小值
    金额限定小数点最后2位
    支付金额不能为空、0、负数
  单日成交最大限额
  如果是固定金额支付,验证篡改金额是否能成功支付(一般支付接口有签名验证,防止篡改)
  支付后验证付款方金额是否扣减
  支付成功金额是否立即到账/按约定延时到账
  支付后是否返回支付信息给用户及收款方
  支付结果
    成功
    失败
    余额不足
    点击取消
    超时
    密码错误
    超限额
  系统是否更新用户支付状态
  网络情况
    未联网扫描
    弱网扫描
  平台方断网后再次恢复能否受到支付信息
4.性能
  多并发用户同时扫码支付,系统是否正常
5.易用性

cp操作设计测试用例

主要从功能、异常、性能三个方面进行设计测试用例
功能
拷贝的文件
1)大小:Qk1k1Qk,100k1Q00k:
2)类型:二进制这件文本文件、mp3、心压缩文件
文件源目录
1)文件中包含各种类型的文件
2)目录深度为0.1,2,3…
文件目标目录
1)目标目录中存在与源文件同名同类型的文件
2)目标目录中存在与源文件同名不同类型的文件
3)目标目录中存在与源文件不同名同类型的文件
4)目标目录中存在与源文件不同名不同类型的文件
异常
参数异常
1)包含特殊字符
2)参数长度超过限制
3)源目录不存在
4)目标目录不存在
文件异常
1)文件没有拷贝权限
2)非法的文件格式和内容
存储介质异常
1)存储介质由损坏
2)拷贝前存储介质已满
3)拷贝中存储介质存满
执行过程异常
1)拷贝过程中删除源文件
2)拷贝过程中删除目标文件
性能
1)拷贝大文件
2)拷贝源目录中存在大量小文件
3)跨文件系统拷贝
4)跨存储介质拷贝
5)并发执行拷贝

水杯设计测试用例

一、功能
倒入水杯中的水超过水杯规定的安全线,观察是否漏水;
水倒满且流出来,观察是否对杯子产生什么影响,比如是否发生形变等;
水杯的容量刻度是否与其他容量相同的水杯一致,比如说明书500ML,那么实际盛水量为500ML:(1)测试数据:500;(2)测试步骤:先打开水杯,将500ML的水倒入水杯,观察水杯能否装下;(3)测试平台:说明书标志为500ML的塑料水杯;(4)预期结果:期望可以装下500ML的水;
水杯上的刻度是否准确;
拧紧盖子,摇晃杯子,观察是否漏水;
是否隔热:大于多少温度后会烫手(要符合产品需求规格说明书);
是否可以折叠(压缩杯子,折叠杯子);
如果是保温杯的话:要求保温效果。
二、界面
外观是否完整、美观;
大小设计是否与说明书一致(高、宽、容量、直径等);
材质设计是否与说明书一致;
图案:(1)是否容易掉落(遇水,遇高温、低温,经常摩擦);(2)是否合法;(3)颜色和需求是否一致;
三、易用性
使用简单,容易操作;
倒水方便;
喝水方便;
外形设计是否符合人体构造学(拿着舒服);
防滑:(1)杯子沾水之后是否防滑;(2)杯子沾上其他液体:饮料、油等其他液体是否防滑;
四、兼容性
杯子能够容纳果汁、碳酸饮料、酒…
容纳硫酸、酒精、汽油等;
五、性能
杯子的耐热性(大于多少温度);
杯子的耐寒性(低于多少温度);
长时间放置是否漏水;
使用最大次数或时间限制(寿命);
耐摔性(掉地上不容易摔坏);
盖子拧到什么程度水倒不出来;
如果是保温杯,测试保温时长;
杯子上放置重物达到什么程度杯子不会损坏(抗压性);
水杯盛放热水的容量;
水杯盛放冷水的容量;
六、安全性
杯子使用的材质是否安全(毒性、细菌性验证);
高温材质释放毒性;
低温材质释放毒性;
七、震动测试
杯子加包装(有填充物),六面震动、检查产品是否能应对恶劣的铁路/公路/航空运输;
杯子从不同高度掉下的损坏程度;
八、可移植性
杯子在正常的不同的地方,温度下都可以正常使用。

视频面试测试用例

功能测试
视频能否连接成功,声音和画面是否正常,能否同步,挂断功能是否正常,单人视频和多人视频是否正常:
性能测试
一长时间视频(如12小时)是否能保持正常,cp、内存消耗等;
稳定性测试一频繁进行视频:
前后台切搀与其他应用切换视频过程中来电适、短信等:
不同网络测试-wifi和流量:
在安卓和OS手机上分别测试,选择不同机型,不同系统版本测试:
界面测试
软件界面文字、图片和0g0显示正常,操作过程中出现的各种提示显示正常。

python的装饰器、迭代器和生成器是什么?

装饰器:python装饰器本质上是一个python函数,可以让其他函数在不需要做任何代码变动的前提下添加额外功能,装饰器的返回值也是一个函数对象。简单的说装饰器就是一个用来返回函数的函数。使用装饰器,我们可以抽离出大量与函数功能本身无关的雷同代码并继续重用。它经常用于有切面需求的场景。比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。

迭代器与生成器的区别?

迭代器里面有两个方法,一个__iter__方法,一个是__next__方法,__iter__用来获得迭代器对象,__next__用来获取下一个元素,我们会根据一个可迭代对象,一直执行__next__来获得下一个元素,当执行到可迭代对象的最后一个元素的时候,在进行next的话会抛出stop iteration异常,只要是可以用for循环都可以进行迭代,比如字符串、列表、元组,迭代器有限制,只能一直next而不能回到开始的元素。
生成器:生成器是一种特殊的迭代器,本质是一个函数,它记住了上一次返回时在函数体中的位置。对生成器函数的第二次调用,跳转到函数上一次挂起的位置,而且记录了程序执行的上下文,生成器不仅仅记住了它的数据状态,也记住了它执行的位置。
生成器就是为了写迭代器更加优雅方便,不需要手动实现__iter__和__next__方法
生成器是生成元素,迭代器是访问集合元素的一种方式。
迭代输出器的内容,迭代器是一种支持next()操作的对象。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值