查看原文
其他

银行软开,不难!

小林coding 小林coding 2024-01-01

图解学习网站:xiaolincoding.com

大家好,我是小林。

之前有同学问我银行软开岗位的面试要怎么准备?难度如何?

银行的面试跟互联网公司的面试还是有区别,银行除了技术面试之外,还会有结构化面试、无领导讨论的面试问题,这类形式主要是考察同学们的软实力,以及解决问题的思路。

那针对技术面试,银行也会问八股文,但是问的不多,而且难度相对互联网公司面试不要太简单了,所以有学历优势的同学,面银行难度不大,只需要准备好一些简单八股文基本都能应对技术面试了,不用学太多内容。

银行开发主要是Java后端,学C++的同学也可以面银行,面试偶尔会出几个C++八股文,但是需要重点深入学习MySQL和网络这些。

这次主要分享邮储、工商银行、中信银行的技术面试问题,范围的考察主要是 Java(基础、集合、多线程)、MySQL(索引+事务)、计算机网络(HTTP、TCP),给准备面试银行的同学做一个参考。

邮储

抽象类和接口的异同?

相同点:

  • 都不能被实例化,接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。

不同点:

  • 实现方式:实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
  • 方法方式:接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
  • 访问修饰符:接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。
  • 变量:抽象类可以包含实例变量和静态变量,而接口只能包含常量(即静态常量)。

线程池创建的几个参数,介绍一下?

线程池是为了减少频繁的创建线程和销毁线程带来的性能损耗。

线程池分为核心线程池,线程池的最大容量,还有等待任务的队列,提交一个任务,如果核心线程没有满,就创建一个线程,如果满了,就是会加入等待队列,如果等待队列满了,就会增加线程,如果达到最大线程数量,如果都达到最大线程数量,就会按照一些丢弃的策略进行处理。

图片

线程池的构造函数有7个参数:

图片

  • corePoolSize:线程池核心线程数量。默认情况下,线程池中线程的数量如果 <= corePoolSize,那么即使这些线程处于空闲状态,那也不会被销毁。
  • maximumPoolSize:线程池中最多可容纳的线程数量。当一个新任务交给线程池,如果此时线程池中有空闲的线程,就会直接执行,如果没有空闲的线程且当前线程池的线程数量小于corePoolSize,就会创建新的线程来执行任务,否则就会将该任务加入到阻塞队列中,如果阻塞队列满了,就会创建一个新线程,从阻塞队列头部取出一个任务来执行,并将新任务加入到阻塞队列末尾。如果当前线程池中线程的数量等于maximumPoolSize,就不会创建新线程,就会去执行拒绝策略。
  • keepAliveTime:当线程池中线程的数量大于corePoolSize,并且某个线程的空闲时间超过了keepAliveTime,那么这个线程就会被销毁。
  • unit:就是keepAliveTime时间的单位。
  • workQueue:工作队列。当没有空闲的线程执行新任务时,该任务就会被放入工作队列中,等待执行。
  • threadFactory:线程工厂。可以用来给线程取名字等等
  • handler:拒绝策略。当一个新任务交给线程池,如果此时线程池中有空闲的线程,就会直接执行,如果没有空闲的线程,就会将该任务加入到阻塞队列中,如果阻塞队列满了,就会创建一个新线程,从阻塞队列头部取出一个任务来执行,并将新任务加入到阻塞队列末尾。如果当前线程池中线程的数量等于maximumPoolSize,就不会创建新线程,就会去执行拒绝策略。

get,post 有什么区别?

根据 RFC 规范,GET 的语义是从服务器获取指定的资源,这个资源可以是静态的文本、页面、图片视频等。GET 请求的参数位置一般是写在 URL 中,URL 规定只能支持 ASCII,所以 GET 请求的参数只允许 ASCII 字符 ,而且浏览器会对 URL 的长度有限制(HTTP协议本身对 URL长度并没有做任何规定)。

比如,你打开我的文章,浏览器就会发送 GET 请求给服务器,服务器就会返回文章的所有文字及资源。

GET 请求

根据 RFC 规范,POST 的语义是根据请求负荷(报文body)对指定的资源做出处理,具体的处理方式视资源类型而不同。POST 请求携带数据的位置一般是写在报文 body 中,body 中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对 body 大小做限制。

比如,你在我文章底部,敲入了留言后点击「提交」(暗示你们留言),浏览器就会执行一次 POST 请求,把你的留言文字放进了报文 body 里,然后拼接好 POST 请求头,通过 TCP 协议发送给服务器。

