常见面经问题

代码随想录

JavaGuild


目录

二、操作系统

什么是操作系统?

返回目录

  • 管理计算机硬件与软件资源的程序。
  • 负责系统的内存管理,硬件设备的管理,文件系统的管理以及应用程序的管理。

系统调用?

返回目录

  • 应用程序在处理数据时,需要调用系统资源,包括内存,CPU,文件系统等资源时候,需要调用操作系统提供的API,这就是系统调用。
  • 用户态:应用程序正常运行在用户态,当需要需要操作系统调用系统资源的时候,应用程序会进入系统态。

进程和线程的区别?

返回目录

  • 进程:操作系统资源分配的最小单位。
  • 线程:CPU进行运算调度的基本单位。
  • 线程是进程内运算/运行单位==车间于生产线。

进程有哪几种状态?

返回目录

  • 创建状态(new)
  • 就绪状态(ready)
  • 运行状态(running)
  • 阻塞状态(waiting)
  • 结束状态(terminated)

进程间的通信方式?

返回目录

  • 管道/匿名管道(Pipes):父子进程间通信。
  • 命名/有名管道(Named Pipes):以磁盘文件的方式存在,可以实现本机任意两个进程通信。
  • 信号(Signal) :用于通知接收进程某个事件已经发生。
  • 消息队列(Message Queuing):消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  • 信号量(Semaphores):信号量是一个计数器,用于多进程对共享数据的访问。
  • 共享内存(Shared memory) :内存屏障,实现进程间的可见性,避免错误引用对象。
  • 套接字(Sockets):客户端和服务器之间通过网络进行通信。套接字是支持 TCP/IP 的网络通信的基本操作单元。

线程间的同步的方式?

返回目录

  • 互斥量(Mutex)
  • 信号量(Semaphore)
  • 事件(Event)

进程的调度算法?

返回目录

  • 先到先服务(FCFS)调度算法
  • 短作业优先(SJF)的调度算法
  • 时间片轮转调度算法
  • 多级反馈队列调度算法
  • 优先级调度

什么是死锁?

返回目录

  • 多个进程/线程,都被无限期地阻塞等待某个资源被释放。

死锁的四个必要条件?

返回目录

  • 互斥条件
  • 不可剥夺条件
  • 持有并请求条件
  • 循环等待条件

解决死锁的方法?

返回目录

  • 预防,避免,检测,解除
  • 预防:
    • 静态分配策略:破坏请求并持有条件:一次性申请完所有资源。
    • 层次分配策略:破坏循环等待条件:一个进程得到某一次的一个资源后,它只能再申请较高一层的资源;当一个进程要释放某层的一个资源时,必须先释放所占用的较高层的资源。
  • 避免:
    • 银行家算法 通过先 试探 分配给该进程资源,然后通过 安全性算法 判断分配后系统是否处于安全状态。
  • 检测:
    • 如果进程-资源分配图中无环路,则此时系统没有发生死锁
    • 如果进程-资源分配图中有环路,且每个资源类仅有一个资源,则系统中已经发生了死锁。
    • 如果进程-资源分配图中有环路,且资源类有多个资源。资源类中存在资源未被使用,那么不会发生死锁,如果都被使用,那么存在非阻塞的进程 ,该进程能够在有限的时间内归还该资源类占有的资源,则不会发生死锁
  • 解除:
    • 立即结束所有进程的执行,重新启动操作系统
    • 撤销涉及死锁的所有进程,解除死锁后继续运行
    • 逐个撤销涉及死锁的进程,回收其资源直至死锁解除
    • 抢占资源:把夺得的资源再分配给涉及死锁的进程直至死锁解除。

常见的几种内存管理机制?

返回目录

  • 块式管理 : 远古时代的计算机操作系统的内存管理方式。
  • 页式管理 :把主存分为大小相等且固定的一页一页的形式,无实际意义。
  • 段式管理 :段式管理把主存分为一段段的,段是有实际意义的,每个段定义了一组逻辑信息。
  • 段页式管理:把主存先分成若干段,每个段又分成若干页。

快表和多级页表?

返回目录

  • 快表 :来加速虚拟地址到物理地址的转换,位于高速缓存。
  • 多级页表:避免把全部页表一直放在内存中占用过多空间。

分页机制和分段机制的共同点和区别?

返回目录

  • 共同点:
    • 分页机制和分段机制都是为了提高内存利用率,减少内存碎片。
    • 页和段都是离散存储的,所以两者都是离散分配内存的方式。但是,每个页和段中的内存是连续的。
  • 区别:
    • 页的大小是固定的,由操作系统决定;而段的大小不固定,取决于我们当前运行的程序。
    • 分页仅仅是为了满足操作系统内存管理的需求,而段是逻辑信息的单位,在程序中可以体现为代码段,数据段,能够更好满足用户的需要。

逻辑(虚拟)地址和物理地址?

返回目录

  • 逻辑地址(指针)由操作系统决定。物理地址指的是真实物理内存中地址,更具体一点来说就是内存地址寄存器中的地址。

CPU寻址了解吗?

返回目录

  • 虚拟寻址的寻址方式。使用虚拟寻址,CPU 需要将虚拟地址翻译成物理地址,这样才能访问到真实的物理内存。
  • 由CPU内存管理单元完成。

为什么需要虚拟地址空间?

返回目录

  • 如果直接把物理地址暴露出来的话会带来严重问题,比如可能对操作系统造成伤害以及给同时运行多个程序造成困难

什么是虚拟内存(Virtual Memory)?

返回目录

  • 定义:内存管理的一种技术
  • 意义:虚拟内存 可以让程序拥有超过系统物理内存大小的可用内存空间。
  • 意义:虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)

局部性原理?

返回目录

  • 时间局部性 :少量代码占据程序执行的大部分时间。
  • 空间局部性 :程序在一段时间内所访问的地址,可能集中在一定的范围之内。

虚拟存储器?

返回目录

  • 虚拟存储器又叫做虚拟内存

虚拟内存的技术实现?

返回目录

  • 虚拟内存的实现需要建立在离散分配的内存管理方式的基础上。
    • 请求分页存储管理
    • 请求分段存储管理
    • 请求段页式存储管理

页面置换算法?

返回目录

  • 最佳页面置换算法:该算法无法实现。一般作为衡量其他置换算法的方法
  • 先进先出页面置换算法:
  • 最近最久未使用页面置换算法:
  • 最少使用页面置换算法:

Linux操作系统

返回目录

目录切换命令

返回目录

  • cd usr: 切换到该目录下 usr 目录
  • cd …(或cd…/): 切换到上一层目录
  • cd /: 切换到系统根目录
  • cd ~: 切换到用户主目录
  • cd -: 切换到上一个操作所在目录

目录的操作命令(增删改查)

返回目录

  • mkdir 目录名称: 增加目录。(增)
  • ls/ll:查看目录信息。(查)
  • find 目录 参数:寻找目录(查)。
  • mv 目录名称 新目录名称: 修改目录的名称(改)。
  • mv 目录名称 目录的新位置: 移动目录的位置—剪切(改)。
  • cp -r 目录名称 目录拷贝的目标位置: 拷贝目录(改),-r 代表递归拷贝 。
  • rm [-rf] 目录 : 删除目录(删)。

文件的操作命令(增删改查)

返回目录

  • touch 文件名称: 文件的创建(增)。
  • cat/more/less/tail 文件名称 :文件的查看(查) 。
  • vim 文件: 修改文件的内容(改)。
  • rm -rf 文件: 删除文件(删)。

压缩文件的操作命令

返回目录

  • z:调用 gzip 压缩命令进行压缩
  • x:代表解压
  • c:打包文件
  • v:显示运行过程
  • f:指定文件名
  • tar -cvf/xvf/zcvf/zxvf 文件。

Linux的权限命令

返回目录

  • 权限查看:ls -l 命令 查看某个目录下的文件或目录的权限
  • 文件类型:
    • d: 代表目录
    • -: 代表文件
    • l: 代表软链接(可以认为是 window 中的快捷方式)
  • 权限:
    • r:代表权限是可读,r 也可以用数字 4 表示
    • w:代表权限是可写,w 也可以用数字 2 表示
    • x:代表权限是可执行,x 也可以用数字 1 表示

Linux用户管理

返回目录

  • useradd 选项 用户名:添加用户账号
  • userdel 选项 用户名:删除用户帐号
  • usermod 选项 用户名:修改帐号
  • passwd 用户名:更改或创建用户的密码
  • passwd -S 用户名 :显示用户账号密码信息
  • passwd -d 用户名: 清除用户密码

Linux系统用户组的管理

返回目录

  • groupadd 选项 用户组 :增加一个新的用户组
  • groupdel 用户组:要删除一个已有的用户组
  • groupmod 选项 用户组 : 修改用户组的属性

网络通信命令

返回目录

  • 查看当前系统的网卡信息:ifconfig。
  • 查看与某台机器的连接情况:ping。
  • 查看当前系统的端口使用:netstat -an。

常用命令

返回目录

  • pwd: 显示当前所在位置
  • sudo + 其他命令:以系统管理者的身份执行指令。
  • grep 要搜索的字符串/要搜索的文件 --color: 搜索命令,–color 代表高亮显示。
  • ps -ef/ps -aux: 这两个命令都是查看当前系统正在运行进程,两者的区别是展示格式不同。
  • kill -9 进程的pid: 杀死进程(-9 表示强制终止。)
  • net-tools 和 iproute2 :配置网络功能的工具。
  • shutdown: shutdown -h now: 指定现在立即关机
  • reboot: 重开机。reboot -w: 做个重开机的模拟(只有纪录并不会真的重开机)。

为什么要学Shell?什么是 Shell?

返回目录

  • Shell 几乎是 IT 企业必须使用的运维自动化编程语言,特别是在运维工作中的服务监控、业务快速部署、服务启动停止、数据备份及处理、日志分析等环节里,shell 是不可缺的。
  • Shell 编程就是对一堆 Linux 命令的逻辑化处理。

三、计算机网络

OSI七层模型是什么?每一层的作用是什么?

返回目录
在这里插入图片描述

TCP/IP四层模型是什么?每一层的作用是什么?

返回目录
在这里插入图片描述

为什么网络要分层?

返回目录
模块化设计:提高了可维护性和可扩展性。
标准化:网络分层有助于标准化协议和接口,从而促进了全球范围内的通信和互联。

应用层有哪些常见的协议?

返回目录

  • HTTP:超文本传输协议。
  • FTP:文件传输协议(基于TCP)。
  • SMTP:简单邮件传输(发送)协议(基于TCP)。
  • IMAP:邮件接收的协议。
  • Telnet:远程登陆协议(基于TCP)。
  • SSH:安全的网络传输协议(基于TCP)。

Telnet:Telnet通常使用基本的用户名和密码身份验证来访问远程系统。由于这种身份验证是明文的,所以容易受到密码猜测和截获的风险。
SSH:SSH提供多种身份验证方法,包括公钥身份验证、证书身份验证等。这些方法通常比Telnet的用户名和密码更安全,因为它们提供了更高的安全性和可扩展性。

TCP与UDP的区别(重要)?

返回目录

  • 是否面向连接 : TCP需要虚拟连接,UDP不需要。
  • 是否是可靠传输 :TCP可靠,UDP不可靠。
    • TCP 在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有超时重传,流量控制,拥塞避免机制。
    • 远地主机在收到 UDP 报文后,不需要给出任何确认,并且不保证数据不丢失,不保证是否顺序到达。
  • 是否有状态 :TCP有状态(记录自己收发消息的状态),UDP无状态(渣男)。
  • 传输效率:TCP 进行传输的时候多了连接、确认、重传等机制,所以 TCP 的传输效率要比 UDP 低很多。
  • 传输形式 : TCP 是面向字节流的,UDP 是面向报文的。
  • 首部开销 :TCP 首部开销(20 ~ 60 字节)比 UDP 首部开销(8 字节)要大。
  • 是否提供广播或多播服务 :TCP 只支持点对点通信,UDP 支持一对一、一对多、多对一、多对多;

什么时候选择TCP,什么时候选 UDP?

返回目录

  • UDP 一般用于即时通信,比如: 语音、 视频 、直播等等。
  • TCP 用于对传输准确性要求特别高的场景,比如文件传输、邮件收发、远程登录等等。

HTTP基于TCP还是UDP?

返回目录

  • HTTP 3.0 之前是基于 TCP 协议的,而 HTTP3.0 将弃用 TCP,改用 基于 UDP 的 QUIC 协议 ,解决 HTTP/2 中存在的队头阻塞问题。

使用TCP的协议有哪些?使用UDP的协议有哪些?

返回目录

  • TCP:HTTP,FTP,SMTP,IMAP,Telnet,SSH。
  • UDP:DHCP,DNS。

TCP三次握手和四次挥手(非常重要)?

返回目录

  • 三次握手:一次请求,两次确认。
    • 客户端向服务端发送同步请求,SYN(SQE=x);
    • 服务端向客户端发送同步和确认请,ACK(SEQ=x+1),SYN(SEQ=y);(回传 SYN 则是为了建立并确认从服务端到客户端的通信。)
    • 客户端向服务端发送确认请求,ACK(SEQ=y+1);
  • 四次挥手:两次请求,两次确认。
    • 客户端向服务端发送终结请求:FIN(SEQ=x);
    • 服务端向客户端发送确认请求:ACK(SEQ=x+1);
    • (可能服务端还有部分数据正在传输。)
    • 服务端向客户端发送终结请求:FIN(SEQ=y);
    • 客户端向服务端发送确认请求:ACK(SEQ=y+1);

TCP如何保证传输的可靠性?(重要)

返回目录

  • 基于数据块传输(数据块:报文段或段)
  • 对失序数据包重新排序以及去重(每个包一个序列号)
  • 校验和(首部和数据的检验和)
  • 超时重传(发送数据后,启动定时器)
  • 流量控制(TCP 的接收端只允许发送端发送接收端缓冲区能接纳的数据:滑动窗口
  • 拥塞控制(慢启动,拥塞避免,超时重传,快重传,快恢复)
    在这里插入图片描述

TCP可靠性传输-滑动窗口机制

  • 滑动窗口基本思想:如果客户端不急着接收ACK,可以利用等待ACK的空挡直接发送下一批数据,这样就可以做到短时间内发送多批数据。我们把需要发送的多批数据的集合看成窗口。
  • 理解滑动窗口的3个关键点:
    • 1)滑动窗口大小如何确定?
      • 滑动窗口大小如何确定?滑动窗口大小依据服务端接收缓冲区的大小确定。
      • 滑动窗口大小为0时,何时才能继续发送数据?
        • 策略一:发送方定期发送不携带数据的报文,接收方返回携带窗口大小的ACK,以此判断是否继续发送数据。
        • 策略二:接收方缓冲区一旦更新立马通知发送方。
    • 2)滑动窗口如何滑动?
      • 滑动窗口根据来自接收方的确认序列号确定左边界,根据接受方缓冲区大小+左边界确认右边界。
        • 例如:服务端收到了1001~2000的数据,那就回复确认序号2001+ACK,表明2001之前的数据已经全部收到,下一次从2001的字节位置开始发;
    • 2)滑动窗口数据传输过程中遇到的两个问题如何解决?
      • 客户端发送的报文丢了(快重传机制)
        • 服务端即便收到了3001~4000的数据,等了一段时间依然没有收到2001~3000的数据,说明2001~3000的报文丢了。此时发送的确认序号不是4001,而是2001,表明目前只收到了2001之前的数据。
        • 站在客户端的角度,客户端如果连续多次(一般是连续三次)收到了2001的确认应答,说明序号为2001之后的报文丢了,此时就会重传2001~3000的报文。服务端收到2001~3000的报文以后,由于之前已经收到了3001~4000 的报文(暂时存放在接收缓冲区),那么服务端相当于已经收到了全部的报文,此时服务端发送的确认序号是 4001,表明4001之前的报文已经全部收到,下一次可以从第4001个位置开始发。
        • 这种重发机制被称为“快重传”,与超时重传不同的是,超时重传是等待60s以后还没有收到ACK,此时客户端就会重新发送报文;而快重传更注重效率,只要收到多个相同的确认序号,就立马重传。
      • 服务端发送的ACK丢了(先收到了排序靠后的ACK)
        • 客户端没有收到2001~3000,但是收到了3001~4000的ACK。没关系,因为服务端必须是按序号回复的,如果服务端没有收到2001~3000的数据,根本不会发送3001~4000的ACK,因此可以认为 服务端已经收到了三批数据。此时滑动窗口的左边界向左移动至4001,同时根据对方接收缓冲区剩余空间大小移动右边界。

ARQ 协议了解吗?

返回目录

  • 自动重传请求,是 OSI 模型中数据链路层和传输层的错误纠正协议之一。
  • 在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认信息,它通常会重新发送,直到收到确认或者重试超过一定的次数。
    • 停止等待 ARQ 协议:每发完一个分组就停止发送,等待对方确认(回复 ACK).
    • 连续 ARQ 协议:发送窗口内的分组可以连续发送出去,而不需要等待对方确认(滑动窗口协议)。

从输入URL到页面展示到底发生了什么?(非常重要)

返回目录
从输入URL到页面展示到底发生了什么

HTTP状态码有哪些?

返回目录

  • 1xx:信息性状态码
  • 2xx:成功状态码
    • 200 OK :请求被成功处理。
    • 201 Created :请求被成功处理并且在服务端创建了一个新的资源。
    • 202 Accepted :服务端已经接收到了请求,但是还未处理。
    • 204 No Content : 服务端已经成功处理了请求,但是没有返回任何内容
  • 3xx :重定向状态码
    • 301 Moved Permanently : 资源被永久重定向了。
    • 302 Found :资源被临时重定向了。
  • 4xx:客户端错误状态码
    • 400 Bad Request : 发送的HTTP请求存在问题。
    • 401 Unauthorized : 未认证却请求需要认证之后才能访问的资源。
    • 403 Forbidden :直接拒绝HTTP请求,不处理。
    • 404 Not Found : 你请求的资源未在服务端找到。
    • 409 Conflict : 表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。
  • 5xx :服务端错误状态码
    • 500 Internal Server Error : 服务端出问题了(通常是服务端出Bug了)。
    • 502 Bad Gateway :我们的网关将请求转发到服务端,但是服务端返回的却是一个错误的响应。

HTTP和HTTPS有什么区别?(重要)-

返回目录

HTTP 1.0和HTTP 1.1有什么区别?

返回目录

  • HTTP 1.0只支持短连接而,HTTP 1.1支持持久连接。
    • 短连接:每次HTTP请求,都需简历TCP连接。
    • 持久连接:一次TCP连接就可以发送多个HTTP请求。
      • 非流水线:类似阻塞同步:需要等待响应报文。
      • 流水线:类似滑动窗口:不用等待响应报文。
  • HTTP 1.1增加host字段
    • HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。
    • 虚拟机的发展,多个虚拟机可共享一个ip地址,只需要端口映射。
  • 状态码-100-节约带宽。
    • 使用只带头域的请求,试探服务器,没有权限则响应401,有权限返回100。
  • 增加Request方法
    • HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
    • HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
      • HEAD    类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
      • OPTIONS   允许客户端查看服务器的性能。
      • CONNECT   HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
      • TRACE   回显服务器收到的请求,主要用于测试或诊断。

HTTP是不保存状态的协议,如何保存用户状态?

返回目录

  • Session 机制的存在就是为了解决这个问题,Session 的主要作用就是通过服务端记录用户的状态。

如何实现 Session 跟踪呢?

返回目录

  • 通过在 Cookie 中附加一个 Session ID 来方式来跟踪.
  • Cookie 被禁用怎么办?
    • 利用 URL 重写,把 Session ID 直接附加在 URL 路径的后面

URI和URL的区别是什么?

返回目录

  • URI :统一资源标志符,可以唯一标识一个资源。
  • URL: 是统一资源定位符
  • URI 的作用像身份证号一样,URL 的作用更像家庭住址一样。URL 是一种具体的 URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。

什么是Mac地址?

返回目录

  • 媒体访问控制地址(主机物理地址)

ARP协议解决了什么问题地位如何?

返回目录

  • 它解决的是网络层地址和链路层地址之间的转换问题。

ARP协议的工作原理?

返回目录

  • ARP 表( IP 地址-MAC 地址映射关系)、广播问询、单播响应。
    • 同一局域网内的 MAC 寻址;
    • 从一个局域网到另一个局域网中的网络设备的寻址。

四、数据结构

什么是数据结构?

返回目录

  • 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。结构包括逻辑结构和物理结构。
    • 逻辑结构4种:
      • 集合
      • 线性结构
      • 树形结构
      • 图状结构
    • 物理结构2种:
      • 顺序存储结构
      • 链式存储结构。

解释一下顺序存储与链式存储?

返回目录

  • 顺序存储结构是用一段连续的存储空间来存储数据元素,可以进行随机访问,访问效率较高。
  • 链式存储结构是用任意的存储空间来存储数据元素,不可以进行随机访问,访问效率较低。

头指针和头结点的区别?

返回目录

  • 头指针:是指向第一个节点存储位置的指针,具有标识作用,头指针是链表的必要元素,无论链表是否为空,头指针都存在。
  • 头结点:是放在第一个元素节点之前,便于在第一个元素节点之前进行插入和删除的操作,头结点不是链表的必须元素,可有可无,头结点的数据域也可以不存储任何信息。

线性结构的特点?

返回目录

  • (1)集合中必存在唯一的一个"第一个元素";
  • (2)集合中必存在唯一的一个"最后的元素";
  • (3)除最后元素之外,其它数据元素均有唯一的"后继";
  • (4)除第一元素之外,其它数据元素均有唯一的"前驱"。

数组和链表的区别?

返回目录

  • 从逻辑结构来看:
    • 数组的存储长度是固定的,它不能适应数据动态增减的情况。
    • 链表能够动态分配存储空间以适应数据动态增减的情况,并且易于进行插入和删除操作。
  • 从访问方式来看
    • 数组在内存中是一片连续的存储空间,可以通过数组下标对数组进行随机访问,访问效率较高。
    • 链表是链式存储结构,存储空间不是必须连续的,可以是任意的,访问必须从前往后依次进行,访问效率较数组来说比较低。

单链表结构和顺序存储结构的区别?

返回目录

  • 当进行插入和删除操作时,顺序存储结构每次都需要移动元素,总的时间复杂度为O(n^2),而链式存储结构确定i位置的指针后,其时间复杂度仅为O(1)。
  • 由于顺序存储结构需要进行预分配存储空间,所以容易造成空间浪费或者溢出。链式存储结构不需要预分配存储空间,元素个数不受限制。

栈和队列的区别?

返回目录
队列:线性结构,一端进另一端出,先进先出。
栈:线性结构,一端进此端出,先进后出。

括号匹配是怎么实现?

返回目录

  • 括号匹配:
    • 扫描代码,遇到左括号入栈。遇到右括号先判断栈是否为空(若为空,左括号太少,匹配失败),若不为空则出栈进行匹配。若代码扫描完毕,栈不为空。(左括号太多,匹配失败)。
  • 表达式计算:
    • 对于给出的一个表达式,从左向右扫描它的字符,并将操作数放入栈S1中,运算符放入栈S2中,但每次扫描到运算符时,要把它同S2的栈顶运算符进行优先级比较,当扫描到的运算符的优先级不高于栈顶运算符的优先级时,取出栈S1的栈顶和次栈顶的两个元素,以及栈S2的栈顶运算符进行运算将结果放入栈S1中(得到的结果依次用T1、T2等表示),重复上述过程,直至栈顶操作符优先级不高于需插入操作符,然后插入操作符,重复上述操作,直至操作符输入完毕,依次弹栈计算。

朴素的匹配算法和KMP算法?

返回目录

  • 朴素的匹配算法:暴力匹配算法: 将主串中所有长度为m的子串依次与模式串匹配对比,直到找到一个完全匹配的子串,或所有的子串都不匹配为止。
  • KMP算法:找到公共前后缀,将下面的子串前缀移动到主串后缀位置。

如何构造哈夫曼树?

返回目录

  • 在集合中找w最小节点求和生成新的结点,放回集合,重复此过程,直至合并为一个结点;将此过程,构造为一棵二叉树,标记路径左0右1。

最小生成树算法实现?

返回目录

  • 普里姆(prim)算法:从某点出发,选择其中最小边,加入新点集,直至所有点遍历完成。
  • 克鲁斯卡尔(kruskal):依次选择最小边,使得无环且所有边遍历完成。

最短路径的算法?

返回目录

  • 迪杰斯特拉(dijastra)算法:时间复杂度为O(n^2):经典的单源最短路径算法主要是其采用的动态规划思想.
  • 弗洛伊德(floyd)算法:时间复杂度为O(n^3) 空间复杂度为O(n ^ 2):经典的求任意顶点之间的最短路径,采用贪心思想。

介绍一下深度优先搜索和广度优先搜索是如何实现的?

返回目录

  • 深度优先搜索:(1)访问起始点 v 0 v_0 v0(2)若 v 0 v_0 v0的第一个邻接点没有被访问过,则深度遍历该邻接点;(3)若 v 0 v_0 v0的第一个邻接点已经被访问,则访问其第二个邻接点,进行深度遍历;重复以上步骤直到所有节点都被访问过为止。
  • 广度优先搜索:(1)访问起始点 v 0 v_0 v0(2)依次遍历 v 0 v_0 v0的所有未访问过得邻接点 (3)再依次访问下一层中未被访问过得邻接点;重复以上步骤,直到所有的顶点都被访问过为止。

介绍一下拓扑排序以及是如何实现的?

返回目录

  • 拓扑排序的步骤:(1)在有向图中任意选择一个没有前驱的节点输出(2)从图中删去该节点以及与它相连的边(3)重复以上步骤,直到所有的顶点都输出或者当前图中不存在无前驱的顶点为止,后者代表该图是有环图,所以可以通过拓扑排序来判断一个图是否存在环。

各种查找方法?简要描述?

返回目录

  • 查找分为静态查找表和动态查找表;静态查找表包括:顺序查找、折半查找、分块查找;动态查找包括:二叉排序树和平衡二叉树。

  • (1)顺序查找:把待查关键字 k e y key key放入哨兵位置 ( i = 0 ) (i=0) (i=0),再从后往前依次把表中元素和 k e y key key比较,如果返回值为0则查找失败,表中没有这个 k e y key key值,如果返回值为元素的位置 i ( i ! = 0 ) i(i!=0) i(i!=0)则查找成功,设置哨兵的位置是为了加快执行速度,时间复杂度为 O ( n ) O(n) O(n),其特点是:结构简单,对顺序结构和链式式结构都适用,但查找效率太低。

  • (2)折半查找:要求查找表为顺序存储结构并且有序,若关键字在表中则返回关键字的位置,若关键字不在表中时停止查找的典型标志是:查找范围的上界<=查找范围的下界。

  • (3)分块查找:先把查找表分为若干子表,要求每个子表的元素都要比后面的子表的元素小,也就是保证块间是有序的(但是子表内不一定有序),把各子表中的最大关键字构成一张索引表,表中还包含各子表的起始地址。特点是:块间有序,块内无序,查找时块间进行索引查找,块内进行顺序查找。

  • (4)二叉排序树:二叉排序树的定义为:一棵空树,或者是一棵具有如下特点的树:如果该树有左子树,则其左子树的所有节点值小于根的值;若该树有右子树,则其右子树的所有节点值均大于根的值;其左右子树也分别为二叉排序树

  • (5)平衡二叉树:平衡二叉树又称为AVL树,它或者是一棵空树或者具有如下特点:他的左子树和右子树的高度差的绝对值不能大于1,且他的左右子树也都是平衡二叉树。

  • 如果再一个平衡二叉树中插入一个节点可能造成失衡,这时就要进行树结构的调整,即平衡旋转。包括4中情况:在左子树的左子树上插入节点时向右进行单向旋转;在右子树的右子树上插入节点时向左进行单向旋转;在左子树的右子树插入节点时先向左旋转再向右旋转;在右子树的左子树插入节点时先向右旋转再向左旋转。

哈希表的概念、构造方法、冲突的解决办法?

返回目录
哈希表又称为散列表,是根据关键字码的值直接进行访问的数据结构,即它通过把关键码的值映射到表中的一个位置以加快查找速度,其中映射函数叫做散列函数,存放记录的数组叫做散列表。

哈希函数的构造方法包括:直接定址法,除留余数法,数字分析法,平方取中法,折叠法,随机数法

(1)直接定址法:取关键字的某个线性函数值作为散列地址,H(key)=a*key+b。

(2)除留余数法:取关键字对p取余的值作为散列地址,其中p

(3)数字分析法:当关键字的位数大于地址的位数,对关键字的各位分布进行分析,选出分布均匀的任意几位作为散列的地址,适用于所有关键字都已知的情况。

(4)平方取中法:对关键字求平方,再取结果中的中间几位作为散列地址。

(5)折叠法:将关键字分为位数相同的几部分,然后取这几部分的叠加和作为散列地址。适用于关键字位数较多,且关键字中每一位上数字分布大致均匀。

(6)随机数法:选择一个随机函数,把关键字的随机函数值作为散列地址。适合于关键字的长度不相同时。

哈希冲突的解决方法包括:开放定址法和拉链法,当冲突发生时,使用某种探测技术形成一个探测序列,然后沿此序列逐个单单元查找,直到找到该关键字或者碰到一个开放的地址为止,探测到开放的地址表明该表中没有此关键字,若要插入,则探测到开放地址时可将新节点插入该地址单元。其中开放定址法包括:线性探查法,二次探查法,双重散列法

(1)线性探查法:基本思想,探查时从地址d开始,首先探查T[d],在探查T[d+1]…直到查到T[m-1],此后循环到T[0],T[1]…直到探测到T[d-1]为止。

(2)二次探查法:基本思想,探查时从地址d开始,首先探查T[d],再探查T[d+12],T[d+22]…等,直到探查到有空余地址或者探查到T[d-1]为止,缺点是无法探查到整个散列空间。

(3)双重散列法:基本思想,使用两个散列函数来确定地址,探查时从地址d开始,首先探查T[d],再探查T[d+h1(d)],T[d+2*h1(d)]…

链接法:将所有关键字为同义词的节点链接在同一个单链表中,若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组,凡是散列地址为i的节点均插入到头指针为i的单链表中。

各种排序算法?简要描述?

返回目录

  • 内部排序包括:插入排序、选择排序、交换排序、归并排序、基数排序。其中插入排序包括:直接插入排序、折半插入排序、希尔排序;选择排序包括:简单选择排序,堆排序;交换排序包括:冒泡排序、快速排序。

  • (1)直接插入排序(稳定):基本思想为:将序列分为有序部分和无序部分,从无序部分依次选择元素与有序部分比较找到合适的位置,将原来的元素往后移,将元素插入到相应位置上。时间复杂度为:O(n^2),空间复杂度为O(1)

  • (2)折半插入排序(稳定):基本思想为:设置三个变量low high mid,令mid=(low+high)/2,若a[mid]>key,则令high=mid-1,否则令low=mid+1,直到low>high时停止循环,对序列中的每个元素做以上处理,找到合适位置将其他元素后移进行插入。比较次数为O(nlog2n),但是因为要后移,因此时间复杂度为O(n^2),空间复杂度为O(1)。 优点是:比较次数大大减少。

  • (3)希尔排序(不稳定):基本思想为:先将序列分为若干个子序列,对各子序列进行直接插入排序,等到序列基本有序时再对整个序列进行一次直接插入排序。优点是:让关键字值小的元素能够很快移动到前面,且序列基本有序时进行直接插入排序时间效率会提升很多,空间复杂度为O(1)。

  • (4)简单选择排序(不稳定):基本思想为:将序列分为2部分,每经过一趟就在无序部分找到一个最小值然后与无序部分的第一个元素交换位置。优点是:实现简单,缺点是:每一趟只能确定一个元素的位置,时间效率低。时间复杂度为O(n^2),空间复杂度为O(1)。

  • (5)堆排序(不稳定):设有一个任意序列,k1,k2,…,kn,当满足下面特点时称之为堆:让此序列排列成完全二叉树,该树具有以下特点,该树中任意节点均大于或小于其左右孩子,此树的根节点为最大值或者最小值。优点是:对大文件效率明显提高,但对小文件效率不明显。时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),空间复杂度为O(1)。

  • (6)冒泡排序(稳定):基本思路为:每一趟都将元素进行两两比较,并且按照“前小后大”的规则进行交换。优点是:每一趟不仅能找到一个最大的元素放到序列后面,而且还把其他元素理顺,如果下一趟排序没有发生交换则可以提前结束排序。时间复杂度为O(n^2),空间复杂度为O(1)。

  • (7)快速排序(不稳定):基本思路为:在序列中任意选择一个元素作为中心,比它大的元素一律向后移动,比它小的元素一律向前移动,形成左右两个子序列,再把子序列按上述操作进行调整,直到所有的子序列中都只有一个元素时序列即为有序。优点是:每一趟不仅能确定一个元素,时间效率较高。时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),空间复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n).

  • (8)归并排序(稳定):基本思想为:把两个或者两个以上的有序表合并成一个新的有序表。时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度和待排序的元素个数相同。

  • (9)基数排序:时间复杂度为:对于n个记录进行链式基数排序的时间复杂度为 O ( d ( n + r d ) ) O(d(n+rd)) O(d(n+rd)),其中每一趟分配的时间复杂度为 O ( n ) O(n) O(n),回收的时间复杂度为 O ( r d ) O(rd) O(rd)

“前小后大”的规则进行交换。优点是:每一趟不仅能找到一个最大的元素放到序列后面,而且还把其他元素理顺,如果下一趟排序没有发生交换则可以提前结束排序。时间复杂度为 O ( n 2 ) O(n^2) O(n2),空间复杂度为O(1)。

  • (7)快速排序(不稳定):基本思路为:在序列中任意选择一个元素作为中心,比它大的元素一律向后移动,比它小的元素一律向前移动,形成左右两个子序列,再把子序列按上述操作进行调整,直到所有的子序列中都只有一个元素时序列即为有序。优点是:每一趟不仅能确定一个元素,时间效率较高。时间复杂度为 O ( n l o g 2 n ) O(nlog2n) O(nlog2n),空间复杂度为 O ( l o g 2 n ) . O(log2n). O(log2n).

  • (8)归并排序(稳定):基本思想为:把两个或者两个以上的有序表合并成一个新的有序表。时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度和待排序的元素个数相同。

  • (9)基数排序:时间复杂度为:对于n个记录进行链式基数排序的时间复杂度为 O ( d ( n + r d ) ) O(d(n+rd)) O(d(n+rd)),其中每一趟分配的时间复杂度为O(n),回收的时间复杂度为 O ( r d ) O(rd) O(rd)

五、Redis

Redis常用命令

什么是 Redis?

返回目录

  • 内存数据库数据库。

Redis为什么这么快?

返回目录

  • Redis 基于内存,内存的访问速度是磁盘的上千倍。
  • Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用。
  • Redis 内置了多种优化过后的数据结构实现,性能非常高。

分布式缓存常见的技术选型方案有哪些?

返回目录

  • Memcached 和 Redis

说一下 Redis和 Memcached的区别和共同点?

返回目录

  • 共同点:基于内存的数据库,一般都用来当做缓存使用。
  • 共同点:都有过期策略。
  • 共同点:两者的性能都非常高。
  • 区别:Redis 支持更丰富的数据类型。
  • Redis 支持数据的持久化,Memcached 不支持持久化。
  • Redis 有灾难恢复机制。
  • Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。
  • Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的。
  • Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。 (Redis 6.0 引入了多线程 IO )。
  • Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。
  • Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。

为什么要用 Redis/为什么要用缓存?

返回目录

  • 高性能:基于内存,访问速度很快。
  • 高并发:一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 Redis 的情况,Redis 集群的话会更高)。

Redis除了做缓存,还能做什么?

返回目录

  • 分布式锁 :一个Key就是一个分布式锁。
  • 消息队列:Redis 自带的 list 数据结构可以作为一个简单的队列使用。
  • 限流 :通过 Redis + Lua 脚本的方式来实现限流,设置一定量的线程访问。其余线程必须阻塞。

Redis可以做消息队列么?

返回目录

  • Redis 5.0 新增加的一个数据结构 Stream 可以用来做消息队列。
  • 和专业的消息队列相比,还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。
  • 可以选择市面上比较成熟的一些消息队列比如 RocketMQ、Kafka。

如何基于Redis 实现分布式锁?

返回目录

  • SETNX 命令是可以帮助我们实现互斥。
  • 释放锁的话,直接通过 DEL 命令删除对应的 key 即可。
    • SET lockKey uniqueValue EX 3 NX

      • lockKey:这是要在Redis数据库中设置的键(或“锁”的名称)。
      • uniqueValue:这是你尝试为lockKey设置的值。通常,这个值是一个唯一的标识符或令牌,用于标识锁的所有者。
      • EX 3:命令的这一部分为锁设置了一个过期时间。在这种情况下,EX代表“expire”(过期),3表示锁将在3秒后自动过期。你可以调整这个值以控制锁应该持有多长时间。
      • NX:这个选项表示“不存在”。它告诉Redis只有在lockKey不存在的情况下才设置值。这对于实现锁机制很重要,因为它确保只有一个客户端可以成功获取锁。

Redis常用的数据结构有哪些?

返回目录

  • 5 种基础数据结构 :String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
  • 3 种特殊数据结构 :HyperLogLogs(基数统计)、Bitmap (位存储)、Geospatial (地理位置)。
    Redis常用命令

String还是Hash存储对象数据更好呢?

返回目录

  • 如果对象中某些字段经常变动或者经常需要单独查询对象中的个别字段信息,Hash 就非常适合。
  • String 存储相对来说更加节省内存

String 的底层实现是什么?

返回目录

  • SDS 动态字符串。
  • SDS可以避免缓冲区溢出。
  • 获取字符串长度的复杂度较低
  • 减少内存分配次数
  • 二进制安全

购物车信息用String还是Hash存储更好呢?

返回目录

  • 由于购物车中的商品频繁修改和变动,购物车信息建议使用 Hash 存储

使用 Redis 实现一个排行榜怎么做?

返回目录

  • 相关的一些 Redis 命令: ZRANGE (从小到大排序) 、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)。
    添加成员和分数到有序集合: 每当有用户或对象执行某个动作(如投票、得分、评分等),你需要将相应的成员添加到有序集合中,并为该成员分配一个分数。分数用于排名。
  • 要使用Redis实现一个排行榜(leaderboard),你可以利用Redis的有序集合(Sorted Set)数据结构。有序集合可以存储多个成员,每个成员都关联一个分数,然后根据分数对成员进行排序。以下是实现排行榜的一般步骤:

使用Redis的ZADD命令可以添加成员到有序集合,并为每个成员指定一个分数。例如:

ZADD leaderboard 1000 player1
ZADD leaderboard 800 player2
ZADD leaderboard 1200 player3