POST 请求

http有状态吗?

是无状态的,本身不维护会话状态。

为了实现状态管理和会话跟踪,常用的方法是使用Cookie和Session。

Cookie 技术

通过在HTTP请求和响应中添加Cookie头部,服务器可以将一些信息保存在客户端,以便在后续的请求中进行识别和关联。而Session则是服务器端根据Cookie中的标识信息来维护会话状态的一种机制。

session和cookie的区别是什么?

  • 作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
  • 存取方式的不同,Cookie只能保存 ASCII,Session可以存任意数据类型,比如UserId等。
  • 有效期不同,Cookie可设置为长时间保持,比如默认登录功能功能,Session一般有效时间较短,客户端关闭或者Session超时都会失效。
  • 隐私策略不同,Cookie存储在客户端,信息容易被窃取;Session存储在服务端,相对安全一些。
  • 存储大小不同, 单个Cookie 保存的数据不能超过 4K,Session可存储数据远高于Cookie。

mysql两种引擎区别?

mysql 的存储引擎主要有myisam,innodb两个存储引擎。

  • 事务支持:InnoDB支持事务,而MyISAM不支持。InnoDB中每一条SQL语言都默认封装成事务,自动提交,这可能会影响执行速度。因此,最好将多条SQL语言放在BEGIN和COMMIT之间,组成一个事务。
  • 外键支持:InnoDB支持外键,而MyISAM不支持。对于一个包含外键的InnoDB表,如果尝试将其转为MyISAM,将会失败。
  • 索引和数据存储:InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件和(主键)索引绑定在一起,必须要有主键,通过主键索引效率很高。MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。
  • 数据文件格式:MyISAM在磁盘存储上有三个文件,每个文件名以表名开头,扩展名指出文件类型,分别是.frm(用于存储表的定义),.MYD(用于存放数据)和.MYI(用于存放表索引)。InnoDB有两种存储方式:共享表空间存储和多表空间存储。两种存储方式的表结构和MyISAM一样,以表名开头,扩展名为.frm。如果使用共享表空间,那么所有表的数据文件和索引文件都保存在一个表空间里。如果使用多表空间,那么每个表都有一个表空间文件用于存储每个表的数据和索引。
  • 数据安全不同:InnoDB 支持崩溃恢复和数据恢复,而 MyISAM 不支持。如果 MySQL 崩溃了或者发生意外故障,InnoDB 可以通过恢复日志来恢复数据。

索引知道吗?

知道的,就是帮助存储引擎快速获取数据的一种数据结构,形象的说就是索引是数据的目录

可以按照四个角度来分类索引。

  • 按「数据结构」分类:B+tree索引、Hash索引、Full-text索引
  • 按「物理存储」分类:聚簇索引(主键索引)、二级索引(辅助索引)
  • 按「字段特性」分类:主键索引、唯一索引、普通索引、前缀索引
  • 按「字段个数」分类:单列索引、联合索引

工商银行

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

主要区别如下:

  • 抽象程度:面向过程编程以过程(函数)为中心,侧重于解决问题的步骤和流程。而面向对象编程以对象为中心,侧重于将问题分解为对象,并通过对象之间的交互来解决问题。面向对象编程提供了更高的抽象级别,可以更自然地模拟现实世界中的事物与关系,更加强调代码的可维护性和重用性。
  • 封装性:面向过程编程将数据和相关的操作封装在不同的函数中,但没有明确的机制来保护数据和函数的访问。而面向对象编程通过类的定义,将数据(属性)和函数(方法)封装在一起,并通过访问修饰符(如public、private)来控制对数据和函数的访问权限,实现了更好的封装性。
  • 继承和多态:面向对象编程提供了继承和多态的特性,可以通过继承机制创建新的类,并重用已有类的属性和方法。同时,多态允许不同的对象对相同的消息作出不同的响应。这些特性使得面向对象编程更加灵活和易于扩展。

tcp和udp区别是什么?

1. 连接

  • TCP 是面向连接的传输层协议,传输数据前先要建立连接。
  • UDP 是不需要连接,即刻传输数据。

2. 服务对象

  • TCP 是一对一的两点服务,即一条连接只有两个端点。
  • UDP 支持一对一、一对多、多对多的交互通信

3. 可靠性

  • TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按序到达。
  • UDP 是尽最大努力交付,不保证可靠交付数据。但是我们可以基于 UDP 传输协议实现一个可靠的传输协议,比如 QUIC 协议。