查看排行榜: 你可以使用ZREVRANGE命令按分数从高到低获取排行榜的成员列表。这会返回排名最高的成员到排名最低的成员。例如:

ZREVRANGE leaderboard 0 -1

查看特定成员的排名: 使用ZREVRANK命令可以查找某个成员的排名(从高到低)。例如:

ZREVRANK leaderboard player1

查看特定成员的分数: 使用ZSCORE命令可以查找某个成员的分数。例如:

ZSCORE leaderboard player1

获取排名区间的成员: 使用ZREVRANGE命令可以获取排名在指定区间的成员列表。这允许你实现分页查看排行榜的功能。例如:

ZREVRANGE leaderboard 0 9

移除成员: 如果需要,你可以使用ZREM命令将某个成员从有序集合中移除,从而排名也会相应更新。例如:

ZREM leaderboard player2

更新成员分数: 如果某个成员的分数需要更新,你可以再次使用ZADD命令为该成员设置新的分数。例如:

ZADD leaderboard 1100 player1

通过使用Redis的有序集合,你可以轻松地实现排行榜功能,并且可以高效地进行排名、分数更新和成员的添加/删除等操作。

使用Set实现抽奖系统需要用到什么命令?

返回目录
要使用 Redis 的 Set 数据结构实现抽奖系统,你可以使用以下几个关键的 Redis 命令:

SADD:用于向集合中添加一个或多个成员。在抽奖系统中,你可以将参与抽奖的用户添加到一个 Redis 集合中。

SADD lottery_participants user1 user2 user3

SMEMBERS:用于获取集合中的所有成员。这可以用于查看所有参与抽奖的用户。

SMEMBERS lottery_participants

SREM:用于从集合中移除一个或多个成员。如果某个用户中奖了,你可以将其从参与者集合中移除。

SREM lottery_participants user2

SRANDMEMBER:用于从集合中随机获取一个或多个成员。在抽奖过程中,你可以使用它来随机选取中奖用户。

SRANDMEMBER lottery_participants 1

SCARD:用于获取集合中的成员数量,可以用于统计参与抽奖的用户总数。

SCARD lottery_participants

使用Bitmap 统计活跃用户怎么做?

返回目录
使用 Redis 的 Bitmap 数据结构可以很有效地统计活跃用户。Bitmap 是一种非常紧凑的数据结构,适合用于记录用户的活跃状态,每位用户对应 Bitmap 中的一位。以下是如何使用 Bitmap 统计活跃用户的一般步骤:

初始化 Bitmap: 首先,你需要为每个用户创建一个对应的 Bitmap。你可以使用 Redis 的 SETBIT 命令初始化 Bitmap 中的位。通常,你会创建一个每天一个的 Bitmap,每个位代表一个用户在某一天是否活跃。

SETBIT daily_active_users 1 1  # 用户1在第1天活跃
SETBIT daily_active_users 2 1  # 用户2在第1天活跃
SETBIT daily_active_users 3 1  # 用户3在第1天活跃

记录用户活跃状态: 每天结束时,你需要更新 Bitmap,记录用户的活跃状态。如果用户在某一天活跃,就将相应的位设置为1。

SETBIT daily_active_users 1 0  # 用户1在第2天不活跃
SETBIT daily_active_users 2 1  # 用户2在第2天活跃
SETBIT daily_active_users 3 0  # 用户3在第2天不活跃

统计活跃用户: 要统计活跃用户,你可以使用 BITCOUNT 命令来计算 Bitmap 中为1的位数。这将告诉你有多少用户在某一天活跃。

BITCOUNT daily_active_users

获取特定日期的活跃用户: 如果你需要查看特定日期的活跃用户列表,可以使用 GETBIT 命令来检查特定用户在某一天是否活跃。

GETBIT daily_active_users 2  # 查询用户2在第2天是否活跃

使用HyperLogLog统计页面UV怎么做?

返回目录
HyperLogLog(HLL)是一种用于估算基数(不重复元素的数量)的数据结构,特别适用于统计独立访客(UV,Unique Visitors)的数量。以下是如何使用 HyperLogLog 统计页面 UV 的一般步骤:

初始化 HyperLogLog: 首先,你需要创建一个 HyperLogLog 数据结构,并为每个页面或每个时间段(如每日或每小时)创建一个独立的 HLL。你可以使用 Redis 的 PFADD 命令初始化 HyperLogLog。

PFADD page_views:20230910 user1 user2 user3

上述命令将向名为 page_views:20230910 的 HyperLogLog 添加用户 user1、user2 和 user3,表示这些用户访问了页面或者在特定时间段内进行了活动。

统计基数(估算UV): 为了获取页面的唯一访问者数量,你可以使用 PFCOUNT 命令,该命令将返回估计的基数(独立访问者数量)。

PFCOUNT page_views:20230910

该命令返回的结果即为页面 page_views:20230910 的估计 UV 数量。

合并 HyperLogLogs(可选): 如果你需要合并不同时间段的 UV 统计数据,可以使用 PFMERGE 命令将多个 HyperLogLog 数据合并成一个。

PFMERGE combined_page_views page_views:20230910 page_views:20230911

上述命令将 page_views:20230910 和 page_views:20230911 的数据合并到名为 combined_page_views 的 HyperLogLog 中。然后,你可以使用 PFCOUNT 命令来获取合并后的估计 UV 数量。

PFCOUNT combined_page_views

Redis单线程模型了解吗?

返回目录

  • Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型 :文件事件处理器(file event handler)。
  • 虽然文件事件处理器以单线程方式运行,但通过使用 I/O 多路复用程序来监听多个套接字。
  • I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗。

I/O 多路复用技术

电器表示需要进行I/O操作的任务,如网络连接或文件读写。
插槽表示I/O多路复用机制,如select、poll、epoll或kqueue。
多功能接线板表示操作系统内核,它协调和管理所有的插槽和电器,确保正确的数据流动。

Redis6.0之前为什么不使用多线程?

返回目录

  • 单线程编程容易并且更容易维护。
  • Redis 的性能瓶颈不在 CPU ,主要在内存和网络。
  • 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。

Redis6.0之后为何引入了多线程?

返回目录

  • Redis6.0 引入多线程主要是为了提高网络 IO 读写性能。

Redis 给缓存数据设置过期时间有啥用?

返回目录

  • 因为内存是有限的,如果缓存中的所有数据都是一直保存的话,分分钟直接 Out of memory。
  • Redis 中除了字符串类型有自己独有设置过期时间的命令 setex 外,其他方法都需要依靠 expire 命令来设置过期时间 。另外, persist 命令可以移除一个键的过期时间。

Redis是如何判断数据是否过期的呢?

返回目录

  • Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。

过期的数据的删除策略了解么?

返回目录

  • 惰性删除 :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
  • 定期删除 : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。

Redis内存淘汰机制了解么?

返回目录

  • 从已设置过期时间的数据集,最近最少使用的数据淘汰。
  • 从已设置过期时间的数据集,挑选将要过期的数据淘汰。
  • 从已设置过期时间的数据集,任意选择数据淘汰。
  • 当内存不足以容纳新写入数据时,移除最近最少使用的数据进行淘汰。
  • 从数据集中任意选择数据淘汰。
  • 禁止驱逐数据,当内存不足以容纳新写入数据时,新写入操作会报错。

怎么保证Redis挂掉之后再重启数据可以进行恢复?

返回目录

  • RDB快照。
  • AOF追加文件。

什么是 RDB持久化?

返回目录

  • 通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。

RDB创建快照时会阻塞主线程吗?

返回目录

  • save : 主线程执行,会阻塞主线程;
  • bgsave : 子线程执行,不会阻塞主线程,默认选项

什么是AOF持久化?

返回目录

  • 开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到内存缓存
    • appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
    • appendfsync everysec #每秒钟同步一次,显式地将多个写命令同步到硬盘
    • appendfsync no #让操作系统决定何时进行同步

AOF日志是如何实现的?

返回目录

  • 关系型数据库(如 MySQL)通常都是执行命令之前记录日志(方便故障恢复),而 Redis AOF 持久化机制是在执行完命令之后再记录日志。
    • 为什么是在执行完命令之后记录日志呢?
      • 优点:避免额外的检查开销,AOF 记录日志不会对命令进行语法检查。
      • 优点:在命令执行完之后再记录,不会阻塞当前的命令执行。
      • 缺点:如果刚执行完命令 Redis 就宕机会导致对应的修改丢失。
      • 缺点:可能会阻塞后续其他命令的执行(AOF 记录日志是在 Redis 主线程中进行的)。

AOF重写了解吗?

返回目录

  • AOF文件太大,会将AOF文件重写为体积更小的AOF文件。

如何选择RDB和AOF?

返回目录

  • RDB:文件很小,适合做数据的备份,灾难恢复。
  • AOF: 实时或者秒级持久化数据,数据安全性更高。

Redis 4.0对于持久化机制做了什么优化?

返回目录

  • Redis 4.0 开始支持 RDB 和 AOF 的混合持久化。
    • 配置项:aof-use-rdb-preamble

如何使用Redis事务?

返回目录

  • 通过 MULTI,EXEC,DISCARD 和 WATCH 等命令来实现事务(transaction)功能。
  • MULTI命令后可以输入多个命令,Redis 不会立即执行这些命令,而是将它们放到队列,当调用了 EXECopen in new window 命令后,再执行所有的命令。
  • 可以通过 DISCARD 命令取消一个事务,它会清空事务队列中保存的所有命令。
  • 可以通过WATCHopen in new window 命令监听指定的 Key,当调用 EXEC 命令执行事务时,如果一个被 WATCH 命令监视的 Key 被 其他客户端/Session 修改的话,整个事务都不会被执行。不过,如果 WATCH 与 事务 在同一个 Session 里,并且被 WATCH 监视的 Key 被修改的操作发生在事务内部,这个事务是可以被执行成功的。

Redis事务支持回滚吗?

返回目录

  • 不支持。

Redis事务支持原子性吗?

返回目录

  • 不支持,指令不能批量执行,即便打包,也是一个一个挨着执行。

如何解决 Redis事务的缺陷?

返回目录

  • Lua 脚本来批量执行多条 Redis 命令。

Redis bigkey?

返回目录

  • 如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey。
  • 除了会消耗更多的内存空间,bigkey 对性能也会有比较大的影响。

大量key集中过期问题?

返回目录

  • 定期删除+惰性/懒汉式删除 策略

使用批量操作减少网络传输?

返回目录

  • mget(获取一个或多个指定 key 的值)、
  • mset(设置一个或多个指定 key 的值)、
  • hmget(获取指定哈希表中一个或者多个指定字段的值)、
  • hmset(同时将一个或多个 field-value 对设置到指定哈希表中)、
  • sadd(向指定集合添加一个或多个元素)。

缓存穿透?

返回目录

  • 大量请求的 key 是不合理的,根本不存在于缓存中,也不存在于数据库中 。
  • 举个例子:某个黑客故意制造一些非法的 key 发起大量请求,导致大量请求落到数据库,结果数据库上也没有查到对应的数据。
    • 解决办法:
      • 1)缓存无效 key,(给无效 key设置过期时间)
      • 2)布隆过滤器,(判断 key 是否合法)

缓存击穿?

返回目录

  • 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期) 。

缓存雪崩?

返回目录

  • 缓存在同一时间大面积的失效,导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。
  • 例如,缓存服务器宕机会导致缓存雪崩现象。

如何保证缓存和数据库数据的一致性?

返回目录

  • 更新数据库成功,而删除缓存这一步失败的情况的话,简单说两个解决方案:

    • 缓存失效时间变短(不推荐,治标不治本)。
    • 增加 cache 更新重试机制(常用)。
  • 保证一致性四种策略

    • 先更新缓存,再更新数据库
    • 先更新数据库,再更新缓存
    • 先淘汰缓存,再更新数据库
    • 先更新数据库,再淘汰缓存

Mysql

什么是关系型数据库?

返回目录

  • 一种建立在关系模型的基础上的数据库。
  • (一对一、一对多、多对多)
  • 常见关系型数据库:
    • MySQL、PostgreSQL、Oracle、SQL Server、SQLite

什么是SQL?

返回目录

  • SQL 是一种结构化查询语言,专门用来与数据库打交道。

什么是 MySQL?

返回目录

  • MySQL 是一种关系型数据库,主要用于持久化系统中的数据,比如用户信息。

MySQL有什么优点?

返回目录

  • 成熟稳定,功能完善,开源免费。
  • 支持事务、分库分表、读写分离、集群。

MySQL 基础架构

返回目录
在这里插入图片描述

MysQL支持哪些存储引擎?默认使用哪个?

返回目录

  • 默认InnoDB,其次有FEDERATED、MEMORY、PERFORMANCE_SCHEMA、MyISAM、MRG_MYISAM、BLACKHOLE、CSV、ARCHIVE

MySQL存储引擎架构了解吗?

返回目录

  • 插件式架构

MylSAM和InnoDB有什么区别?

返回目录

  • 事务、锁、外键、索引、持久化、底层结构
    • InnoDB 支持事务,支持事务并发,MyISAM 不支持事务,不支持事务并发。
    • InnoDB 支持行级锁、表级锁、MVCC,MyISAM 只有表级锁
    • InnoDB 支持外键,MyISAM 不支持。
    • InnoDB 支持持久化->灾难恢复,MyISAM 不支持。
    • InnoDB 数据和索引一起,MyISAM索引文件和数据文件分离。

外键

  • 外键定义:从表中某唯一性字段的关联约束。
  • 外键字段:通常,从表外键字段的数据类型应与主表上的主键字段相匹配。

MylSAM和InnoDB如何选择?

返回目录

  • 通常使用的都是 InnoDB 存储引擎;在某些读密集的情况下,使用 MyISAM 也是合适的。

何谓事务?

返回目录

  • 事务是逻辑上的一组操作,要么都执行,要么都不执行

何谓数据库事务?

返回目录
数据库操作指令,要么全部执行成功,要么全部不执行 。

并发事务带来了哪些问题?

返回目录

  • 脏读:事务1读取并修改数据,并没有提交,事务2读取修改后的数据,事务1突然回滚。导致事务2脏读。
  • 丢失修改:事务1对数据修改了,事务2也对数据修改了,则事务1修改结果被丢失。
  • 不可重复读:事务1连续两次读数据,之间事务2修改数据,导致事务1,两次读取结果不一样。
  • 幻读:事务1读取了几行数据,接着事务2插入了一些数据,事务1发现多了一些原本不存在的记录。

不可重复读和幻读有什么区别?

返回目录

  • 不可重复读的重点是内容修改或者记录减少。
  • 幻读的重点在于记录新增。

并发事务的控制方式有哪些?

返回目录

  • 读写锁 和 MVCC(CAS):悲观锁和乐观锁(版本号/时间戳)区别。
  • 控制方式划分:
    • 共享锁(S 锁) :又称读锁/共享锁。
    • 排他锁(X 锁) :又称写锁/独占锁。
  • 锁粒度划分:
    • 表级锁。
    • 行级锁。
  • 读写锁:事务获得读锁,其他可再次获取,但是不可获得写锁。如果事务获得写锁,其他事务不能获得读锁,也不能获得写锁。

SQL标准定义了哪些事务隔离级别?

返回目录

  • 读取未提交:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • 读取已提交(mvcc):可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • 可重复读(mvcc):可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • 可串行化(锁):最高的隔离级别,可以防止脏读、不可重复读以及幻读。

MySQL的隔离级别是基于锁实现的吗?

返回目录

  • MySQL 的隔离级别基于锁和 MVCC 机制共同实现的。

MySQL的默认隔离级别是什么?

返回目录

  • REPEATABLE-READ(可重读)

MySQL中常见的日志有哪些?

返回目录

  • redo log(重做日志,事务日志)
  • undo log(回滚日志)
  • bin log(归档日志)
  • relay log(中继日志)
  • slow query log(慢查询日志)
  • general query log(一般查询日志)
  • error log(错误日志)

慢查询日志有什么用?

返回目录

  • 记录在 MySQL 中执行时间超过指定时间的查询语句。
  • 排查慢SQL

binlog主要记录了什么?

返回目录

  • bin log记录了数据库所有DDL和DML操作(不包含 SELECT 和 SHOW等命令,因为这类操作对数据本身并没有修改)。
  • DDL(Data Definition Language, DDL):CREATE、ALTER与DROP
  • DML(Data Manipulation Language):INSERT、UPDATE、DELETE

redo log 如何保证事务的持久性?

返回目录

  • 定期刷盘:当向数据库写入数据时,先向缓存写入,此时缓存中这个数据页称为脏页,按照设定的更新策略,定期刷到磁盘中,称为刷脏页。

页修改之后为什么不直接刷盘呢?

返回目录

  • 这样做的主要原因是为了提高写入性能和保证数据的一致性。
  • 如果每次有修改操作就直接写入磁盘,会严重影响写入性能。而且,频繁的磁盘写入会导致磁盘的寿命缩短。此外,直接将修改操作写入磁盘可能会导致数据不一致的问题,例如操作系统或硬件出现错误或中断等情况下。
  • 在事务提交的时候,会将 redo log 中的修改操作同步到磁盘上的数据文件。

binlog和redolog有什么区别?

返回目录

  • redolog:用于保证事务的持久性。
  • binlog:记录了数据库的所有更新操作。binlog 用于在主从复制、数据库恢复等场景下使用。
  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  • MySQL的binlog是逻辑日志,其记录是对应的SQL语句。而innodb存储引擎层面的redolog日志是物理日志,保存了数据库中的值。
  • 有点类似于Redis,RDB快照和AOF文件。

undo log如何保证事务的原子性?

返回目录

  • InnoDB 会先将修改前的数据写入到 undo log 中,然后进行数据的修改操作,如果事务回滚,则可以利用 undo log 中的信息将数据恢复到修改之前的状态,从而实现事务的原子性。
  • 修改之前,相当于RDB快照,然后修改,出现问题,用快照恢复。

为什么要建立索引?

返回目录

  • 自上而下的去进行查询(像二分查找),可以在一定程度避免走全表查询。

哪些情况适合建立索引?

返回目录

  • 查询占主要的应用(主键,外键,where,Group,order)
    • 频繁作为where条件语句查询的字段(where)
    • 关联字段需要建立索引(外键)
    • 排序字段可以建立索引(Order)
    • 分组字段可以建立索引(Group)
    • 统计字段可以建立索引(主键)

哪些情况下不适合建索引?

返回目录

  • 频繁更新、参与列计算、重复且分布均匀的的字段、where条件中用不到的字段
    • 频繁更新的字段不适合建立索引
    • 参与列计算的列不适合建索引
    • 数据不适合建索引
    • where条件中用不到的字段不适合建立索引

为什么索引是使用B+树?(重点)

返回目录

  • 高效的查找:查找操作的时间复杂度为O(log n)(树结构)
  • 有序存储:适用于范围查询:B+树的叶子节点构成了一个双向链表,这意味着在B+树上执行范围查询非常高效。(叶子结点结构:双向链表)
  • 简化内部节点结构:节省内存空间(分支结点结构)
  • 支持高并发更友好:B树也支持高并发,但在某些情况下可能需要更频繁的平衡操作。(分支结点结构)

为什么索引不使用B树呢?(重点)

  • 有序存储:适用于范围查询(叶子结点结构:双向链表)
  • 简化内部节点结构:节省内存空间(分支结点结构)
  • 支持高并发更友好:B树也支持高并发,但在某些情况下可能需要更频繁的平衡操作。(分支结点结构)

索引分为那几类?

返回目录

  • 聚簇索引和非聚簇索引。
    • 主键索引: 也简称主键。它可以提高查询效率,并提供唯一性约束。一张表中只能有一个主键。
    • 普通索引:就是普普通通的索引。
    • 唯一索引:索引的值不能重复。
    • 复合索引:在工作中用得比较频繁的一个索引。

什么是聚簇索引?(重点)

返回目录

  • 聚簇索引就是将数据(一行一行的数据)跟索引结构放到一块,innodb存储引擎使用的就是聚簇索引。

使用聚簇索引的优缺点?(知道)

返回目录

  • 优点

    • 再次读取数据高效:访问同一数据页不同行记录时,已经把页加载到了Buffer中(读取数据是按页读取的),再次访问时,会在内存中完成访问,不必访问磁盘。
    • 物理排序,更适合按顺序查询:适合那些经常需要按照索引列的顺序进行查询。
    • 减少磁盘I/O:由于聚簇索引决定了数据的物理存储顺序,它可以显著减少磁盘I/O操作。
    • 避免重复数据存储 :聚簇索引中的索引列的值是唯一的,这意味着不会有重复的数据存储在聚簇索引中。这有助于节省存储空间。
  • 缺点

    • 更新操作开销高:由于聚簇索引决定了数据的物理存储顺序,当执行插入、删除或更新操作时,可能需要重组数据页,这会导致额外的开销。
  • 辅助索引(非聚簇索引)的叶子节点,存储主键值(而不是数据的存放地址),可能会导致回表。

  • 因为MyISAM的主索引并非聚簇索引,那么他的数据的物理地址必然是凌乱的

    • 不过,如果涉及到大数据量的排序、全表扫描、count之类的操作的话,还是MyISAM占优势些,因为索引所占空间小,这些操作是需要在内存中完成的。

为什么推荐使用自增主键作为索引?

返回目录

  • 出现页分裂(比如之前的索引已经紧凑的排列在一起了,你此时需要在已经紧凑排列好的数据中插入数据就会导致前面已经排好序的索引出现松动和重构排序,但是使用自增id就不会出现这种情况了),导致索引树调整复杂度变大。

什么叫回表?(重点)

返回目录

  • 拿着辅助索引查询出来的主键去聚簇索引中进行查询,这个过程就是叫回表;

什么叫索引覆盖?(重点)

返回目录

  • 如果一个查询是先走辅助索引的,那么通过这个辅助索引就直接获取到我们想要的全部数据了,不需要进行回表,这个过程就叫做索引覆盖;

什么是最左前缀原则?(重点)

返回目录

  • 从最左的索引开始匹配,遇到范围查询就会让后面范围列后的索引失效;
    • mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 ,如果建立(a,b,c,d)顺序的联合索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

MySQL索引失效的几种情况(重点)

返回目录

  • ①like查询以%开头,因为会导致查询出来的结果无序;
  • ②类型转换,列计算也会可能会让索引失效,因为结果可能是无序的,也可能是有序的;
  • ③在一些查询的语句中,MySQL认为走全表扫描比索引更加快也会导致索引失效;
  • ④如果条件中有or并且or连接的字段中有列没有索引,那么即使其中有条件带索引也不会使用索引 ,要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引,当检索条件有or但是所有的条件都有索引时,索引不失效,可以走【两个索引】,这叫索引合并(取二者的并集);
  • ⑤复合索引不满足最左原则就不能使用全部索引

常见的索引优化手段有哪些?

返回目录

  • ① 尽可能的使用复合索引而不是索引的组合;
  • ②创建索引尽量让辅助索引进行索引覆盖 而不是回表;
  • ③在可以使用主键id的表中,尽量使用自增主键id,这样可以避免页分裂;
  • ④查询的时候尽量不要使用select * ,这样可以避免大量的回表;
  • ⑤尽量少使用子查询,能使用外连接就使用外连接,这样可以避免产生笛卡尔集;
  • ⑥能使用短索引就是用短索引,这样可以在非叶子节点存储更多的索引列降低树的层高,并且减少空间的开销;

谈―下你对MySQL索引的理解?

返回目录

  • 索引的b+树结构,为什么使用b+树说一下,然后再说一下聚簇索引,回表和索引覆盖,然后再谈一下索引失效;

表级锁和行级锁了解吗?有什么区别?

返回目录

  • 表级锁: MySQL 中锁定粒度最大的一种锁(全局锁除外),是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。不过,触发锁冲突的概率最高,高并发下效率极低。表级锁和存储引擎无关,MyISAM 和 InnoDB 引擎都支持表级锁。
  • 行级锁: MySQL 中锁定粒度最小的一种锁,是 针对索引字段加的锁 ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存储引擎有关,是在存储引擎层面实现的。

行级锁的使用有什么注意事项?

返回目录

  • InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字段加的锁。当我们执行 UPDATE、DELETE 语句时,如果 WHERE条件中字段没有命中唯一索引或者索引失效的话,就会导致扫描全表对表中的所有行记录进行加锁。这个在我们日常工作开发中经常会遇到,一定要多多注意!!!

InnoDB有哪几类行锁?

返回目录

  • 记录锁(Record Lock) :也被称为记录锁,属于单个行记录上的锁。
  • 间隙锁(Gap Lock) :锁定一个范围,不包括记录本身。
  • 临键锁(Next-Key Lock) :Record Lock+Gap Lock,锁定一个范围,包含记录本身,主要目的是为了解决幻读问题

共享锁和排他锁呢?

返回目录

  • 共享锁(S 锁) :又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
  • 排他锁(X 锁) :又称写锁/独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。

意向锁有什么作用?

返回目录

  • 做意向锁可快速判断是否可以对某个表使用表锁。
  • 意向锁是表级锁,共有两种:
    • 意向共享锁(Intention Shared Lock,IS 锁):事务有意向对表中的某些记录加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁
    • 意向排他锁(Intention Exclusive Lock,IX 锁):事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。
  • 意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享/排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。

当前读和快照读有什么区别?

返回目录

  • 快照读(一致性非锁定读)就是单纯的 SELECT 语句,但不包括下面这两类 SELECT 语句:
    • SELECT … FOR UPDATE
    • SELECT … LOCK IN SHARE MODE
  • 当前读 (一致性锁定读)就是给行记录加 X 锁或 S 锁。
    • 对读的记录加一个X锁
    • SELECT…FOR UPDATE
    • 对读的记录加一个S锁
    • SELECT…LOCK IN SHARE MODE
    • 对修改的记录加一个X锁
    • INSERT…
    • UPDATE…
    • DELETE…

自增锁有了解吗?

返回目录

  • 关系型数据库设计表的时候,通常会有一列作为自增主键。InnoDB 中的自增主键会涉及一种比较特殊的表级锁—自增锁

能用MySQL直接存储文件(比如图片)吗?

返回目录

  • 可以是可以,直接存储文件对应的二进制数据即可,会严重影响数据库性能。

MySQL如何存储IP地址?

返回目录

  • 可以将 IP 地址转换成整形数据存储,性能更好,占用空间也更小。
    • INET_ATON() : 把 ip 转为无符号整型 (4-8 位)
    • INET_NTOA() : 把整型的 ip 转为地址

有哪些常见的SQL优化手段?

返回目录

如何分析SQL的性能?

返回目录

  • 通过慢查日志等定位那些执行效率较低的SQL语句
  • explain 分析SQL的执行计划
    • 重点关注type、rows、filtered、extra。
  • show profile 分析
  • trace

读写分离和分库分表了解吗?

返回目录

  • 避免使用select
  • 用union all代替union
  • 小表驱动大表
  • 批量操作
  • 多用limit
  • in中值太多
  • 增量查询
  • 高效的分页
  • 用连接查询代替子查询
  • join的表不宜过多
  • join时要注意
  • 控制索引的数量
  • 选择合理的字段类型
  • 提升group by的效率
  • 索引优化

测试用例

转载

等价类划分法(解决穷举场景)

返回目录

  • 定义:在所有测试数据中,具有某种共同特征的数据集合进行划分。
  • 分类:
    • (1)有效等价类:满足需求的数据集合。
    • (2)无效等价类:不满足需求的数据集合。
  • 步骤:
    • (1)明确需求。
    • (2)确定有效和无效等价类。
    • (3)提取数据编写测试用例。
  • 适用场景:
    • 需要有大量数据测试输入,但是没法穷举测试的地方。
    • 例如:输入框、下拉列表、单选复选框
  • 应用场景:电话号码是否合法?
    • 明确需求:长度(11位),类型(数字),规则(首位非0)
    • 确定有效/无效类:
      • 长度:有效等价类(11位数字),无效等价类(<11位数字,>11位数字)
      • 类型:有效等价类(11位数字),无效等价类(11非数字)
      • 规则:有效等价类(11位数字),无效等价类(11位首位为0数字)
    • 提取数据编写测试用例:
      • 长度:有效:12345678911,无效:123,1234567891123
      • 类型:有效:12345678911,无效:123456789ab
      • 规则:有效:12345678911,无效:01234567891

边界值分析法(解决边界限制问题)

返回目录

  • 定义:边界范围节点:选取正好等于、刚好大于、刚好小于边界的值作为测试数据。
    • (1)上点:边界上的点(刚好等于)。
    • (2)离点:距离上点最近的点(刚好大于、刚好小于)。
    • (3)内点:范围内的点(区间范围内的数据)。
  • 步骤:边界值法设计用例步骤:
    • (1)明确需求。
    • (2)确定有效和无效等价类。
    • (3)确定边界范围值。
    • (4)提取数据编写测试用例。
  • 优化:7个优化为5个点
    • 上点:必选(不考虑区间开闭)。
    • 内点:必选(建议选择中间范围)。
    • 离点:开内闭外(考虑区间开闭,开区间选择内部离点,闭区间选择外部离点)。
  • 适用场景:
    • (1)在等价类的基础上针对有边界范围的测试数据输入的地方(重点关注边界)。
    • (2)常见词语描述:大小、尺寸、重量、最大、最小、至多、至少等修饰词语。
    • (3)典型代表:有边界范围的输入框测试。

三、判定表法(解决多条件有依赖关系测试问题)(组合问题)

返回目录

  • 定义:是一种以表格形式表达多条件逻辑判断的工具。
  • 组成:
    • (1)条件桩:列出问题中的所有条件,列出条件的次序无关紧要。
    • (2)动作桩:列出问题中可能采取的操作,操作的排列顺序没有约束。
    • (3)条件项:列出条件对应的取值,所有可能情况下的真假值。
    • (4)动作项:列出条件项的、各种取值情况下应该采取的动作结果。
  • 规则:
    • (1)判定表中贯穿条件项和动作项的一列就是一条规则。
    • (2)假设有n个条件,每个条件的取值有两个(0,1),全组合有2的n次方种规则。(全排列)
  • 步骤:
    • (1)明确需求。
    • (2)画出判定表:
    • (3)根据规则编写测试用例。
  • 适用场景:
    • 输入条件之间有组合关系,输入条件和输出结果之间有依赖关系。
    • 判定表法一般适用于条件组合数量较少的情况(比如4个条件以下)。 注:条件超过4个可以使用正交法解决。
  • 应用场景
    在这里插入图片描述

四、场景法/流程图法(解决业务逻辑测试)

返回目录

  • 定义:通过覆盖流程路径来设计测试用例。
  • 意义:平时测试的都是单个功能点进行测试,容易忽略多个功能的组合测试。
  • 适用场景:根据实际的应用场景,来测试业务用例,可以使用场景法。
  • 场景法案例:ATM机取款
    在这里插入图片描述

五、错误推测法

返回目录

  • 定义:通过经验推测出系统可能出现的问题。
  • 思想:根据经验列出可能出现问题的清单,根据清单分析问题可能原因,推测发现缺陷。
  • 场景:
    • (1)时间紧任务量大时,根据之前项目类似经验找出易出现的模块重点测试。
    • (2)时间宽裕通过该方法列出之前出现问题较多的模块再次测试。

六、因果图(作为判定表的辅助)

返回目录

  • 核心:因——输入条件、果——输出结果
  • 定义:用图解的方法表示输入的各种组合关系,写出判定表,从而设计相应的测试用例
  • 适用范围:适用于分析程序输入条件的各种组合情况,以及输入与输出之间的依赖关系
  • 步骤:
    • (1)明确需求
    • (2)画出因果图
    • (3)将因果图转换为判定表
  • 基本符号:C表示原因,E表示结果
  • 在这里插入图片描述

七、正交表(正交排列法)

返回目录

  • 定义:使用最小的测试过程集合获得最大的测试覆盖率
  • 特点:均匀分散,齐整可比
  • 适用范围:可能的输入数据或输出数据的组合数量很大时
  • 正交表的概念:
    • 一种特制的表,一般的正交表标记为: L n ( m k ) L_n(m^k) Ln(mk)
    • n表示行数==水平数平方。
    • k是因素数。
    • m是水平数。
  • 步骤:
    • 确定需求中的因素数(所有的输入)与对应的水平数(输入的取值)。
    • 根据因素数与水平数选取正交表 。
    • 用需求中的文字代替正交表中的字母 。
    • L 9 ( 3 4 ) L_9(3^4) L9(34)
      在这里插入图片描述

测试基础


1.1 软件测试

返回目录

请你分别介绍一下单元测试、集成测试、确认测试、系统测试、验收测试。

返回目录

  • 单元测试,集中对用源代码实现的每一个程序单元进行测试,检查各个程序模块是否正确地实现了规定的功能。 (对单个模块进行测试)。
  • 集成测试,把已测试过的模块组装起来进行测试。(对模块之间进行测试)。
    • 非增式集成测试:将单元测试后的模块按照总体的结构图一次性集成起来,然后把连接的整体进行程序测试。
      • 程序错误易出现,不容易集成成果,适合于规模小的开发系统。
    • 增式集成测试:在单元测试的基础上,采用自顶向下或自底向上逐层安装测试,直到最后安装测试完毕。
      • 将错误分解,容易找到错误并测试成功,适合于大规模的开发系统。
    • 两种测试均使用:接口测试方法测试。
  • 确认测试,检查已实现的软件是否满足各种需求,以及软件配置是否完全、正确。(对完整的组件进行需求测试)。
  • 系统测试,把已经经过确认的软件纳入实际运行环境中,与其它系统成份组合在一起进行测试。(对组件之间兼容性进行测试)。
    • 功能测试:
    • 性能测试:基准,负载,稳定性,压力,并发
    • 安全测试:
    • 回归测试:修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。
  • 验收测试,包括Alpha测试和Beta测试。
    • α测试 是由一个用户在开发环境下进行的测试。
    • β测试 是由软件的多个用户在一个或多个用户的实际使用环境下进行的测试。

什么是黑盒?什么是白盒?什么是灰盒?黑盒和白盒的测试方法分别有哪些?

返回目录

  • 黑盒:黑盒测试也称功能测试或数据驱动测试。
    • 等价类划分法;边界值分析法、因果图法、场景法、正交实验设计法、判定表驱动分析法、错误推测法、功能图分析法。
  • 白盒测试:也称为结构测试或逻辑驱动测试。
  • 白盒测试方法包括:语句覆盖、判定覆盖、条件覆盖、判定条件覆盖、条件组合覆盖、路径覆盖。
    • 1.语句覆盖每条语句至少执行一次。
    • 2.判定覆盖每个判定的每个分支至少执行一次。
    • 3.条件覆盖每个判定的每个条件应取到各种可能的值。
    • 4.判定/条件覆盖同时满足判定覆盖条件覆盖。
    • 5.条件组合覆盖每个判定中各条件的每一种组合至少出现一次。
    • 6.路径覆盖使程序中每一条可能的路径至少执行一次。
  • 灰盒测试,是介于白盒测试与黑盒测试之间的一种测试,灰盒测试多用于集成测试阶段,不仅关注输出、输入的正确性,同时也关注程序内部的情况。灰盒多用来集成测试阶段,黑盒多用来系统测试功能测试阶段。

什么是性能?

返回目录

  • 时间:系统处理用户请求响应的时间。
  • 资源:系统运行过程中,系统资源消耗情况。

性能测试分类?

返回目录

  • 常见性能测试有5种:
    • 基准测试:
      • 狭义上:就是单用户测试。(单用户循环多次得到的数据)。
      • 广义上讲:建立基准线,当系统的软硬件环境发生变化之后再进行一次基准测试以确定变化对性能的影响。
    • 负载测试:
      • 通过逐步增加系统负载,确定在满足系统的性能指标(如响应时间等)情况下,找出系统所能够承受的最大负载量的测试。
    • 稳定性测试:
      • 在服务器稳定运行(用户正常的业务负载下)的情况下进行长时间测试(1天-1周等),并最终保证服务器能满足线上业务需求。
    • 压力测试:
      • 在强负载下的测试,查看系统在峰值情况下是否功能隐患、系统是否具有良好的容错能力和可恢复能力。
    • 并发测试:
      • 并发测试(绝对并发)︰是指在极短的时间内,发送多个请求,来验证服务器对并发的处理能力。

性能测试指标

返回目录

  • 响应时间:服务器处理时间 + 网络传输时间
  • 并发用户数:某一时刻同时向服务器发送请求的用户数
  • 吞吐量:单位时间内处理客户端请求数量。
  • 点击数:所有的页面元素(图片、链接、框架等)的请求总数量,点击数是请求数,不是页面上的一次点击。
  • 错误率:错误率是性能指标,是高负载下的失败业务的概率(处理失败业务数/总处理业务数)。
  • 资源利用率:资源使用量/总资源可用量
    • CPU使用率:不高于75%-85%
    • 内存(大小)使用率:不高于80%
    • 磁盘l0(速率):不高于90%
    • 网络(速率):不高于80%

接口测试流程?

返回目录

  • 1、需求分析:分析客户的需求可不可行。
  • 2、编写测试计划:概要设计(集成测试依据),详细设计(单元测试依据:详细设计描述:模块功能规格说明)。
  • 3、编写测试用例:测试用例就是指导测试的文档。
  • 4、执行测试:提交bug,回归测试。
  • 5、编写测试总结报告。

性能测试流程?

返回目录

  • 性能测试需求分析:熟悉需求,获取性能需求指标。
  • 性能测试计划及方案:测什么,谁来测、怎么测。
  • 性能测试用例设计:用来验证系统是否符合需求。
  • 性能测试执行:建立测试环境、编写测试脚本、性能测试监控、执行测试脚本。
  • 性能分析和调优:分析性能结果,针对性能bug调优
  • 性能测试报告总结。

集成测试依据,单元测试依据

返回目录

  • 集成测试依据:概要设计
  • 单元测试依据:模块功能规格说明

测试人员分工

返回目录

  • 测试设计人员:负责设计测试用例以及设计测试过程。
  • 测试经理:制定测试计划。
  • 测试经理组织召集开发和测试的相关人员:评估测试活动。

内测、封测、公测的区别

返回目录

  • 内测:就是不公开游戏,发号给部分玩家,让玩家在游戏的同时找到游戏的问题;
  • 封测:是内部人员自己在测试游戏。
  • 公测:就是游戏公开,所有玩家都可以玩,并在其中找到问题,给官方在对游戏里的问题给予处理。

基本路径法覆盖路径与谓词结点的关系

返回目录

  • 基本路径=谓词节点+1 ;

软件测试误区

返回目录

  • (1) 期望用测试自动化代替大部分人工劳动
  • (2)忽视需求阶段的参与
  • (3)软件测试是技术要求不高的岗位

测试的关键问题是?

返回目录

  • 如何选择测试用例
  • 测试用例是测试程序正确性与否的关键。一个覆盖完全的测试用例可以测试出程序是否正确运行,是否有bug等等,是最重要的。