4. 拥塞控制、流量控制

  • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
  • UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。

5. 首部开销

  • TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
  • UDP 首部只有 8 个字节,并且是固定不变的,开销较小。

6. 传输方式

  • TCP 是流式传输,没有边界,但保证顺序和可靠。
  • UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。

7. 分片不同

  • TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
  • UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。

TCP 和 UDP 应用场景:

由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:

  • FTP 文件传输;
  • HTTP / HTTPS;

由于 UDP 面向无连接,它可以随时发送数据,再加上 UDP 本身的处理既简单又高效,因此经常用于:

  • 包总量较少的通信,如 DNSSNMP 等;
  • 视频、音频等多媒体通信;
  • 广播通信;

数据库的一致性是什么?

一致性:是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。

比如,用户 A 和用户 B 在银行分别有 800 元和 600 元,总共 1400 元,用户 A 给用户 B 转账 200 元,分为两个步骤,从 A 的账户扣除 200 元和对 B 的账户增加 200 元。一致性就是要求上述步骤操作后,最后的结果是用户 A 还有 600 元,用户 B 有 800 元,总共 1400 元,而不会出现用户 A 扣除了 200 元,但用户 B 未增加的情况(该情况,用户 A 和 B 均为 600 元,总共 1200 元)。

一致性怎么实现?

mysql 事务的实现原理:

  • 持久性是通过 redo log (重做日志)来保证的;
  • 原子性是通过 undo log(回滚日志) 来保证的;
  • 隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;
  • 一致性则是通过持久性+原子性+隔离性来保证;

事务的隔离级别有哪些?

这四个隔离级别如下:

  • 读未提交,指一个事务还没提交时,它做的变更就能被其他事务看到;
  • 读提交,指一个事务提交之后,它做的变更才能被其他事务看到;
  • 可重复读,指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别
  • 串行化;会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;

按隔离水平高低排序如下:

图片

针对不同的隔离级别,并发事务时可能发生的现象也会不同。

图片

也就是说:

  • 在「读未提交」隔离级别下,可能发生脏读、不可重复读和幻读现象;
  • 在「读提交」隔离级别下,可能发生不可重复读和幻读现象,但是不可能发生脏读现象;
  • 在「可重复读」隔离级别下,可能发生幻读现象,但是不可能脏读和不可重复读现象;
  • 在「串行化」隔离级别下,脏读、不可重复读和幻读现象都不可能会发生。

mysql默认隔离机制是什么?

可重复读隔离级别

Redis的基本数据类型有哪些?

Redis 提供了丰富的数据类型,常见的有五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)

img
img

随着 Redis 版本的更新,后面又支持了四种数据类型:BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。Redis 五种数据类型的应用场景:

  • String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
  • List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。
  • Hash 类型:缓存对象、购物车等。
  • Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
  • Zset 类型:排序场景,比如排行榜、电话和姓名排序等。

Redis 后续版本又支持四种数据类型,它们的应用场景如下:

  • BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;
  • HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
  • GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
  • Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。

中信银行

C++中堆和栈的区别

申请方式

  • 栈:由系统自动分配。例如,声明在函数中一个局部变量 int b,系统自动在栈中为b开辟空间。

  • 堆:需要程序员自己申请,并指明大小,在C语言中通过malloc函数,如 p1 = (char *)malloc(10);,在C++中用new运算符,如p2 = new char[20]

申请后系统的响应

  • 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
  • 堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

申请大小的限制

  • 栈:栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,操作系统中,栈的大小是几MB,如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

  • 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

生命周期

  • 栈:栈的内存管理是自动的,变量的内存会在其作用域结束时自动释放
  • 堆:堆的内存管理需要手动进行,需要使用new关键字分配内存,并使用delete或delete[]关键字释放内存,否则会导致内存泄漏。

C++的编译过程介绍一下?

C++的编译过程经过了预处理、编译、汇编和链接四个主要阶段:

img
  • 预处理:预处理阶段会对源代码进行处理,主要包括展开宏定义、处理条件编译指令(如#include、#define、#ifdef等)以及删除注释等。预处理的结果是生成一个经过宏展开和条件处理后的纯C++源代码文件。

  • 编译(Compilation):编译阶段将预处理后的源代码翻译为汇编语言,生成汇编代码。编译器会进行词法分析、语法分析和语义分析,检查代码的正确性,并生成中间代码表示。

  • 汇编:汇编阶段将汇编代码转换为机器可以执行的目标文件。汇编器会将汇编代码转化为机器指令,并生成与机器硬件平台相关的目标文件(通常以".obj"或".o"为扩展名)。

  • 链接:链接阶段将目标文件与其他必要的库文件链接在一起,生成可执行程序。链接器会解析目标文件中的符号引用,将其与其他目标文件或库文件中的符号定义进行匹配,最终生成一个完整的可执行文件。在链接阶段,还会进行地址重定位、符号解析、符号表生成等操作,确保程序的正确执行。

内存泄露怎么避免?

可以使用使用智能指针,C++提供了智能指针(如std::shared_ptr、std::unique_ptr等),可以自动管理动态分配的内存。智能指针利用了RAII(资源获取即初始化)的原则,在对象生命周期结束时自动释放内存,避免了显式调用delete的繁琐和遗。

也可以使用内存泄漏检测工具(如Valgrind等)来分析程序,在程序运行过程中检测内存泄漏,并及时修复。

mysql数据库哈希索引、B+树索引的区别?

主要区别有以下几点:

  • 数据结构:哈希索引使用哈希表的结构,将索引值通过哈希函数映射为一个唯一的哈希码,而B+树索引使用平衡树的结构,将索引值按照大小顺序组织成一个树形结构。
  • 查找方式:哈希索引使用哈希函数计算出哈希码,直接通过哈希码进行查找,查找速度非常快,时间复杂度为O(1);而B+树索引通过从根节点到叶子节点的遍历来进行查找,时间复杂度一般为O(logN)。
  • 范围查询:哈希索引适合等值查询,即根据索引值精确查找;而B+树索引支持范围查询,可以根据索引值的大小范围进行查找。

每一种存储引擎支持的索引类型不一定相同,我在表中总结了 MySQL 常见的存储引擎 InnoDB、MyISAM 和 Memory 分别支持的索引类型。

img

InnoDB 是在 MySQL 5.5 之后成为默认的 MySQL 存储引擎,B+Tree 索引类型也是 MySQL 存储引擎采用最多的索引类型。

索引失效有哪些例子?

6 种会发生索引失效的情况:

  • 当我们使用左或者左右模糊匹配的时候,也就是 like %xx 或者 like %xx%这两种方式都会造成索引失效;
  • 当我们在查询条件中对索引列使用函数,就会导致索引失效。
  • 当我们在查询条件中对索引列进行表达式计算,也是无法走索引的。
  • MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较。如果字符串是索引列,而条件语句中的输入参数是数字的话,那么索引列会发生隐式类型转换,由于隐式类型转换是通过 CAST 函数实现的,等同于对索引列使用了函数,所以就会导致索引失效。
  • 联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配,否则就会导致索引失效。
  • 在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。

行锁怎么实现的?

InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。

前面也提到,普通的 select 语句是不会对记录加锁的,因为它属于快照读。如果要在查询时对记录加行锁,可以使用下面这两个方式,这种查询会加锁的语句称为锁定读

//对读取的记录加共享锁
select ... lock in share mode;

//对读取的记录加独占锁
select ... for update;

上面这两条语句必须在一个事务中,因为当事务提交了,锁就会被释放,所以在使用这两条语句的时候,要加上 begin、start transaction 或者 set autocommit = 0。

共享锁(S锁)满足读读共享,读写互斥。独占锁(X锁)满足写写互斥、读写互斥。

img

行级锁的类型主要有三类:

  • Record Lock,记录锁,也就是仅仅把一条记录锁上;
  • Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
  • Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

Record Lock

Record Lock 称为记录锁,锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的:

  • 当一个事务对一条记录加了 S 型记录锁后,其他事务也可以继续对该记录加 S 型记录锁(S 型与 S 锁兼容),但是不可以对该记录加 X 型记录锁(S 型与 X 锁不兼容);
  • 当一个事务对一条记录加了 X 型记录锁后,其他事务既不可以对该记录加 S 型记录锁(S 型与 X 锁不兼容),也不可以对该记录加 X 型记录锁(X 型与 X 锁不兼容)。

举个例子,当一个事务执行了下面这条语句:

mysql > begin;
mysql > select * from t_test where id = 1 for update;

就是对 t_test 表中主键 id 为 1 的这条记录加上 X 型的记录锁,这样其他事务就无法对这条记录进行修改了。

img

当事务执行 commit 后,事务过程中生成的锁都会被释放。

Gap Lock

Gap Lock 称为间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。

假设,表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。

img

间隙锁虽然存在 X 型间隙锁和 S 型间隙锁,但是并没有什么区别,间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻影记录而提出的

Next-Key Lock

Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

假设,表中有一个范围 id 为(3,5] 的 next-key lock,那么其他事务即不能插入 id = 4 记录,也不能修改 id = 5 这条记录。

img

所以,next-key lock 即能保护该记录,又能阻止其他事务将新纪录插入到被保护记录前面的间隙中。

next-key lock 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的

比如,一个事务持有了范围为 (1, 10] 的 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,就会被阻塞。

虽然相同范围的间隙锁是多个事务相互兼容的,但对于记录锁,我们是要考虑 X 型与 S 型关系,X 型的记录锁与 X 型的记录锁是冲突的。

什么是回表?

主键索引的 B+Tree 和二级索引的 B+Tree 区别如下:

  • 主键索引的 B+Tree 的叶子节点存放的是实际数据,所有完整的用户记录都存放在主键索引的 B+Tree 的叶子节点里;
  • 二级索引的 B+Tree 的叶子节点存放的是主键值,而不是实际数据。

我这里将前面的商品表中的 product_no (商品编码)字段设置为二级索引,那么二级索引的 B+Tree 如下图(图中叶子节点之间我画了单向链表,但是实际上是双向链表,原图我找不到了,修改不了,偷个懒我不重画了,大家脑补成双向链表就行)。

二级索引 B+Tree

其中非叶子的 key 值是 product_no(图中橙色部分),叶子节点存储的数据是主键值(图中绿色部分)。

如果我用 product_no 二级索引查询商品,如下查询语句:

select * from product where product_no = '0002';

会先检二级索引中的 B+Tree 的索引值(商品编码,product_no),找到对应的叶子节点,然后获取主键值,然后再通过主键索引中的 B+Tree 树查询到对应的叶子节点,然后获取整行数据。这个过程叫「回表」,也就是说要查两个 B+Tree 才能查到数据。如下图(图中叶子节点之间我画了单向链表,但是实际上是双向链表,原图我找不到了,修改不了,偷个懒我不重画了,大家脑补成双向链表就行):

回表

不过,当查询的数据是能在二级索引的 B+Tree 的叶子节点里查询到,这时就不用再查主键索引查,比如下面这条查询语句:

select id from product where product_no = '0002';

这种在二级索引的 B+Tree 就能查询到结果的过程就叫作「覆盖索引」,也就是只需要查一个 B+Tree 就能找到数据

TCP怎样实现可靠性?

TCP协议主要通过以下几点来保证传输可靠性:连接管理、序列号、确认应答、超时重传、流量控制、拥塞控制。

  • 连接管理:即三次握手和四次挥手。连接管理机制能够建立起可靠的连接,这是保证传输可靠性的前提。
  • 序列号:TCP将每个字节的数据都进行了编号,这就是序列号。序列号的具体作用如下:能够保证可靠性,既能防止数据丢失,又能避免数据重复。能够保证有序性,按照序列号顺序进行数据包还原。能够提高效率,基于序列号可实现多次发送,一次确认。
  • 确认应答:接收方接收数据之后,会回传ACK报文,报文中带有此次确认的序列号,用于告知发送方此次接收数据的情况。在指定时间后,若发送端仍未收到确认应答,就会启动超时重传。
  • 超时重传:超时重传主要有两种场景:数据包丢失:在指定时间后,若发送端仍未收到确认应答,就会启动超时重传,向接收端重新发送数据包。确认包丢失:当接收端收到重复数据(通过序列号进行识别)时将其丢弃,并重新回传ACK报文。
  • 流量控制:接收端处理数据的速度是有限的,如果发送方发送数据的速度过快,就会导致接收端的缓冲区溢出,进而导致丢包。为了避免上述情况的发生,TCP支持根据接收端的处理能力,来决定发送端的发送速度。这就是流量控制。流量控制是通过在TCP报文段首部维护一个滑动窗口来实现的。
  • 拥塞控制:拥塞控制就是当网络拥堵严重时,发送端减少数据发送。拥塞控制是通过发送端维护一个拥塞窗口来实现的。可以得出,发送端的发送速度,受限于滑动窗口和拥塞窗口中的最小值。拥塞控制方法分为:慢开始,拥塞避免、快重传和快恢复。

历史好文:

华为终于开奖了!全靠爱信等?

终于拿到阿里的小奖状!

有大厂实习,贼加分!

求你了,写简历用点心

继续滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存