测试用例什么时候开始设计

返回目录

  • 系统测试用例设计应该从需求完成的时候开始。
  • 设计测试用例越靠近需求阶段,我们就能越早发现需求问题,在软件开发过程问题得到越早的修正,那么所花的代价就会越小。在需求阶段发现问题,我们可能只需要修改下文档。

黑盒测试在设计测试用例时,主要需要研究?

返回目录

  • 测试用例编写的依据:软件的需求文档,开发的设计文档。

涉及路径的测试有那些?

返回目录

  • 单元测试和集成测试

测试驱动开发的方式?

返回目录

  • CMM:“能力成熟度模型”,把软件开发视为一个过程,并根据这一原则对软件开发和维护进行过程监控和研究,以使其更加科学化、标准化、使企业能够更好地实现商业目标。
  • CMMI:将各种能力成熟度模型整合到同一架构中去,由此建立起包括软件工程、系统工程和软件采购等在内的诸模型的集成,以解决除软件开发以外的软件系统工程和软件采购工作中的迫切需求。

先深度后广度增量测试方法

返回目录

  • 二叉树先序遍历

既可用于黑盒测试,又可用于白盒测试的方法?

返回目录

  • 边界值法

软件测试计划评审会需要那些人参加

返回目录

  • 项目经理、客户(可选)、配置管理员、测试经理、开发组长等人的参加。

IOS单元测试框架有那些?

返回目录

  • OCUnit 是 OC 官方测试框架, 现在被 XCTest 所取代。
  • XCTest 是与 Foundation 框架平行的测试框架。
  • GHUnit 是第三方的测试框架。
  • OCMock都是第三方的测试框架。

什么是接口测试?有什么用?

返回目录

  • 定义:接口测试:是对系统或组件之间的接口进行测试。
  • 作用:测试系统或组件之间交互的数据的正确性,以及逻辑依赖关系的正确性!
  • 原理:用工具或者代码模拟客户端,向服务器发送请求,校验服务器传回来的响应数据是否和预期的结果一致;
  • 特点:
    • 可提前介入,提早发现Bug。
    • 可绕过前端页面的一些校验规则,发现一些页面操作发现不了的问题。
    • 低成本高效益,可实现自动化。
    • 从用户的角度对系统进行全面的检测。

接口测试流程

返回目录

  • 甲方提出要求。
  • 产品经理根据要求编写需求文档。
    • UI设计师出设计图。
    • 开发者根据需求文档,编写接口文档。
    • 测试人员拿到接口文档和需求文档编写测试用例。

接口测试的实现方式?

返回目录

  • 使用接口测试工具来实现(比如: JMeter、Postman、fiddler)
  • 通过编写代码来实现(比如: Python + Requests+UnitTest)

什么是自动化接口测试?

返回目录

  • 利用工具代码,代替人工,自动判断响应结果和预期结果是否一致,依赖断言。

HTTP请求?

返回目录

  • 定义:基于请求与响应模式的应用层的协议。
  • 特点:
    • 支持客户端/服务器。
    • 模式简单快速。
    • 灵活。
    • 无连接。
    • 无状态。
  • 组成:请求行,请求头,空行,请求体。

请求行?

返回目录

  • 作用:
  • 组成:3部分
    • 请求方法。
      • GET:查询。—―没有请求体
      • POST:新增。――登录、注册主要使用。有请求体
      • PUT:修改。――有请求体。
      • DELETE:删除。—―没有请求体
    • URL:资源定位符。
    • 协议版本。

请求头?

返回目录

  • 作用:向服务器描述客户端(浏览器)的基本信息。
  • 语法:k:v 键值对儿
  • User-Agent:向服务器描述浏览器的类型(防爬虫)。
  • Content-Type:向服务器描述请求体的数据类型。
    • application/x-www-form-urlencoded(表单数据类型)

请求体?

  • GET、DELETE请求方法,没有请求体。
  • POST、PUT请求方法,有请求体。
  • 请求体的数据类型,受请求头中Content-Type的值影响

HTTP响应?

返回目录

  • 定义:服务器回发送给客户端的数据语法格式。
  • 组成:响应行(状态行)、响应头、空行、响应体
    在这里插入图片描述

状态行?

返回目录

  • 语法格式:协议版本(空格)状态码(空格)状态码描述\rln
  • 协议版本: http1.0/http1.1/http2.0
  • 状态码:针对Http请求,响应的状态
  • 状态码描述:
    • 1xx:信息性状态码
    • 2xx:成功状态码
      • 200 OK :请求被成功处理。
      • 201 Created :请求被成功处理并且在服务端创建了一个新的资源。
      • 202 Accepted :服务端已经接收到了请求,但是还未处理。
      • 204 No Content : 服务端已经成功处理了请求,但是没有返回任何内容
    • 3xx :重定向状态码
      • 301 Moved Permanently : 资源被永久重定向了。
      • 302 Found :资源被临时重定向了。
    • 4xx:客户端错误状态码
      • 400 Bad Request : 发送的HTTP请求存在问题。
      • 401 Unauthorized : 未认证却请求需要认证之后才能访问的资源。
      • 403 Forbidden :直接拒绝HTTP请求,不处理。
      • 404 Not Found : 你请求的资源未在服务端找到。
      • 409 Conflict : 表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。
    • 5xx :服务端错误状态码
      • 500 Internal Server Error : 服务端出问题了(通常是服务端出Bug了)。
      • 502 Bad Gateway :我们的网关将请求转发到服务端,但是服务端返回的却是一个错误的响应。

响应头

返回目录

  • 作用:向客户端描述服务器的基本信息。
  • 语法:k:v键值对儿
  • Content-Type:向客户端描述 响应体的数据类型

响应体

返回目录

  • http响应报文,大多数是有响应体
  • 响应体的数据类型,受响应头中 ContentIType 的值影响。
    • json 类型
    • 表单 类型
    • 图片 类型

传统风格的接口

返回目录

  • 使用GET、POST 实现所有数据的增删改查操作。
  • 针对用户的某一个操作,URL不唯一。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oGDic4xp-1666678980669)(assets/1650595084080.png)]
  • 优点:
      1. 对开发的技术要求低。只需要掌握get、post即可
      1. 代码灵活

RESTful 接口

返回目录

  • 使用GET/POST/PUT/DELETE分别表示查、增、改、删使
  • 用一个URL对应一个唯一的资源。
  • 接口之间传递的数据最常用格式为JSON。

URL格式?

  • 协议部分:“http",常见的协议有HTTP,HTTPS、FTP等。
  • 域名部分:“www.baidu.com”,也可以使用IP地址作为域名使用(定位网络环境中的一台主机)。
  • 端口部分:“8080",端口可以省略,默认端口(HTTP:80,HTTPS:443(加密),FTP:21,mysql: 3306,redis: 6379)。
  • 资源路径部分:/news/index.html。
  • 查询参数部分:uid=123&page=1。
什么是接口文档?

返回目录

  • 定义:也叫API文档。是由开发人员编写。描述接口相关信息的文档。
  • 作用:
      1. 方便前端开发人员和后端开发人员,在开发时进行对接。
      1. 在人员更迭时,方便新入职的员工快速接手项目。
      1. 方便测试人员编写接口测试用例。
  • 展现形式:
    • 在线文档(html)
    • 离线文档
      • wordo
      • xmind
      • pfd
      • Excel
  • 结构:
    • 基本信息:

      接口描述:URL:(协议+域名)+资源路径。

      请求方法:POST/GET/DELETE/PUT

    • 请求参数:

      • 请求头
      • 请求体(GET、DELETE没有)
    • 返回结果:

      • 状态码、状态描述
      • 响应体
  • 应用举例:登录接口为例:
    • 基本信息:

      • 接口描述:URL: http:/lihrm-test.itheima.net/api/sys/login
      • 请求方法:POST
      • 协议版本:默认 http/1.1
    • 请求参数:

      • 请求头: Content-Type:application/json
      • 请求体:{“mobile”:“13800000002",“password”:“123456”}
    • 返回结果:

      • 响应行:状态码、状态描述:200 ok

      • 响应头: 无

      • 响应体:

        • 第一种情况:“success”:true,“code”:10000,"message"∵"操作成功! ", “data”.“xxx”}
        • 第二种情况:{“success”:false,“code”:20001 ," message":“用户名或密码错误” ," data":null}
        • 第三种情况:{“success”.false,“code”:99999,"message"∵"抱歉,系统繁忙,请稍后重试! ",“data”:null}

为什么要进行接口测试用例设计?

返回目录

    1. 防止漏测
    1. 管理工作进度,评估工作量

接口测试维度有哪些?

返回目录

  • 功能测试:
    • 单接口功能:测试单个接口功能。
      • 参考:登陆界面用例设计要点
      • 设计用例:8大要素:编号,标题,项目描述,优先级,预置条件,测试数据、执行步骤 ,预期结果
    • 业务场景功能:根据业务流程,按顺序展开接口测试。
      • 登录成功–添加员工–查询–修改–查询–删除–查询员工列表
  • 性能测试:
    • 响应时长:从发出请求,到服务器回发响应,所经历的时长。
    • 吞吐量:TPS(单位时间事务数),QPS(单位时间请求数)
    • 并发数:同一时间,同时向服务器的接口发送请求,所能正确处理的数量。
    • 服务器资源使用率:接口工作中,服务器硬件资源使用占比情况。
  • 安全测试:
    • 敏感数据是否加密:密码,身份证号,银行卡号…
    • SQL注入:在用户能够输入的位置,写SQL语句,看是否能够得到数据库查询的结果
    • 其他:

登陆界面用例设计要点?

返回目录

  • 1.测试页面布局、控件的位置是否精准

  • 2.针对用户名的编辑框中的数据值,展开测试

    • 正确手机号、手机号有特殊字符、手机号不足11位、手机号超11位、手机号为空…
  • 3.针对密码的编辑框中的数据值,展开测试

    • 正确密码、错误密码、密码有特殊字符、密码1位、密码100位、密码为空…
  • 4.针对验证码的编辑框中的数据值,展开测试

    • 正确验证码、错误验证码、过期验证码、验证码为空…

Postman

postman断言

返回目录

    1. Postman的断言是使用JavaScript语言编写的,写在Tests标签页里
    1. Tests中的脚本在发送请求之后执行(后置脚本)。
    • 会把断言的结果(PASS/FAIL)最终在Test Results标签页中展示
    1. 常见的断言片段:
    • 状态码断言。
    • 响应体内容包含某字符串断言。
    • 响应体内容等于某字符串断言。
    • 响应体JSON数据检查断言。
    • 响应头标签断言
-----------------------------------------------------------------------
pm.test("Status code is 200", function () {
      pm.response.to.have.status(200);//判断响应状态码是否等于200
});
pm: Postman的一个实例
test: Postman实例的一个方法,有两个参数:
参数1:"Status code is 200"”断言完成后,给出的提示信息. 
参数2:匿名函数调用。
pm.response.to.have.status(200);的意思是postman的响应结果中,有状态码 200
-----------------------------------------------------------------------
pm.test("Body matches string", function () {
      pm.expect(pm.response.text()).to.include("string_you_want_to_search");
});
pm.expect(pm.response.text()).to.include("string_you_want_to_search");意思是:postman实例预期结果中包含 "string_you_want_to_search" 字符串
"string_you_want_to_search": 是预期结果!
-----------------------------------------------------------------------
pm.test("断言响应体等于某个字符串(对象)", function () {
      pm.response.to.have.body("response_body_string");
});
pm.response.to.have.body( "response_body_string")意思:postman 的响应结果中有响应体为"response_body_string"
"response_body_string" 是预期结果
-----------------------------------------------------------------------
pm.test( "our test name", function(){
       var jsonData = pm.response.json();
  	   pm.expect(jsonData.value).to.eql(100);
});
var jsonData = pm. response.json();意思是:
定义一个变量,名称叫 jsonData 值为:json格式的响应体数据pm.expect(jsonData.value).to.eq1 (100);意思是:
postman预期json格式的响应结果中xxx(key)的值为 xxx
-----------------------------------------------------------------------
pm.test("Content-Type is present",function(){
       pm.response.to.have.header("Content-Type");
});
pm.response.to.have.header("Content-Type"):postman中的响应头中有Content-Type
-----------------------------------------------------------------------

全局变量和环境变量

返回目录

  • 全局变量:全局变量是全局唯一的,不可重复定义的变量。
  • 环境变量:一个变量只能属于某个环境,在某一个环境中变量不可重复定义。
  • 设置变量:
    • 手动设置:环境,全局变量;
    • 代码设置:pm.globals.set("var_name", value);
    • 代码设置:pm.environment.set("var_name", value);
  • 获取变量值
    • 请求参数中获取:{{var_name}}:直接在界面上用
    • 代码中获取:var value = pm.globals.get("var_name" );
    • 代码中获取:var value = pm.environment.get("var_name");

Postman请求前置脚本

返回目录

  • 书写在Pre-request Script标签中。
  • Postman在 http请求发送之前,会自动执行,该脚本中的代码。
  • 案例:
    • 创建请求前置脚用例集和http请页。
    • 指定请求方法GET,URL: http://www.baidu.com
    • 在Pre-request Script标签页中,写入代码,获取时间戳,写入到全局变量中
      var timestamp = new Date().getTime()//获取 时间戳
      pm.globals.set("gbl_timestamp",timestamp)//将时间戳保存到全局变量中
    • 点击Send按钮,促使 Pre-request Script标签页中被自动执行。点击右上角的“眼睛"图标查看Globals中,多出一个全局变量。
    • 在 URL中,借助查询参数,使用全局变量。 {{glb_timestamp}} 点击Send按钮,发送带有时间翟戳的请求。
    • 查看,在Postman 菜单栏中“view" --> Show Postman Console 中查看

Postman的关联

返回目录

  • 定义:解决接口和接口之前调用依赖关系,借助全局变量和环境变量解决。
  • 实现:
    • 组织A接口http请求数据,发送A接口请求。
    • 获取A接口返回的响应数据,写入全局、环境变量中
    • 组织B接http请求,从全局、环境变量中获取A返回的数据。

newman生成测试报告

返回目录

#使用newman命令,运行导出的测试集脚本,打开cmd输入:
newman run 测试脚本文件 -e 环境变量文件 -d 测试数据文件 -r html --reporter-html-export report.html
newman run demo.postman_collection.json -r html
newman run demo.postman_collection.json -r html --reporter-html-export report.html

Jmeter

返回目录

性能测试工具Loadrunner与Jmeter异同?

返回目录

  • 相同点:
    • 都能模拟大量用户。
    • 都能支持多协议。
    • 都有监控及分析报表功能。
  • 不同点:
    • 用户量: Loadrunner 多,Jmeter 少 。
    • 分析报表:Loadrunner 精确,Jmeter 较差 。
    • IP欺骗功能:Loadrunner 支持,Jmeter 不支持 。
    • 收费:Loadrunner 收费,Jmeter 免费 。
    • 体积:Loadrunner 大,Jmeter 小 。
    • 扩展性:Loadrunner 不能扩展,Jmeter 有扩展组件 。

Jemeter元件有哪些?

返回目录

  • 1.初始化测试数据—配置元件
  • 2.对请求参数化进行赋值一前置处理器
  • 3.调用GET /POST方法发送请求—取样器
  • 4.提取响应中特定字段的值—后置处理程器
  • 5.对提取出来的值与预期结果进行对比—断言
  • 6.在控制台查看脚本运行的结果—监听器

元件和组件有什么关系?

返回目录

  • 元件由组件构成,组件是原件的构成单位。

Jemeter元件基本作用域?

返回目录

  • Ø取样器:核心,没有作用域
  • Ø逻辑控制器:只对其子节点中的取样器和逻辑控制器起作用
  • Ø其他元件:
    • 如果是某个取样器的子节点,则该元件只对其父节点起作用
    • 如果其父节点不是取样器,则其作用域是该元件父节点下的其他所有后代节点(包括子节点,子节点的子节点等)

元件执行顺序?

返回目录

  • 在同一个作用域(目录/级别/缩进)的不同元件的执行顺序:
    • 配置元件 - 前置处理程序 - 定时器 - 取样器 - 后置处理程序 - 断言 - 监听器。
  • 在同一个作用域(目录/级别/缩进)的相同元件的执行顺序:
    • 从上到下的顺序依次执行

Jmeter线程组

返回目录

  • 定义:控制Jmeter用于执行测试的一组用户。
  • Setup线程组:预测试操作,所有脚本之前执行。
  • 普通线程组:执行测试用例,可以有1个或者多个(并行/串行)。
  • Teardown线程组:测试后操作,所有脚本之后执行。

谈谈你对Jmeter参数化的了解

返回目录

  • 定义:使用不同的测试数据,调用相同的测试方法进行测试。
  • 本质:实现测试数据与测试方法的分离。
  • 实现方式:
    • 用户定义的变量 —— 全局变量
    • 用户参数 —— 为每个用户分配不同的参数值
    • CSV数据文件设置 —— 文件方式参数化
    • 函数 —— 随机数据
    • 数据库

4种参数化方式有何不同?如何选择适当的方式?

返回目录

  • 用户定义的变量:

    • 作用:定义全局变量。
    • 局限性:每次取值(无论是否相同的用户)都是固定值。
  • 用户参数:

    • 作用:保证不同的用户针对同一组参数,可以取到不同的值。
    • 局限性:同一个用户在多次循环时,取到相同的值。
  • csv数据文件设置:

    • 作用:保证不同的用户及同一用户多次循环时,都可以取到不同的值。
    • 局限性:需要手动进行测试数据的设置。
  • 函数:

    • 作用:保证不同的用户及多次循环时,都可以取到不同的值,不需要提前设置
    • 局限性:输入数据有特定的业务要求时无法使用(如:登录时的用户名密码)

Jemeter常用断言

返回目录

  • 响应断言
  • JSON断言
  • 持续时间断言

Jmeter关联

返回目录

  • 关联:当请求之间有依赖关系,比如一个请求的入参是另一个请求返回的数据,这时候就需要用到关联处理。
  • 常用方法:
    • 正则表达式提取器
      • <title>百度一下,你就知道</title><title>百度一下,你就知道</title> -> <title>(.*?)</title>
      • 021-1234-1234->(.*?)-(.*?)-(.*?)\n
    • XPath提取器
      • //a[@id='kw']: 在HTML页面中,找出a标签(有一个属性为id,且id的值为kw)
      • //a : 找出所有的a标签
    • JSON提取器

Jmeter 参数引用和Postman参数引用

返回目录

  • Jmeter:${}
  • Postman:{{}}

Jmeter属性

返回目录

  • Jmeter属性的配置函数
    • _setProperty函数:将值保存成JMeter属性。
    • property函数:在其他线程组中使用property函数读取属性。
  • Jmeter属性的函数执行
    • Beanshell取样器来执行。
    • 其他线程组中使用property函数。

Jmeter自动录制脚本

返回目录

  • JMeter录制脚本:在没有接口文档的旧项目当中,快速录制web页面产生的http接口请求,帮助编写接口测试脚本。(抓包)

Jmeter直连数据库

返回目录

  • 用作请求的参数化
  • 用作结果的断言
  • 准备测试数据
  • 清理垃圾数据

直连数据库步骤

返回目录

  • 引入MySQL驱动
    • 方式一:在测试计划面板点击“浏览…"按钮,将你的JDBC驱动添加进来。
    • 方式二:将MySQL驱动jar包放入到llib/ext目录下,重启JMeter。
  • 连接MySQL数据库
    • 添加方式:测试计划 --> 线程组–> (右键添加) 配置元件 --> JDBC Connection Configuration

常用控制器有哪些?

返回目录

  • 如果(if)控制器。
  • 循环控制器。
  • ForEach控制器。

常用定时器

返回目录

  • 同步定时器。

    • 同步定时器:阻塞线程(累积一定的请求),当在规定的时间内达到一定的线程数量,这些线程会在同一个时间点一起释放,瞬间产生很大的压力。
  • 常数吞吐量定时器。

    • 以某个频率发送请求,持续一段时间。
  • 固定定时器

    • 锁定一定时间。

Selenium

自动化测试

返回目录

  • 说明:让程序或工具代替人为对程序功能验证的过程
    • 解决:

        1. 回归测试[重点]
        1. 压力测试
        1. 兼容性(1. 浏览器、2. 分辨率 3. 操作系统)
        1. 提高测试效率
    • 优点:

        1. 在最短时间内运行最多的bug
        1. 脚本重复运行
        1. 减少人为错误
        1. 克服手工测试局限性(图片大小)
    • 误区:

        1. 自动化测试比手工厉害
        1. 自动化测试比手工发现更多bug
        1. 自动化测试可以完全替代手工测试
        1. 软件所有功能都适合自动化测试。

自动化测试分类

返回目录

    1. Web自动化测试
    1. 移动自动化测试(app自动化)
    1. 接口自动化(工具、代码)
    1. 单元测试

web自动化测试

返回目录

  • 概念:让程序代替人为对web项目进功能验证过程

  • 什么web项目适合自动化?

      1. 需求变动不频繁
      1. 需要回归测试项目
      1. 项目周期长
  • web自动化开始进行阶段?

    • 手工测试之后
  • web自动化所属分类

      1. 黑盒测试
      1. 灰盒测试
      1. 白盒测试
  • 提示:

    • web自动化测试属于黑盒测试

自动化工具

返回目录

  • 主流自动化工具

      1. QTP: 收费 支持(支持web、桌面软件自动化)
      1. selenium: 免费 开源 只支持web项目
      1. Robot framework: 基于python扩展关键字驱动自动化工具。2014年停止更新
  • selenium特点:

      1. 开源、免费
      1. 跨平台(Linux windows mac)
      1. 支持多浏览器:谷歌 火狐 IE
      1. 支持多语言:python、java…等等
      1. 成熟稳定:已经被 谷歌、百度大型公司广泛使用
      1. 功能强大:支持商业化大部分功能,并且由于开源,可以定制化需求功能。

第一个案例

返回目录

from selenium import webdriver
from time import sleep
driver = webdriver.Chrome()# 获取浏览器驱动
driver.get(“http://www.baidu.com”) # 打开url
sleep(3) # 暂停3秒
driver.quit() # 关闭浏览器驱动

元素定位

返回目录

  • 4.1 为什么要使用元素定位?
    • 要使用web自动化操作元素,必须首先找到此元素。
  • 4.2 定位工具
    • 火狐:Firebug (F12获取直接点击 Friebug图标)
    • 谷歌:F12键(开发者工具)
  • 4.3 定位元素时依赖于什么?
      1. 标签名
      1. 属性
      1. 层级
      1. 路径
  • 4.4 定位方式
      1. 基于元素属性特有定位方式(id\name\class_name)
        • driver.find_element_by_id(id)
        • drivr.find_element_by_name(name)
        • driver.find_element_by_class_name()
      1. 基于元素标签名称定位:tag_name
        • driver.find_element_by_tag_name()
      1. 定位超链接文本(link_text、partial_link_text)
        • driver.find_element_by_link_text()
        • driver.find_element_by_partial_link_text()
      1. 基于元素路径定位(xpath)
          1. 路径
          • 绝对路径:以单斜杠开头逐级开始编写,不能跳级。如:/html/body/div/p[1]/input
          • 相对路径:以双斜杠开头,双斜杠后边跟元素名称,不知元素名称可以使用*代替。如: //input或则 //*
          1. 路径结合属性:语法:在Xpath中,所有的属性必须使用@符号修饰 如://*[@id=‘id值’]
          1. 路径结合逻辑(多个属性):语法://*[@id=“id值” and @属性=‘属性值’]
          1. 路径结合层级://*[@id=‘父级id属性值’]/input
      1. 基于选择器(css)
        • driver.find_element_by_css_selector()
          • id 选择器:#id 如:#passwordA
          • class 选择器:.class 如:.telA
          • 元素选择器:element 如:input
          • 属性选择器:[属性名=属性值]
          • 层级选择器: p>input或则p input
        • driver. find element_ by_ css_ selector( “#passwordA”). send_ keys(" python" )
        • driver. find_ element_ by_ css_ selector(“. ipt” ). send_ keys(" python" )

元素定位(一组)

方法:driver.find_elements_by_xxx()
注:其余同上。

元素操作

返回目录

    1. 元素操作
    • 1). send_keys() # 输入方法
    • 2). click() # 点击方法
    • 3). clear() # 清空
  • 元素信息操作

    • 1). text 获取元素文本 如:driver.text
    • 2). size 获取元素大小 如:driver.size
    • 3). get_attribute 获取元素属性值 如:driver.get_attribute(“id”)
    • 4). is_displayed 判断元素是否可见 如:element.is_displayed()
    • 5). is_enabled 判断元素是否可用 如: element.is_enabled()
    • 6). is_selected 判断元素是否被选中 如:element.is_selected()
    1. 浏览器常用操作
    • 1). driver.maximize_window() # 最大化浏览器
    • 2). driver.set_window_size(w, h) # 设置浏览器大小 单位像素
    • 3). driver.set_window_position(x, y) # 设置浏览器位置
    • 4). driver.back() # 后退操作
    • 5). driver.forward() # 前进操作
    • 6). driver.refresh() # 刷新操作
    • 7). driver.close() # 关闭当前主窗口(主窗口:默认启动哪个界面,就是主窗口)
    • 8). driver.quit() # 关闭由driver对象启动的所有窗口
    • 9). driver.title # 获取当前页面title信息
    • 10). drive.current_url # 获取当前页面url信息
    1. 鼠标操作(ActionChains类—>导包 from selenium.webdriver.common.action_chains import ActionChains)
      1. context_click() # 右击,应用:context_click(element).perform()
      1. double_click() # 双击,应用:double_click(element).perform()
      1. drag_and_drop() # 拖拽,应用:drag_and_drop(source, target).perform()
      1. move_to_element() #悬停,应用: move_to_element(element).perform()
  • 键盘操作(from selenium.webdriver.common.keys import Keys)

    • 组合键:element.send_keys(Keys.CONTROL, ‘c’)
    • 单键:element.send_keys(Keys.XXX)
    1. 元素等待【重点】
    • 定义:页面代码没有加载完毕,导致元素定位未能找到对应的元素。
    • 分类:
        1. 隐式等待:driver.implicitly_wait(30) # 一般情况下设置30秒
        1. 显示等待:WebDriverWait(driver,timeout=10, poll_frequency=0.5).until(lambda x:x.find_element_by_id(“#user”)).send_keys(“admin”)
    • 参数:
      • timeout: 超时时间
      • poll_frequency:访问频率,默认0.5秒找一次元素
      • x: x为driver,它是WebDriverWait类将传入的driver赋值给类self._driver,until方法调用了self._driver;
    1. 下拉框选择
    • 1 下拉选择框

        1. 导包:from selenium.webdriver.support.select improt Select
        1. 实例化:s = Select(element)
        1. 调用方法:s.select_by_index()
        • 提供哪些方法
          1. select_by_index() # 通过下标定位
          1. select_by_value() # 通过value值
          1. select_by_visible_text() #显示文本
    • 2 弹出框

      • 方法:driver.switch_to.alert
        • alert.text # 获取文本
        • alert.accept() # 同意
        • alert.dismiss() # 取消
    • 3 滚动条操作

      • 在selenium中没有直接提供定位滚动条组件方法,但是它提供了执行js语句方法,可以通过js语句来控制滚动条操作。
        • js = “window.scrollTo(0,10000)”
        • driver.execute_script(js)
    • 4 frame表单切换

      • driver.switch_to.default_content()#回到主目录
      • driver.switch_to.frame(“id||name||element”)#iframe或frame只有在主目录才有相关元素信息,不回到主目录,切换语句会报错。
    • 5 多窗口切换

      • 页面存在多个窗口式,selenium默认焦点只会在主窗口上所有的元素,不切换切换窗口,无法操作除主窗口以外的窗口内元素。
          1. driver.current_window_handle # 获取当前主窗口句柄
          1. driver.window_handles # 获取当前由driver启动所有窗口句柄
          1. driver.switch_to.window(handle) # 切换窗口
    • 6 窗口截图

      • driver.get_screenshot_as_file(imgepath)
    • 7验证码处理

        1. 去掉验证码(项目在测试环境、公司自己的项目)
        1. 设置万能验证码(测试环境或线上环境,公司自己项目)
        1. 使用验证码识别技术 (由于现在的验证码千奇百怪,导致识别率太低)
        1. 使用cookie解决(推荐)
          1. driver.get_cookies() # 获取所有的cookie
          1. driver.add_cookies({字典}) # 设置cookie

数据驱动

返回目录

  • 什么是数据驱动?
    • 定义:以数据来驱动整个测试用例的执行。
    • PO设计模式+参数化技术;
  • 数据驱动常用的格式
      1. JSON(重点)
      1. XML
      1. EXCEL
      1. CSV
      1. TXT
  • PO模式
    • 1)两层:对象逻辑层+业务数据层
    • 2)三层:对象库层+逻辑层+业务数据层
    • 3)四层:对象库层+逻辑层+业务层+数据层
  • 数据驱动案例

Spring

返回目录

SpringMVC

返回目录

Mybatis

返回目录

Java编程语言

返回目录

Java语言有哪些特点?

返回目录

  • 平台无关性,支持多线程,安全可靠,网络编程方便,编译解释并存。

JVM vs JDK vs JRE

返回目录

  • JVM:虚拟机。
  • JDK: 类库+javac编译器+虚拟机。
  • JRE:类库+虚拟机。

什么是字节码?

返回目录

  • 虚拟机可理解的代码。

Java 程序运行流程

返回目录
在这里插入图片描述- 注:JIT:及时编译器,完成第一次编译后,其会将字节码对应的机器码保存下来,下次直接使用。

采用字节码的好处是什么?

返回目录

  • 可移植

为什么不全部使用AOT呢?

返回目录
AOT: .class->机器码
JIT: .class->解释器->JIT->机器码。
缺点:不支持反射机制 == C++编译器+VM虚拟机。

为什么说Java语言"编译与解释并存"?

返回目录

  • .java->Javac编译->.class->解释->JIT编译->机器码。

Oracle JDK vs OpenJDK

返回目录

  • Oracle JDK 是 OpenJDK 优化产物。

Java和C++的区别?

返回目录

  • Java 不提供指针。
  • Java 的类是单继承的。
  • Java 有自动垃圾回收机制(GC)。
  • Java 只支持方法重载

注释有哪几种形式?

返回目录

  • 单行注释
  • 多行注释
  • 文档注释

标识符和关键字的区别是什么?

返回目录

  • 标识符:编程取名。
  • 关键字:特殊标识符。

Java语言关键字||数据类型有哪些?

返回目录
在这里插入图片描述

continue、break 和return的区别是什么?

返回目录

  • continue:结束本次循环,进入下一循环。
  • break:跳出循环。
  • return:跳出函数。

Java中的几种基本数据类型了解么?

返回目录
在这里插入图片描述

基本类型和包装类型的区别?

返回目录

  • 包装类型不赋值就是 null。
  • 包装类型可用于泛型。
  • 包装类型存于进程堆。

包装类型的缓存机制了解么?

返回目录

  • 大部分使用。
  • Byte,Short,Integer,Long->[-128,127]缓存数据。
  • Character->[0,127]缓存数据.。

自动装箱与拆箱了解吗?原理是什么?

返回目录

  • 装箱:将基本类型用它们对应的引用类型包装起来。
  • 拆箱:将包装类型转换为基本数据类型。
  • 原理: 调用用包装类的valueOf()方法,拆箱调用了 xxxValue()方法。

为什么浮点数运算的时候会有精度丢失的风险?

返回目录

  • 数据类型的宽度一定,小数过长,只能截断。

3*0.1 == 0.3 将会返回什么? true 还是 false?

返回目录

  • false,因为有些浮点数不能完全精确的表示出来。

如何解决浮点数运算的精度丢失问题?

返回目录

  • BigDecimal 防止浮点数精度丢失。(涉及钱的场景)

超过long整型的数据应该如何表示?

返回目录

  • long型10进制长度大概19位。
  • BigInteger 内部使用 int[] 数组来存储任意大小的整形数据。

面向对象和面向过程的区别?

返回目录

  • 面向过程:通过一个个方法来解决问题。
  • 面向对象:通过对象的方法来解决问题。

对象的相等和引用相等的区别?

返回目录

  • 对象的相等:对象数据结构信息相同。
  • 对象引用的相等:指向对象的地址(指针相同)。

类的构造方法的作用是什么?

返回目录

  • 完成对象的初始化工作

如果一个类没有声明构造方法,该程序能正确执行吗?

返回目录

  • 能,有默认构造。

构造方法有哪些特点?是否可被override?

返回目录

  • 构造方法不能被 override(重写),但是可以 overload(重载)

面向对象三大特征?

返回目录

  • 封装,继承,多态

接口和抽象类有什么共同点和区别?

返回目录

  • 相同点:不能被实例化,内部数据结构信息不完整。
  • 不同点:抽象类可被继承一步一步完善内部数据结构信息,接口只能一次性实现。
  • 不同点:接口主要用于对类的行为进行约束,实现了某个接口就有了对应的行为。
  • 不同点:类只能继承一个抽象类,但可以实现多个接口。
  • 不同点:接口中成员变量只能是 public static final 类型(不可修改),而抽象类成员变量默认 default(可修改)。

深拷贝和浅拷贝区别了解吗?

返回目录

  • 浅拷贝:对于内部对象,只是简单拷贝地址,无内部对象内部数据结构信息。
  • 深拷贝:对于内部对象,完全拷贝内部对象数据结构信息。

什么是引用拷贝?

返回目录

  • 引用拷贝==拷贝对象地址

Object常用方法?

返回目录

  • getClass()
  • hashCode()
  • equals(Object obj)
  • clone()
  • toString()
  • notify()
  • notifyAll()
  • wait(long timeout)
  • wait(long timeout, int nanos)
  • wait()
  • finalize()

== 和 equals() 的区别

返回目录

  • == 简单比较值。
  • equals()比较对象内部数据结构信息。

String、StringBuffer、StringBuilder 的区别?

返回目录

  • String对象是不可变的,而StringBuffer和StringBuilder是可变字符序列.
  • StringBuffer:线程安全不同步。
  • StringBuilder:线程安全同步

Exception和Error有什么区别?

返回目录

  • Exception :程序本身可以处理的异常。
  • Error 属于程序无法处理的异常(错误)。

Checked Exception和 UncheckedException有什么区别?

返回目录

  • Checked Exception: 编译器会检测的异常:IO异常,SQL异常,类/文件未找到异常。
  • UncheckedException: 编译器不会检查的异常,运行时抛出。除零异常,类型转化异常,下标越界异常。

Throwable 类常用方法有哪些?

返回目录

  • getMessage(): 异常简要信息。
  • toString(): 异常详细信息。
  • getLocalizedMessage(): 本地化信息。
  • printStackTrace(): 控制台打印信息。

try-catch-finally 如何使用?

返回目录

  • try块 : 捕获异常。
  • catch块:处理异常。
  • finally 块:后续处理(虚拟机正常:强制执行)。

finally 中的代码一定会执行吗?

返回目录

  • 不一定:虚拟机不正常

如何使用try-with-resources 代替try-catch-finally?

返回目录

  • try-with-resources: 面对必须要关闭的资源使用。
  • try-catch-finally:其他。

异常使用有哪些需要注意的地方?

返回目录

  • 不要把异常定义为静态变量
  • 日志打印异常之后就不要再抛出异常

什么是泛型?有什么作用?

返回目录

  • 定义:类型在使用时指定的数据类型。
  • 形式: c l a s s < T > class <T> class<T>:具体数据类型, c l a s s < ? > class<?> class<?>:未知数据类型。
  • 作用:多种数据类型执行相同的代码。

泛型的使用方式有哪几种?

返回目录

  • 泛型类、泛型接口、泛型方法

项目中哪里用到了泛型?

返回目录

  • Spring IOC 容器通过反射加载内存中的类。

何谓反射?反射的优缺点?反射的应用场景?

返回目录

  • 程序运行期间,对于任何一个类都知道其属性和方法,对于任何一个对象都能调用其属性和方法。
  • 优点:方便对象信息回调及框架的构建。
  • 缺点:可回调运行时对象信息,增加安全问题。
  • 应用场景:对象信息回调,框架的编写。

何谓SPI?SPI和API有什么区别?SPI的优缺点?

返回目录

  • SPI定义:服务提供者的接口。
  • SPI应用:Spring框架,数据库驱动加载。
  • 区别:SPI服务实现方接口,API服务调用方接口。
  • 缺点:会加载所有实现类,不能按需加载。
  • 缺点:多个 ServiceLoader 同时 load 时,会有并发问题。

什么是序列化?什么是反序列化?如果有些字段不想进行序列化怎么办?

返回目录

  • 序列化定义: 将数据结构或对象转换成二进制字节流的过程
  • 反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
  • 不想序列化:使用 transient关键字。

常见序列化协议有哪些?

返回目录

  • Hessian、Kryo、Protobuf、ProtoStuff

为什么不推荐使用JDK自带的序列化?

返回目录

  • 不支持跨语言调用,性能差 ,存在安全问题

Java lO流了解吗?

返回目录

  • 定义:输入和输出接口

I/O流为什么要分为字节流和字符流呢?

返回目录

  • 字节流:InputStream/OutputStream
  • 字符流:Reader/Writer
  • 字符流需要转化,比较耗时,因此需要字节流。
  • 不知道编码类型,字节流转化为字符会出现乱码,因此需要字符流。

Java lO中的设计模式有哪些?

返回目录

  • 装饰器模式:可以在不改变原有对象的情况下拓展其功能。
  • 适配器模式:主要用于接口互不兼容的类的协调工作。
  • 工厂模式:用于创建对象。
  • 观察者模式:用于监听服务。
    • StandardWatchEventKinds.ENTRY_CREATE :文件创建。
    • StandardWatchEventKinds.ENTRY_DELETE : 文件删除。
    • StandardWatchEventKinds.ENTRY_MODIFY : 文件修改。

BIO、NIO和AIO的区别?

返回目录

  • BIO:同步阻塞 IO 模型:应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间。
  • NIO:同步非阻塞 IO 模型:不断轮询发起 read 调用,直至内核数据准备就绪。
  • AIO :异步 IO 模型:基于事件和回调机制实现。

什么是语法糖?

返回目录

  • 方便开发而设计的一种语法。

Java中有哪些常见的语法糖?

返回目录

  • 泛型、自动拆装箱、变长参数、枚举、内部类、增强 for 循环、try-with-resources 语法、lambda 表达式等。

说说List, Set, Queue,Map四者的区别?

返回目录

  • List:元素有序、可重复。
  • Set:元素无序的、不可重复。
  • Queue:元素有序、可重复。
  • Map:key 无序、不可重复,value 无序、可重复。

集合框架底层数据结构总结如何选用集合?

返回目录

  • 存取键值对:选用HashMap:需要排序:TreeMap:需要线程安全:ConcurrentHashMap。
  • 存取普通元素/不可重复:选用HashSet:需要排序:TreeSet:需要线程安全:ConcurrentHashSet。
  • 存取普通元素/可重复:选用ArrayList 或 LinkedList:需要线程安全:ConcurrentArrayList。

为什么要使用集合?

返回目录

  • 存储多种数据类型的数据。

ArrayList和Vector的区别?

返回目录

  • ArrayList线程不安全。
  • Vector线程安全。

ArrayList 与LinkedList区别?

返回目录

  • 底层数据结构:ArrayList:数组,LinkedList双向链表。
  • 插入和删除:ArrayList 等同于数组插入和删除,LinkedList等同于链表插入删除。
  • 快速随机访问:ArrayList支持,LinkedList不支持。
  • 内存空间:ArrayList会预留空间,LinkedList单个元素内存占用多。

说—说ArrayList的扩容机制吧

返回目录

  • ArrayList 初始化容量为10,插入元素超过该值,申请1.5倍原容量大小数组空间,调用Arrays.copyof方法,复制原数组数据到新数组。

comparable和Comparator的区别

返回目录

  • comparable接口:使用compareTo(Object obj)方法排序。
  • comparator接口:使用compare(Object obj1, Object obj2)方法排序。

无序性和不可重复性的含义是什么?

返回目录

  • 无序性:并非按照数组索引的顺序添加 ,根据数据的哈希值决定。
  • 不可重复性:添加元素不可重复。

比较HashSet、LinkedHashSet和TreeSet三者的异同?

返回目录

  • 相同点:保证元素唯一,非线程安全。
  • 不同点: 底层数据机构不同
    • HashSet:哈希表(基于 HashMap 实现)。
    • LinkedHashSet:哈希表和链表。
    • TreeSet:红黑树,元素有序。

Queue与Deque的区别?

返回目录

  • Queue 是单端队列
  • Deque 是双端队列

ArrayDeque 与LinkedList 的区别?

返回目录

  • ArrayDeque :基于可变长的数组和双指针实现, LinkedList 则基于链表实现。
  • ArrayDeque 不支持存储 NULL 数据,但 LinkedList 支持。
  • ArrayDeque 是在 JDK1.6 才被引入的,而LinkedList 早在 JDK1.2 时就已经存在。
  • ArrayDeque 插入可能存在扩容, 均摊后为 O(1)。 LinkedList 不需要扩容,可能需要申请新的堆空间,均摊性能相比更慢。
  • 性能:ArrayDeque 来实现队列要比 LinkedList 更好。

说—说 PriorityQueue?

返回目录

  • 实现:根堆。默认小根堆。
  • 时间:logn.
  • 安全:非线程安全。
  • 应用:堆排序、求第K大的数、带权图的遍历。

HashMap和Hashtable 的区别?

返回目录

  • 安全:HashMap 非线程安全,Hashtable 线程安全。
  • 效率:HashMap效率更高。
  • Null key/value :HashMap允许Null key(一个)/value(多个),Hashtable 不允许 null 键和 null 值。
  • 初始和扩容:Hashtable :11+2n+1,HashMap:16+2^n;
  • 底层:HashMap:数组小于64->数组扩容,链表大于8->链表转红黑。

HashMap和HashSet区别?

返回目录

  • 底层:HashMap:键值对。HashSet:对象元素。
  • 原理:HashMap 使用Key计算 hashcode,HashSet 使用对象计算 hashcode ,
  • HashSet :对于两个对象来说 hashcode 可能相同,所以equals()方法用来判断对象的相等性

HashMap和TreeMap 区别?

返回目录

  • TreeMap 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力。

HashSet 如何检查重复?

返回目录

  • HashSet :对于两个对象来说 hashcode 可能相同,所以equals()方法用来判断对象的相等性

HashMap的底层实现?

返回目录

  • 数组和链表 结合在一起使用也就是 链表散列

HashMap的长度为什么是2的幂次方?

返回目录

  • 通过哈希函数计算的散列值对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ (n - 1) & hash”。(n 代表数组长度)。这也就解释了 HashMap 的长度为什么是 2 的幂次方。

HashMap多线程操作导致死循环问题?

返回目录

  • 造成元素之间会形成一个循环链表。
  • JDK7 HashMap 在多线程下出现死循环的原因是,扩容的时候采用了头插法,会发生链表反转,在一定情况下会出现环形链表,进而触发死循环。JDK8 虽然将头插法修改为尾插法,但其"增删改"的操作仍旧是非原子的,所以还是线程不安全的。

HashMap有哪几种常见的遍历方式?

返回目录

  • 7种。
    • 使用迭代器(Iterator)EntrySet 的方式进行遍历;
    • 使用迭代器(Iterator)KeySet 的方式进行遍历;
    • 使用 For Each EntrySet 的方式进行遍历;
    • 使用 For Each KeySet 的方式进行遍历;
    • 使用 Lambda 表达式的方式进行遍历;
    • 使用 Streams API 单线程的方式进行遍历;
    • 使用 Streams API 多线程的方式进行遍历。

ConcurrentHashMap 和 Hashtable 的区别?

返回目录

  • 底层:ConcurrentHashMap 底层采用 Segment数组+(HashEntry)分段的数组+链表 实现, Hashtable :数组+链表。
  • 安全:ConcurrentHashMap:(Segment)分段锁。Hashtable 使用 synchronized 来保证线程安全。

ConcurrentHashMap线程安全的具体实现方式/底层具体实现

返回目录

  • JDK1.8之前:使用分段锁。
  • 冲突:拉链法
    在这里插入图片描述
  • JDK1.8之后:采用 Node + CAS + synchronized
  • 冲突:拉链法结合红黑树
  • 在这里插入图片描述

JDK 1.7和JDK 1.8的ConcurrentHashMap实现有什么不同?

返回目录

  • 同上

什么是线程和进程?

返回目录

  • 进程:操作系统进行资源分配的最小单位。
  • 线程:CPU进行运算调度的基本单位。
    在这里插入图片描述

情简要描述线程与进程的关系,区别和优缺点?

返回目录

  • 关系:主从关系,线程是进程的运行单位==车间里的生产线
  • 相同:进程上下文切换:【用户态】->【内核态】->【用户态】,线程:【用户态】->【内核态】->【用户态】
  • 区别:进程:资源分配单位,线程:运算单位。
  • 区别:进程上下文切换代价高,线程上下文切换代价低。
  • 区别:进程上下文切换:包含进程表、页表、打开文件表等信息,线程:用户堆栈信息。
  • 优缺点:进程上下文切换代价高,线程上下文切换代价低。

程序计数器为什么是私有的?

返回目录

  • 原因:程序计数器私有,主要是为了线程切换后能恢复到正确的执行位置

虚拟机栈和本地方法栈为什么是私有的?

返回目录

  • 为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。

一句话简单了解堆和方法区

返回目录
在这里插入图片描述

并发与并行的区别

返回目录

  • 并发:单个CPU处理多个线程。
  • 并行:多个CPU处理多个线程。

同步和异步的区别

返回目录

  • 同步:发送方和受理方速度一致。
  • 异步:发送方和受理方速度不一致。

为什么要使用多线程?

返回目录

  • 提高CPU利用率,提高程序执行效率。
  • 形式:单个CPU通过调度算法并发执行多个线程,多个CPU并行处理多个线程。
  • 作用:并发执行的线程,其中某个线程阻塞,可切换上下文运行其他线程,CPU不用一直等待,调高CPU利用率。

使用多线程可能带来什么问题?

返回目录

  • 内存泄漏、死锁、线程不安全

说说线程的生命周期和状态?

返回目录

  • 6 种不同状态
  • NEW: 初始状态,线程被创建出来但没有被调用 start() 。
  • RUNNABLE: 运行状态,线程被调用了 start()等待运行的状态。
  • BLOCKED :阻塞状态,需要等待锁释放。
  • WAITING:等待状态,表示该线程需要等待其他线程做出一些特定动作(通知或中断)。
  • TIME_WAITING:超时等待状态,可以在指定的时间后自行返回而不是像 WAITING 那样一直等待。
  • TERMINATED:终止状态,表示该线程已经运行完毕。

什么是上下文切换?

返回目录

  • 进程或线程运行时的条件和状态信息。
  • 主动让出 CPU。
  • 时间片用完。
  • 调用了阻塞类型的系统中断,比如请求 IO,线程被阻塞。
  • 被终止或结束运行。

什么是线程死锁?

返回目录

  • 定义:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

如何预防线程死锁?

返回目录
①互斥条件:一个资源只能被一个线程占有,当这个资源被占用后其他线程就只能等待。

②不可剥夺条件:当一个线程不主动释放资源时,此资源一直被拥有线程占有。

③请求并持有条件:线程已经拥有一个资源后仍然不满足,又尝试请求新的资源。

④循环等待条件:产生死锁一定是发生了线程资源环路链。

  • 预防:
    • 破坏请求并持有条件:静态分配策略:一个进程/线程必须在执行前就申请到它所需要的全部资源。
    • 破坏循环等待条件:层次分配策略:所有的资源被分成了多个层次,一个进程得到某一次的一个资源后,它只能再申请较高一层的资源;当一个进程要释放某层的一个资源时,必须先释放所占用的较高层的资源。
  • 缺点:预防系统发生死锁 ,但是会导致 低效的进程运行 和 资源使用率。

如何避免线程死锁?

返回目录

  • 原理:每当在未申请者分配资源前先测试系统状态,若把系统资源分配给申请者会产生死锁,则拒绝分配,否则接受申请,并为它分配资源。
  • 实现:银行家算法 通过先 试探 分配给该进程资源,然后通过 安全性算法 判断分配后系统是否处于安全状态。

sleep()方法和wait()方法对比

返回目录

  • 区别:sleep() 方法没有释放锁,而 wait() 方法释放了锁 。
  • 区别:wait() 通常被用于线程间交互/通信,sleep()通常被用于暂停执行。
  • 区别:wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify()或者 notifyAll() 方法。sleep()方法执行完成后, 线程会自动苏醒,或者也可以使用 wait(long timeout) 超时后线程会自动苏醒。
  • 区别:sleep() 是 Thread 类的静态本地方法,wait() 则是 Object 类的本地方法。

为什么wait()方法不定义在Thread中?

返回目录

  • wait() 让获得对象锁的线程实现等待。
  • sleep() 让当前线程暂停执行。

可以直接调用Thread类的run方法吗?

返回目录

  • 调用 start() 方法方可启动线程并使线程进入就绪状态,直接执行 run() 方法的话不会以多线程的方式执行。

volatile 关键字

返回目录

  • 防止指令重排序:我们用synchronize实现一个单例的时候,通常使用 volatile 修饰对象,防止指令重排,不至于其他线程得到未被初始化的对象。
  • 可见性:内存屏障。

如何保证变量的可见性?

返回目录

  • 可见性:在某个线程内的cpu缓存区内,如果被volatile修饰的变量被修改,那么就会触发StoreLoad指令,将缓存区内的变量立即写回主内存,并立即使其他线程内cpu缓存内该变量的值失效。

如何禁止指令重排序?

返回目录

  • 防止重排序:一个对象被volatile修饰,以为着所有对该对象操作的指令,都将按照默认顺序执行,不能按照语义相同就重新排序。

volatile可以保证原子性么?

返回目录

  • 原子性:只能保证单次读写的原子性,不支持多个操作的原子性(也就是不支持事务)。

什么是悲观锁?使用场景是什么?

返回目录

  • 定义:认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁。
  • 应用:多写场景。

如何实现乐观锁?

返回目录

  • 定义:只需在提交修改的时候去验证对应的资源是否被其它线程修改过(具体方法:版本号机制+CAS 算法)。
  • 应用:多读场景。

乐观锁存在哪些问题?

返回目录

  • ABA 问题是乐观锁最常见的问题。(解决办法:追加上版本号或者时间戳)
  • 循环时间长开销大。
  • 只能保证一个共享变量的原子操作。

synchronized是什么?有什么用?如何使用synchronized?

返回目录

  • 定义:synchronized 实现同步的 重量级锁。
  • 作用:实现线程同步。
  • 使用:修饰实例方法(锁当前对象实例),修饰静态方法(锁当前类),修饰代码块(锁指定对象/类)。
  • 优化:自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁

可以给String对象上锁吗?

返回目录

  • 尽量不要使用 ,虚拟机字符串常量池具有缓存功能。

构造方法可以用synchronized 修饰么?

返回目录

  • 构造方法不能使用 synchronized 关键字修饰。

synchronized 底层原理了解吗?

返回目录

  • synchronized 属于 重量级锁,依赖监视器锁(monitor)实现,涉及monitorenter 和 monitorexit 指令,监视器锁(monitor)依赖于底层的操作系统的 Mutex Lock 来实现。

JDK1.6之后的synchronized 底层做了哪些优化?

返回目录

  • 偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术。

synchronized和volatile有什么区别?

返回目录

  • volatile:轻量级实现,synchronized:重量级实现。
  • volatile:只能用于变量,synchronized:可以修饰方法以及代码块 。
  • volatile:只有可见性,无原子性,synchronized:两者都可保证。
  • volatile:解决变量同步性,synchronized:解决资源同步性。

ReentrantLock是什么?

返回目录

  • 定义:可重入且独占式的锁,实现Lock接口。

公平锁和非公平锁有什么区别?

返回目录

  • 公平锁:先申请的线程先得到锁。
  • 非公平锁:优先级高/随机获得锁。

synchronized和 ReentrantLock有什么区别?

返回目录

  • ReentrantLock显示获得锁。->syn.隐式
  • ReentrantLock可响应中断。->syn.不响应
  • ReentrantLock JDK级别。->syn.虚拟机
  • ReentrantLock 通过Condition绑定条件。->syn.不具备
  • ReentrantLock 发生异常不会释放锁。->syn.会释放
  • ReentrantLock 可实现不公平锁。->syn.非公平

什么是中断,有什么用?

返回目录

  • 定义:CPU响应紧急事件,打断正常执行程序。
  • 作用:为了响应紧急事件。
  • 机制:CPU通过调度算法,正常执行某一进程中的一个或多个线程,此时,有紧急事件出现,CPU会立即上下文切换,执行紧急事件程序。

可中断锁和不可中断锁有什么区别?

返回目录

  • 不可中断锁:线程拿到锁,CPU就不能立即处理紧急事件。->synchronized
  • 可中断锁:线程拿到锁,CPU能立即切换上下文,处理紧急事件。->ReentrantLock

ReentrantReadWriteLock是什么?

返回目录

  • 定义:可重入的读写锁,
  • 优点:既可以保证多个线程同时读的效率, 同时又可以保证有写入操作时的线程安全。

共享锁和独占锁有什么区别?

返回目录

  • 共享锁 :一把锁可以被多个线程同时获得。
  • 独占锁 :一把锁只能被一个线程获得。

线程持有读锁还能获取写锁吗?

返回目录

  • 持有读锁,不能再获取写锁。
  • 持有写锁,可再持有读锁。

读锁为什么不能升级为写锁?

返回目录

  • 引起资源抢夺,影响性能。
  • 可能造成死锁。

StampedLock是什么?

返回目录
性能更好的读写锁,不可重入且不支持条件变量 Conditon。

StampedLock 的性能为什么更好?

返回目录

  • StampedLock支持乐观读,写的时候可以读(Stamp+CAS)。ReentrantReadWriteLock:写的时候不能读。

StampedLock 适合什么场景?

返回目录

  • 读多写少。

StampedLock的底层原理了解吗?

返回目录

  • 基于 CLH 锁 实现,CLH 锁是对自旋锁的一种改良,是一种隐式的链表队列。StampedLock 通过 CLH 队列进行线程的管理,通过同步状态值 state 来表示锁的状态和类型。

ThreadLocal有什么用?如何使用ThreadLocal?

返回目录

  • 主要解决的就是让每个线程绑定自己的值。

ThreadLocal 原理了解吗?

返回目录

  • ThreadLocal 维护了一个 ThreadLocalMap 可以理解为 定制化的 HashMap。
  • 我们调用 ThreadLocalMap类对应的 get()、set()方法。

ThreadLocal内存泄露问题是怎么导致的?

返回目录

  • ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,而 value 是强引用。所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。

什么是线程池?

返回目录

  • 线程池就是管理一系列线程的资源池。

为什么要用线程池?

返回目录

  • 降低资源消耗。
  • 提高响应速度。
  • 提高线程的可管理性。

如何创建线程池?

返回目录

  • 方式一:通过ThreadPoolExecutor构造函数来创建(推荐)
    • FixedThreadPool : 该方法返回一个固定线程数量的线程池。
    • SingleThreadExecutor: 该方法返回一个只有一个线程的线程池。
    • CachedThreadPool: 该方法返回一个可根据实际情况调整线程数量的线程池。
    • ScheduledThreadPool :该返回一个用来在给定的延迟后运行任务或者定期执行任务的线程池。
  • 方式二:通过 Executor 框架的工具类 Executors 来创建。

为什么不推荐使用内置线程池?

返回目录

  • Executors 返回线程池对象的弊端如下:
    • FixedThreadPool 和 SingleThreadExecutor : 使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
    • CachedThreadPool :使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
    • ScheduledThreadPool : 使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。

线程池常见参数有哪些?如何解释?

返回目录

  • ThreadPoolExecutor 3 个最重要的参数:
    • corePoolSize : 任务队列未达到队列容量时,最大可以同时运行的线程数量。
    • maximumPoolSize : 任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
    • workQueue: 新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。
  • ThreadPoolExecutor其他常见参数 :
    • keepAliveTime:线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;
    • unit : keepAliveTime 参数的时间单位。
    • threadFactory :executor 创建新线程的时候会用到。
    • handler :饱和策略。关于饱和策略下面单独介绍一下。

线程池的饱和策略有哪些?

返回目录

  • 同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时,ThreadPoolTaskExecutor 定义一些策略:
    • ThreadPoolExecutor.AbortPolicy: 抛出 RejectedExecutionException来拒绝新任务的处理。
    • ThreadPoolExecutor.CallerRunsPolicy: 调用执行自己的线程运行任务,也就是直接在调用execute方法的线程中运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。
    • ThreadPoolExecutor.DiscardPolicy: 不处理新任务,直接丢弃掉。
    • ThreadPoolExecutor.DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。

线程池常用的阻塞队列有哪些?

返回目录

  • LinkedBlockingQueue(无界队列)
  • SynchronousQueue(同步队列)
  • DelayedWorkQueue(延迟阻塞队列)

线程池处理任务的流程了解吗?

返回目录
在这里插入图片描述

如何设定线程池的大小?

返回目录

  • CPU 密集型任务(N+1): N(CPU 核心数)+1
  • I/O 密集型任务(2N):

如何动态修改线程池的参数?

返回目录

  • ThreadPoolExecutor类提供了set方法。
    • setCorePoolSize()
    • setMaximumPoolSize()

线程池基本使用

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池,包含3个线程
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // 提交任务给线程池
        for (int i = 1; i <= 5; i++) {
            Runnable task = new Task(i);
            executorService.execute(task);
        }

        // 关闭线程池
        executorService.shutdown();
    }

    static class Task implements Runnable {
        private int taskId;

        public Task(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            System.out.println("Task " + taskId + " is executing by " + Thread.currentThread().getName());
        }
    }
}

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ArrayBlockingQueue;

public class CustomThreadPool {
    public static void main(String[] args) {
        // 构造 ThreadPoolExecutor
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2,  // 核心线程数
            4,  // 最大线程数
            60, // 线程存活时间
            TimeUnit.SECONDS, // 时间单位
            new ArrayBlockingQueue<>(2), // 任务队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
        );

        // 提交任务给线程池
        for (int i = 1; i <= 6; i++) {
            Runnable task = new Task(i);
            executor.execute(task);
        }

        // 关闭线程池
        executor.shutdown();
    }

    static class Task implements Runnable {
        private int taskId;

        public Task(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            System.out.println("Task " + taskId + " is executing by " + Thread.currentThread().getName());
        }
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class store{
    private static long count = 10000;

    public synchronized static long get(){
        if(count>0){
            count-=100;
        }
        return count;
    }
    public static synchronized boolean check(){
        if(count>0) return true;
        else return false;
    }
}

class Task implements Runnable{
    private int taskId;

    public Task(int taskId){
        this.taskId = taskId;
    }
    @Override
    public void run() {
        while (store.check()){
            System.out.println("线程 "+taskId+"执行,剩余count数量为: "+store.get());
        }
    }
}

public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for(int i=1;i<=3;i++){
            executorService.execute(new Task(i));
        }
        executorService.shutdown();

    }
}


Future类有什么用?

返回目录

  • 当我们执行某一耗时的任务时,可以将这个耗时任务交给一个子线程去异步执行,同时我们可以干点其他事情,不用傻傻等待耗时任务执行完成。等我们的事情干完后,我们再通过 Future 类获取到耗时任务的执行结果。

Callable和 Future有什么关系?

返回目录

  • Future<-实现<-FutureTask<-封装<-Callable 和 Runnable对象。

CompletableFuture类有什么用?

返回目录

  • CompletableFuture 类可以解决Future 不支持异步任务的编排组合、获取计算结果的 get() 方法为阻塞调用等缺陷

AQS是什么?

返回目录

  • 抽象队列同步器。

AQS的原理是什么?

返回目录

  • 线程阻塞,放入AQS队列。
  • AQS 是用 CLH 队列锁 实现的,即将暂时获取不到锁的线程加入到队列中。

Semaphore有什么用?

返回目录

  • synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源。
  • 而Semaphore(信号量)可以用来控制同时访问特定资源的线程数量。

Semaphore的原理是什么?

返回目录

  • Semaphore 是共享锁的一种实现,它默认构造 AQS 的 state 值为 permits,你可以将 permits 的值理解为许可证的数量,只有拿到许可证的线程才能执行。

CountDownLatch有什么用?

返回目录

  • CountDownLatch 允许 count 个线程阻塞在一个地方,直至所有线程的任务都执行完毕。
  • 线程阻塞只允许有count个。

CountDownLatch的原理是什么?

返回目录

用过CountDownLatch么?什么场景下用的?

返回目录

CyclicBarrier有什么用?

返回目录

CyclicBarrier的原理是什么?

返回目录

Java内存回收机制

  • GC回收引用
    • ① 可达性分析算法:GC Roots到对象不可达时,则证明此对象是不可用。
    • ② 强引用:new 一个新对象的方式来创建强引用。
    • ③ 软引用:内存不够的情况下才会被回收。
    • ④ 弱引用:只能存活到下一次垃圾回收发生之前
    • ⑤ 虚引用:设置虚引用关的唯一目的就是能在这个对象被回收时收到一个系统通知。
  • GC回收算法
    • (1)标记-清除算法:效率不高,产生碎片,大对象无法分配。
    • (2)标记-整理算法:让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
    • (3)复制算法:内存分为相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。
  • 分代管理
    • 新生代:采用复制算法。
    • 老年代:采用标记清理算法或则标记整理算法。
  • GC垃圾回收器
    • Minor GC: 发生在新生代上,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。
    • Full GC: 发生在老年代上,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。
  • GC触发条件
    • Minor GC: 当 Eden 空间满时。
    • Full GC:调用 System.gc()、老年代空间不足、JDK 1.7 及以前的永久代空间不足。

Jenkins

返回目录

软件开发流程

在这里插入图片描述

  • Jenkins 本质上是一个Shell脚本与一堆插件(maven,Git,JDK)的集合,利用Git拉取仓库代码;基于maven编译,测试,打包;使用Linux命令进行项目部署。

1.jenkins是什么

  • Jenkins是一个开源的、可扩展的持续集成、交付、部署(软件/代码的编译、打包、部署)的基于web界面的平台。允许持续集成和持续交付项目,无论用的是什么平台,可以处理任何类型的构建或持续集成。

2.为什么使用jenkins

  • Jenkins是一种使用Java编程语言编写的开源持续集成软件工具,用于实时测试和报告较大代码库中的孤立更改。 Jenkins软件使开发人员能够快速找到并解决代码库中的缺陷,并自动进行构建测试。

3.CI/CD是什么

  • CI(Continuous integration,中文意思是持续集成)是一种软件开发时间。持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。

  • CD(Continuous Delivery, 中文意思持续交付)是在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境(类生产环境)中。比如,我们完成单元测试后,可以把代码部署到连接数据库的Staging环境中更多的测试。如果代码没有问题,可以继续手动部署到生产环境。

4.集中常见的CI工具

  • 以下是前8种持续集成工具的列表:
    Jenkins
    TeamCity
    Travis
    CIGo
    CDBamboo
    GitLabCI
    CircleCI
    Codeship

5.什么是Jenkins pipeline

  • Pipeline,简而言之,就是一台运行于Jenkins上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排与可视化。
    Jenkins Pipeline是一组插件,让Jenkins可以实现持续交付管道的落地和实施。

6.为什么在jenkins中使用管道

  • Pipeline在Jenkins上添加了一套强大的自动化工具,支持从简单的持续集成到全面的持续交付管道的用例。 通过对一系列相关任务进行建模,用户可以利用Pipeline的许多功能: 代码:流水线是用代码实现的,通常会检查到源代码管理中,从而使团队能够编辑,查看和迭代其交付流水线。耐用:管道可以在Jenkins主服务器的计划内和计划外重启中生存。可暂停:管道可以选择停止并等待人工输入或批准,然后再继续管道运行。多功能:管道支持复杂的现实世界中的连续交付需求,包括加入,循环和并行执行工作的能力。

7.什么是jenkinsfile?为什么使用jenkinsfile

  • Jenkinsfile是一个文本文件,其中包含Jenkins Pipeline的定义,并已签入源代码管理
    虽然用于定义管道的脚本语法和jenkinsfile类似,但通常认为在项目中定义管道Jenkinsfile并检查源代码管理是最佳实践。
  • 为所有分支和请求自动创建一个管道构建过程。
    管道上的代码审查/迭代。
    审核追踪管道

8.什么是Blue Ocean

  • Blue Ocean是pipeline的可视化UI。同时他兼容经典的自由模式的job。Jenkins Pipeline从头开始设计,但仍与自由式作业兼容,Blue Ocean减少了经典模式下的混乱并为团队中的每个成员增加了清晰度。Blue Ocean的主要特点包括:

  • 连续交付(CD)管道的复杂可视化,可以让您快速直观地理解管道状态。
    管道编辑器 - 通过引导用户通过直观和可视化的过程来创建管道,从而使管道的创建变得平易近人。
    个性化以适应团队中每个成员的基于角色的需求。
    在需要干预和/或出现问题时确定精确度。Blue Ocean显示的标注了关键步骤,促进异常处理和提高生产力。

9.如何在jenkins中备份和复制文件

  • 创建备份,需要做的就是定期备份JENKINS_HOME目录。 这包含所有构建作业配置,从属节点配置以及构建历史记录。 要创建Jenkins设置的备份,只需复制此目录。

10.jenkins的优势是什么

  • Jenkins的优势包括:
    在开发环境的早期阶段, 错误跟踪很容易。
    提供大量的插件支持。
    对代码的迭代改进。
    构建失败会在集成阶段进行缓存。
    对于每个代码提交更改, 都会生成一个自动生成报告通知。
    为了将构建报告的成功或失败通知开发人员, 它与LDAP邮件服务器集成在一起。
    实现持续集成的敏捷开发和测试驱动的开发。
    通过简单的步骤, 即可自动完成maven发布项目。

11.Jenkins主要整合了两个组成部分?

  • Jenkins与以下两个组件集成在一起:
    GIT, SVN等版本控制系统,并构建诸如Apache Maven之类的工具。

12.Jenkins中一些由用的插件

  • 下面我将提到一些重要插件:
    Maven 2 project
    Amazon EC2
    HTML publisher
    Copy artifact
    Join
    Green Balls

TestNG

  • TestNG是用于单元测试和自动化测试的最广泛使用的测试框架之一。

问题 1 什么是TestNG?

  • 答。TestNG(NG for Next Generation)是一个测试框架,可以与 selenium 或任何其他自动化工具集成,以提供多种功能,如断言、报告、并行测试执行等。

问题 2 TestNG 有哪些优点?答。以下是TestNG的优点-

  • TestNG 提供了不同的断言来帮助检查预期和实际结果。
  • 它提供测试方法的并行执行。
  • 我们可以在 TestNG 中定义一种测试方法对其他测试方法的依赖。
  • 我们可以为 Selenium 中的测试方法指定优先级。
  • 它允许将测试方法分组到测试组中。
  • 它允许使用@DataProvider 注释进行数据驱动的测试。
  • 它具有对报告的固有支持。
  • 它支持使用@Parameters 注释参数化测试用例。

问题 3 TestNG 与 Selenium WebDriver 有何不同?

  • 答。Selenium 是一种自动化工具,我们可以使用它来自动化基于 Web 的应用程序。为了在测试自动化套件中添加测试功能——Selenium 与 TestNG 结合在一起。使用 TestNG,我们可以在我们的自动化套件中拥有不同的功能,例如不同类型的断言、报告、并行执行和参数化等。简而言之,对于进行自动化测试,Selenium有助于“自动化”,而 TestNG 有助于“测试”功能.

问题 4 testng.xml文件有什么用?

  • 答。testng.xml 文件用于配置整个测试套件。在 testng.xml 文件中,我们可以创建测试套件、创建测试组、标记并行执行的测试、添加侦听器以及将参数传递给测试脚本。我们还可以使用这个 testng.xml 文件从命令提示符/终端或 Jenkins 触发测试套件。

问题 5 我们如何对测试用例进行分组,例如 Sanity 套件、回归套件等的单独测试用例?

  • 答。使用TestNG 中的组属性,我们可以将测试方法分配给不同的组。

//Test method belonging to sanity suite only

问题 6 我们如何通过 testng.xml 文件排除测试方法的执行?

  • 答。使用testng.xml 文件中的exclude标记,我们可以从执行中排除特定的测试方法。

问题 7 有哪些常用的TestNG注解?

  • 答。常用的TestNG注解是-

@Test – @Test 注释将方法标记为测试方法。
@BeforeSuite – 在此套件中的所有测试运行之前,带注释的方法只会运行一次。
@AfterSuite – 在此套件中的所有测试都运行后,带注释的方法将只运行一次。
@BeforeClass – 在当前类中的第一个测试方法被调用之前,带注释的方法只会运行一次。
@AfterClass – 在当前类中的所有测试方法都运行后,带注释的方法将只运行一次。
@BeforeTest – 带注释的方法将在任何属于 标签内的类的测试方法运行之前运行。
@AfterTest – 注释方法将在属于 标签内的类的所有测试方法运行后运行。
@ BeforeMethod -被注释的方法将通过@Test注释标记每个测试方法之前运行。
@ AfterMethod -被注释的方法将通过@Test注释标记每个测试方法之后运行。
@DataProvider – @DataProvider 注解用于将测试数据传递给测试方法。测试方法将根据通过数据提供者方法传递的数据行数运行。

问题 8 基于不同注解的测试方法的执行顺序是什么?

答。TestNG 中的测试方法遵循 Suite->Test->Class->Method 顺序结合 Before annotations->Test annotations->After annotations 顺序。所以,执行的顺序是:@BeforeSuite@BeforeTest@BeforeClass@BeforeMethod@Test@AfterMethod@AfterClass@AfterTest@AfterSuite

问题 9 TestNG 提供了哪些常见的断言?

  • 答。testNG 提供的一些常见断言是-
    assertEquals(String actual, String expected, String message) 和参数中的其他重载数据类型
    assertNotEquals(double data1, double data2, String message) 等参数中的重载数据类型
    assertFalse(布尔条件,字符串消息)
    assertTrue(布尔条件,字符串消息)
    assertNotNull(Object 对象)
    失败(布尔条件,字符串消息)
    真(字符串消息)

问题 10 我们如何禁用或阻止测试用例运行?

  • 答。通过将“启用”属性设置为 false,我们可以禁用测试方法的运行。

问题 11 我们如何使用 TestNG 是一种测试方法依赖于其他方法?

  • 答。在TestNG的@Test注解中使用dependsOnMethods参数,我们可以让一个测试方法只有在依赖测试方法成功执行后才能运行。
    @Test(dependsOnMethods = { “preTests” })

问题 12 我们如何在TestNG中设置测试用例的优先级?

  • 答。我们可以使用@Test 注释中的“priority”参数来定义测试用例的优先级。优先级值较低的测试将首先执行。例子
    @Test(priority=1)

问题 13 TestNG 中测试用例的默认优先级是多少?

  • 答。未指定时,测试的默认优先级为整数值 0。因此,如果我们有一个优先级为 1 的测试用例和一个没有任何优先级的测试用例,那么将首先执行没有任何优先级值的测试(默认值为 0 并且测试优先级较低的先执行)。

问题 14 我们如何在循环中多次运行测试方法(不使用任何数据提供者)?

  • 答。使用 invocationCount 参数并将其值设置为整数值,使测试方法在循环中运行 n 次。

@Test(invocationCount = 10)

问题 15 什么是线程池大小?我们如何使用它?

  • 答。所述 threadPoolSize属性指定的线程的数目被分配到的测试方法。这与invocationCount 属性结合使用 。线程数将除以 invocationCount 属性中指定的测试方法的迭代次数。
    @Test(threadPoolSize = 5, invocationCount = 10)

问题 16 TestNG中的软断言和硬断言有什么区别?

  • 答。软断言 (SoftAssert) 允许我们在一个测试方法中拥有多个断言,即使断言失败,测试方法仍会继续执行剩余的测试。最后可以使用 softAssert.assertAll() 方法整理所有断言的结果。
    @Test
    在这里,即使第一个断言失败,测试仍将继续执行并在第二个断言下方打印消息。另一方面,硬断言是 TestNG 提供的常用断言。在任何失败的情况下硬断言的情况下,测试执行停止,阻止执行测试方法中的任何进一步步骤。

问题 17 如果 testNG 测试未在指定时间内执行,如何使该测试失败?

  • 答。我们可以使用 @Test 注解的timeOut属性。分配给此 timeOut 属性的值将用作上限,如果测试未在此时间范围内执行,则它将因 timeOut 异常而失败。

@Test(timeOut = 1000)

问题 18 我们如何有条件地跳过测试用例?

  • 答。使用 SkipException,我们可以有条件地跳过测试用例。抛出skipException时,测试方法在测试执行报告中被标记为跳过,抛出异常后的任何语句都不会被执行。

问题 19 即使它所依赖的测试方法或组失败或被跳过,我们如何确保测试方法运行?

  • 答。使用@Test 注释的“alwaysRun”属性,我们可以确保即使它所依赖的测试方法或组失败或被跳过,测试方法也会运行。
    在这里,即使 parentTest 失败,dependentTest 也不会被跳过,而是会因为“alwaysRun=true”而被执行。如果我们从@Test 中删除“alwaysRun=true”属性,那么报告将显示一个失败和一个跳过的测试,而不会尝试运行dependentTest 方法。

问题 20 我们如何使用 TestNG 将参数传递给测试脚本?

  • 答。使用@Parameter 注释和testng.xml 中的“parameter”标签,我们可以将参数传递给测试脚本。

问题 21 我们如何使用 TestNG 创建数据驱动的框架?

  • 答。使用@DataProvider,我们可以创建一个数据驱动的框架,其中数据被传递到关联的测试方法,并针对从@DataProvider 方法传递的不同测试数据值进行多次迭代测试。使用@DataProvider 批注的方法返回对象的二维数组。

问题 22 TestNG中@Listener注解有什么用?

  • 答。TestNG 为我们提供了不同类型的侦听器,我们可以使用这些侦听器在事件被触发时执行一些操作。通常,testNG 侦听器用于配置报告和日志记录。testNG 中使用最广泛的侦听器之一是 ITestListener 接口。它有 onTestSuccess、onTestFailure、onTestSkipped 等方法。我们需要实现这个接口,创建一个我们自己的监听器类。在使用@Listener 注释之后, 我们可以使用指定对于特定测试类应该使用我们定制的侦听器类。
    @Listeners(PackageName.CustomizedListenerClassName.class)

问题 23 TestNG中@Factory注解有什么用?

  • 答。@Factory 注释有助于测试用例的动态执行。使用@Factory 注释,我们可以在运行时将参数传递给整个测试类。传递的参数可以被该类的一个或多个测试方法使用。示例 - 有两个类 TestClass 和 TestFactory 类。由于@Factory 注解,TestClass 类中的测试方法将使用数据“k1”和“k2”运行两次

问题 24 @Factory 和 @DataProvider 注解有什么区别?

  • 答。@Factory 方法创建测试类的实例,并使用不同的数据集运行该类中的所有测试方法。而@DataProvider 绑定到单个测试方法并多次运行特定方法。

问题 25 我们如何使用 TestNG 并行运行测试用例?

  • 答。为了并行运行测试,只需将这两个键值对添加到 testng.xml 文件的套件标记中 。
    并行=“{方法/测试/类}”
    thread-count=“{您要同时运行的线程数}”。

【TestNG】TestNG基础

Thrift协议

RPC接口和Thrift协议

  • 1.1 从网络协议来说,Http协议与Thrift同属于应用层, 他们的底层都是tcp协议。
  • 1.2 RPC(Remote Procedure Call)是远程过程调用,它是一种通过网络从du远程计算机程序上请求服务,顾而不需要了解底层网络技术的协议。
  • 1.3 thrift是一种rpc常用的通信协议,它使用idl定义rpc方法和数据结构,使用thrift编译器根据idl定义生成不同语言的客户端代码和服务端代码,由这些生成的代码实现远程方法的调用和调用参数的解析。

Thrift和Http的区别

  • 2.1 传输协议:
    RPC:可以基于HTTP协议,也可以基于TCP协议
    HTTP:基于HTTP协议
  • 2.2 传输效率:
    RPC:使用自定义的TCP协议,可以让请求报文体积更小,或者使用HTTP2协议,也可以很好的减小报文体积,提高传输效率
    HTTP:如果是基于http1.1的协议,请求中会包含很多无用的内容,如果是基于HTTP2.0,那么简单的封装下可以作为一个RPC来使用,这时标准的RPC框架更多的是服务治理。
  • 2.3 性能消耗:
    RPC:可以基于thrift实现高效的二进制传输
    HTTP:大部分是基于json实现的,字节大小和序列化耗时都比thrift要更消耗性能
  • 2.4 负载均衡:
    RPC:基本自带了负载均衡策略
    HTTP:需要配置Nginx、HAProxy配置
  • 2.5 服务治理:(下游服务新增,重启,下线时如何不影响上游调用者)
      RPC:能做到自动通知,不影响上游
      HTTP:需要事先通知,如修改NGINX配置。

Thrift的优点

  • Thrift协议我们可以精简请求的数据,来尽可能少的传输我们的数据。当前,rpc也可以通过http协议来进行传输。Thrift默认提供了数据与实体类的转换,不需要我们显示的进行数据与实体类的转换。一般来说,RPC服务主要是针对大型企业的,传输快,更高效。
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值